[PATCH v3 3/4] win32u: Move raw input device list management from user32.

Zebediah Figura wine at gitlab.winehq.org
Tue Jun 28 11:54:52 CDT 2022


From: Zebediah Figura <zfigura at codeweavers.com>

---
 dlls/user32/rawinput.c       | 433 +---------------------------------
 dlls/user32/user32.spec      |   4 +-
 dlls/user32/user_main.c      |   2 -
 dlls/user32/user_private.h   |   6 -
 dlls/win32u/message.c        |   5 +-
 dlls/win32u/ntuser_private.h |   2 -
 dlls/win32u/rawinput.c       | 434 ++++++++++++++++++++++++++++++++++-
 dlls/win32u/syscall.c        |   2 +
 dlls/win32u/win32u.spec      |   4 +-
 dlls/win32u/win32u_private.h |   1 +
 dlls/wow64win/syscall.h      |   2 +
 dlls/wow64win/user.c         |  55 +++++
 include/ntuser.h             |   2 +
 13 files changed, 503 insertions(+), 449 deletions(-)

diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c
index 588d1685cc0..98854d75690 100644
--- a/dlls/user32/rawinput.c
+++ b/dlls/user32/rawinput.c
@@ -39,368 +39,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(rawinput);
 
-struct device
-{
-    WCHAR *path;
-    HANDLE file;
-    HANDLE handle;
-    RID_DEVICE_INFO info;
-    struct hid_preparsed_data *data;
-};
-
-static struct device *rawinput_devices;
-static unsigned int rawinput_devices_count, rawinput_devices_max;
-
-static CRITICAL_SECTION rawinput_devices_cs;
-static CRITICAL_SECTION_DEBUG rawinput_devices_cs_debug =
-{
-    0, 0, &rawinput_devices_cs,
-    { &rawinput_devices_cs_debug.ProcessLocksList, &rawinput_devices_cs_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": rawinput_devices_cs") }
-};
-static CRITICAL_SECTION rawinput_devices_cs = { &rawinput_devices_cs_debug, -1, 0, 0, 0, 0 };
-
-static BOOL array_reserve(void **elements, unsigned int *capacity, unsigned int count, unsigned int size)
-{
-    unsigned int new_capacity, max_capacity;
-    void *new_elements;
-
-    if (count <= *capacity)
-        return TRUE;
-
-    max_capacity = ~(SIZE_T)0 / size;
-    if (count > max_capacity)
-        return FALSE;
-
-    new_capacity = max(4, *capacity);
-    while (new_capacity < count && new_capacity <= max_capacity / 2)
-        new_capacity *= 2;
-    if (new_capacity < count)
-        new_capacity = max_capacity;
-
-    if (!(new_elements = realloc(*elements, new_capacity * size)))
-        return FALSE;
-
-    *elements = new_elements;
-    *capacity = new_capacity;
-
-    return TRUE;
-}
-
-static ULONG query_reg_value( HKEY hkey, const WCHAR *name,
-                              KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size )
-{
-    unsigned int name_size = name ? lstrlenW( name ) * sizeof(WCHAR) : 0;
-    UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name };
-
-    if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
-                         info, size, &size ))
-        return 0;
-
-    return size - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
-}
-
-static struct device *add_device( HKEY key, DWORD type )
-{
-    static const WCHAR symbolic_linkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
-    char value_buffer[4096];
-    KEY_VALUE_PARTIAL_INFORMATION *value = (KEY_VALUE_PARTIAL_INFORMATION *)value_buffer;
-    static const RID_DEVICE_INFO_KEYBOARD keyboard_info = {0, 0, 1, 12, 3, 101};
-    static const RID_DEVICE_INFO_MOUSE mouse_info = {1, 5, 0, FALSE};
-    struct hid_preparsed_data *preparsed = NULL;
-    HID_COLLECTION_INFORMATION hid_info;
-    struct device *device = NULL;
-    RID_DEVICE_INFO info;
-    IO_STATUS_BLOCK io;
-    WCHAR *path, *pos;
-    NTSTATUS status;
-    unsigned int i;
-    UINT32 handle;
-    HANDLE file;
-
-    if (!query_reg_value( key, symbolic_linkW, value, sizeof(value_buffer) ))
-    {
-        ERR( "failed to get symbolic link value\n" );
-        return NULL;
-    }
-
-    if (!(path = malloc( value->DataLength + sizeof(WCHAR) )))
-        return NULL;
-    memcpy( path, value->Data, value->DataLength );
-    path[value->DataLength / sizeof(WCHAR)] = 0;
-
-    /* upper case everything but the GUID */
-    for (pos = path; *pos && *pos != '{'; pos++) *pos = towupper(*pos);
-
-    file = CreateFileW( path, GENERIC_READ | GENERIC_WRITE,
-                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
-    if (file == INVALID_HANDLE_VALUE)
-    {
-        ERR( "Failed to open device file %s, error %lu.\n", debugstr_w(path), GetLastError() );
-        free( path );
-        return NULL;
-    }
-
-    status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io,
-                                    IOCTL_HID_GET_WINE_RAWINPUT_HANDLE,
-                                    NULL, 0, &handle, sizeof(handle) );
-    if (status)
-    {
-        ERR( "Failed to get raw input handle, status %#lx.\n", status );
-        goto fail;
-    }
-
-    memset( &info, 0, sizeof(info) );
-    info.cbSize = sizeof(info);
-    info.dwType = type;
-
-    switch (type)
-    {
-    case RIM_TYPEHID:
-        status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io,
-                                        IOCTL_HID_GET_COLLECTION_INFORMATION,
-                                        NULL, 0, &hid_info, sizeof(hid_info) );
-        if (status)
-        {
-            ERR( "Failed to get collection information, status %#lx.\n", status );
-            goto fail;
-        }
-
-        info.hid.dwVendorId = hid_info.VendorID;
-        info.hid.dwProductId = hid_info.ProductID;
-        info.hid.dwVersionNumber = hid_info.VersionNumber;
-
-        if (!(preparsed = malloc( hid_info.DescriptorSize )))
-        {
-            ERR( "Failed to allocate memory.\n" );
-            goto fail;
-        }
-
-        status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io,
-                                        IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
-                                        NULL, 0, preparsed, hid_info.DescriptorSize );
-        if (status)
-        {
-            ERR( "Failed to get collection descriptor, status %#lx.\n", status );
-            goto fail;
-        }
-
-        info.hid.usUsagePage = preparsed->usage_page;
-        info.hid.usUsage = preparsed->usage;
-        break;
-
-    case RIM_TYPEMOUSE:
-        info.mouse = mouse_info;
-        break;
-
-    case RIM_TYPEKEYBOARD:
-        info.keyboard = keyboard_info;
-        break;
-    }
-
-    for (i = 0; i < rawinput_devices_count && !device; ++i)
-        if (rawinput_devices[i].handle == UlongToHandle(handle))
-            device = rawinput_devices + i;
-
-    if (device)
-    {
-        TRACE( "Updating device %#x / %s.\n", handle, debugstr_w(path) );
-        free(device->data);
-        CloseHandle(device->file);
-        free( device->path );
-    }
-    else if (array_reserve((void **)&rawinput_devices, &rawinput_devices_max,
-                           rawinput_devices_count + 1, sizeof(*rawinput_devices)))
-    {
-        device = &rawinput_devices[rawinput_devices_count++];
-        TRACE( "Adding device %#x / %s.\n", handle, debugstr_w(path) );
-    }
-    else
-    {
-        ERR("Failed to allocate memory.\n");
-        goto fail;
-    }
-
-    device->path = path;
-    device->file = file;
-    device->handle = ULongToHandle(handle);
-    device->info = info;
-    device->data = preparsed;
-
-    return device;
-
-fail:
-    free( preparsed );
-    CloseHandle( file );
-    free( path );
-    return NULL;
-}
-
-static HKEY reg_open_key( HKEY root, const WCHAR *name, ULONG name_len )
-{
-    UNICODE_STRING nameW = { name_len, name_len, (WCHAR *)name };
-    OBJECT_ATTRIBUTES attr;
-    HANDLE ret;
-
-    attr.Length = sizeof(attr);
-    attr.RootDirectory = root;
-    attr.ObjectName = &nameW;
-    attr.Attributes = 0;
-    attr.SecurityDescriptor = NULL;
-    attr.SecurityQualityOfService = NULL;
-
-    if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) return 0;
-    return ret;
-}
-
-static const WCHAR device_classesW[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
-static const WCHAR guid_devinterface_hidW[] = L"{4d1e55b2-f16f-11cf-88cb-001111000030}";
-static const WCHAR guid_devinterface_keyboardW[] = L"{884b96c3-56ef-11d1-bc8c-00a0c91405dd}";
-static const WCHAR guid_devinterface_mouseW[] = L"{378de44c-56ef-11d1-bc8c-00a0c91405dd}";
-
-static void enumerate_devices( DWORD type, const WCHAR *class )
-{
-    WCHAR buffer[1024];
-    KEY_NODE_INFORMATION *subkey_info = (void *)buffer;
-    HKEY class_key, device_key, iface_key;
-    unsigned int i, j;
-    DWORD size;
-
-    wcscpy( buffer, device_classesW );
-    wcscat( buffer, class );
-    if (!(class_key = reg_open_key( NULL, buffer, wcslen( buffer ) * sizeof(WCHAR) )))
-        return;
-
-    for (i = 0; !NtEnumerateKey( class_key, i, KeyNodeInformation, buffer, sizeof(buffer), &size ); ++i)
-    {
-        if (!(device_key = reg_open_key( class_key, subkey_info->Name, subkey_info->NameLength )))
-        {
-            ERR( "failed to open %s\n", debugstr_wn(subkey_info->Name, subkey_info->NameLength / sizeof(WCHAR)) );
-            continue;
-        }
-
-        for (j = 0; !NtEnumerateKey( device_key, j, KeyNodeInformation, buffer, sizeof(buffer), &size ); ++j)
-        {
-            if (!(iface_key = reg_open_key( device_key, subkey_info->Name, subkey_info->NameLength )))
-            {
-                ERR( "failed to open %s\n", debugstr_wn(subkey_info->Name, subkey_info->NameLength / sizeof(WCHAR)) );
-                continue;
-            }
-
-            add_device( iface_key, type );
-            NtClose( iface_key );
-        }
-
-        NtClose( device_key );
-    }
-
-    NtClose( class_key );
-}
-
-void CDECL rawinput_update_device_list(void)
-{
-    DWORD idx;
-
-    TRACE("\n");
-
-    EnterCriticalSection(&rawinput_devices_cs);
-
-    /* destroy previous list */
-    for (idx = 0; idx < rawinput_devices_count; ++idx)
-    {
-        free(rawinput_devices[idx].data);
-        CloseHandle(rawinput_devices[idx].file);
-        free( rawinput_devices[idx].path );
-    }
-    rawinput_devices_count = 0;
-
-    enumerate_devices( RIM_TYPEHID, guid_devinterface_hidW );
-    enumerate_devices( RIM_TYPEMOUSE, guid_devinterface_mouseW );
-    enumerate_devices( RIM_TYPEKEYBOARD, guid_devinterface_keyboardW );
-
-    LeaveCriticalSection(&rawinput_devices_cs);
-}
-
-
-static struct device *find_device_from_handle(HANDLE handle)
-{
-    UINT i;
-    for (i = 0; i < rawinput_devices_count; ++i)
-        if (rawinput_devices[i].handle == handle)
-            return rawinput_devices + i;
-    rawinput_update_device_list();
-    for (i = 0; i < rawinput_devices_count; ++i)
-        if (rawinput_devices[i].handle == handle)
-            return rawinput_devices + i;
-    return NULL;
-}
-
-
-BOOL rawinput_device_get_usages(HANDLE handle, USAGE *usage_page, USAGE *usage)
-{
-    struct device *device;
-
-    *usage_page = *usage = 0;
-
-    if (!(device = find_device_from_handle(handle))) return FALSE;
-    if (device->info.dwType != RIM_TYPEHID) return FALSE;
-
-    *usage_page = device->info.hid.usUsagePage;
-    *usage = device->info.hid.usUsage;
-    return TRUE;
-}
-
-
-/***********************************************************************
- *              GetRawInputDeviceList   (USER32.@)
- */
-UINT WINAPI GetRawInputDeviceList(RAWINPUTDEVICELIST *devices, UINT *device_count, UINT size)
-{
-    static UINT last_check;
-    UINT i, ticks = GetTickCount();
-
-    TRACE("devices %p, device_count %p, size %u.\n", devices, device_count, size);
-
-    if (size != sizeof(*devices))
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return ~0U;
-    }
-
-    if (!device_count)
-    {
-        SetLastError(ERROR_NOACCESS);
-        return ~0U;
-    }
-
-    if (ticks - last_check > 2000)
-    {
-        last_check = ticks;
-        rawinput_update_device_list();
-    }
-
-    if (!devices)
-    {
-        *device_count = rawinput_devices_count;
-        return 0;
-    }
-
-    if (*device_count < rawinput_devices_count)
-    {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        *device_count = rawinput_devices_count;
-        return ~0U;
-    }
-
-    for (i = 0; i < rawinput_devices_count; ++i)
-    {
-        devices[i].hDevice = rawinput_devices[i].handle;
-        devices[i].dwType = rawinput_devices[i].info.dwType;
-    }
-
-    return rawinput_devices_count;
-}
-
 /***********************************************************************
  *              GetRawInputDeviceInfoA   (USER32.@)
  */
