[HID_WINETEST] Import from Wine Staging 3.3. CORE-14434
authorAmine Khaldi <amine.khaldi@reactos.org>
Tue, 3 Apr 2018 12:32:05 +0000 (13:32 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Tue, 3 Apr 2018 12:32:05 +0000 (13:32 +0100)
dll/win32/hid/CMakeLists.txt
modules/rostests/winetests/CMakeLists.txt
modules/rostests/winetests/hid/CMakeLists.txt [new file with mode: 0644]
modules/rostests/winetests/hid/device.c [new file with mode: 0644]
modules/rostests/winetests/hid/testlist.c [new file with mode: 0644]

index e045cfe..9e966ec 100644 (file)
@@ -1,11 +1,11 @@
 
+spec2def(hid.dll hid.spec ADD_IMPORTLIB)
+
 list(APPEND SOURCE
     hid.c
     stubs.c
     precomp.h)
 
-spec2def(hid.dll hid.spec)
-
 add_library(hid SHARED
     ${SOURCE}
     hid.rc
index fff038f..959db2b 100644 (file)
@@ -30,6 +30,7 @@ add_subdirectory(faultrep)
 add_subdirectory(fusion)
 add_subdirectory(gdi32)
 add_subdirectory(gdiplus)
+add_subdirectory(hid)
 add_subdirectory(hlink)
 add_subdirectory(hnetcfg)
 add_subdirectory(imagehlp)
diff --git a/modules/rostests/winetests/hid/CMakeLists.txt b/modules/rostests/winetests/hid/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6a20859
--- /dev/null
@@ -0,0 +1,5 @@
+
+add_executable(hid_winetest device.c testlist.c)
+set_module_type(hid_winetest win32cui)
+add_importlibs(hid_winetest hid setupapi msvcrt kernel32)
+add_rostests_file(TARGET hid_winetest)
diff --git a/modules/rostests/winetests/hid/device.c b/modules/rostests/winetests/hid/device.c
new file mode 100644 (file)
index 0000000..ce9b3a5
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2017 Aric Stewart
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windows.h"
+#include "setupapi.h"
+#include "hidusage.h"
+#include "ddk/hidsdi.h"
+
+#include "wine/test.h"
+
+#define READ_MAX_TIME 5000
+
+typedef void (device_test)(HANDLE device);
+
+static void test_device_info(HANDLE device)
+{
+    PHIDP_PREPARSED_DATA ppd;
+    HIDP_CAPS Caps;
+    NTSTATUS status;
+    BOOL rc;
+    WCHAR device_name[128];
+
+    rc = HidD_GetPreparsedData(device, &ppd);
+    ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
+    status = HidP_GetCaps(ppd, &Caps);
+    ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
+    rc = HidD_GetProductString(device, device_name, sizeof(device_name));
+    ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
+    trace("Found device %s (%02x, %02x)\n", wine_dbgstr_w(device_name), Caps.UsagePage, Caps.Usage);
+    rc = HidD_FreePreparsedData(ppd);
+    ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
+}
+
+static void run_for_each_device(device_test *test)
+{
+    GUID hid_guid;
+    HDEVINFO info_set;
+    DWORD index = 0;
+    SP_DEVICE_INTERFACE_DATA interface_data;
+    DWORD detail_size = MAX_PATH * sizeof(WCHAR);
+    SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
+
+    HidD_GetHidGuid(&hid_guid);
+
+    ZeroMemory(&interface_data, sizeof(interface_data));
+    interface_data.cbSize = sizeof(interface_data);
+
+    data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data) + detail_size);
+    data->cbSize = sizeof(*data);
+
+    info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
+    while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
+    {
+        index ++;
+
+        if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
+        {
+            HANDLE file = CreateFileW(data->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+            if (file == INVALID_HANDLE_VALUE)
+            {
+                trace("Failed to access device %s, likely not plugged in or access is denied.\n", wine_dbgstr_w(data->DevicePath));
+                continue;
+            }
+
+            test(file);
+
+            CloseHandle(file);
+        }
+    }
+    HeapFree(GetProcessHeap(), 0, data);
+    SetupDiDestroyDeviceInfoList(info_set);
+}
+
+static HANDLE get_device(USHORT page, USHORT usages[], UINT usage_count, DWORD access)
+{
+    GUID hid_guid;
+    HDEVINFO info_set;
+    DWORD index = 0;
+    SP_DEVICE_INTERFACE_DATA interface_data;
+    DWORD detail_size = MAX_PATH * sizeof(WCHAR);
+    SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
+    NTSTATUS status;
+    BOOL rc;
+
+    HidD_GetHidGuid(&hid_guid);
+
+    ZeroMemory(&interface_data, sizeof(interface_data));
+    interface_data.cbSize = sizeof(interface_data);
+
+    data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + detail_size);
+    data->cbSize = sizeof(*data);
+
+    info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
+    while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
+    {
+        index ++;
+
+        if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
+        {
+            PHIDP_PREPARSED_DATA ppd;
+            HIDP_CAPS Caps;
+            HANDLE file = CreateFileW(data->DevicePath, access, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+            if (file == INVALID_HANDLE_VALUE)
+            {
+                trace("Failed to access device %s, likely not plugged in or access is denied.\n", wine_dbgstr_w(data->DevicePath));
+                continue;
+            }
+            rc = HidD_GetPreparsedData(file, &ppd);
+            ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
+            status = HidP_GetCaps(ppd, &Caps);
+            ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
+            rc = HidD_FreePreparsedData(ppd);
+            ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
+            if (!page || page == Caps.UsagePage)
+            {
+                int j;
+                if (!usage_count)
+                {
+                    HeapFree(GetProcessHeap(), 0, data);
+                    SetupDiDestroyDeviceInfoList(info_set);
+                    return file;
+                }
+                for (j = 0; j < usage_count; j++)
+                    if (!usages[j] || usages[j] == Caps.Usage)
+                    {
+                        HeapFree(GetProcessHeap(), 0, data);
+                        SetupDiDestroyDeviceInfoList(info_set);
+                        return file;
+                    }
+            }
+            CloseHandle(file);
+        }
+    }
+    HeapFree(GetProcessHeap(), 0, data);
+    SetupDiDestroyDeviceInfoList(info_set);
+    return NULL;
+}
+
+static void process_data(HIDP_CAPS Caps, PHIDP_PREPARSED_DATA ppd, CHAR *data, DWORD data_length)
+{
+    INT i;
+    NTSTATUS status;
+
+    if (Caps.NumberInputButtonCaps)
+    {
+        USAGE button_pages[100];
+
+        for (i = 1; i < 0xff; i++)
+        {
+            ULONG usage_length = 100;
+            status = HidP_GetUsages(HidP_Input, i, 0, button_pages, &usage_length, ppd, data, data_length);
+            ok (status == HIDP_STATUS_SUCCESS || usage_length == 0,
+                "HidP_GetUsages failed (%x) but usage length still %i\n", status, usage_length);
+            if (usage_length)
+            {
+                CHAR report[50];
+                int count;
+                int j;
+
+                count = usage_length;
+                j = 0;
+                report[0] = 0;
+                trace("\tButtons [0x%x: %i buttons]:\n", i, usage_length);
+                for (count = 0; count < usage_length; count += 15)
+                {
+                    for (j=count; j < count+15 && j < usage_length; j++)
+                    {
+                        CHAR btn[7];
+                        sprintf(btn, "%i ", button_pages[j]);
+                        strcat(report, btn);
+                    }
+                    trace("\t\t%s\n", report);
+                }
+            }
+        }
+    }
+
+    if (Caps.NumberInputValueCaps)
+    {
+        ULONG value;
+        USHORT length;
+        HIDP_VALUE_CAPS *values = NULL;
+
+        values = HeapAlloc(GetProcessHeap(), 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps);
+        length = Caps.NumberInputValueCaps;
+        status = HidP_GetValueCaps(HidP_Input, values, &length, ppd);
+        ok(status == HIDP_STATUS_SUCCESS, "Failed to get value caps (%x)\n",status);
+
+        trace("\tValues:\n");
+        for (i = 0; i < length; i++)
+        {
+            status = HidP_GetUsageValue(HidP_Input, values[i].UsagePage, 0,
+                values[i].Range.UsageMin, &value, ppd, data, data_length);
+            ok(status == HIDP_STATUS_SUCCESS, "Failed to get value [%i,%i] (%x)\n",
+                values[i].UsagePage, values[i].Range.UsageMin, status);
+            trace("[%02x, %02x]: %u\n",values[i].UsagePage, values[i].Range.UsageMin, value);
+        }
+
+        HeapFree(GetProcessHeap(), 0, values);
+    }
+}
+
+static void test_read_device(void)
+{
+    PHIDP_PREPARSED_DATA ppd;
+    HIDP_CAPS Caps;
+    OVERLAPPED overlapped;
+    WCHAR device_name[128];
+    CHAR *data = NULL;
+    DWORD read;
+    BOOL rc;
+    NTSTATUS status;
+    DWORD timeout, tick, spent, max_time;
+    char *report;
+
+    USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
+    HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
+
+    if (!device)
+        device = get_device(0x0, NULL, 0x0, GENERIC_READ);
+
+    if (!device)
+    {
+        trace("No device found for reading\n");
+        return;
+    }
+    rc = HidD_GetProductString(device, device_name, sizeof(device_name));
+    ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
+    trace("Read tests on device :%s\n",wine_dbgstr_w(device_name));
+
+    rc = HidD_GetPreparsedData(device, &ppd);
+    ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
+    status = HidP_GetCaps(ppd, &Caps);
+    ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
+    data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
+
+    memset(&overlapped, 0, sizeof(overlapped));
+    overlapped.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+    if (winetest_interactive)
+    {
+        max_time = READ_MAX_TIME;
+        timeout = 1000;
+    }
+    else
+        max_time = timeout = 100;
+    if (winetest_interactive)
+        trace("Test your device for the next %i seconds\n", max_time/1000);
+    report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
+    tick = GetTickCount();
+    spent = 0;
+    do
+    {
+        ReadFile(device, data, Caps.InputReportByteLength, NULL, &overlapped);
+        if (WaitForSingleObject(overlapped.hEvent, timeout) != WAIT_OBJECT_0)
+        {
+            ResetEvent(overlapped.hEvent);
+            spent = GetTickCount() - tick;
+            trace("REMAINING: %d ms\n", max_time - spent);
+            continue;
+        }
+        ResetEvent(overlapped.hEvent);
+        spent = GetTickCount() - tick;
+        GetOverlappedResult(device, &overlapped, &read, FALSE);
+        if (read)
+        {
+            int i;
+
+            report[0] = 0;
+            for (i = 0; i < read && i < Caps.InputReportByteLength; i++)
+            {
+                char bytestr[5];
+                sprintf(bytestr, "%x ", (BYTE)data[i]);
+                strcat(report, bytestr);
+            }
+            trace("Input report (%i): %s\n", read, report);
+
+            process_data(Caps, ppd, data, read);
+        }
+        trace("REMAINING: %d ms\n", max_time - spent);
+    } while(spent < max_time);
+
+    CloseHandle(overlapped.hEvent);
+    rc = HidD_FreePreparsedData(ppd);
+    ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
+    CloseHandle(device);
+    HeapFree(GetProcessHeap(), 0, data);
+    HeapFree(GetProcessHeap(), 0, report);
+}
+
+static void test_get_input_report(void)
+{
+    PHIDP_PREPARSED_DATA ppd;
+    HIDP_CAPS Caps;
+    WCHAR device_name[128];
+    CHAR *data = NULL;
+    DWORD tick, spent, max_time;
+    char *report;
+    BOOL rc;
+    NTSTATUS status;
+
+    USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
+    HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
+
+    if (!device)
+        device = get_device(0x0, NULL, 0x0, GENERIC_READ);
+
+    if (!device)
+    {
+        trace("No device found for testing\n");
+        return;
+    }
+    rc = HidD_GetProductString(device, device_name, sizeof(device_name));
+    ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
+    trace("HidD_GetInputRpeort tests on device :%s\n",wine_dbgstr_w(device_name));
+
+    rc = HidD_GetPreparsedData(device, &ppd);
+    ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
+    status = HidP_GetCaps(ppd, &Caps);
+    ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
+    data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
+
+    if (winetest_interactive)
+        max_time = READ_MAX_TIME;
+    else
+        max_time = 100;
+    if (winetest_interactive)
+        trace("Test your device for the next %i seconds\n", max_time/1000);
+    report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
+    tick = GetTickCount();
+    spent = 0;
+    do
+    {
+        int i;
+
+        data[0] = 0; /* Just testing report ID 0 for now, That will catch most devices */
+        rc = HidD_GetInputReport(device, data, Caps.InputReportByteLength);
+        spent = GetTickCount() - tick;
+
+        if (rc)
+        {
+            ok(data[0] == 0, "Report ID (0) is not the first byte of the data\n");
+            report[0] = 0;
+            for (i = 0; i < Caps.InputReportByteLength; i++)
+            {
+                char bytestr[5];
+                sprintf(bytestr, "%x ", (BYTE)data[i]);
+                strcat(report, bytestr);
+            }
+            trace("Input report (%i): %s\n", Caps.InputReportByteLength, report);
+
+            process_data(Caps, ppd, data, Caps.InputReportByteLength);
+        }
+        else
+            trace("Failed to get Input Report, (%x)\n", rc);
+        trace("REMAINING: %d ms\n", max_time - spent);
+        Sleep(500);
+    } while(spent < max_time);
+
+    rc = HidD_FreePreparsedData(ppd);
+    ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
+    CloseHandle(device);
+    HeapFree(GetProcessHeap(), 0, data);
+    HeapFree(GetProcessHeap(), 0, report);
+}
+
+START_TEST(device)
+{
+    run_for_each_device(test_device_info);
+    test_read_device();
+    test_get_input_report();
+}
diff --git a/modules/rostests/winetests/hid/testlist.c b/modules/rostests/winetests/hid/testlist.c
new file mode 100644 (file)
index 0000000..12e1f21
--- /dev/null
@@ -0,0 +1,12 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define STANDALONE
+#include <wine/test.h>
+
+extern void func_device(void);
+
+const struct test winetest_testlist[] =
+{
+    { "device", func_device },
+    { 0, 0 }
+};