From 11344cc9631192fa0bb5a3d90f5956c09ec67991 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Bernon?= <rbernon@codeweavers.com>
Date: Mon, 26 Aug 2019 16:06:59 +0200
Subject: [PATCH] dinput8: Add support for rawinput based devices.

This adds a global message window that will receive WM_INPUT messages.

Devices that use rawinput will conflict with any rawinput device
registered outside of dinput, as exposed by the unit tests.
---
 dlls/dinput/device_private.h |  3 ++
 dlls/dinput/dinput_main.c    | 80 ++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+)

diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h
index fe5644f21c7..2fac4f0e61e 100644
--- a/dlls/dinput/device_private.h
+++ b/dlls/dinput/device_private.h
@@ -69,6 +69,9 @@ struct IDirectInputDeviceImpl
     HWND                        win;
     int                         acquired;
 
+    BOOL                        use_raw_input; /* use raw input instead of low-level messages */
+    RAWINPUTDEVICE              raw_device;    /* raw device to (un)register */
+
     LPDIDEVICEOBJECTDATA        data_queue;  /* buffer for 'GetDeviceData'.                 */
     int                         queue_len;   /* valid size of the queue                     */
     int                         queue_head;  /* position to write new event into queue      */
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c
index 2e561502406..d0937d35e5b 100644
--- a/dlls/dinput/dinput_main.c
+++ b/dlls/dinput/dinput_main.c
@@ -97,6 +97,10 @@ static const struct dinput_device *dinput_devices[] =
 
 HINSTANCE DINPUT_instance;
 
+static ATOM        di_em_win_class;
+static const WCHAR di_em_winW[] = {'D','I','E','m','W','i','n',0};
+static HWND        di_em_win;
+
 static BOOL check_hook_thread(void);
 static CRITICAL_SECTION dinput_hook_crit;
 static struct list direct_input_list = LIST_INIT( direct_input_list );
@@ -637,6 +641,47 @@ static HRESULT WINAPI IDirectInputWImpl_QueryInterface(LPDIRECTINPUT7W iface, RE
     return IDirectInputAImpl_QueryInterface( &This->IDirectInput7A_iface, riid, ppobj );
 }
 
+static LRESULT WINAPI di_em_win_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    IDirectInputDeviceImpl *dev;
+    RAWINPUT ri;
+    UINT size = sizeof(ri);
+    int rim = GET_RAWINPUT_CODE_WPARAM(wparam);
+
+    TRACE( "%p %d %lx %lx\n", hwnd, msg, wparam, lparam );
+
+    if (msg == WM_INPUT && (rim == RIM_INPUT || rim == RIM_INPUTSINK))
+    {
+        if (GetRawInputData( (HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER) ) > sizeof(ri))
+            WARN( "Unable to read raw input data\n" );
+    }
+
+    return DefWindowProcW(hwnd, msg, wparam, lparam);
+}
+
+static void register_di_em_win_class(void)
+{
+    static WNDCLASSEXW class;
+
+    ZeroMemory(&class, sizeof(class));
+    class.cbSize = sizeof(class);
+    class.lpfnWndProc = di_em_win_wndproc;
+    class.hInstance = DINPUT_instance;
+    class.lpszClassName = di_em_winW;
+
+    if (!(di_em_win_class = RegisterClassExW( &class )))
+        WARN( "Unable to register message window class\n" );
+}
+
+static void unregister_di_em_win_class(void)
+{
+    if (!di_em_win_class)
+        return;
+
+    if (!UnregisterClassW( MAKEINTRESOURCEW( di_em_win_class ), DINPUT_instance ))
+        WARN( "Unable to unregister message window class\n" );
+}
+
 static HRESULT initialize_directinput_instance(IDirectInputImpl *This, DWORD dwVersion)
 {
     if (!This->initialized)
@@ -1668,11 +1713,13 @@ static LRESULT CALLBACK LL_hook_proc( int code, WPARAM wparam, LPARAM lparam )
     EnterCriticalSection( &dinput_hook_crit );
     LIST_FOR_EACH_ENTRY( dev, &acquired_mouse_list, IDirectInputDeviceImpl, entry )
     {
+        if (dev->use_raw_input) continue;
         TRACE("calling dinput_mouse_hook (%p %lx %lx)\n", dev, wparam, lparam);
         skip |= dinput_mouse_hook( &dev->IDirectInputDevice8A_iface, wparam, lparam );
     }
     LIST_FOR_EACH_ENTRY( dev, &acquired_keyboard_list, IDirectInputDeviceImpl, entry )
     {
+        if (dev->use_raw_input) continue;
         TRACE("calling dinput_keyboard_hook (%p %lx %lx)\n", dev, wparam, lparam);
         skip |= dinput_keyboard_hook( &dev->IDirectInputDevice8A_iface, wparam, lparam );
     }
@@ -1728,6 +1775,9 @@ static DWORD WINAPI hook_thread_proc(void *param)
     static HHOOK kbd_hook, mouse_hook;
     MSG msg;
 
+    di_em_win = CreateWindowW( MAKEINTRESOURCEW(di_em_win_class), di_em_winW,
+                               0, 0, 0, 0, 0, HWND_MESSAGE, 0, DINPUT_instance, NULL );
+
     /* Force creation of the message queue */
     PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE );
     SetEvent(param);