@@ -424,7 +62,7 @@ UINT WINAPI GetRawInputDeviceInfoA(HANDLE device, UINT command, void *data, UINT
         else
             nameW = NULL;
 
-        ret = GetRawInputDeviceInfoW(device, command, nameW, &nameW_sz);
+        ret = NtUserGetRawInputDeviceInfo( device, command, nameW, &nameW_sz );
 
         if (ret && ret != ~0U)
             WideCharToMultiByte(CP_ACP, 0, nameW, -1, data, *data_size, NULL, NULL);
@@ -436,74 +74,7 @@ UINT WINAPI GetRawInputDeviceInfoA(HANDLE device, UINT command, void *data, UINT
         return ret;
     }
 
-    return GetRawInputDeviceInfoW(device, command, data, data_size);
-}
-
-/***********************************************************************
- *              GetRawInputDeviceInfoW   (USER32.@)
- */
-UINT WINAPI GetRawInputDeviceInfoW(HANDLE handle, UINT command, void *data, UINT *data_size)
-{
-    struct hid_preparsed_data *preparsed;
-    RID_DEVICE_INFO info;
-    struct device *device;
-    DWORD len, data_len;
-
-    TRACE("handle %p, command %#x, data %p, data_size %p.\n",
-            handle, command, data, data_size);
-
-    if (!data_size)
-    {
-        SetLastError(ERROR_NOACCESS);
-        return ~0U;
-    }
-    if (!(device = find_device_from_handle(handle)))
-    {
-        SetLastError(ERROR_INVALID_HANDLE);
-        return ~0U;
-    }
-
-    data_len = *data_size;
-    switch (command)
-    {
-    case RIDI_DEVICENAME:
-        if ((len = wcslen( device->path ) + 1) <= data_len && data)
-            memcpy( data, device->path, len * sizeof(WCHAR) );
-        *data_size = len;
-        break;
-
-    case RIDI_DEVICEINFO:
-        if ((len = sizeof(info)) <= data_len && data)
-            memcpy(data, &device->info, len);
-        *data_size = len;
-        break;
-
-    case RIDI_PREPARSEDDATA:
-        if (!(preparsed = device->data)) len = 0;
-        else len = preparsed->caps_size + FIELD_OFFSET(struct hid_preparsed_data, value_caps[0]) +
-                   preparsed->number_link_collection_nodes * sizeof(struct hid_collection_node);
-
-        if (preparsed && len <= data_len && data)
-            memcpy(data, preparsed, len);
-        *data_size = len;
-        break;
-
-    default:
-        FIXME("command %#x not supported\n", command);
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return ~0U;
-    }
-
-    if (!data)
-        return 0;
-
-    if (data_len < len)
-    {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return ~0U;
-    }
-
-    return *data_size;
+    return NtUserGetRawInputDeviceInfo( device, command, data, data_size );
 }
 
 /***********************************************************************
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index 3e20277251a..e1cc18f7287 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -369,8 +369,8 @@
 @ stdcall GetRawInputBuffer(ptr ptr long) NtUserGetRawInputBuffer
 @ stdcall GetRawInputData(ptr long ptr ptr long) NtUserGetRawInputData
 @ stdcall GetRawInputDeviceInfoA(ptr long ptr ptr)
-@ stdcall GetRawInputDeviceInfoW(ptr long ptr ptr)
-@ stdcall GetRawInputDeviceList(ptr ptr long)
+@ stdcall GetRawInputDeviceInfoW(ptr long ptr ptr) NtUserGetRawInputDeviceInfo
+@ stdcall GetRawInputDeviceList(ptr ptr long) NtUserGetRawInputDeviceList
 # @ stub GetReasonTitleFromReasonCode
 @ stdcall GetRegisteredRawInputDevices(ptr ptr long) NtUserGetRegisteredRawInputDevices
 @ stdcall GetScrollBarInfo(long long ptr)
diff --git a/dlls/user32/user_main.c b/dlls/user32/user_main.c
index 038f1f0796f..2391131e4a5 100644
--- a/dlls/user32/user_main.c
+++ b/dlls/user32/user_main.c
@@ -161,8 +161,6 @@ static const struct user_callbacks user_funcs =
     free_win_ptr,
     notify_ime,
     post_dde_message,
-    rawinput_update_device_list,
-    rawinput_device_get_usages,
     SCROLL_SetStandardScrollPainted,
     unpack_dde_message,
     register_imm,
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index 71248e03bb8..a646f9a234d 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -60,14 +60,8 @@ extern HMODULE user32_module DECLSPEC_HIDDEN;
 struct dce;
 struct tagWND;
 
-struct hardware_msg_data;
-extern BOOL rawinput_device_get_usages(HANDLE handle, USAGE *usage_page, USAGE *usage);
-extern void CDECL rawinput_update_device_list(void);
-
 extern BOOL post_dde_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, DWORD dest_tid,
                               DWORD type ) DECLSPEC_HIDDEN;
-extern BOOL process_rawinput_message( MSG *msg, UINT hw_id,
-                                      const struct hardware_msg_data *msg_data ) DECLSPEC_HIDDEN;
 extern BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
                                 void **buffer, size_t size ) DECLSPEC_HIDDEN;
 extern void free_cached_data( UINT format, HANDLE handle ) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c
index 94a1bc71520..0603a248f60 100644
--- a/dlls/win32u/message.c
+++ b/dlls/win32u/message.c
@@ -2381,9 +2381,8 @@ NTSTATUS send_hardware_message( HWND hwnd, const INPUT *input, const RAWINPUT *r
             hid_usage_page = ((USAGE *)rawinput->data.hid.bRawData)[0];
             hid_usage = ((USAGE *)rawinput->data.hid.bRawData)[1];
         }
-        if (input->hi.uMsg == WM_INPUT && user_callbacks &&
-            !user_callbacks->rawinput_device_get_usages( rawinput->header.hDevice,
-                                                         &hid_usage_page, &hid_usage ))
+        if (input->hi.uMsg == WM_INPUT &&
+            !rawinput_device_get_usages( rawinput->header.hDevice, &hid_usage_page, &hid_usage ))
         {
             WARN( "unable to get HID usages for device %p\n", rawinput->header.hDevice );
             return STATUS_INVALID_HANDLE;
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index fd70c022c6f..82d70ec93bb 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -40,8 +40,6 @@ struct user_callbacks
     void (CDECL *notify_ime)( HWND hwnd, UINT param );
     BOOL (CDECL *post_dde_message)( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, DWORD dest_tid,
                                     DWORD type );
-    void (CDECL *rawinput_update_device_list)(void);
-    BOOL (CDECL *rawinput_device_get_usages)(HANDLE handle, USHORT *usage_page, USHORT *usage);
     void (WINAPI *set_standard_scroll_painted)( HWND hwnd, INT bar, BOOL visible );
     BOOL (CDECL *unpack_dde_message)( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
                                       void **buffer, size_t size );
diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c
index 1caf11f2e76..81403771c4a 100644
--- a/dlls/win32u/rawinput.c
+++ b/dlls/win32u/rawinput.c
@@ -24,8 +24,14 @@
 #endif
 
 #include <stdbool.h>
+#include <pthread.h>
+
 #include "win32u_private.h"
 #include "ntuser_private.h"
+#define WIN32_NO_STATUS
+#include "winioctl.h"
+#include "ddk/hidclass.h"
+#include "wine/hid.h"
 #include "wine/server.h"
 #include "wine/debug.h"
 
@@ -188,6 +194,432 @@ static bool rawinput_from_hardware_message( RAWINPUT *rawinput, const struct har
     return true;
 }
 
+struct device
+{
+    WCHAR *path;
+    HANDLE file;
+    HANDLE handle;
+    RID_DEVICE_INFO info;
+    struct hid_preparsed_data *data;
+};
+
+static struct device *rawinput_devices;
+static unsigned int rawinput_devices_count, rawinput_devices_max;
+
+static pthread_mutex_t rawinput_devices_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static bool array_reserve( void **elements, unsigned int *capacity, unsigned int count, unsigned int size )
+{
+    unsigned int new_capacity, max_capacity;
+    void *new_elements;
+
+    if (count <= *capacity)
+        return true;
+
+    max_capacity = ~(unsigned int)0 / size;
+    if (count > max_capacity)
+        return false;
+
+    new_capacity = max( 4, *capacity );
+    while (new_capacity < count && new_capacity <= max_capacity / 2)
+        new_capacity *= 2;
+    if (new_capacity < count)
+        new_capacity = max_capacity;
+
+    if (!(new_elements = realloc( *elements, new_capacity * size )))
+        return false;
+
+    *elements = new_elements;
+    *capacity = new_capacity;
+
+    return true;
+}
+
+static struct device *add_device( HKEY key, DWORD type )
+{
+    static const WCHAR symbolic_linkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k',0};
+    char value_buffer[4096];
+    KEY_VALUE_PARTIAL_INFORMATION *value = (KEY_VALUE_PARTIAL_INFORMATION *)value_buffer;
+    static const RID_DEVICE_INFO_KEYBOARD keyboard_info = {0, 0, 1, 12, 3, 101};
+    static const RID_DEVICE_INFO_MOUSE mouse_info = {1, 5, 0, FALSE};
+    struct hid_preparsed_data *preparsed = NULL;
+    HID_COLLECTION_INFORMATION hid_info;
+    struct device *device = NULL;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING string;
+    RID_DEVICE_INFO info;
+    IO_STATUS_BLOCK io;
+    WCHAR *path, *pos;
+    NTSTATUS status;
+    unsigned int i;
+    UINT32 handle;
+    HANDLE file;
+
+    if (!query_reg_value( key, symbolic_linkW, value, sizeof(value_buffer) ))
+    {
+        ERR( "failed to get symbolic link value\n" );
+        return NULL;
+    }
+
+    if (!(path = malloc( value->DataLength + sizeof(WCHAR) )))
+        return NULL;
+    memcpy( path, value->Data, value->DataLength );
+    path[value->DataLength / sizeof(WCHAR)] = 0;
+
+    /* upper case everything but the GUID */
+    for (pos = path; *pos && *pos != '{'; pos++) *pos = towupper( *pos );
+
+    /* path is in DOS format and begins with \\?\ prefix */
+    path[1] = '?';
+
+    RtlInitUnicodeString( &string, path );
+    InitializeObjectAttributes( &attr, &string, OBJ_CASE_INSENSITIVE, NULL, NULL );
+    if ((status = NtOpenFile( &file, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &attr, &io,
+                              FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT )))
+    {
+        ERR( "Failed to open device file %s, status %#x.\n", debugstr_w(path), status );
+        free( path );
+        return NULL;
+    }
+
+    path[1] = '\\';
+
+    status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io, IOCTL_HID_GET_WINE_RAWINPUT_HANDLE,
+                                    NULL, 0, &handle, sizeof(handle) );
+    if (status)
+    {
+        ERR( "Failed to get raw input handle, status %#x.\n", status );
+        goto fail;
+    }
+
+    memset( &info, 0, sizeof(info) );
+    info.cbSize = sizeof(info);
+    info.dwType = type;
+
+    switch (type)
+    {
+    case RIM_TYPEHID:
+        status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io,
+                                        IOCTL_HID_GET_COLLECTION_INFORMATION,
+                                        NULL, 0, &hid_info, sizeof(hid_info) );
+        if (status)
+        {
+            ERR( "Failed to get collection information, status %#x.\n", status );
+            goto fail;
+        }
+
+        info.hid.dwVendorId = hid_info.VendorID;
+        info.hid.dwProductId = hid_info.ProductID;
+        info.hid.dwVersionNumber = hid_info.VersionNumber;
+
+        if (!(preparsed = malloc( hid_info.DescriptorSize )))
+        {
+            ERR( "Failed to allocate memory.\n" );
+            goto fail;
+        }
+
+        status = NtDeviceIoControlFile( file, NULL, NULL, NULL, &io,
+                                        IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
+                                        NULL, 0, preparsed, hid_info.DescriptorSize );
+        if (status)
+        {
+            ERR( "Failed to get collection descriptor, status %#x.\n", status );
+            goto fail;
+        }
+
+        info.hid.usUsagePage = preparsed->usage_page;
+        info.hid.usUsage = preparsed->usage;
+        break;
+
+    case RIM_TYPEMOUSE:
+        info.mouse = mouse_info;
+        break;
+
+    case RIM_TYPEKEYBOARD:
+        info.keyboard = keyboard_info;
+        break;
+    }
+
+    for (i = 0; i < rawinput_devices_count && !device; ++i)
+    {
+        if (rawinput_devices[i].handle == UlongToHandle(handle))
+            device = rawinput_devices + i;
+    }
+
+    if (device)
+    {
+        TRACE( "Updating device %#x / %s.\n", handle, debugstr_w(path) );
+        free( device->data );
+        NtClose( device->file );
+        free( device->path );
+    }
+    else if (array_reserve( (void **)&rawinput_devices, &rawinput_devices_max,
+                            rawinput_devices_count + 1, sizeof(*rawinput_devices) ))
+    {
+        device = &rawinput_devices[rawinput_devices_count++];
+        TRACE( "Adding device %#x / %s.\n", handle, debugstr_w(path) );
+    }
+    else
+    {
+        ERR( "Failed to allocate memory.\n" );
+        goto fail;
+    }
+
+    device->path = path;
+    device->file = file;
+    device->handle = ULongToHandle(handle);
+    device->info = info;
+    device->data = preparsed;
+
+    return device;
+
+fail:
+    free( preparsed );
+    NtClose( file );
+    free( path );
+    return NULL;
+}
+
+static const WCHAR device_classesW[] =
+{
+    '\\','R','e','g','i','s','t','r','y',
+    '\\','M','a','c','h','i','n','e',
+    '\\','S','y','s','t','e','m',
+    '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
+    '\\','C','o','n','t','r','o','l',
+    '\\','D','e','v','i','c','e','C','l','a','s','s','e','s','\\',0
+};
+static const WCHAR guid_devinterface_hidW[] =
+{
+    '{','4','d','1','e','5','5','b','2','-','f','1','6','f','-','1','1','c','f',
+    '-','8','8','c','b','-','0','0','1','1','1','1','0','0','0','0','3','0','}',0
+};
+static const WCHAR guid_devinterface_keyboardW[] =
+{
+    '{','8','8','4','b','9','6','c','3','-','5','6','e','f','-','1','1','d','1',
+    '-','b','c','8','c','-','0','0','a','0','c','9','1','4','0','5','d','d','}',0
+};
+static const WCHAR guid_devinterface_mouseW[] =
+{
+    '{','3','7','8','d','e','4','4','c','-','5','6','e','f','-','1','1','d','1',
+    '-','b','c','8','c','-','0','0','a','0','c','9','1','4','0','5','d','d','}',0
+};
+
+static void enumerate_devices( DWORD type, const WCHAR *class )
+{
+    WCHAR buffer[1024];
+    KEY_NODE_INFORMATION *subkey_info = (void *)buffer;
+    HKEY class_key, device_key, iface_key;
+    unsigned int i, j;
+    DWORD size;
+
+    wcscpy( buffer, device_classesW );
+    wcscat( buffer, class );
+    if (!(class_key = reg_open_key( NULL, buffer, wcslen( buffer ) * sizeof(WCHAR) )))
+        return;
+
+    for (i = 0; !NtEnumerateKey( class_key, i, KeyNodeInformation, buffer, sizeof(buffer), &size ); ++i)
+    {
+        if (!(device_key = reg_open_key( class_key, subkey_info->Name, subkey_info->NameLength )))
+        {
+            ERR( "failed to open %s\n", debugstr_wn(subkey_info->Name, subkey_info->NameLength / sizeof(WCHAR)) );
+            continue;
+        }
+
+        for (j = 0; !NtEnumerateKey( device_key, j, KeyNodeInformation, buffer, sizeof(buffer), &size ); ++j)
+        {
+            if (!(iface_key = reg_open_key( device_key, subkey_info->Name, subkey_info->NameLength )))
+            {
+                ERR( "failed to open %s\n", debugstr_wn(subkey_info->Name, subkey_info->NameLength / sizeof(WCHAR)) );
+                continue;
+            }
+
+            add_device( iface_key, type );
+            NtClose( iface_key );
+        }
+
+        NtClose( device_key );
+    }
+
+    NtClose( class_key );
+}
+
+static void rawinput_update_device_list(void)
+{
+    unsigned int i;
+
+    TRACE( "\n" );
+
+    pthread_mutex_lock( &rawinput_devices_mutex );
+
+    /* destroy previous list */
+    for (i = 0; i < rawinput_devices_count; ++i)
+    {
+        free( rawinput_devices[i].data );
+        NtClose( rawinput_devices[i].file );
+        free( rawinput_devices[i].path );
+    }
+    rawinput_devices_count = 0;
+
+    enumerate_devices( RIM_TYPEHID, guid_devinterface_hidW );
+    enumerate_devices( RIM_TYPEMOUSE, guid_devinterface_mouseW );
+    enumerate_devices( RIM_TYPEKEYBOARD, guid_devinterface_keyboardW );
+
+    pthread_mutex_unlock( &rawinput_devices_mutex );
+}
+
+static struct device *find_device_from_handle( HANDLE handle )
+{
+    unsigned int i;
+
+    for (i = 0; i < rawinput_devices_count; ++i)
+    {
+        if (rawinput_devices[i].handle == handle)
+            return rawinput_devices + i;
+    }
+
+    rawinput_update_device_list();
+
+    for (i = 0; i < rawinput_devices_count; ++i)
+    {
+        if (rawinput_devices[i].handle == handle)
+            return rawinput_devices + i;
+    }
+    return NULL;
+}
+
+BOOL rawinput_device_get_usages( HANDLE handle, USAGE *usage_page, USAGE *usage )
+{
+    struct device *device;
+
+    *usage_page = *usage = 0;
+
+    if (!(device = find_device_from_handle( handle ))) return FALSE;
+    if (device->info.dwType != RIM_TYPEHID) return FALSE;
+
+    *usage_page = device->info.hid.usUsagePage;
+    *usage = device->info.hid.usUsage;
+    return TRUE;
+}
+
+/**********************************************************************
+ *         NtUserGetRawInputDeviceList   (win32u.@)
+ */
+UINT WINAPI NtUserGetRawInputDeviceList( RAWINPUTDEVICELIST *devices, UINT *device_count, UINT size )
+{
+    static unsigned int last_check;
+    unsigned int i, ticks = NtGetTickCount();
+
+    TRACE("devices %p, device_count %p, size %u.\n", devices, device_count, size);
+
+    if (size != sizeof(*devices))
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return ~0u;
+    }
+
+    if (!device_count)
+    {
+        SetLastError( ERROR_NOACCESS );
+        return ~0u;
+    }
+
+    if (ticks - last_check > 2000)
+    {
+        last_check = ticks;
+        rawinput_update_device_list();
+    }
+
+    if (!devices)
+    {
+        *device_count = rawinput_devices_count;
+        return 0;
+    }
+
+    if (*device_count < rawinput_devices_count)
+    {
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        *device_count = rawinput_devices_count;
+        return ~0u;
+    }
+
+    for (i = 0; i < rawinput_devices_count; ++i)
+    {
+        devices[i].hDevice = rawinput_devices[i].handle;
+        devices[i].dwType = rawinput_devices[i].info.dwType;
+    }
+
+    return rawinput_devices_count;
+}
+
+/**********************************************************************
+ *         NtUserGetRawInputDeviceInfo   (win32u.@)
+ */
+UINT WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data, UINT *data_size )
+{
+    const struct hid_preparsed_data *preparsed;
+    struct device *device;
+    RID_DEVICE_INFO info;
+    DWORD len, data_len;
+
+    TRACE( "handle %p, command %#x, data %p, data_size %p.\n", handle, command, data, data_size );
+
+    if (!data_size)
+    {
+        SetLastError( ERROR_NOACCESS );
+        return ~0u;
+    }
+    if (!(device = find_device_from_handle( handle )))
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return ~0u;
+    }
+
+    data_len = *data_size;
+    switch (command)
+    {
+    case RIDI_DEVICENAME:
+        if ((len = wcslen( device->path ) + 1) <= data_len && data)
+            memcpy( data, device->path, len * sizeof(WCHAR) );
+        *data_size = len;
+        break;
+
+    case RIDI_DEVICEINFO:
+        if ((len = sizeof(info)) <= data_len && data)
+            memcpy( data, &device->info, len );
+        *data_size = len;
+        break;
+
+    case RIDI_PREPARSEDDATA:
+        if (!(preparsed = device->data))
+            len = 0;
+        else
+            len = preparsed->caps_size + FIELD_OFFSET(struct hid_preparsed_data, value_caps[0]) +
+                  preparsed->number_link_collection_nodes * sizeof(struct hid_collection_node);
+
+        if (preparsed && len <= data_len && data)
+            memcpy( data, preparsed, len );
+        *data_size = len;
+        break;
+
+    default:
+        FIXME( "command %#x not supported\n", command );
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return ~0u;
+    }
+
+    if (!data)
+        return 0;
+
+    if (data_len < len)
+    {
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        return ~0u;
+    }
+
+    return *data_size;
+}
+
 /**********************************************************************
  *         NtUserGetRawInputBuffer   (win32u.@)
  */
