Correct DInput implementation will require it to iterate on the internal HID structures, instead of using the higher-level value / button caps abstraction.
This will make Wine DInput incompatible with native HID stack, unless the internal structure match.
These tests exhibit native internal structures, which are close to what Wine uses, so it should not be too complicated to restore compatibility.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput8/tests/hid.c | 470 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+)
diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 86e878d5ecb..cb1f4d61b7a 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -2710,6 +2710,475 @@ static void test_hid_driver( DWORD report_id, DWORD polled ) SetCurrentDirectoryW( cwd ); }
+/* undocumented HID internal preparsed data structure */ + +struct hidp_kdr_caps +{ + USHORT usage_page; + UCHAR report_id; + UCHAR start_bit; + USHORT bit_size; + USHORT report_count; + USHORT start_byte; + USHORT total_bits; + ULONG bit_field; + USHORT end_byte; + USHORT link_collection; + USAGE link_usage_page; + USAGE link_usage; + ULONG flags; + ULONG padding[8]; + USAGE usage_min; + USAGE usage_max; + USHORT string_min; + USHORT string_max; + USHORT designator_min; + USHORT designator_max; + USHORT data_index_min; + USHORT data_index_max; + USHORT null_value; + USHORT unknown; + LONG logical_min; + LONG logical_max; + LONG physical_min; + LONG physical_max; + LONG units; + LONG units_exp; +}; + +/* named array continues on next caps */ +#define HIDP_KDR_CAPS_ARRAY_HAS_MORE 0x01 +#define HIDP_KDR_CAPS_IS_CONSTANT 0x02 +#define HIDP_KDR_CAPS_IS_BUTTON 0x04 +#define HIDP_KDR_CAPS_IS_ABSOLUTE 0x08 +#define HIDP_KDR_CAPS_IS_RANGE 0x10 +#define HIDP_KDR_CAPS_IS_STRING_RANGE 0x40 +#define HIDP_KDR_CAPS_IS_DESIGNATOR_RANGE 0x80 + +struct hidp_kdr_node +{ + USAGE usage; + USAGE usage_page; + USHORT parent; + USHORT number_of_children; + USHORT next_sibling; + USHORT first_child; + ULONG collection_type; +}; + +struct hidp_kdr +{ + char magic[8]; + USAGE usage; + USAGE usage_page; + USHORT unknown[2]; + USHORT input_caps_start; + USHORT input_caps_count; + USHORT input_caps_end; + USHORT input_report_byte_length; + USHORT output_caps_start; + USHORT output_caps_count; + USHORT output_caps_end; + USHORT output_report_byte_length; + USHORT feature_caps_start; + USHORT feature_caps_count; + USHORT feature_caps_end; + USHORT feature_report_byte_length; + USHORT caps_size; + USHORT number_link_collection_nodes; + struct hidp_kdr_caps caps[1]; + /* struct hidp_kdr_node nodes[1] */ +}; + +static void test_hidp_kdr(void) +{ +#include "psh_hid_macros.h" + const unsigned char report_desc[] = + { + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Application), + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 127), + PHYSICAL_MINIMUM(1, -128), + PHYSICAL_MAXIMUM(1, 127), + + USAGE(1, HID_USAGE_GENERIC_RZ), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 0), + FEATURE(1, Data|Var|Abs), + USAGE(1, HID_USAGE_GENERIC_SLIDER), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + FEATURE(1, Data|Var|Abs), + + USAGE(1, HID_USAGE_GENERIC_X), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + UNIT(1, 0x100e), + UNIT_EXPONENT(1, -3), + INPUT(1, Data|Var|Abs), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), + USAGE(1, HID_USAGE_GENERIC_Y), + DESIGNATOR_MINIMUM(1, 1), + DESIGNATOR_MAXIMUM(1, 4), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + INPUT(1, Cnst|Var|Abs), + USAGE(1, HID_USAGE_GENERIC_Z), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + INPUT(1, Data|Var|Rel), + USAGE(1, HID_USAGE_GENERIC_RX), + USAGE(1, HID_USAGE_GENERIC_RY), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 2), + LOGICAL_MINIMUM(1, 7), + INPUT(1, Data|Var|Abs|Null), + + COLLECTION(1, Application), + USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|1), + USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|2), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 8), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + INPUT(1, Data|Var|Abs), + + USAGE_MINIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|3), + USAGE_MAXIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|8), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + LOGICAL_MINIMUM(1, 3), + LOGICAL_MAXIMUM(1, 8), + INPUT(1, Data|Ary|Abs), + + USAGE_MINIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|9), + USAGE_MAXIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|12), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 4), + LOGICAL_MINIMUM(1, 9), + LOGICAL_MAXIMUM(1, 12), + INPUT(2, Data|Ary|Abs|Buff), + + USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|13), + USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|14), + USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|15), + USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|16), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + LOGICAL_MINIMUM(1, 13), + LOGICAL_MAXIMUM(1, 16), + INPUT(1, Data|Ary|Abs), + END_COLLECTION, + END_COLLECTION, + }; +#include "pop_hid_macros.h" + + static const HIDP_CAPS expect_hidp_caps = + { + .Usage = HID_USAGE_GENERIC_JOYSTICK, + .UsagePage = HID_USAGE_PAGE_GENERIC, + .InputReportByteLength = 15, + }; + static const HID_DEVICE_ATTRIBUTES attributes = + { + .Size = sizeof(HID_DEVICE_ATTRIBUTES), + .VendorID = 0x1209, + .ProductID = 0x0001, + .VersionNumber = 0x0100, + }; + static const struct hidp_kdr expect_kdr = + { + .magic = "HidP KDR", + .usage = 0x04, + .usage_page = 0x01, + .input_caps_count = 13, + .input_caps_end = 13, + .input_report_byte_length = 15, + .output_caps_start = 13, + .output_caps_end = 13, + .feature_caps_start = 13, + .feature_caps_count = 2, + .feature_caps_end = 14, + .feature_report_byte_length = 3, + .caps_size = 1560, + .number_link_collection_nodes = 2, + }; + static const struct hidp_kdr_caps expect_caps[] = + { + { + .usage_page = 0x01, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x1, .total_bits = 0x08, + .bit_field = 0x002, .end_byte = 0x2, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08, + .usage_min = 0x30, .usage_max = 0x30, .logical_min = 1, .logical_max = 127, .physical_min = -128, + .physical_max = 127, .units = 0xe, .units_exp = -3 + }, + { + .usage_page = 0x01, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x2, .total_bits = 0x08, + .bit_field = 0x003, .end_byte = 0x3, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x8a, + .usage_min = 0x31, .usage_max = 0x31, .designator_min = 1, .designator_max = 4, .data_index_min = 0x01, + .data_index_max = 0x01, .logical_min = 1, .logical_max = 127, .physical_min = -128, .physical_max = 127 + }, + { + .usage_page = 0x01, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x3, .total_bits = 0x08, + .bit_field = 0x006, .end_byte = 0x4, .link_usage_page = 0x01, .link_usage = 0x04, .usage_min = 0x32, + .usage_max = 0x32, .data_index_min = 0x02, .data_index_max = 0x02, .logical_min = 1, .logical_max = 127, + .physical_min = -128, .physical_max = 127 + }, + { + .usage_page = 0x01, .bit_size = 0x10, .report_count = 0x1, .start_byte = 0x6, .total_bits = 0x10, + .bit_field = 0x042, .end_byte = 0x8, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08, + .usage_min = 0x34, .usage_max = 0x34, .data_index_min = 0x03, .data_index_max = 0x03, .null_value = 1, + .logical_min = 7, .logical_max = 127, .physical_min = -128, .physical_max = 127 + }, + { + .usage_page = 0x01, .bit_size = 0x10, .report_count = 0x1, .start_byte = 0x4, .total_bits = 0x10, + .bit_field = 0x042, .end_byte = 0x6, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08, + .usage_min = 0x33, .usage_max = 0x33, .data_index_min = 0x04, .data_index_max = 0x04, .null_value = 1, + .logical_min = 7, .logical_max = 127, .physical_min = -128, .physical_max = 127 + }, + { + .usage_page = 0x09, .start_bit = 1, .bit_size = 0x01, .report_count = 0x7, .start_byte = 0x8, .total_bits = 0x07, + .bit_field = 0x002, .end_byte = 0x9, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0c, + .usage_min = 0x02, .usage_max = 0x02, .data_index_min = 0x05, .data_index_max = 0x05, + }, + { + .usage_page = 0x09, .bit_size = 0x01, .report_count = 0x1, .start_byte = 0x8, .total_bits = 0x01, + .bit_field = 0x002, .end_byte = 0x9, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0c, + .usage_min = 0x01, .usage_max = 0x01, .data_index_min = 0x06, .data_index_max = 0x06, + }, + { + .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x9, .total_bits = 0x08, + .bit_field = 0x000, .end_byte = 0xa, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x1c, + .usage_min = 0x03, .usage_max = 0x08, .data_index_min = 0x07, .data_index_max = 0x0c, .null_value = 3, + .logical_min = 8 + }, + { + .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x4, .start_byte = 0xa, .total_bits = 0x20, + .bit_field = 0x100, .end_byte = 0xe, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x1c, + .usage_min = 0x09, .usage_max = 0x0c, .data_index_min = 0x0d, .data_index_max = 0x10, .null_value = 9, + .logical_min = 12 + }, + { + .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08, + .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0d, + .usage_min = 0x10, .usage_max = 0x10, .data_index_min = 0x14, .data_index_max = 0x14, .null_value = 13, + .logical_min = 16 + }, + { + .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08, + .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0d, + .usage_min = 0x0f, .usage_max = 0x0f, .data_index_min = 0x13, .data_index_max = 0x13, .null_value = 13, + .logical_min = 16 + }, + { + .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08, + .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0d, + .usage_min = 0x0e, .usage_max = 0x0e, .data_index_min = 0x12, .data_index_max = 0x12, .null_value = 13, + .logical_min = 16 + }, + { + .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08, + .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0c, + .usage_min = 0x0d, .usage_max = 0x0d, .data_index_min = 0x11, .data_index_max = 0x11, .null_value = 13, + .logical_min = 16 + }, + { + .usage_page = 0x01, .bit_size = 0x10, .report_count = 0x1, .start_byte = 0x1, .total_bits = 0x10, + .bit_field = 0x002, .end_byte = 0x3, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08, + .usage_min = 0x36, .usage_max = 0x36, .logical_min = 1, .logical_max = 127, .physical_min = -128, + .physical_max = 127 + }, + { + }, + }; + static const struct hidp_kdr_node expect_nodes[] = + { + { + .usage = 0x04, + .usage_page = 0x01, + .parent = 0, + .number_of_children = 0x1, + .next_sibling = 0, + .first_child = 0x1, + .collection_type = 0x1, + }, + { + .usage = 0x00, + .usage_page = 0x01, + .parent = 0, + .number_of_children = 0, + .next_sibling = 0, + .first_child = 0, + .collection_type = 0x1, + }, + }; + + char buffer[FIELD_OFFSET( SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath[MAX_PATH] )]; + SP_DEVICE_INTERFACE_DATA iface = {sizeof(SP_DEVICE_INTERFACE_DATA)}; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (void *)buffer; + SP_DEVINFO_DATA device = {sizeof(SP_DEVINFO_DATA)}; + WCHAR cwd[MAX_PATH], tempdir[MAX_PATH]; + PHIDP_PREPARSED_DATA preparsed_data; + DWORD i, report_id = 0, polled = 0; + struct hidp_kdr *kdr; + LSTATUS status; + HDEVINFO set; + HANDLE file; + HKEY hkey; + BOOL ret; + + GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd ); + GetTempPathW( ARRAY_SIZE(tempdir), tempdir ); + SetCurrentDirectoryW( tempdir ); + + status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\CurrentControlSet\Services\winetest", + 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ); + ok( !status, "RegCreateKeyExW returned %#x\n", status ); + + status = RegSetValueExW( hkey, L"ReportID", 0, REG_DWORD, (void *)&report_id, sizeof(report_id) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + status = RegSetValueExW( hkey, L"PolledMode", 0, REG_DWORD, (void *)&polled, sizeof(polled) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + status = RegSetValueExW( hkey, L"Descriptor", 0, REG_BINARY, (void *)report_desc, sizeof(report_desc) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + status = RegSetValueExW( hkey, L"Attributes", 0, REG_BINARY, (void *)&attributes, sizeof(attributes) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + status = RegSetValueExW( hkey, L"Caps", 0, REG_BINARY, (void *)&expect_hidp_caps, sizeof(expect_hidp_caps) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + status = RegSetValueExW( hkey, L"Expect", 0, REG_BINARY, NULL, 0 ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + status = RegSetValueExW( hkey, L"Input", 0, REG_BINARY, NULL, 0 ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + if (!pnp_driver_start( L"driver_hid.dll" )) goto done; + + set = SetupDiGetClassDevsW( &GUID_DEVINTERFACE_HID, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT ); + ok( set != INVALID_HANDLE_VALUE, "failed to get device list, error %#x\n", GetLastError() ); + for (i = 0; SetupDiEnumDeviceInfo( set, i, &device ); ++i) + { + ret = SetupDiEnumDeviceInterfaces( set, &device, &GUID_DEVINTERFACE_HID, 0, &iface ); + ok( ret, "failed to get interface, error %#x\n", GetLastError() ); + ok( IsEqualGUID( &iface.InterfaceClassGuid, &GUID_DEVINTERFACE_HID ), "wrong class %s\n", + debugstr_guid( &iface.InterfaceClassGuid ) ); + ok( iface.Flags == SPINT_ACTIVE, "got flags %#x\n", iface.Flags ); + + iface_detail->cbSize = sizeof(*iface_detail); + ret = SetupDiGetDeviceInterfaceDetailW( set, &iface, iface_detail, sizeof(buffer), NULL, NULL ); + ok( ret, "failed to get interface path, error %#x\n", GetLastError() ); + + if (wcsstr( iface_detail->DevicePath, L"\\?\hid#winetest#1" )) break; + } + SetupDiDestroyDeviceInfoList( set ); + + file = CreateFileW( iface_detail->DevicePath, FILE_READ_ACCESS | FILE_WRITE_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() ); + + ret = HidD_GetPreparsedData( file, &preparsed_data ); + ok( ret, "HidD_GetPreparsedData failed with error %u\n", GetLastError() ); + + kdr = (struct hidp_kdr *)preparsed_data; + todo_wine + ok( !strncmp( kdr->magic, expect_kdr.magic, 8 ), "got %s expected %s\n", + debugstr_an(kdr->magic, 8), debugstr_an(expect_kdr.magic, 8) ); + + if (!strncmp( kdr->magic, expect_kdr.magic, 8 )) + { + check_member( *kdr, expect_kdr, "%04x", usage ); + check_member( *kdr, expect_kdr, "%04x", usage_page ); + check_member( *kdr, expect_kdr, "%#x", unknown[0] ); + check_member( *kdr, expect_kdr, "%#x", unknown[1] ); + check_member( *kdr, expect_kdr, "%d", input_caps_start ); + check_member( *kdr, expect_kdr, "%d", input_caps_count ); + check_member( *kdr, expect_kdr, "%d", input_caps_end ); + check_member( *kdr, expect_kdr, "%d", input_report_byte_length ); + check_member( *kdr, expect_kdr, "%d", output_caps_start ); + check_member( *kdr, expect_kdr, "%d", output_caps_count ); + check_member( *kdr, expect_kdr, "%d", output_caps_end ); + check_member( *kdr, expect_kdr, "%d", output_report_byte_length ); + check_member( *kdr, expect_kdr, "%d", feature_caps_start ); + check_member( *kdr, expect_kdr, "%d", feature_caps_count ); + check_member( *kdr, expect_kdr, "%d", feature_caps_end ); + check_member( *kdr, expect_kdr, "%d", feature_report_byte_length ); + check_member( *kdr, expect_kdr, "%d", caps_size ); + check_member( *kdr, expect_kdr, "%d", number_link_collection_nodes ); + + for (i = 0; i < ARRAY_SIZE(expect_caps); ++i) + { + winetest_push_context( "caps[%d]", i ); + check_member( kdr->caps[i], expect_caps[i], "%04x", usage_page ); + check_member( kdr->caps[i], expect_caps[i], "%d", report_id ); + check_member( kdr->caps[i], expect_caps[i], "%d", start_bit ); + check_member( kdr->caps[i], expect_caps[i], "%d", bit_size ); + check_member( kdr->caps[i], expect_caps[i], "%d", report_count ); + check_member( kdr->caps[i], expect_caps[i], "%d", start_byte ); + check_member( kdr->caps[i], expect_caps[i], "%d", total_bits ); + check_member( kdr->caps[i], expect_caps[i], "%#x", bit_field ); + check_member( kdr->caps[i], expect_caps[i], "%d", end_byte ); + check_member( kdr->caps[i], expect_caps[i], "%d", link_collection ); + check_member( kdr->caps[i], expect_caps[i], "%04x", link_usage_page ); + check_member( kdr->caps[i], expect_caps[i], "%04x", link_usage ); + check_member( kdr->caps[i], expect_caps[i], "%#x", flags ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[0] ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[1] ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[2] ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[3] ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[4] ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[5] ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[6] ); + check_member( kdr->caps[i], expect_caps[i], "%#x", padding[7] ); + check_member( kdr->caps[i], expect_caps[i], "%04x", usage_min ); + check_member( kdr->caps[i], expect_caps[i], "%04x", usage_max ); + check_member( kdr->caps[i], expect_caps[i], "%d", string_min ); + check_member( kdr->caps[i], expect_caps[i], "%d", string_max ); + check_member( kdr->caps[i], expect_caps[i], "%d", designator_min ); + check_member( kdr->caps[i], expect_caps[i], "%d", designator_max ); + check_member( kdr->caps[i], expect_caps[i], "%#x", data_index_min ); + check_member( kdr->caps[i], expect_caps[i], "%#x", data_index_max ); + check_member( kdr->caps[i], expect_caps[i], "%d", null_value ); + check_member( kdr->caps[i], expect_caps[i], "%d", unknown ); + check_member( kdr->caps[i], expect_caps[i], "%d", logical_min ); + check_member( kdr->caps[i], expect_caps[i], "%d", logical_max ); + check_member( kdr->caps[i], expect_caps[i], "%d", physical_min ); + check_member( kdr->caps[i], expect_caps[i], "%d", physical_max ); + check_member( kdr->caps[i], expect_caps[i], "%#x", units ); + check_member( kdr->caps[i], expect_caps[i], "%#x", units_exp ); + winetest_pop_context(); + } + + for (i = 0; i < ARRAY_SIZE(expect_nodes); ++i) + { + struct hidp_kdr_node *nodes = (struct hidp_kdr_node *)((char *)kdr->caps + kdr->caps_size); + winetest_push_context( "nodes[%d]", i ); + check_member( nodes[i], expect_nodes[i], "%04x", usage ); + check_member( nodes[i], expect_nodes[i], "%04x", usage_page ); + check_member( nodes[i], expect_nodes[i], "%d", parent ); + check_member( nodes[i], expect_nodes[i], "%d", number_of_children ); + check_member( nodes[i], expect_nodes[i], "%d", next_sibling ); + check_member( nodes[i], expect_nodes[i], "%d", first_child ); + check_member( nodes[i], expect_nodes[i], "%#x", collection_type ); + winetest_pop_context(); + } + } + + HidD_FreePreparsedData( preparsed_data ); + + CloseHandle( file ); + +done: + pnp_driver_stop(); + SetCurrentDirectoryW( cwd ); +} + START_TEST( hid ) { HANDLE mapping; @@ -2741,6 +3210,7 @@ START_TEST( hid ) ok( okfile != INVALID_HANDLE_VALUE, "failed to create file, error %u\n", GetLastError() );
subtest( "driver_hid" ); + test_hidp_kdr(); test_hid_driver( 0, FALSE ); test_hid_driver( 1, FALSE ); test_hid_driver( 0, TRUE );
Although not completely correct it should be usable already, and running the tests would require setting the registry key before and cleaning it up after otherwise.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/device.c | 9 +++---- dlls/dinput/device_private.h | 2 +- dlls/dinput/joystick.c | 44 ++++++++++++++++++++++++---------- dlls/dinput/joystick_hid.c | 3 +-- dlls/dinput/joystick_private.h | 1 + dlls/dinput/mouse.c | 11 +++++---- 6 files changed, 44 insertions(+), 26 deletions(-)
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index 25084f82a9c..e04d40e5a7c 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -305,14 +305,11 @@ BOOL get_app_key(HKEY *defkey, HKEY *appkey) /****************************************************************************** * Get a config key from either the app-specific or the default config */ -DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name, - char *buffer, DWORD size ) +DWORD get_config_key( HKEY defkey, HKEY appkey, const WCHAR *name, WCHAR *buffer, DWORD size ) { - if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) - return 0; + if (appkey && !RegQueryValueExW( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
- if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) - return 0; + if (defkey && !RegQueryValueExW( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
return ERROR_FILE_NOT_FOUND; } diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h index 0e72ee77bf1..90eab627c26 100644 --- a/dlls/dinput/device_private.h +++ b/dlls/dinput/device_private.h @@ -97,7 +97,7 @@ extern HRESULT direct_input_device_alloc( SIZE_T size, const IDirectInputDevice8 extern const IDirectInputDevice8AVtbl dinput_device_a_vtbl DECLSPEC_HIDDEN;
extern BOOL get_app_key(HKEY*, HKEY*) DECLSPEC_HIDDEN; -extern DWORD get_config_key(HKEY, HKEY, const char*, char*, DWORD) DECLSPEC_HIDDEN; +extern DWORD get_config_key( HKEY, HKEY, const WCHAR *, WCHAR *, DWORD ) DECLSPEC_HIDDEN;
/* Routines to do DataFormat / WineFormat conversions */ extern void fill_DataFormat(void *out, DWORD size, const void *in, const DataFormat *df) DECLSPEC_HIDDEN; diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c index 60153d0d0f3..d4f5b819471 100644 --- a/dlls/dinput/joystick.c +++ b/dlls/dinput/joystick.c @@ -273,10 +273,18 @@ void dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags)
BOOL device_disabled_registry(const char* name, BOOL disable) { - static const char disabled_str[] = "disabled"; - static const char enabled_str[] = "enabled"; - static const char joystick_key[] = "Joysticks"; - char buffer[MAX_PATH]; + DIDEVICEINSTANCEW instance; + + MultiByteToWideChar( CP_ACP, 0, name, -1, instance.tszInstanceName, MAX_PATH ); + return device_instance_is_disabled( &instance, disable ); +} + +BOOL device_instance_is_disabled( DIDEVICEINSTANCEW *instance, BOOL disable ) +{ + static const WCHAR disabled_str[] = {'d', 'i', 's', 'a', 'b', 'l', 'e', 'd', 0}; + static const WCHAR enabled_str[] = {'e', 'n', 'a', 'b', 'l', 'e', 'd', 0}; + static const WCHAR joystick_key[] = {'J', 'o', 'y', 's', 't', 'i', 'c', 'k', 's', 0}; + WCHAR buffer[MAX_PATH]; HKEY hkey, appkey, temp;
get_app_key(&hkey, &appkey); @@ -284,28 +292,29 @@ BOOL device_disabled_registry(const char* name, BOOL disable) /* Joystick settings are in the 'joysticks' subkey */ if (appkey) { - if (RegOpenKeyA(appkey, joystick_key, &temp)) temp = 0; + if (RegOpenKeyW( appkey, joystick_key, &temp )) temp = 0; RegCloseKey(appkey); appkey = temp; } + if (hkey) { - if (RegOpenKeyA(hkey, joystick_key, &temp)) temp = 0; + if (RegOpenKeyW( hkey, joystick_key, &temp )) temp = 0; RegCloseKey(hkey); hkey = temp; }
/* Look for the "controllername"="disabled" key */ - if (!get_config_key(hkey, appkey, name, buffer, sizeof(buffer))) + if (!get_config_key( hkey, appkey, instance->tszInstanceName, buffer, sizeof(buffer) )) { - if (!disable && !strcmp(disabled_str, buffer)) + if (!disable && !strcmpW( disabled_str, buffer )) { - TRACE("Disabling joystick '%s' based on registry key.\n", name); + TRACE( "Disabling joystick '%s' based on registry key.\n", debugstr_w(instance->tszInstanceName) ); disable = TRUE; } - else if (disable && !strcmp(enabled_str, buffer)) + else if (disable && !strcmpW( enabled_str, buffer )) { - TRACE("Enabling joystick '%s' based on registry key.\n", name); + TRACE( "Enabling joystick '%s' based on registry key.\n", debugstr_w(instance->tszInstanceName) ); disable = FALSE; } } @@ -854,6 +863,15 @@ DWORD joystick_map_pov(const POINTL *p) return p->y < 0 ? 0 : !p->y ? -1 : 18000; }
+static DWORD get_config_key_a( HKEY defkey, HKEY appkey, const char *name, char *buffer, DWORD size ) +{ + if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0; + + if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0; + + return ERROR_FILE_NOT_FOUND; +} + /* * Setup the dinput options. */ @@ -870,7 +888,7 @@ HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_
/* get options */
- if (!get_config_key(hkey, appkey, "DefaultDeadZone", buffer, sizeof(buffer))) + if (!get_config_key_a( hkey, appkey, "DefaultDeadZone", buffer, sizeof(buffer) )) { This->deadzone = atoi(buffer); TRACE("setting default deadzone to: "%s" %d\n", buffer, This->deadzone); @@ -879,7 +897,7 @@ HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_ This->axis_map = HeapAlloc(GetProcessHeap(), 0, This->device_axis_count * sizeof(int)); if (!This->axis_map) return DIERR_OUTOFMEMORY;
- if (!get_config_key(hkey, appkey, This->name, buffer, sizeof(buffer))) + if (!get_config_key_a( hkey, appkey, This->name, buffer, sizeof(buffer) )) { static const char *axis_names[] = {"X", "Y", "Z", "Rx", "Ry", "Rz", "Slider1", "Slider2", diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 3c69c74b4fc..3ef6f0db9cc 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -1043,8 +1043,7 @@ static HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTAN if (version >= 0x0800 && type != DI8DEVCLASS_ALL && type != DI8DEVCLASS_GAMECTRL) return S_FALSE;
- if (device_disabled_registry( "HID", TRUE )) - return DIERR_DEVICENOTREG; + if (device_instance_is_disabled( instance, FALSE )) return DIERR_DEVICENOTREG;
TRACE( "found device %s, usage %04x:%04x, product %s, instance %s, name %s\n", debugstr_w(device_path), instance->wUsagePage, instance->wUsage, debugstr_guid( &instance->guidProduct ), diff --git a/dlls/dinput/joystick_private.h b/dlls/dinput/joystick_private.h index 9cc30605234..4fe51d17067 100644 --- a/dlls/dinput/joystick_private.h +++ b/dlls/dinput/joystick_private.h @@ -58,6 +58,7 @@ HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_ DWORD joystick_map_pov(const POINTL *p) DECLSPEC_HIDDEN;
BOOL device_disabled_registry(const char* name, BOOL disable) DECLSPEC_HIDDEN; +BOOL device_instance_is_disabled( DIDEVICEINSTANCEW *instance, BOOL disable ) DECLSPEC_HIDDEN;
ULONG WINAPI JoystickWGenericImpl_Release(LPDIRECTINPUTDEVICE8W iface);
diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index 145a792f396..9f696cb414a 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -140,10 +140,13 @@ static HRESULT mousedev_enum_device(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEIN
static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, SysMouseImpl **out ) { + static const WCHAR mouse_wrap_override_w[] = {'M','o','u','s','e','W','a','r','p','O','v','e','r','r','i','d','e',0}; + static const WCHAR disable_w[] = {'d','i','s','a','b','l','e',0}; + static const WCHAR force_w[] = {'f','o','r','c','e',0}; SysMouseImpl* newDevice; LPDIDATAFORMAT df = NULL; unsigned i; - char buffer[20]; + WCHAR buffer[20]; HKEY hkey, appkey; HRESULT hr;
@@ -155,11 +158,11 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, SysMouseIm newDevice->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
get_app_key(&hkey, &appkey); - if (!get_config_key(hkey, appkey, "MouseWarpOverride", buffer, sizeof(buffer))) + if (!get_config_key(hkey, appkey, mouse_wrap_override_w, buffer, sizeof(buffer))) { - if (!_strnicmp(buffer, "disable", -1)) + if (!strncmpiW(buffer, disable_w, -1)) newDevice->warp_override = WARP_DISABLE; - else if (!_strnicmp(buffer, "force", -1)) + else if (!strncmpiW(buffer, force_w, -1)) newDevice->warp_override = WARP_FORCE_ON; } if (appkey) RegCloseKey(appkey);
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput8/tests/hid.c | 227 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+)
diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index cb1f4d61b7a..1dc42909c7f 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -16,6 +16,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define DIRECTINPUT_VERSION 0x0800 + #include <stdarg.h> #include <stddef.h>
@@ -40,18 +42,29 @@
#include "objbase.h"
+#define COBJMACROS +#include "wingdi.h" +#include "dinput.h" + #include "initguid.h" #include "ddk/wdm.h" #include "ddk/hidclass.h" #include "ddk/hidsdi.h" #include "ddk/hidpi.h" #include "ddk/hidport.h" +#include "devguid.h"
#include "wine/test.h" #include "wine/mssign.h"
#include "driver_hid.h"
+static HINSTANCE instance; + +#define EXPECT_VIDPID MAKELONG( 0x1209, 0x0001 ) +static const WCHAR expect_vidpid_str[] = L"VID_1209&PID_0001"; +static const GUID expect_guid_product = {EXPECT_VIDPID,0x0000,0x0000,{0x00,0x00,'P','I','D','V','I','D'}}; + static struct winetest_shared_data *test_data; static HANDLE okfile;
@@ -574,6 +587,18 @@ static BOOL pnp_driver_start( const WCHAR *resource ) #define check_member( val, exp, fmt, member ) \ check_member_( __FILE__, __LINE__, val, exp, fmt, member )
+#define check_member_guid_( file, line, val, exp, member ) \ + ok_( file, line )(IsEqualGUID( &(val).member, &(exp).member ), "got " #member " %s, expected %s\n", \ + debugstr_guid( &(val).member ), debugstr_guid( &(exp).member )) +#define check_member_guid( val, exp, member ) \ + check_member_guid_( __FILE__, __LINE__, val, exp, member ) + +#define check_member_wstr_( file, line, val, exp, member ) \ + ok_( file, line )(!wcscmp( (val).member, (exp).member ), "got " #member " %s, expected %s\n", \ + debugstr_w((val).member), debugstr_w((exp).member)) +#define check_member_wstr( val, exp, member ) \ + check_member_wstr_( __FILE__, __LINE__, val, exp, member ) + #define check_hidp_caps( a, b ) check_hidp_caps_( __LINE__, a, b ) static inline void check_hidp_caps_( int line, HIDP_CAPS *caps, const HIDP_CAPS *exp ) { @@ -3179,11 +3204,209 @@ done: SetCurrentDirectoryW( cwd ); }
+static void cleanup_registry_keys(void) +{ + static const WCHAR joystick_oem_path[] = L"System\CurrentControlSet\Control\MediaProperties\" + "PrivateProperties\Joystick\OEM"; + static const WCHAR dinput_path[] = L"System\CurrentControlSet\Control\MediaProperties\" + "PrivateProperties\DirectInput"; + HKEY root_key; + + /* These keys are automatically created by DInput and they store the + list of supported force-feedback effects. OEM drivers are supposed + to provide a list in HKLM for the vendor-specific force-feedback + support. + + We need to clean them up, or DInput will not refresh the list of + effects from the PID report changes. + */ + RegCreateKeyExW( HKEY_CURRENT_USER, joystick_oem_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); + RegDeleteTreeW( root_key, expect_vidpid_str ); + RegCloseKey( root_key ); + + RegCreateKeyExW( HKEY_CURRENT_USER, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); + RegDeleteTreeW( root_key, expect_vidpid_str ); + RegCloseKey( root_key ); + + RegCreateKeyExW( HKEY_LOCAL_MACHINE, joystick_oem_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); + RegDeleteTreeW( root_key, expect_vidpid_str ); + RegCloseKey( root_key ); + + RegCreateKeyExW( HKEY_LOCAL_MACHINE, dinput_path, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root_key, NULL ); + RegDeleteTreeW( root_key, expect_vidpid_str ); + RegCloseKey( root_key ); +} + +static BOOL dinput_driver_start( const BYTE *desc_buf, ULONG desc_len, const HIDP_CAPS *caps ) +{ + static const HID_DEVICE_ATTRIBUTES attributes = + { + .Size = sizeof(HID_DEVICE_ATTRIBUTES), + .VendorID = LOWORD( EXPECT_VIDPID ), + .ProductID = HIWORD( EXPECT_VIDPID ), + .VersionNumber = 0x0100, + }; + DWORD report_id = 1; + DWORD polled = 0; + LSTATUS status; + HKEY hkey; + + status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\CurrentControlSet\Services\winetest", + 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ); + ok( !status, "RegCreateKeyExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"ReportID", 0, REG_DWORD, (void *)&report_id, sizeof(report_id) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"PolledMode", 0, REG_DWORD, (void *)&polled, sizeof(polled) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Descriptor", 0, REG_BINARY, (void *)desc_buf, desc_len ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Attributes", 0, REG_BINARY, (void *)&attributes, sizeof(attributes) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Caps", 0, REG_BINARY, (void *)caps, sizeof(*caps) ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Expect", 0, REG_BINARY, NULL, 0 ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + status = RegSetValueExW( hkey, L"Input", 0, REG_BINARY, NULL, 0 ); + ok( !status, "RegSetValueExW returned %#x\n", status ); + + return pnp_driver_start( L"driver_hid.dll" ); +} + +static BOOL CALLBACK find_test_device( const DIDEVICEINSTANCEW *devinst, void *context ) +{ + if (IsEqualGUID( &devinst->guidProduct, &expect_guid_product )) + *(DIDEVICEINSTANCEW *)context = *devinst; + return DIENUM_CONTINUE; +} + +static void test_simple_joystick(void) +{ +#include "psh_hid_macros.h" + static const unsigned char report_desc[] = + { + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Application), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Report), + REPORT_ID(1, 1), + + USAGE(1, HID_USAGE_GENERIC_X), + USAGE(1, HID_USAGE_GENERIC_Y), + LOGICAL_MINIMUM(1, 0xe7), + LOGICAL_MAXIMUM(1, 0x38), + PHYSICAL_MINIMUM(1, 0xe7), + PHYSICAL_MAXIMUM(1, 0x38), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + INPUT(1, Data|Var|Abs), + + USAGE(1, HID_USAGE_GENERIC_HATSWITCH), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 8), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 8), + REPORT_SIZE(1, 4), + REPORT_COUNT(1, 1), + INPUT(1, Data|Var|Abs|Null), + + USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON), + USAGE_MINIMUM(1, 1), + USAGE_MAXIMUM(1, 2), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 4), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + END_COLLECTION, + }; +#undef REPORT_ID_OR_USAGE_PAGE +#include "pop_hid_macros.h" + + static const HIDP_CAPS hid_caps = + { + .InputReportByteLength = 4, + }; + + const DIDEVICEINSTANCEW expect_devinst = + { + .dwSize = sizeof(DIDEVICEINSTANCEW), + .guidInstance = expect_guid_product, + .guidProduct = expect_guid_product, + .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK, + .tszInstanceName = L"Wine test root driver", + .tszProductName = L"Wine test root driver", + .guidFFDriver = GUID_NULL, + .wUsagePage = HID_USAGE_PAGE_GENERIC, + .wUsage = HID_USAGE_GENERIC_JOYSTICK, + }; + + WCHAR cwd[MAX_PATH], tempdir[MAX_PATH]; + DIDEVICEINSTANCEW devinst = {0}; + IDirectInputDevice8W *device; + IDirectInput8W *di; + HRESULT hr; + ULONG ref; + + GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd ); + GetTempPathW( ARRAY_SIZE(tempdir), tempdir ); + SetCurrentDirectoryW( tempdir ); + + cleanup_registry_keys(); + if (!dinput_driver_start( report_desc, sizeof(report_desc), &hid_caps )) goto done; + + hr = DirectInput8Create( instance, DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&di, NULL ); + if (FAILED(hr)) + { + win_skip( "DirectInput8Create returned %#x\n", hr ); + goto done; + } + + hr = IDirectInput8_EnumDevices( di, DI8DEVCLASS_ALL, find_test_device, &devinst, DIEDFL_ALLDEVICES ); + ok( hr == DI_OK, "IDirectInput8_EnumDevices returned: %#x\n", hr ); + if (!IsEqualGUID( &devinst.guidProduct, &expect_guid_product )) + { + win_skip( "device not found, skipping tests\n" ); + IDirectInput8_Release( di ); + goto done; + } + + check_member( devinst, expect_devinst, "%d", dwSize ); + check_member_guid( devinst, expect_devinst, guidProduct ); + todo_wine + check_member( devinst, expect_devinst, "%#x", dwDevType ); + todo_wine + check_member_wstr( devinst, expect_devinst, tszInstanceName ); + todo_wine + check_member_wstr( devinst, expect_devinst, tszProductName ); + check_member_guid( devinst, expect_devinst, guidFFDriver ); + check_member( devinst, expect_devinst, "%04x", wUsagePage ); + check_member( devinst, expect_devinst, "%04x", wUsage ); + + hr = IDirectInput8_CreateDevice( di, &expect_guid_product, &device, NULL ); + ok( hr == DI_OK, "IDirectInput8_CreateDevice returned %#x\n", hr ); + + ref = IDirectInputDevice8_Release( device ); + ok( ref == 0, "IDirectInputDeviceW_Release returned %d\n", ref ); + + ref = IDirectInput8_Release( di ); + ok( ref == 0, "IDirectInput8_Release returned %d\n", ref ); + +done: + pnp_driver_stop(); + cleanup_registry_keys(); + SetCurrentDirectoryW( cwd ); +} + START_TEST( hid ) { HANDLE mapping; BOOL is_wow64;
+ instance = GetModuleHandleW( NULL ); pSignerSign = (void *)GetProcAddress( LoadLibraryW( L"mssign32" ), "SignerSign" );
if (IsWow64Process( GetCurrentProcess(), &is_wow64 ) && is_wow64) @@ -3216,6 +3439,10 @@ START_TEST( hid ) test_hid_driver( 0, TRUE ); test_hid_driver( 1, TRUE );
+ CoInitialize( NULL ); + test_simple_joystick(); + CoUninitialize(); + UnmapViewOfFile( test_data ); CloseHandle( mapping ); CloseHandle( okfile );
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput8/tests/hid.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 1dc42909c7f..c2221c475d8 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -3389,6 +3389,43 @@ static void test_simple_joystick(void) hr = IDirectInput8_CreateDevice( di, &expect_guid_product, &device, NULL ); ok( hr == DI_OK, "IDirectInput8_CreateDevice returned %#x\n", hr );
+ hr = IDirectInputDevice8_GetDeviceInfo( device, NULL ); + ok( hr == E_POINTER, "IDirectInputDevice8_GetDeviceInfo returned %#x\n", hr ); + devinst.dwSize = sizeof(DIDEVICEINSTANCEW) + 1; + hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst ); + ok( hr == DIERR_INVALIDPARAM, "IDirectInputDevice8_GetDeviceInfo returned %#x\n", hr ); + + devinst.dwSize = sizeof(DIDEVICEINSTANCE_DX3W); + hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst ); + ok( hr == DI_OK, "IDirectInputDevice8_GetDeviceInfo returned %#x\n", hr ); + todo_wine + check_member_guid( devinst, expect_devinst, guidInstance ); + check_member_guid( devinst, expect_devinst, guidProduct ); + todo_wine + check_member( devinst, expect_devinst, "%#x", dwDevType ); + todo_wine + check_member_wstr( devinst, expect_devinst, tszInstanceName ); + todo_wine + check_member_wstr( devinst, expect_devinst, tszProductName ); + + memset( &devinst, 0, sizeof(devinst) ); + devinst.dwSize = sizeof(DIDEVICEINSTANCEW); + hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst ); + ok( hr == DI_OK, "IDirectInputDevice8_GetDeviceInfo returned %#x\n", hr ); + check_member( devinst, expect_devinst, "%d", dwSize ); + todo_wine + check_member_guid( devinst, expect_devinst, guidInstance ); + check_member_guid( devinst, expect_devinst, guidProduct ); + todo_wine + check_member( devinst, expect_devinst, "%#x", dwDevType ); + todo_wine + check_member_wstr( devinst, expect_devinst, tszInstanceName ); + todo_wine + check_member_wstr( devinst, expect_devinst, tszProductName ); + check_member_guid( devinst, expect_devinst, guidFFDriver ); + check_member( devinst, expect_devinst, "%04x", wUsagePage ); + check_member( devinst, expect_devinst, "%04x", wUsage ); + ref = IDirectInputDevice8_Release( device ); ok( ref == 0, "IDirectInputDeviceW_Release returned %d\n", ref );
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput8/tests/hid.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index c2221c475d8..69e85830be5 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -3330,6 +3330,15 @@ static void test_simple_joystick(void) { .InputReportByteLength = 4, }; + static const DIDEVCAPS expect_caps = + { + .dwSize = sizeof(DIDEVCAPS), + .dwFlags = DIDC_ATTACHED | DIDC_EMULATED, + .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK, + .dwAxes = 2, + .dwPOVs = 1, + .dwButtons = 2, + };
const DIDEVICEINSTANCEW expect_devinst = { @@ -3347,6 +3356,7 @@ static void test_simple_joystick(void) WCHAR cwd[MAX_PATH], tempdir[MAX_PATH]; DIDEVICEINSTANCEW devinst = {0}; IDirectInputDevice8W *device; + DIDEVCAPS caps = {0}; IDirectInput8W *di; HRESULT hr; ULONG ref; @@ -3426,6 +3436,27 @@ static void test_simple_joystick(void) check_member( devinst, expect_devinst, "%04x", wUsagePage ); check_member( devinst, expect_devinst, "%04x", wUsage );
+ hr = IDirectInputDevice8_GetCapabilities( device, NULL ); + ok( hr == E_POINTER, "IDirectInputDevice8_GetCapabilities returned %#x\n", hr ); + hr = IDirectInputDevice8_GetCapabilities( device, &caps ); + todo_wine + ok( hr == DIERR_INVALIDPARAM, "IDirectInputDevice8_GetCapabilities returned %#x\n", hr ); + caps.dwSize = sizeof(DIDEVCAPS); + hr = IDirectInputDevice8_GetCapabilities( device, &caps ); + ok( hr == DI_OK, "IDirectInputDevice8_GetCapabilities returned %#x\n", hr ); + check_member( caps, expect_caps, "%d", dwSize ); + check_member( caps, expect_caps, "%#x", dwFlags ); + todo_wine + check_member( caps, expect_caps, "%#x", dwDevType ); + check_member( caps, expect_caps, "%d", dwAxes ); + check_member( caps, expect_caps, "%d", dwButtons ); + check_member( caps, expect_caps, "%d", dwPOVs ); + check_member( caps, expect_caps, "%d", dwFFSamplePeriod ); + check_member( caps, expect_caps, "%d", dwFFMinTimeResolution ); + check_member( caps, expect_caps, "%d", dwFirmwareRevision ); + check_member( caps, expect_caps, "%d", dwHardwareRevision ); + check_member( caps, expect_caps, "%d", dwFFDriverVersion ); + ref = IDirectInputDevice8_Release( device ); ok( ref == 0, "IDirectInputDeviceW_Release returned %d\n", ref );