From e66e34ef7b5ad61bda09067e6ea8f8990017b5de Mon Sep 17 00:00:00 2001 From: Vitaliy Margolen Date: Sat, 18 Mar 2006 11:26:37 -0700 Subject: [PATCH] dinput: Create single thread for mouse and keyboard hook. Put keyboard & mouse hook callbacks into separate thread. Move few global variables into object. Delete no longer used crit section. For hooks to work properly hook callback have to be in a thread with message loop. Some games create separate threads just to handle mouse and/or keyboard events that do not have message loop. --- dlls/dinput/device.c | 138 +++++++++++++++++++++++++++++++++++++++++++ dlls/dinput/device_private.h | 2 + dlls/dinput/keyboard.c | 132 ++++++++++++++++------------------------- dlls/dinput/mouse.c | 33 ++++------- 4 files changed, 202 insertions(+), 103 deletions(-) diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index 8cca6f5ca3a..40412ffb61f 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -32,9 +32,11 @@ #include "wine/unicode.h" #include "windef.h" #include "winbase.h" +#include "winuser.h" #include "winerror.h" #include "dinput.h" #include "device_private.h" +#include "dinput_private.h" WINE_DEFAULT_DEBUG_CHANNEL(dinput); @@ -898,3 +900,139 @@ HRESULT WINAPI IDirectInputDevice8WImpl_GetImageInfo(LPDIRECTINPUTDEVICE8W iface return DI_OK; } + +/****************************************************************************** + * DInput hook thread + */ + +static LRESULT CALLBACK dinput_hook_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static HHOOK kbd_hook, mouse_hook; + BOOL res; + + TRACE("got message %x %p %p\n", message, (LPVOID)wParam, (LPVOID)lParam); + switch (message) + { + case WM_USER+0x10: + if (wParam == WH_KEYBOARD_LL) + { + if (lParam) + { + if (kbd_hook) return 0; + kbd_hook = SetWindowsHookExW(WH_KEYBOARD_LL, (LPVOID)lParam, DINPUT_instance, 0); + return (LRESULT)kbd_hook; + } + else + { + if (!kbd_hook) return 0; + res = UnhookWindowsHookEx(kbd_hook); + kbd_hook = NULL; + return res; + } + } + else if (wParam == WH_MOUSE_LL) + { + if (lParam) + { + if (mouse_hook) return 0; + mouse_hook = SetWindowsHookExW(WH_MOUSE_LL, (LPVOID)lParam, DINPUT_instance, 0); + return (LRESULT)mouse_hook; + } + else + { + if (!mouse_hook) return 0; + res = UnhookWindowsHookEx(mouse_hook); + mouse_hook = NULL; + return res; + } + } + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + } + return DefWindowProcW(hWnd, message, wParam, lParam); +} + +static HANDLE signal_event; + +static DWORD WINAPI hook_thread_proc(void *param) +{ + static const WCHAR classW[]={'H','o','o','k','_','L','L','_','C','L',0}; + MSG msg; + WNDCLASSEXW wcex; + HWND hwnd; + + memset(&wcex, 0, sizeof(wcex)); + wcex.cbSize = sizeof(wcex); + wcex.lpfnWndProc = dinput_hook_WndProc; + wcex.lpszClassName = classW; + wcex.hInstance = GetModuleHandleW(0); + + if (!RegisterClassExW(&wcex)) ERR("Error registering window class\n"); + hwnd = CreateWindowExW(0, classW, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, 0); + *(HWND*)param = hwnd; + + SetEvent(signal_event); + if (hwnd) + { + while (GetMessageW(&msg, 0, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + else ERR("Error creating message window\n"); + + DestroyWindow(hwnd); + UnregisterClassW(wcex.lpszClassName, wcex.hInstance); + return 0; +} + +static CRITICAL_SECTION dinput_hook_crit; +static CRITICAL_SECTION_DEBUG dinput_critsect_debug = +{ + 0, 0, &dinput_hook_crit, + { &dinput_critsect_debug.ProcessLocksList, &dinput_critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": dinput_hook_crit") } +}; +static CRITICAL_SECTION dinput_hook_crit = { &dinput_critsect_debug, -1, 0, 0, 0, 0 }; + +static HWND get_thread_hwnd(void) +{ + static HANDLE hook_thread; + static HWND hook_thread_hwnd; + + EnterCriticalSection(&dinput_hook_crit); + if (!hook_thread) + { + DWORD tid; + HWND hwnd; + + signal_event = CreateEventW(NULL, FALSE, FALSE, NULL); + hook_thread = CreateThread(NULL, 0, hook_thread_proc, &hwnd, 0, &tid); + if (signal_event && hook_thread) + { + HANDLE handles[2]; + handles[0] = signal_event; + handles[1] = hook_thread; + WaitForMultipleObjects(2, handles, FALSE, INFINITE); + } + CloseHandle(signal_event); + + if (!(hook_thread_hwnd = hwnd)) + { + /* Thread failed to create window - reset things so we could try again later */ + CloseHandle(hook_thread); + hook_thread = 0; + } + } + LeaveCriticalSection(&dinput_hook_crit); + + return hook_thread_hwnd; +} + +HHOOK set_dinput_hook(int hook_id, LPVOID proc) +{ + return (HHOOK)SendMessageW(get_thread_hwnd(), WM_USER+0x10, (WPARAM)hook_id, (LPARAM)proc); +} diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h index 4f2347bcb6f..6c967e723fd 100644 --- a/dlls/dinput/device_private.h +++ b/dlls/dinput/device_private.h @@ -52,6 +52,8 @@ extern void fill_DataFormat(void *out, const void *in, DataFormat *df) ; extern DataFormat *create_DataFormat(const DIDATAFORMAT *wine_format, LPCDIDATAFORMAT asked_format, int *offset) ; extern void release_DataFormat(DataFormat *df) ; +extern HHOOK set_dinput_hook(int hook_id, LPVOID proc); + /* Used to fill events in the queue */ #define GEN_EVENT(offset,data,xtime,seq) \ { \ diff --git a/dlls/dinput/keyboard.c b/dlls/dinput/keyboard.c index aa6cbcbe387..9d6ea8c7670 100644 --- a/dlls/dinput/keyboard.c +++ b/dlls/dinput/keyboard.c @@ -52,7 +52,7 @@ struct SysKeyboardImpl IDirectInputImpl* dinput; - HANDLE hEvent; + HANDLE hEvent; /* SysKeyboardAImpl */ int acquired; int buffersize; /* set in 'SetProperty' */ @@ -78,73 +78,57 @@ static SysKeyboardImpl* current_lock = NULL; static BYTE DInputKeyState[WINE_DINPUT_KEYBOARD_MAX_KEYS]; /* array for 'GetDeviceState' */ -static CRITICAL_SECTION keyboard_crit; -static CRITICAL_SECTION_DEBUG critsect_debug = -{ - 0, 0, &keyboard_crit, - { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": keyboard_crit") } -}; -static CRITICAL_SECTION keyboard_crit = { &critsect_debug, -1, 0, 0, 0, 0 }; - -static LONG keyboard_users = 0; -static HHOOK keyboard_hook = NULL; - LRESULT CALLBACK KeyboardCallback( int code, WPARAM wparam, LPARAM lparam ) { - BYTE dik_code; - BOOL down; - DWORD timestamp; - KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam; - BYTE new_diks; + SysKeyboardImpl *This = (SysKeyboardImpl *)current_lock; + BYTE dik_code; + BOOL down; + KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam; + BYTE new_diks; - TRACE("(%d,%d,%ld)\n", code, wparam, lparam); + TRACE("(%d,%d,%ld)\n", code, wparam, lparam); - /** returns now if not HC_ACTION */ - if (code != HC_ACTION) return CallNextHookEx(keyboard_hook, code, wparam, lparam); + /* returns now if not HC_ACTION */ + if (code != HC_ACTION) return CallNextHookEx(0, code, wparam, lparam); - { dik_code = hook->scanCode; if (hook->flags & LLKHF_EXTENDED) dik_code |= 0x80; down = !(hook->flags & LLKHF_UP); - timestamp = hook->time; - } - /** returns now if key event already known */ - new_diks = (down ? 0x80 : 0); - /*if (new_diks != DInputKeyState[dik_code]) return CallNextHookEx(keyboard_hook, code, wparam, lparam); TO BE FIXED */ + /** returns now if key event already known */ + new_diks = (down ? 0x80 : 0); + /*if (new_diks != DInputKeyState[dik_code]) return CallNextHookEx(keyboard_hook, code, wparam, lparam); TO BE FIXED */ - DInputKeyState[dik_code] = new_diks; - TRACE(" setting %02X to %02X\n", dik_code, DInputKeyState[dik_code]); + DInputKeyState[dik_code] = new_diks; + TRACE(" setting %02X to %02X\n", dik_code, DInputKeyState[dik_code]); - if (current_lock != NULL) { - if (current_lock->hEvent) SetEvent(current_lock->hEvent); + if (This->hEvent) SetEvent(This->hEvent); - if (current_lock->buffer != NULL) { - int n; - - EnterCriticalSection(&(current_lock->crit)); - - n = (current_lock->start + current_lock->count) % current_lock->buffersize; - - current_lock->buffer[n].dwOfs = dik_code; - current_lock->buffer[n].dwData = down ? 0x80 : 0; - current_lock->buffer[n].dwTimeStamp = timestamp; - current_lock->buffer[n].dwSequence = current_lock->dinput->evsequence++; + if (This->buffer != NULL) + { + int n; + + EnterCriticalSection(&(This->crit)); + + n = (This->start + This->count) % This->buffersize; + + This->buffer[n].dwOfs = dik_code; + This->buffer[n].dwData = down ? 0x80 : 0; + This->buffer[n].dwTimeStamp = hook->time; + This->buffer[n].dwSequence = This->dinput->evsequence++; - TRACE("Adding event at offset %d : %ld - %ld - %ld - %ld\n", n, - current_lock->buffer[n].dwOfs, current_lock->buffer[n].dwData, current_lock->buffer[n].dwTimeStamp, current_lock->buffer[n].dwSequence); + TRACE("Adding event at offset %d : %ld - %ld - %ld - %ld\n", n, + This->buffer[n].dwOfs, This->buffer[n].dwData, This->buffer[n].dwTimeStamp, This->buffer[n].dwSequence); - if (current_lock->count == current_lock->buffersize) { - current_lock->start = ++current_lock->start % current_lock->buffersize; - current_lock->overflow = TRUE; - } else - current_lock->count++; + if (This->count == This->buffersize) { + This->start = ++This->start % This->buffersize; + This->overflow = TRUE; + } + else This->count++; - LeaveCriticalSection(&(current_lock->crit)); + LeaveCriticalSection(&(This->crit)); } - } - return CallNextHookEx(keyboard_hook, code, wparam, lparam); + return CallNextHookEx(0, code, wparam, lparam); } static GUID DInput_Wine_Keyboard_GUID = { /* 0ab8648a-7735-11d2-8c73-71df54a96441 */ @@ -241,7 +225,6 @@ static BOOL keyboarddev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEI static SysKeyboardImpl *alloc_device(REFGUID rguid, const void *kvt, IDirectInputImpl *dinput) { SysKeyboardImpl* newDevice; - DWORD kbd_users; newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysKeyboardImpl)); newDevice->lpVtbl = kvt; @@ -250,13 +233,6 @@ static SysKeyboardImpl *alloc_device(REFGUID rguid, const void *kvt, IDirectInpu newDevice->dinput = dinput; InitializeCriticalSection(&(newDevice->crit)); - EnterCriticalSection(&keyboard_crit); - kbd_users = InterlockedIncrement(&keyboard_users); - if (1 == kbd_users) { - keyboard_hook = SetWindowsHookExW( WH_KEYBOARD_LL, KeyboardCallback, DINPUT_instance, 0 ); - } - LeaveCriticalSection(&keyboard_crit); - return newDevice; } @@ -307,29 +283,21 @@ const struct dinput_device keyboard_device = { static ULONG WINAPI SysKeyboardAImpl_Release(LPDIRECTINPUTDEVICE8A iface) { - SysKeyboardImpl *This = (SysKeyboardImpl *)iface; - ULONG ref; - DWORD kbd_users; - - ref = InterlockedDecrement(&(This->ref)); - if (ref) - return ref; - - EnterCriticalSection(&keyboard_crit); - kbd_users = InterlockedDecrement(&keyboard_users); - if (0 == kbd_users) { - UnhookWindowsHookEx( keyboard_hook ); - keyboard_hook = 0; - } - LeaveCriticalSection(&keyboard_crit); + SysKeyboardImpl *This = (SysKeyboardImpl *)iface; + ULONG ref; - /* Free the data queue */ - HeapFree(GetProcessHeap(),0,This->buffer); + ref = InterlockedDecrement(&(This->ref)); + if (ref) return ref; - DeleteCriticalSection(&(This->crit)); + set_dinput_hook(WH_KEYBOARD_LL, NULL); - HeapFree(GetProcessHeap(),0,This); - return DI_OK; + /* Free the data queue */ + HeapFree(GetProcessHeap(),0,This->buffer); + + DeleteCriticalSection(&(This->crit)); + + HeapFree(GetProcessHeap(),0,This); + return DI_OK; } static HRESULT WINAPI SysKeyboardAImpl_SetProperty( @@ -563,7 +531,7 @@ static HRESULT WINAPI SysKeyboardAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface) This->buffer = NULL; } - /*keyboard_hook = SetWindowsHookExW( WH_KEYBOARD_LL, KeyboardCallback, DINPUT_instance, 0 );*/ + set_dinput_hook(WH_KEYBOARD_LL, KeyboardCallback); return DI_OK; } @@ -576,6 +544,8 @@ static HRESULT WINAPI SysKeyboardAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface) if (This->acquired == 0) return DI_NOEFFECT; + set_dinput_hook(WH_KEYBOARD_LL, NULL); + /* No more locks */ if (current_lock == This) current_lock = NULL; diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index 9b06b57da28..53b5499bff4 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -126,7 +126,6 @@ struct SysMouseImpl LONG prevX, prevY; /* These are used in case of relative -> absolute transitions */ POINT org_coords; - HHOOK hook; HWND win; DWORD dwCoopLevel; POINT mapped_center; @@ -329,15 +328,11 @@ static ULONG WINAPI SysMouseAImpl_Release(LPDIRECTINPUTDEVICE8A iface) ref = InterlockedDecrement(&(This->ref)); if (ref) return ref; - + + set_dinput_hook(WH_MOUSE_LL, NULL); + /* Free the data queue */ HeapFree(GetProcessHeap(),0,This->data_queue); - - if (This->hook) { - UnhookWindowsHookEx( This->hook ); - if (This->dwCoopLevel & DISCL_EXCLUSIVE) - ShowCursor(TRUE); /* show cursor */ - } DeleteCriticalSection(&(This->crit)); /* Free the DataFormat */ @@ -426,7 +421,7 @@ static LRESULT CALLBACK dinput_mouse_hook( int code, WPARAM wparam, LPARAM lpara static long last_event = 0; int wdata; - if (code != HC_ACTION) return CallNextHookEx( This->hook, code, wparam, lparam ); + if (code != HC_ACTION) return CallNextHookEx( 0, code, wparam, lparam ); EnterCriticalSection(&(This->crit)); dwCoop = This->dwCoopLevel; @@ -549,7 +544,7 @@ static LRESULT CALLBACK dinput_mouse_hook( int code, WPARAM wparam, LPARAM lpara if (dwCoop & DISCL_NONEXCLUSIVE) { /* Pass the events down to previous handlers (e.g. win32 input) */ - ret = CallNextHookEx( This->hook, code, wparam, lparam ); + ret = CallNextHookEx( 0, code, wparam, lparam ); } else { /* Ignore message */ ret = 1; @@ -557,7 +552,6 @@ static LRESULT CALLBACK dinput_mouse_hook( int code, WPARAM wparam, LPARAM lpara return ret; } - static void dinput_window_check(SysMouseImpl* This) { RECT rect; DWORD centerX, centerY; @@ -615,7 +609,7 @@ static HRESULT WINAPI SysMouseAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface) /* Install our mouse hook */ if (This->dwCoopLevel & DISCL_EXCLUSIVE) ShowCursor(FALSE); /* hide cursor */ - This->hook = SetWindowsHookExA( WH_MOUSE_LL, dinput_mouse_hook, DINPUT_instance, 0 ); + set_dinput_hook(WH_MOUSE_LL, dinput_mouse_hook); /* Get the window dimension and find the center */ GetWindowRect(This->win, &rect); @@ -651,16 +645,11 @@ static HRESULT WINAPI SysMouseAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface) if (0 == This->acquired) { return DI_NOEFFECT; } - - /* Reinstall previous mouse event handler */ - if (This->hook) { - UnhookWindowsHookEx( This->hook ); - This->hook = 0; - - if (This->dwCoopLevel & DISCL_EXCLUSIVE) - ShowCursor(TRUE); /* show cursor */ - } - + + set_dinput_hook(WH_MOUSE_LL, NULL); + if (This->dwCoopLevel & DISCL_EXCLUSIVE) + ShowCursor(TRUE); /* show cursor */ + /* No more locks */ if (current_lock == (IDirectInputDevice8A*) This) current_lock = NULL; -- 2.11.4.GIT