90 lines
2.6 KiB
TypeScript
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;
|
|
}
|
|
}
|