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

Vitaliy Margolen wine-patch at kievinfo.com
Sat Mar 18 12:26:37 CST 2006


ChangeLog:
dinput: Create single thread for mouse and keyboard hook procs.

Add synch event.
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
one of such games (Bug 4264) and hopefully should fir all the rest as well (Bug 2981).

 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 --------------
69a5820bb286a35a85d0df8a5d6d7f60c0a1132a
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 9b06b57..53b5499 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 */
@@ -426,7 +421,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;
@@ -549,7 +544,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;
@@ -557,7 +552,6 @@ static LRESULT CALLBACK dinput_mouse_hoo
     return ret;
 }
 
-
 static void dinput_window_check(SysMouseImpl* This) {
     RECT rect;
     DWORD centerX, centerY;
@@ -615,7 +609,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);
@@ -651,16 +645,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