@@ -353,7 +785,7 @@ BOOL process_rawinput_message( MSG *msg, UINT hw_id, const struct hardware_msg_d
 
     if (msg->message == WM_INPUT_DEVICE_CHANGE)
     {
-        if (user_callbacks) user_callbacks->rawinput_update_device_list();
+        rawinput_update_device_list();
     }
     else
     {
diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c
index 49746725816..e68519f15e5 100644
--- a/dlls/win32u/syscall.c
+++ b/dlls/win32u/syscall.c
@@ -147,6 +147,8 @@ static void * const syscalls[] =
     NtUserGetProp,
     NtUserGetRawInputBuffer,
     NtUserGetRawInputData,
+    NtUserGetRawInputDeviceInfo,
+    NtUserGetRawInputDeviceList,
     NtUserGetRegisteredRawInputDevices,
     NtUserGetSystemDpiForProcess,
     NtUserGetThreadDesktop,
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index dab6e2a5ed8..68201f763ab 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -985,8 +985,8 @@
 @ stub NtUserGetQueueStatusReadonly
 @ stdcall -syscall NtUserGetRawInputBuffer(ptr ptr long)
 @ stdcall -syscall NtUserGetRawInputData(ptr long ptr ptr long)
-@ stub NtUserGetRawInputDeviceInfo
-@ stub NtUserGetRawInputDeviceList
+@ stdcall -syscall NtUserGetRawInputDeviceInfo(ptr long ptr ptr)
+@ stdcall -syscall NtUserGetRawInputDeviceList(ptr ptr long)
 @ stub NtUserGetRawPointerDeviceData
 @ stdcall -syscall NtUserGetRegisteredRawInputDevices(ptr ptr long)
 @ stub NtUserGetRequiredCursorSizes
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index a3f3855de7a..d3f359fd4ee 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -437,6 +437,7 @@ extern LRESULT send_message_timeout( HWND hwnd, UINT msg, WPARAM wparam, LPARAM
 
 /* rawinput.c */
 extern BOOL process_rawinput_message( MSG *msg, UINT hw_id, const struct hardware_msg_data *msg_data ) DECLSPEC_HIDDEN;
+extern BOOL rawinput_device_get_usages( HANDLE handle, USHORT *usage_page, USHORT *usage ) DECLSPEC_HIDDEN;
 
 /* sysparams.c */
 extern BOOL enable_thunk_lock DECLSPEC_HIDDEN;
diff --git a/dlls/wow64win/syscall.h b/dlls/wow64win/syscall.h
index 903f146025d..30b6795f8e8 100644
--- a/dlls/wow64win/syscall.h
+++ b/dlls/wow64win/syscall.h
@@ -134,6 +134,8 @@
     SYSCALL_ENTRY( NtUserGetProp ) \
     SYSCALL_ENTRY( NtUserGetRawInputBuffer ) \
     SYSCALL_ENTRY( NtUserGetRawInputData ) \
+    SYSCALL_ENTRY( NtUserGetRawInputDeviceInfo ) \
+    SYSCALL_ENTRY( NtUserGetRawInputDeviceList ) \
     SYSCALL_ENTRY( NtUserGetRegisteredRawInputDevices ) \
     SYSCALL_ENTRY( NtUserGetSystemDpiForProcess ) \
     SYSCALL_ENTRY( NtUserGetThreadDesktop ) \
diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c
index 87c522071aa..d582e5a7282 100644
--- a/dlls/wow64win/user.c
+++ b/dlls/wow64win/user.c
@@ -72,6 +72,12 @@ typedef struct
     UINT32 hwndTarget;
 } RAWINPUTDEVICE32;
 
+typedef struct
+{
+    UINT32 hDevice;
+    DWORD dwType;
+} RAWINPUTDEVICELIST32;
+
 static MSG *msg_32to64( MSG *msg, MSG32 *msg32 )
 {
     if (!msg32) return NULL;
@@ -1056,3 +1062,52 @@ NTSTATUS WINAPI wow64_NtUserGetRegisteredRawInputDevices( UINT *args )
         return NtUserGetRegisteredRawInputDevices( NULL, count, sizeof(RAWINPUTDEVICE) );
     }
 }
+
+NTSTATUS WINAPI wow64_NtUserGetRawInputDeviceInfo( UINT *args )
+{
+    HANDLE handle = get_handle( &args );
+    UINT command = get_ulong( &args );
+    void *data = get_ptr( &args );
+    UINT *data_size = get_ptr( &args );
+
+    return NtUserGetRawInputDeviceInfo( handle, command, data, data_size );
+}
+
+NTSTATUS WINAPI wow64_NtUserGetRawInputDeviceList( UINT *args )
+{
+    RAWINPUTDEVICELIST32 *devices32 = get_ptr( &args );
+    UINT *count = get_ptr( &args );
+    UINT size = get_ulong( &args );
+
+    if (size != sizeof(RAWINPUTDEVICELIST32))
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return ~0u;
+    }
+
+    if (devices32)
+    {
+        RAWINPUTDEVICELIST *devices64;
+        unsigned int ret, i;
+
+        if (!(devices64 = Wow64AllocateTemp( (*count) * sizeof(*devices64) )))
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            return FALSE;
+        }
+
+        ret = NtUserGetRawInputDeviceList( devices64, count, sizeof(RAWINPUTDEVICELIST) );
+        if (ret == ~0u) return ret;
+
+        for (i = 0; i < *count; ++i)
+        {
+            devices32[i].hDevice = (UINT_PTR)devices64[i].hDevice;
+            devices32[i].dwType  = devices64[i].dwType;
+        }
+        return ret;
+    }
+    else
+    {
+        return NtUserGetRawInputDeviceList( NULL, count, sizeof(RAWINPUTDEVICELIST) );
+    }
+}
diff --git a/include/ntuser.h b/include/ntuser.h
index 9c4aaf041a4..3862eb55054 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -621,6 +621,8 @@ ULONG   WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process );
 DWORD   WINAPI NtUserGetQueueStatus( UINT flags );
 UINT    WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT header_size );
 UINT    WINAPI NtUserGetRawInputData( HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size );
+UINT    WINAPI NtUserGetRawInputDeviceInfo( HANDLE handle, UINT command, void *data, UINT *data_size );
+UINT    WINAPI NtUserGetRawInputDeviceList( RAWINPUTDEVICELIST *devices, UINT *device_count, UINT size );
 UINT    WINAPI NtUserGetRegisteredRawInputDevices( RAWINPUTDEVICE *devices, UINT *device_count, UINT size );
 ULONG   WINAPI NtUserGetSystemDpiForProcess( HANDLE process );
 HMENU   WINAPI NtUserGetSystemMenu( HWND hwnd, BOOL revert );
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/313



More information about the wine-devel mailing list