poe2-bot/src-old/game/WindowManager.ts
2026-02-13 01:12:11 -05:00

90 lines
2.6 KiB
TypeScript

import koffi from 'koffi';
import { logger } from '../util/logger.js';
// Win32 types
const HWND = 'int';
const BOOL = 'bool';
const RECT = koffi.struct('RECT', {
left: 'long',
top: 'long',
right: 'long',
bottom: 'long',
});
// Load user32.dll
const user32 = koffi.load('user32.dll');
const FindWindowW = user32.func('FindWindowW', HWND, ['str16', 'str16']);
const SetForegroundWindow = user32.func('SetForegroundWindow', BOOL, [HWND]);
const ShowWindow = user32.func('ShowWindow', BOOL, [HWND, 'int']);
const BringWindowToTop = user32.func('BringWindowToTop', BOOL, [HWND]);
const GetForegroundWindow = user32.func('GetForegroundWindow', HWND, []);
const GetWindowRect = user32.func('GetWindowRect', BOOL, [HWND, koffi.out(koffi.pointer(RECT))]);
const IsWindow = user32.func('IsWindow', BOOL, [HWND]);
const keybd_event = user32.func('keybd_event', 'void', ['uint8', 'uint8', 'uint32', 'uint']);
const MapVirtualKeyW = user32.func('MapVirtualKeyW', 'uint32', ['uint32', 'uint32']);
// Constants
const SW_RESTORE = 9;
const VK_MENU = 0x12; // Alt key
const KEYEVENTF_KEYUP = 0x0002;
export class WindowManager {
private hwnd: number = 0;
constructor(private windowTitle: string) {}
findWindow(): number {
this.hwnd = FindWindowW(null as unknown as string, this.windowTitle);
if (this.hwnd === 0) {
logger.warn({ title: this.windowTitle }, 'Window not found');
} else {
logger.info({ title: this.windowTitle, hwnd: this.hwnd }, 'Window found');
}
return this.hwnd;
}
focusWindow(): boolean {
if (!this.hwnd || !IsWindow(this.hwnd)) {
this.findWindow();
}
if (!this.hwnd) return false;
// Restore if minimized
ShowWindow(this.hwnd, SW_RESTORE);
// Alt-key trick to bypass SetForegroundWindow restriction
const altScan = MapVirtualKeyW(VK_MENU, 0);
keybd_event(VK_MENU, altScan, 0, 0);
keybd_event(VK_MENU, altScan, KEYEVENTF_KEYUP, 0);
BringWindowToTop(this.hwnd);
const result = SetForegroundWindow(this.hwnd);
if (!result) {
logger.warn('SetForegroundWindow failed');
}
return result;
}
getWindowRect(): { left: number; top: number; right: number; bottom: number } | null {
if (!this.hwnd || !IsWindow(this.hwnd)) {
this.findWindow();
}
if (!this.hwnd) return null;
const rect = { left: 0, top: 0, right: 0, bottom: 0 };
const success = GetWindowRect(this.hwnd, rect);
if (!success) return null;
return rect;
}
isGameFocused(): boolean {
const fg = GetForegroundWindow();
return fg === this.hwnd && this.hwnd !== 0;
}
getHwnd(): number {
return this.hwnd;
}
}