mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2024-11-19 17:06:04 -07:00
Compare commits
9 commits
512e694e7a
...
ece2510d58
Author | SHA1 | Date | |
---|---|---|---|
|
ece2510d58 | ||
|
c5a726e8a3 | ||
|
a10ea7e662 | ||
|
b787e0654a | ||
|
29226ef249 | ||
|
933b885ce7 | ||
|
c3b1f1430c | ||
|
a308076081 | ||
|
6e79260df0 |
10 changed files with 496 additions and 67 deletions
|
@ -275,10 +275,11 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void *
|
|||
.serialnumber = {'0','0','0','0',0},
|
||||
};
|
||||
struct iohid_device *impl;
|
||||
USAGE_AND_PAGE usages;
|
||||
CFStringRef str;
|
||||
|
||||
desc.usages.UsagePage = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDPrimaryUsagePageKey)));
|
||||
desc.usages.Usage = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDPrimaryUsageKey)));
|
||||
usages.UsagePage = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDPrimaryUsagePageKey)));
|
||||
usages.Usage = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDPrimaryUsageKey)));
|
||||
|
||||
desc.vid = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDVendorIDKey)));
|
||||
desc.pid = CFNumberToDWORD(IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDProductIDKey)));
|
||||
|
@ -289,8 +290,8 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void *
|
|||
desc.is_bluetooth = !CFStringCompare(str, CFSTR(kIOHIDTransportBluetoothValue), 0) ||
|
||||
!CFStringCompare(str, CFSTR(kIOHIDTransportBluetoothLowEnergyValue), 0);
|
||||
|
||||
if (desc.usages.UsagePage != HID_USAGE_PAGE_GENERIC ||
|
||||
!(desc.usages.Usage == HID_USAGE_GENERIC_JOYSTICK || desc.usages.Usage == HID_USAGE_GENERIC_GAMEPAD))
|
||||
if (usages.UsagePage != HID_USAGE_PAGE_GENERIC ||
|
||||
!(usages.Usage == HID_USAGE_GENERIC_JOYSTICK || usages.Usage == HID_USAGE_GENERIC_GAMEPAD))
|
||||
{
|
||||
/* winebus isn't currently meant to handle anything but these, and
|
||||
* opening keyboards, mice, or the Touch Bar on older MacBooks triggers
|
||||
|
|
|
@ -198,7 +198,7 @@ static void set_hat_value(struct unix_device *iface, int index, int value)
|
|||
hid_device_set_hatswitch_y(iface, index, y);
|
||||
}
|
||||
|
||||
static BOOL descriptor_add_haptic(struct sdl_device *impl)
|
||||
static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force)
|
||||
{
|
||||
USHORT i, count = 0;
|
||||
USAGE usages[16];
|
||||
|
@ -227,16 +227,16 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl)
|
|||
if ((impl->effect_support & EFFECT_SUPPORT_PHYSICAL))
|
||||
{
|
||||
/* SDL_HAPTIC_SQUARE doesn't exist */
|
||||
if (impl->effect_support & SDL_HAPTIC_SINE) usages[count++] = PID_USAGE_ET_SINE;
|
||||
if (impl->effect_support & SDL_HAPTIC_TRIANGLE) usages[count++] = PID_USAGE_ET_TRIANGLE;
|
||||
if (impl->effect_support & SDL_HAPTIC_SAWTOOTHUP) usages[count++] = PID_USAGE_ET_SAWTOOTH_UP;
|
||||
if (impl->effect_support & SDL_HAPTIC_SAWTOOTHDOWN) usages[count++] = PID_USAGE_ET_SAWTOOTH_DOWN;
|
||||
if (impl->effect_support & SDL_HAPTIC_SPRING) usages[count++] = PID_USAGE_ET_SPRING;
|
||||
if (impl->effect_support & SDL_HAPTIC_DAMPER) usages[count++] = PID_USAGE_ET_DAMPER;
|
||||
if (impl->effect_support & SDL_HAPTIC_INERTIA) usages[count++] = PID_USAGE_ET_INERTIA;
|
||||
if (impl->effect_support & SDL_HAPTIC_FRICTION) usages[count++] = PID_USAGE_ET_FRICTION;
|
||||
if (impl->effect_support & SDL_HAPTIC_CONSTANT) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE;
|
||||
if (impl->effect_support & SDL_HAPTIC_RAMP) usages[count++] = PID_USAGE_ET_RAMP;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_SINE)) usages[count++] = PID_USAGE_ET_SINE;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_TRIANGLE)) usages[count++] = PID_USAGE_ET_TRIANGLE;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_SAWTOOTHUP)) usages[count++] = PID_USAGE_ET_SAWTOOTH_UP;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_SAWTOOTHDOWN)) usages[count++] = PID_USAGE_ET_SAWTOOTH_DOWN;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_SPRING)) usages[count++] = PID_USAGE_ET_SPRING;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_DAMPER)) usages[count++] = PID_USAGE_ET_DAMPER;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_INERTIA)) usages[count++] = PID_USAGE_ET_INERTIA;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_FRICTION)) usages[count++] = PID_USAGE_ET_FRICTION;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE;
|
||||
if (force || (impl->effect_support & SDL_HAPTIC_RAMP)) usages[count++] = PID_USAGE_ET_RAMP;
|
||||
|
||||
if (!hid_device_add_physical(&impl->unix_device, usages, count))
|
||||
return FALSE;
|
||||
|
@ -360,7 +360,7 @@ static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface)
|
|||
if (!hid_device_end_input_report(iface))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
if (!descriptor_add_haptic(impl))
|
||||
if (!descriptor_add_haptic(impl, physical_usage.Usage == HID_USAGE_SIMULATION_AUTOMOBILE_SIMULATION_DEVICE))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
if (!hid_device_end_report_descriptor(iface))
|
||||
|
@ -414,7 +414,7 @@ static NTSTATUS build_controller_report_descriptor(struct unix_device *iface)
|
|||
if (!hid_device_end_input_report(iface))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
if (!descriptor_add_haptic(impl))
|
||||
if (!descriptor_add_haptic(impl, FALSE))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
if (!hid_device_end_report_descriptor(iface))
|
||||
|
@ -443,17 +443,12 @@ static void sdl_device_destroy(struct unix_device *iface)
|
|||
static NTSTATUS sdl_device_start(struct unix_device *iface)
|
||||
{
|
||||
struct sdl_device *impl = impl_from_unix_device(iface);
|
||||
NTSTATUS status;
|
||||
|
||||
pthread_mutex_lock(&sdl_cs);
|
||||
|
||||
if (impl->sdl_controller) status = build_controller_report_descriptor(iface);
|
||||
else status = build_joystick_report_descriptor(iface);
|
||||
impl->started = !status;
|
||||
|
||||
impl->started = TRUE;
|
||||
pthread_mutex_unlock(&sdl_cs);
|
||||
|
||||
return status;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void sdl_device_stop(struct unix_device *iface)
|
||||
|
@ -595,7 +590,7 @@ static NTSTATUS sdl_device_physical_effect_control(struct unix_device *iface, BY
|
|||
|
||||
TRACE("iface %p, index %u, control %04x, iterations %u.\n", iface, index, control, iterations);
|
||||
|
||||
if (impl->effect_ids[index] < 0) return STATUS_UNSUCCESSFUL;
|
||||
if (id < 0) return STATUS_SUCCESS;
|
||||
|
||||
switch (control)
|
||||
{
|
||||
|
@ -991,8 +986,6 @@ static void sdl_add_device(unsigned int index)
|
|||
if (controller)
|
||||
{
|
||||
desc.is_gamepad = TRUE;
|
||||
desc.usages.UsagePage = HID_USAGE_PAGE_GENERIC;
|
||||
desc.usages.Usage = HID_USAGE_GENERIC_GAMEPAD;
|
||||
axis_count = 6;
|
||||
}
|
||||
else
|
||||
|
@ -1000,12 +993,12 @@ static void sdl_add_device(unsigned int index)
|
|||
int button_count = pSDL_JoystickNumButtons(joystick);
|
||||
axis_count = pSDL_JoystickNumAxes(joystick);
|
||||
desc.is_gamepad = (axis_count == 6 && button_count >= 14);
|
||||
desc.usages.UsagePage = HID_USAGE_PAGE_GENERIC;
|
||||
desc.usages.Usage = HID_USAGE_GENERIC_JOYSTICK;
|
||||
}
|
||||
|
||||
for (axis_offset = 0; axis_offset < axis_count; axis_offset += (options.split_controllers ? 6 : axis_count))
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!axis_offset) strcpy(buffer, product);
|
||||
else snprintf(buffer, ARRAY_SIZE(buffer), "%s %d", product, axis_offset / 6);
|
||||
ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.product, ARRAY_SIZE(desc.product));
|
||||
|
@ -1019,6 +1012,15 @@ static void sdl_add_device(unsigned int index)
|
|||
impl->id = id;
|
||||
impl->axis_offset = axis_offset;
|
||||
|
||||
if (impl->sdl_controller) status = build_controller_report_descriptor(&impl->unix_device);
|
||||
else status = build_joystick_report_descriptor(&impl->unix_device);
|
||||
if (status)
|
||||
{
|
||||
list_remove(&impl->unix_device.entry);
|
||||
impl->unix_device.vtbl->destroy(&impl->unix_device);
|
||||
return;
|
||||
}
|
||||
|
||||
bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -728,12 +728,6 @@ static void lnxev_device_destroy(struct unix_device *iface)
|
|||
|
||||
static NTSTATUS lnxev_device_start(struct unix_device *iface)
|
||||
{
|
||||
struct lnxev_device *impl = lnxev_impl_from_unix_device(iface);
|
||||
NTSTATUS status;
|
||||
|
||||
if ((status = build_report_descriptor(iface, impl->base.udev_device)))
|
||||
return status;
|
||||
|
||||
pthread_mutex_lock(&udev_cs);
|
||||
start_polling_device(iface);
|
||||
pthread_mutex_unlock(&udev_cs);
|
||||
|
@ -1254,7 +1248,6 @@ static void udev_add_device(struct udev_device *dev, int fd)
|
|||
#ifdef HAS_PROPER_INPUT_HEADER
|
||||
else if (!strcmp(subsystem, "input"))
|
||||
{
|
||||
const USAGE_AND_PAGE device_usage = *what_am_I(dev, fd);
|
||||
static const WCHAR evdev[] = {'e','v','d','e','v',0};
|
||||
struct input_id device_id = {0};
|
||||
char buffer[MAX_PATH];
|
||||
|
@ -1275,8 +1268,6 @@ static void udev_add_device(struct udev_device *dev, int fd)
|
|||
|
||||
if (!desc.serialnumber[0] && ioctl(fd, EVIOCGUNIQ(sizeof(buffer)), buffer) >= 0)
|
||||
ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber));
|
||||
|
||||
desc.usages = device_usage;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1321,6 +1312,13 @@ static void udev_add_device(struct udev_device *dev, int fd)
|
|||
strcpy(impl->devnode, devnode);
|
||||
impl->device_fd = fd;
|
||||
|
||||
if (build_report_descriptor(&impl->unix_device, impl->udev_device))
|
||||
{
|
||||
list_remove(&impl->unix_device.entry);
|
||||
impl->unix_device.vtbl->destroy(&impl->unix_device);
|
||||
return;
|
||||
}
|
||||
|
||||
bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -417,7 +417,7 @@ static DWORD check_bus_option(const WCHAR *option, DWORD default_value)
|
|||
return default_value;
|
||||
}
|
||||
|
||||
static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages)
|
||||
static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages, UINT buttons)
|
||||
{
|
||||
char buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[1024])];
|
||||
KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
|
||||
|
@ -436,6 +436,48 @@ static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages)
|
|||
if (is_dualshock4_gamepad(vid, pid)) prefer_hidraw = TRUE;
|
||||
if (is_dualsense_gamepad(vid, pid)) prefer_hidraw = TRUE;
|
||||
|
||||
switch (vid)
|
||||
{
|
||||
case 0x044f:
|
||||
if (pid == 0xb679) prefer_hidraw = TRUE; /* ThrustMaster T-Rudder */
|
||||
if (pid == 0xb687) prefer_hidraw = TRUE; /* ThrustMaster TWCS Throttle */
|
||||
if (pid == 0xb10a) prefer_hidraw = TRUE; /* ThrustMaster T.16000M Joystick */
|
||||
break;
|
||||
case 0x16d0:
|
||||
if (pid == 0x0d61) prefer_hidraw = TRUE; /* Simucube 2 Sport */
|
||||
if (pid == 0x0d60) prefer_hidraw = TRUE; /* Simucube 2 Pro */
|
||||
if (pid == 0x0d5f) prefer_hidraw = TRUE; /* Simucube 2 Ultimate */
|
||||
if (pid == 0x0d5a) prefer_hidraw = TRUE; /* Simucube 1 */
|
||||
break;
|
||||
case 0x0eb7:
|
||||
if (pid == 0x183b) prefer_hidraw = TRUE; /* Fanatec ClubSport Pedals v3 */
|
||||
if (pid == 0x1839) prefer_hidraw = TRUE; /* Fanatec ClubSport Pedals v1/v2 */
|
||||
break;
|
||||
case 0x231d:
|
||||
/* comes with 128 buttons in the default configuration */
|
||||
if (buttons == 128) prefer_hidraw = TRUE;
|
||||
/* if customized, less than 128 buttons may be shown, decide by PID */
|
||||
if (pid == 0x0200) prefer_hidraw = TRUE; /* VKBsim Gladiator EVO Right Grip */
|
||||
if (pid == 0x0201) prefer_hidraw = TRUE; /* VKBsim Gladiator EVO Left Grip */
|
||||
if (pid == 0x0126) prefer_hidraw = TRUE; /* VKB-Sim Space Gunfighter */
|
||||
if (pid == 0x0127) prefer_hidraw = TRUE; /* VKB-Sim Space Gunfighter L */
|
||||
break;
|
||||
case 0x3344:
|
||||
/* comes with 31 buttons in the default configuration, or 128 max */
|
||||
if ((buttons == 31) || (buttons == 128)) prefer_hidraw = TRUE;
|
||||
/* users may have configured button limits, usually 32/50/64 */
|
||||
if ((buttons == 32) || (buttons == 50) || (buttons == 64)) prefer_hidraw = TRUE;
|
||||
/* if customized, arbitrary amount of buttons may be shown, decide by PID */
|
||||
if (pid == 0x412f) prefer_hidraw = TRUE; /* Virpil Constellation ALPHA-R */
|
||||
if (pid == 0x812c) prefer_hidraw = TRUE; /* Virpil Constellation ALPHA-L */
|
||||
break;
|
||||
case 0x03eb:
|
||||
/* users may have configured button limits, usually 32/50/64 */
|
||||
if ((buttons == 32) || (buttons == 50) || (buttons == 64)) prefer_hidraw = TRUE;
|
||||
if (pid == 0x2055) prefer_hidraw = TRUE; /* ATMEL/VIRPIL/200325 VPC Throttle MT-50 CM2 */
|
||||
break;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&str, L"EnableHidraw");
|
||||
if (!NtQueryValueKey(driver_key, &str, KeyValuePartialInformation, info,
|
||||
sizeof(buffer) - sizeof(WCHAR), &size))
|
||||
|
@ -684,22 +726,41 @@ static NTSTATUS get_device_descriptors(UINT64 unix_device, BYTE **report_desc, U
|
|||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static USAGE_AND_PAGE get_hidraw_device_usages(UINT64 unix_device)
|
||||
static USAGE_AND_PAGE get_device_usages(UINT64 unix_device, UINT *buttons)
|
||||
{
|
||||
HIDP_DEVICE_DESC device_desc;
|
||||
USAGE_AND_PAGE usages = {0};
|
||||
UINT report_desc_length;
|
||||
UINT i, count = 0, report_desc_length;
|
||||
HIDP_BUTTON_CAPS *button_caps;
|
||||
BYTE *report_desc;
|
||||
NTSTATUS status;
|
||||
HIDP_CAPS caps;
|
||||
|
||||
if (!(status = get_device_descriptors(unix_device, &report_desc, &report_desc_length, &device_desc)))
|
||||
{
|
||||
PHIDP_PREPARSED_DATA preparsed = device_desc.CollectionDesc[0].PreparsedData;
|
||||
usages.UsagePage = device_desc.CollectionDesc[0].UsagePage;
|
||||
usages.Usage = device_desc.CollectionDesc[0].Usage;
|
||||
|
||||
if ((status = HidP_GetCaps(preparsed, &caps)) == HIDP_STATUS_SUCCESS &&
|
||||
(button_caps = malloc(sizeof(*button_caps) * caps.NumberInputButtonCaps)))
|
||||
{
|
||||
status = HidP_GetButtonCaps(HidP_Input, button_caps, &caps.NumberInputButtonCaps, preparsed);
|
||||
if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetButtonCaps returned %#lx\n", status);
|
||||
else for (i = 0; i < caps.NumberInputButtonCaps; i++)
|
||||
{
|
||||
if (button_caps[i].UsagePage != HID_USAGE_PAGE_BUTTON) continue;
|
||||
if (button_caps[i].IsRange) count = max(count, button_caps[i].Range.UsageMax);
|
||||
else count = max(count, button_caps[i].NotRange.Usage);
|
||||
}
|
||||
free(button_caps);
|
||||
}
|
||||
|
||||
HidP_FreeCollectionDescription(&device_desc);
|
||||
RtlFreeHeap(GetProcessHeap(), 0, report_desc);
|
||||
}
|
||||
|
||||
*buttons = count;
|
||||
return usages;
|
||||
}
|
||||
|
||||
|
@ -749,18 +810,21 @@ static DWORD CALLBACK bus_main_thread(void *args)
|
|||
case BUS_EVENT_TYPE_DEVICE_CREATED:
|
||||
{
|
||||
struct device_desc desc = event->device_created.desc;
|
||||
if (desc.is_hidraw && !desc.usages.UsagePage) desc.usages = get_hidraw_device_usages(event->device);
|
||||
if (!desc.is_hidraw != !is_hidraw_enabled(desc.vid, desc.pid, &desc.usages))
|
||||
USAGE_AND_PAGE usages;
|
||||
UINT buttons;
|
||||
|
||||
usages = get_device_usages(event->device, &buttons);
|
||||
if (!desc.is_hidraw != !is_hidraw_enabled(desc.vid, desc.pid, &usages, buttons))
|
||||
{
|
||||
struct device_remove_params params = {.device = event->device};
|
||||
WARN("ignoring %shidraw device %04x:%04x with usages %04x:%04x\n", desc.is_hidraw ? "" : "non-",
|
||||
desc.vid, desc.pid, desc.usages.UsagePage, desc.usages.Usage);
|
||||
desc.vid, desc.pid, usages.UsagePage, usages.Usage);
|
||||
winebus_call(device_remove, ¶ms);
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE("creating %shidraw device %04x:%04x with usages %04x:%04x\n", desc.is_hidraw ? "" : "non-",
|
||||
desc.vid, desc.pid, desc.usages.UsagePage, desc.usages.Usage);
|
||||
desc.vid, desc.pid, usages.UsagePage, usages.Usage);
|
||||
|
||||
device = bus_create_hid_device(&event->device_created.desc, event->device);
|
||||
if (device) IoInvalidateDeviceRelations(bus_pdo, BusRelations);
|
||||
|
|
|
@ -49,14 +49,6 @@ static void mouse_destroy(struct unix_device *iface)
|
|||
|
||||
static NTSTATUS mouse_start(struct unix_device *iface)
|
||||
{
|
||||
const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_MOUSE};
|
||||
if (!hid_device_begin_report_descriptor(iface, &device_usage))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 3))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_end_report_descriptor(iface))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -123,9 +115,21 @@ static const struct device_desc mouse_device_desc =
|
|||
|
||||
static NTSTATUS mouse_device_create(void *args)
|
||||
{
|
||||
const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_MOUSE};
|
||||
struct device_create_params *params = args;
|
||||
struct unix_device *iface;
|
||||
|
||||
if (!(iface = hid_device_create(&mouse_vtbl, sizeof(struct mouse_device))))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_begin_report_descriptor(iface, &device_usage))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 3))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_end_report_descriptor(iface))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
params->desc = mouse_device_desc;
|
||||
params->device = (UINT_PTR)hid_device_create(&mouse_vtbl, sizeof(struct mouse_device));
|
||||
params->device = (UINT_PTR)iface;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -140,14 +144,6 @@ static void keyboard_destroy(struct unix_device *iface)
|
|||
|
||||
static NTSTATUS keyboard_start(struct unix_device *iface)
|
||||
{
|
||||
const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_KEYBOARD};
|
||||
if (!hid_device_begin_report_descriptor(iface, &device_usage))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_KEYBOARD, 0, 101))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_end_report_descriptor(iface))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -214,9 +210,21 @@ static const struct device_desc keyboard_device_desc =
|
|||
|
||||
static NTSTATUS keyboard_device_create(void *args)
|
||||
{
|
||||
const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_KEYBOARD};
|
||||
struct device_create_params *params = args;
|
||||
struct unix_device *iface;
|
||||
|
||||
if (!(iface = hid_device_create(&keyboard_vtbl, sizeof(struct keyboard_device))))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_begin_report_descriptor(iface, &device_usage))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_KEYBOARD, 0, 101))
|
||||
return STATUS_NO_MEMORY;
|
||||
if (!hid_device_end_report_descriptor(iface))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
params->desc = keyboard_device_desc;
|
||||
params->device = (UINT_PTR)hid_device_create(&keyboard_vtbl, sizeof(struct keyboard_device));
|
||||
params->device = (UINT_PTR)iface;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ struct device_desc
|
|||
UINT version;
|
||||
UINT input;
|
||||
UINT uid;
|
||||
USAGE_AND_PAGE usages;
|
||||
BOOL is_gamepad;
|
||||
BOOL is_hidraw;
|
||||
BOOL is_bluetooth;
|
||||
|
@ -151,8 +150,8 @@ enum unix_funcs
|
|||
static inline const char *debugstr_device_desc(struct device_desc *desc)
|
||||
{
|
||||
if (!desc) return "(null)";
|
||||
return wine_dbg_sprintf("{vid %04x, pid %04x, version %04x, input %d, uid %08x, usage %04x:%04x, is_gamepad %u, is_hidraw %u, is_bluetooth %u}",
|
||||
desc->vid, desc->pid, desc->version, desc->input, desc->uid, desc->usages.UsagePage, desc->usages.Usage,
|
||||
return wine_dbg_sprintf("{vid %04x, pid %04x, version %04x, input %d, uid %08x, is_gamepad %u, is_hidraw %u, is_bluetooth %u}",
|
||||
desc->vid, desc->pid, desc->version, desc->input, desc->uid,
|
||||
desc->is_gamepad, desc->is_hidraw, desc->is_bluetooth);
|
||||
}
|
||||
|
||||
|
|
|
@ -1240,3 +1240,211 @@ HRESULT WINAPI RoResolveNamespace(HSTRING name, HSTRING windowsMetaDataDir,
|
|||
|
||||
return RO_E_METADATA_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
struct parse_type_context
|
||||
{
|
||||
DWORD allocated_parts_count;
|
||||
DWORD parts_count;
|
||||
HSTRING *parts;
|
||||
};
|
||||
|
||||
static HRESULT add_part(struct parse_type_context *context, const WCHAR *part, size_t length)
|
||||
{
|
||||
DWORD new_parts_count;
|
||||
HSTRING *new_parts;
|
||||
HRESULT hr;
|
||||
|
||||
if (context->parts_count == context->allocated_parts_count)
|
||||
{
|
||||
new_parts_count = context->allocated_parts_count ? context->allocated_parts_count * 2 : 4;
|
||||
new_parts = CoTaskMemRealloc(context->parts, new_parts_count * sizeof(*context->parts));
|
||||
if (!new_parts)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
context->allocated_parts_count = new_parts_count;
|
||||
context->parts = new_parts;
|
||||
}
|
||||
|
||||
if (FAILED(hr = WindowsCreateString(part, length, &context->parts[context->parts_count])))
|
||||
return hr;
|
||||
|
||||
context->parts_count++;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT parse_part(struct parse_type_context *context, const WCHAR *input, unsigned int length)
|
||||
{
|
||||
const WCHAR *start, *end, *ptr;
|
||||
|
||||
start = input;
|
||||
end = start + length;
|
||||
|
||||
/* Remove leading spaces */
|
||||
while (start < end && *start == ' ')
|
||||
start++;
|
||||
|
||||
/* Remove trailing spaces */
|
||||
while (end - 1 >= start && end[-1] == ' ')
|
||||
end--;
|
||||
|
||||
/* Only contains spaces */
|
||||
if (start == end)
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
|
||||
/* Has spaces in the middle */
|
||||
for (ptr = start; ptr < end; ptr++)
|
||||
{
|
||||
if (*ptr == ' ')
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
}
|
||||
|
||||
return add_part(context, start, end - start);
|
||||
}
|
||||
|
||||
static HRESULT parse_type(struct parse_type_context *context, const WCHAR *input, unsigned int length)
|
||||
{
|
||||
unsigned int i, parameter_count, nested_level;
|
||||
const WCHAR *start, *end, *part_start, *ptr;
|
||||
HRESULT hr;
|
||||
|
||||
start = input;
|
||||
end = start + length;
|
||||
part_start = start;
|
||||
ptr = start;
|
||||
|
||||
/* Read until the end of input or '`' or '<' or '>' or ',' */
|
||||
while (ptr < end && *ptr != '`' && *ptr != '<' && *ptr != '>' && *ptr != ',')
|
||||
ptr++;
|
||||
|
||||
/* If the type name has '`' and there are characters before '`' */
|
||||
if (ptr > start && ptr < end && *ptr == '`')
|
||||
{
|
||||
/* Move past the '`' */
|
||||
ptr++;
|
||||
|
||||
/* Read the number of type parameters, expecting '1' to '9' */
|
||||
if (!(ptr < end && *ptr >= '1' && *ptr <= '9'))
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
parameter_count = *ptr - '0';
|
||||
|
||||
/* Move past the number of type parameters, expecting '<' */
|
||||
ptr++;
|
||||
if (!(ptr < end && *ptr == '<'))
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
|
||||
/* Add the name of parameterized interface, e.g., the "interface`1" in "interface`1<parameter>" */
|
||||
if (FAILED(hr = parse_part(context, part_start, ptr - part_start)))
|
||||
return hr;
|
||||
|
||||
/* Move past the '<' */
|
||||
ptr++;
|
||||
nested_level = 1;
|
||||
|
||||
/* Read parameters inside brackets, e.g., the "p1" and "p2" in "interface`2<p1, p2>" */
|
||||
for (i = 0; i < parameter_count; i++)
|
||||
{
|
||||
/* Read a new parameter */
|
||||
part_start = ptr;
|
||||
|
||||
/* Read until ','. The comma must be at the same nested bracket level */
|
||||
while (ptr < end)
|
||||
{
|
||||
if (*ptr == '<')
|
||||
{
|
||||
nested_level++;
|
||||
ptr++;
|
||||
}
|
||||
else if (*ptr == '>')
|
||||
{
|
||||
/* The last parameter before '>' */
|
||||
if (i == parameter_count - 1 && nested_level == 1)
|
||||
{
|
||||
if (FAILED(hr = parse_type(context, part_start, ptr - part_start)))
|
||||
return hr;
|
||||
|
||||
nested_level--;
|
||||
ptr++;
|
||||
|
||||
/* Finish reading all parameters */
|
||||
break;
|
||||
}
|
||||
|
||||
nested_level--;
|
||||
ptr++;
|
||||
}
|
||||
else if (*ptr == ',' && nested_level == 1)
|
||||
{
|
||||
/* Parse the parameter, which can be another parameterized type */
|
||||
if (FAILED(hr = parse_type(context, part_start, ptr - part_start)))
|
||||
return hr;
|
||||
|
||||
/* Move past the ',' */
|
||||
ptr++;
|
||||
|
||||
/* Finish reading one parameter */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mismatching brackets or not enough parameters */
|
||||
if (nested_level != 0 || i != parameter_count)
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
|
||||
/* The remaining characters must be spaces */
|
||||
while (ptr < end)
|
||||
{
|
||||
if (*ptr++ != ' ')
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
/* Contain invalid '`', '<', '>' or ',' */
|
||||
else if (ptr != end)
|
||||
{
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
}
|
||||
/* Non-parameterized */
|
||||
else
|
||||
{
|
||||
return parse_part(context, part_start, ptr - part_start);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT WINAPI RoParseTypeName(HSTRING type_name, DWORD *parts_count, HSTRING **parts)
|
||||
{
|
||||
struct parse_type_context context = {0};
|
||||
const WCHAR *input;
|
||||
unsigned int i;
|
||||
HRESULT hr;
|
||||
|
||||
TRACE("%s %p %p.\n", debugstr_hstring(type_name), parts_count, parts);
|
||||
|
||||
/* Empty string */
|
||||
if (!WindowsGetStringLen(type_name))
|
||||
return E_INVALIDARG;
|
||||
|
||||
input = WindowsGetStringRawBuffer(type_name, NULL);
|
||||
/* The string has a leading space */
|
||||
if (input[0] == ' ')
|
||||
return RO_E_METADATA_INVALID_TYPE_FORMAT;
|
||||
|
||||
*parts_count = 0;
|
||||
*parts = NULL;
|
||||
if (FAILED(hr = parse_type(&context, input, wcslen(input))))
|
||||
{
|
||||
for (i = 0; i < context.parts_count; i++)
|
||||
WindowsDeleteString(context.parts[i]);
|
||||
CoTaskMemFree(context.parts);
|
||||
return hr;
|
||||
}
|
||||
|
||||
*parts_count = context.parts_count;
|
||||
*parts = context.parts;
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -814,11 +814,159 @@ static void test_RoResolveNamespace(void)
|
|||
RoUninitialize();
|
||||
}
|
||||
|
||||
static void test_RoParseTypeName(void)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const WCHAR *type_name;
|
||||
HRESULT hr;
|
||||
DWORD parts_count;
|
||||
const WCHAR *parts[16];
|
||||
}
|
||||
tests[] =
|
||||
{
|
||||
/* Invalid type names */
|
||||
{L"", E_INVALIDARG},
|
||||
{L" ", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"`", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"<", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L">", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L",", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"<>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"`<>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a b", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a,b", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"1<>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L" a", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L" a ", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a<", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a<>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`<>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`1<>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a<b>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`<b> ", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"`1<b>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L" a`1<b>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`1<b>c", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`1<b,>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`2<b, <c, d>>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`10<b1, b2, b3, b4, b5, b6, b7, b8, b9, b10>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`0xa<b1, b2, b3, b4, b5, b6, b7, b8, b9, b10>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
{L"a`a<b1, b2, b3, b4, b5, b6, b7, b8, b9, b10>", RO_E_METADATA_INVALID_TYPE_FORMAT},
|
||||
/* Valid type names */
|
||||
{L"1", S_OK, 1, {L"1"}},
|
||||
{L"a", S_OK, 1, {L"a"}},
|
||||
{L"-", S_OK, 1, {L"-"}},
|
||||
{L"a ", S_OK, 1, {L"a"}},
|
||||
{L"0`1<b>", S_OK, 2, {L"0`1", L"b"}},
|
||||
{L"a`1<b>", S_OK, 2, {L"a`1", L"b"}},
|
||||
{L"a`1<b> ", S_OK, 2, {L"a`1", L"b"}},
|
||||
{L"a`1<b >", S_OK, 2, {L"a`1", L"b"}},
|
||||
{L"a`1< b>", S_OK, 2, {L"a`1", L"b"}},
|
||||
{L"a`1< b >", S_OK, 2, {L"a`1", L"b"}},
|
||||
{L"a`2<b,c>", S_OK, 3, {L"a`2", L"b", L"c"}},
|
||||
{L"a`2<b, c>", S_OK, 3, {L"a`2", L"b", L"c"}},
|
||||
{L"a`2<b ,c>", S_OK, 3, {L"a`2", L"b", L"c"}},
|
||||
{L"a`2<b , c>", S_OK, 3, {L"a`2", L"b", L"c"}},
|
||||
{L"a`3<b, c, d>", S_OK, 4, {L"a`3", L"b", L"c", L"d"}},
|
||||
{L"a`1<b`1<c>>", S_OK, 3, {L"a`1", L"b`1", L"c"}},
|
||||
{L"a`1<b`2<c, d>>", S_OK, 4, {L"a`1", L"b`2", L"c", L"d"}},
|
||||
{L"a`2<b`2<c, d>, e>", S_OK, 5, {L"a`2", L"b`2", L"c", L"d", L"e"}},
|
||||
{L"a`2<b, c`2<d, e>>", S_OK, 5, {L"a`2", L"b", L"c`2", L"d", L"e"}},
|
||||
{L"a`9<b1, b2, b3, b4, b5, b6, b7, b8, b9>", S_OK, 10, {L"a`9", L"b1", L"b2", L"b3", L"b4", L"b5", L"b6", L"b7", L"b8", L"b9"}},
|
||||
{L"Windows.Foundation.IExtensionInformation", S_OK, 1, {L"Windows.Foundation.IExtensionInformation"}},
|
||||
{L"Windows.Foundation.IReference`1<Windows.UI.Color>", S_OK, 2, {L"Windows.Foundation.IReference`1", L"Windows.UI.Color"}},
|
||||
{L"Windows.Foundation.Collections.IIterator`1<Windows.Foundation.Collections.IMapView`2<Windows.Foundation.Collections.IVector`1<String>, String>>",
|
||||
S_OK, 5, {L"Windows.Foundation.Collections.IIterator`1",
|
||||
L"Windows.Foundation.Collections.IMapView`2",
|
||||
L"Windows.Foundation.Collections.IVector`1",
|
||||
L"String",
|
||||
L"String"}},
|
||||
};
|
||||
HSTRING type_name, *parts;
|
||||
const WCHAR *buffer;
|
||||
DWORD parts_count;
|
||||
unsigned int i, j;
|
||||
HRESULT hr;
|
||||
|
||||
/* Parameter checks */
|
||||
hr = WindowsCreateString(L"a", 1, &type_name);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
|
||||
hr = RoParseTypeName(NULL, &parts_count, &parts);
|
||||
ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
|
||||
|
||||
/* Crash on Windows */
|
||||
if (0)
|
||||
{
|
||||
hr = RoParseTypeName(type_name, NULL, &parts);
|
||||
ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
|
||||
|
||||
hr = RoParseTypeName(type_name, &parts_count, NULL);
|
||||
ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
|
||||
}
|
||||
|
||||
hr = RoParseTypeName(type_name, &parts_count, &parts);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
ok(parts_count == 1, "Got unexpected %ld.\n", parts_count);
|
||||
hr = WindowsDeleteString(parts[0]);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
CoTaskMemFree(parts);
|
||||
hr = WindowsDeleteString(type_name);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
|
||||
/* Parsing checks */
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++)
|
||||
{
|
||||
winetest_push_context("%s", wine_dbgstr_w(tests[i].type_name));
|
||||
|
||||
if (tests[i].type_name)
|
||||
{
|
||||
hr = WindowsCreateString(tests[i].type_name, wcslen(tests[i].type_name), &type_name);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
type_name = NULL;
|
||||
}
|
||||
|
||||
parts_count = 0;
|
||||
hr = RoParseTypeName(type_name, &parts_count, &parts);
|
||||
ok(hr == tests[i].hr, "Got unexpected hr %#lx.\n", hr);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
hr = WindowsDeleteString(type_name);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
winetest_pop_context();
|
||||
continue;
|
||||
}
|
||||
ok(parts_count == tests[i].parts_count, "Got unexpected %lu.\n", parts_count);
|
||||
|
||||
for (j = 0; j < parts_count; j++)
|
||||
{
|
||||
winetest_push_context("%s", wine_dbgstr_w(tests[i].parts[j]));
|
||||
|
||||
buffer = WindowsGetStringRawBuffer(parts[j], NULL);
|
||||
ok(!lstrcmpW(tests[i].parts[j], buffer), "Got unexpected %s.\n", wine_dbgstr_w(buffer));
|
||||
hr = WindowsDeleteString(parts[j]);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
|
||||
winetest_pop_context();
|
||||
}
|
||||
CoTaskMemFree(parts);
|
||||
|
||||
hr = WindowsDeleteString(type_name);
|
||||
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
|
||||
winetest_pop_context();
|
||||
}
|
||||
}
|
||||
|
||||
START_TEST(wintypes)
|
||||
{
|
||||
IsWow64Process(GetCurrentProcess(), &is_wow64);
|
||||
|
||||
test_IApiInformationStatics();
|
||||
test_IPropertyValueStatics();
|
||||
test_RoParseTypeName();
|
||||
test_RoResolveNamespace();
|
||||
}
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
@ stub RoGetMetaDataFile
|
||||
@ stdcall RoIsApiContractMajorVersionPresent(wstr long ptr)
|
||||
@ stub RoIsApiContractPresent
|
||||
@ stub RoParseTypeName
|
||||
@ stdcall RoParseTypeName(ptr ptr ptr)
|
||||
@ stdcall RoResolveNamespace(ptr ptr long ptr ptr ptr ptr ptr)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <hstring.h>
|
||||
|
||||
HRESULT WINAPI RoIsApiContractMajorVersionPresent(const WCHAR *, UINT16, BOOL *);
|
||||
HRESULT WINAPI RoParseTypeName(HSTRING, DWORD *, HSTRING **);
|
||||
HRESULT WINAPI RoResolveNamespace(HSTRING, HSTRING, DWORD, const HSTRING *, DWORD *, HSTRING **, DWORD *, HSTRING **);
|
||||
|
||||
#endif /* _ROMETADATARESOLUTION_H */
|
||||
|
|
Loading…
Reference in a new issue