dinput: Create single thread for mouse and keyboard hook procs. [try 3]

Vitaliy Margolen wine-patch at kievinfo.com
Wed Feb 22 09:10:16 CST 2006


ChangeLog:
dinput: Create single thread for mouse and keyboard hooks.

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. This patch fixes
such games (Bug 4264) - mouse and (Bug 2981) - keyboard.

Try#2 (based on suggestions from Alexandre):
Using a message window in a dedicated thread with normal message loop.
Hooks are installed and removed using SendMessage to this window.
Only one mouse and one keyboard hook is permitted.
Hook handles are managed by dedicated thread and not given back to
caller.

Try#3 (with Robs suggestions)
Use critical section.

 dlls/dinput/device.c         |  132 ++++++++++++++++++++++++++++++++++++++++++
 dlls/dinput/device_private.h |    2 +
 dlls/dinput/keyboard.c       |  130 ++++++++++++++++-------------------------
 dlls/dinput/mouse.c          |   33 ++++-------
 4 files changed, 195 insertions(+), 102 deletions(-)
-------------- next part --------------
1d9ce1356dd2a63f2208994eab1e80abd697a01e
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
index 8cca6f5..4acdb3d 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,133 @@ HRESULT WINAPI IDirectInputDevice8WImpl_
     
     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);
+        WaitForSingleObject(signal_event, 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 4f2347b..6c967e7 100644
--- a/dlls/dinput/device_private.h
+++ b/dlls/dinput/device_private.h
@@ -52,6 +52,8 @@ extern void fill_DataFormat(void *out, c
 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 aa6cbcb..9d6ea8c 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 = N
 
 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(DWO
 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(REF
     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_devi
 
 static ULONG WINAPI SysKeyboardAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
 {
-	SysKeyboardImpl *This = (SysKeyboardImpl *)iface;
-	ULONG ref;
-	DWORD kbd_users;
+    SysKeyboardImpl *This = (SysKeyboardImpl *)iface;
+    ULONG ref;
 
-	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);
+    ref = InterlockedDecrement(&(This->ref));
+    if (ref) return ref;
 
-	/* Free the data queue */
-	HeapFree(GetProcessHeap(),0,This->buffer);
+    set_dinput_hook(WH_KEYBOARD_LL, NULL);
 
-	DeleteCriticalSection(&(This->crit));
+    /* Free the data queue */
+    HeapFree(GetProcessHeap(),0,This->buffer);
 
-	HeapFree(GetProcessHeap(),0,This);
-	return DI_OK;
+    DeleteCriticalSection(&(This->crit));
+
+    HeapFree(GetProcessHeap(),0,This);
+    return DI_OK;
 }
 
 static HRESULT WINAPI SysKeyboardAImpl_SetProperty(
@@ -563,7 +531,7 @@ static HRESULT WINAPI SysKeyboardAImpl_A
           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_U
         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 6570d1f..11fb2bd 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_Releas
     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 */
@@ -422,7 +417,7 @@ static LRESULT CALLBACK dinput_mouse_hoo
     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;
@@ -545,7 +540,7 @@ static LRESULT CALLBACK dinput_mouse_hoo
     
     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;
@@ -553,7 +548,6 @@ static LRESULT CALLBACK dinput_mouse_hoo
     return ret;
 }
 
-
 static void dinput_window_check(SysMouseImpl* This) {
     RECT rect;
     DWORD centerX, centerY;
@@ -611,7 +605,7 @@ static HRESULT WINAPI SysMouseAImpl_Acqu
     /* 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);
@@ -647,16 +641,11 @@ static HRESULT WINAPI SysMouseAImpl_Unac
     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;


More information about the wine-patches mailing list