@@ -1778,6 +1828,9 @@ static DWORD WINAPI hook_thread_proc(void *param)
         DispatchMessageW(&msg);
     }
 
+    DestroyWindow( di_em_win );
+    di_em_win = NULL;
+
     FreeLibraryAndExitThread(DINPUT_instance, 0);
 }
 
@@ -1860,6 +1913,31 @@ void check_dinput_hooks(LPDIRECTINPUTDEVICE8W iface, BOOL acquired)
         hook_thread_event = NULL;
     }
 
+    if (dev->use_raw_input)
+    {
+        if (acquired)
+        {
+            dev->raw_device.dwFlags = 0;
+            if (dev->dwCoopLevel & DISCL_BACKGROUND)
+                dev->raw_device.dwFlags |= RIDEV_INPUTSINK;
+            if (dev->dwCoopLevel & DISCL_EXCLUSIVE)
+                dev->raw_device.dwFlags |= RIDEV_NOLEGACY;
+            if ((dev->dwCoopLevel & DISCL_EXCLUSIVE) && dev->raw_device.usUsage == 2)
+                dev->raw_device.dwFlags |= RIDEV_CAPTUREMOUSE;
+            if ((dev->dwCoopLevel & DISCL_EXCLUSIVE) && dev->raw_device.usUsage == 6)
+                dev->raw_device.dwFlags |= RIDEV_NOHOTKEYS;
+            dev->raw_device.hwndTarget = di_em_win;
+        }
+        else
+        {
+            dev->raw_device.dwFlags = RIDEV_REMOVE;
+            dev->raw_device.hwndTarget = NULL;
+        }
+
+        if (!RegisterRawInputDevices( &dev->raw_device, 1, sizeof(RAWINPUTDEVICE) ))
+            WARN( "Unable to (un)register raw device %x:%x\n", dev->raw_device.usUsagePage, dev->raw_device.usUsage );
+    }
+
     if (acquired)
         hook_change_finished_event = CreateEventW( NULL, FALSE, FALSE, NULL );
     PostThreadMessageW( hook_thread_id, WM_USER+0x10, 1, (LPARAM)hook_change_finished_event );
@@ -1894,9 +1972,11 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved)
       case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls(inst);
         DINPUT_instance = inst;
+        register_di_em_win_class();
         break;
       case DLL_PROCESS_DETACH:
         if (reserved) break;
+        unregister_di_em_win_class();
         DeleteCriticalSection(&dinput_hook_crit);
         break;
     }
-- 
2.27.0

