michael@0: const EVENT_OBJECT_SHOW = 0x8002; michael@0: const EVENT_OBJECT_HIDE = 0x8003; michael@0: const WINEVENT_OUTOFCONTEXT = 0; michael@0: const WINEVENT_SKIPOWNPROCESS = 2; michael@0: const QS_ALLINPUT = 0x04FF; michael@0: const INFINITE = 0xFFFFFFFF; michael@0: const WAIT_OBJECT_0 = 0; michael@0: const WAIT_TIMEOUT = 258; michael@0: const PM_NOREMOVE = 0; michael@0: michael@0: function DialogWatcher(titleText, onDialogStart, onDialogEnd) { michael@0: this.titleText = titleText; michael@0: this.onDialogStart = onDialogStart; michael@0: this.onDialogEnd = onDialogEnd; michael@0: } michael@0: michael@0: DialogWatcher.prototype.init = function() { michael@0: this.hwnd = undefined; michael@0: if (!this.user32) { michael@0: this.user32 = ctypes.open("user32.dll"); michael@0: } michael@0: if (!this.findWindow) { michael@0: this.findWindow = user32.declare("FindWindowW", michael@0: ctypes.winapi_abi, michael@0: ctypes.uintptr_t, michael@0: ctypes.jschar.ptr, michael@0: ctypes.jschar.ptr); michael@0: } michael@0: if (!this.winEventProcType) { michael@0: this.winEventProcType = ctypes.FunctionType(ctypes.stdcall_abi, michael@0: ctypes.void_t, michael@0: [ctypes.uintptr_t, michael@0: ctypes.uint32_t, michael@0: ctypes.uintptr_t, michael@0: ctypes.long, michael@0: ctypes.long, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t]).ptr; michael@0: } michael@0: if (!this.setWinEventHook) { michael@0: this.setWinEventHook = user32.declare("SetWinEventHook", michael@0: ctypes.winapi_abi, michael@0: ctypes.uintptr_t, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t, michael@0: ctypes.uintptr_t, michael@0: this.winEventProcType, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t); michael@0: } michael@0: if (!this.unhookWinEvent) { michael@0: this.unhookWinEvent = user32.declare("UnhookWinEvent", michael@0: ctypes.winapi_abi, michael@0: ctypes.int, michael@0: ctypes.uintptr_t); michael@0: } michael@0: if (!this.pointType) { michael@0: this.pointType = ctypes.StructType("tagPOINT", michael@0: [ { "x": ctypes.long }, michael@0: { "y": ctypes.long } ] ); michael@0: } michael@0: if (!this.msgType) { michael@0: this.msgType = ctypes.StructType("tagMSG", michael@0: [ { "hwnd": ctypes.uintptr_t }, michael@0: { "message": ctypes.uint32_t }, michael@0: { "wParam": ctypes.uintptr_t }, michael@0: { "lParam": ctypes.intptr_t }, michael@0: { "time": ctypes.uint32_t }, michael@0: { "pt": this.pointType } ] ); michael@0: } michael@0: if (!this.peekMessage) { michael@0: this.peekMessage = user32.declare("PeekMessageW", michael@0: ctypes.winapi_abi, michael@0: ctypes.int, michael@0: this.msgType.ptr, michael@0: ctypes.uintptr_t, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t); michael@0: } michael@0: if (!this.msgWaitForMultipleObjects) { michael@0: this.msgWaitForMultipleObjects = user32.declare("MsgWaitForMultipleObjects", michael@0: ctypes.winapi_abi, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t, michael@0: ctypes.uintptr_t.ptr, michael@0: ctypes.int, michael@0: ctypes.uint32_t, michael@0: ctypes.uint32_t); michael@0: } michael@0: if (!this.getWindowTextW) { michael@0: this.getWindowTextW = user32.declare("GetWindowTextW", michael@0: ctypes.winapi_abi, michael@0: ctypes.int, michael@0: ctypes.uintptr_t, michael@0: ctypes.jschar.ptr, michael@0: ctypes.int); michael@0: } michael@0: if (!this.messageBox) { michael@0: // Handy for debugging this code michael@0: this.messageBox = user32.declare("MessageBoxW", michael@0: ctypes.winapi_abi, michael@0: ctypes.int, michael@0: ctypes.uintptr_t, michael@0: ctypes.jschar.ptr, michael@0: ctypes.jschar.ptr, michael@0: ctypes.uint32_t); michael@0: } michael@0: }; michael@0: michael@0: DialogWatcher.prototype.getWindowText = function(hwnd) { michael@0: var bufType = ctypes.ArrayType(ctypes.jschar); michael@0: var buffer = new bufType(256); michael@0: michael@0: if (this.getWindowTextW(hwnd, buffer, buffer.length)) { michael@0: return buffer.readString(); michael@0: } michael@0: }; michael@0: michael@0: DialogWatcher.prototype.processWindowEvents = function(timeout) { michael@0: var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) { michael@0: var nhwnd = Number(hwnd) michael@0: if (event == EVENT_OBJECT_SHOW) { michael@0: if (nhwnd == self.hwnd) { michael@0: // We've already picked up this event via FindWindow michael@0: return; michael@0: } michael@0: var windowText = self.getWindowText(hwnd); michael@0: if (windowText == self.titleText && self.onDialogStart) { michael@0: self.hwnd = nhwnd; michael@0: self.onDialogStart(nhwnd); michael@0: } michael@0: } else if (event == EVENT_OBJECT_HIDE && nhwnd == self.hwnd && self.onDialogEnd) { michael@0: self.onDialogEnd(); michael@0: self.hwnd = null; michael@0: } michael@0: }; michael@0: var self = this; michael@0: var callback = this.winEventProcType(function(hook, event, hwnd, idObject, michael@0: idChild, dwEventThread, michael@0: dwmsEventTime) { michael@0: onWinEvent(self, hook, event, hwnd, idObject, idChild, dwEventThread, michael@0: dwmsEventTime); michael@0: } ); michael@0: var hook = this.setWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, michael@0: 0, callback, 0, 0, michael@0: WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); michael@0: if (!hook) { michael@0: return; michael@0: } michael@0: // Check if the window is already showing michael@0: var hwnd = this.findWindow(null, this.titleText); michael@0: if (hwnd && hwnd > 0) { michael@0: this.hwnd = Number(hwnd); michael@0: if (this.onDialogStart) { michael@0: this.onDialogStart(this.hwnd); michael@0: } michael@0: } michael@0: michael@0: if (!timeout) { michael@0: timeout = INFINITE; michael@0: } michael@0: michael@0: var waitStatus = WAIT_OBJECT_0; michael@0: var expectingStart = this.onDialogStart && this.hwnd === undefined; michael@0: var startWaitTime = Date.now(); michael@0: while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) { michael@0: waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ? michael@0: INFINITE : timeout, 0); michael@0: if (waitStatus == WAIT_OBJECT_0) { michael@0: var msg = new this.msgType; michael@0: this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE); michael@0: } michael@0: if (waitStatus == WAIT_TIMEOUT || (Date.now() - startWaitTime) >= timeout) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: this.unhookWinEvent(hook); michael@0: // Returns true if the hook was successful, something was found, and we never timed out michael@0: return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0; michael@0: }; michael@0: