From: Arkadiusz Hiler ahiler@codeweavers.com
The HID input report format is different between Linux (hid_logitech_hidpp) and Windows when it comes to axis HID usages.
To correct that we can use the device through SDL and craft our own HID mapping. --- dlls/winebus.sys/bus_sdl.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 0529b35fbea..b3b69a518b5 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -251,6 +251,13 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force) return TRUE; }
+static const USAGE_AND_PAGE g920_absolute_usages[] = +{ + {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_X}, /* wheel */ + {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Y}, /* accelerator */ + {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Z}, /* brake */ + {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_RZ}, /* clutch */ +}; static const USAGE_AND_PAGE absolute_axis_usages[] = { {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_X}, @@ -275,19 +282,34 @@ static const USAGE_AND_PAGE relative_axis_usages[] = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_WHEEL}, };
-static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface) +static int get_absolute_usages(const struct device_desc *desc, const USAGE_AND_PAGE **absolute_usages) +{ + if (desc->vid == 0x046d && desc->pid == 0xc262) + { + *absolute_usages = g920_absolute_usages; + return ARRAY_SIZE(g920_absolute_usages); + } + + *absolute_usages = absolute_axis_usages; + return ARRAY_SIZE(absolute_axis_usages); +} + +static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface, const struct device_desc *desc) { const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_JOYSTICK}; struct sdl_device *impl = impl_from_unix_device(iface); int i, button_count, axis_count, ball_count, hat_count; USAGE_AND_PAGE physical_usage;
+ const USAGE_AND_PAGE *absolute_usages = NULL; + size_t absolute_usages_count = get_absolute_usages(desc, &absolute_usages); + axis_count = pSDL_JoystickNumAxes(impl->sdl_joystick); if (options->split_controllers) axis_count = min(6, axis_count - impl->axis_offset); - if (axis_count > ARRAY_SIZE(absolute_axis_usages)) + if (axis_count > absolute_usages_count) { - FIXME("More than %zu absolute axes found, ignoring.\n", ARRAY_SIZE(absolute_axis_usages)); - axis_count = ARRAY_SIZE(absolute_axis_usages); + FIXME("More than %zu absolute axes found, ignoring.\n", absolute_usages_count); + axis_count = absolute_usages_count; }
ball_count = pSDL_JoystickNumBalls(impl->sdl_joystick); @@ -343,8 +365,8 @@ static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface)
for (i = 0; i < axis_count; i++) { - if (!hid_device_add_axes(iface, 1, absolute_axis_usages[i].UsagePage, - &absolute_axis_usages[i].Usage, FALSE, -32768, 32767)) + if (!hid_device_add_axes(iface, 1, absolute_usages[i].UsagePage, + &absolute_usages[i].Usage, FALSE, -32768, 32767)) return STATUS_NO_MEMORY; }
@@ -993,7 +1015,7 @@ static void sdl_add_device(unsigned int index) 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); + else status = build_joystick_report_descriptor(&impl->unix_device, &desc); if (status) { list_remove(&impl->unix_device.entry);
From: Arkadiusz Hiler ahiler@codeweavers.com
--- dlls/winebus.sys/bus_sdl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index b3b69a518b5..91cb785fdff 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -938,6 +938,7 @@ static void sdl_add_device(unsigned int index)
SDL_Joystick* joystick; SDL_JoystickID id; + SDL_JoystickType joystick_type; SDL_GameController *controller = NULL; const char *product, *sdl_serial; char guid_str[33], buffer[ARRAY_SIZE(desc.product)]; @@ -949,7 +950,10 @@ static void sdl_add_device(unsigned int index) return; }
- if (options->map_controllers && pSDL_IsGameController(index)) + joystick_type = pSDL_JoystickGetType(joystick); + if (options->map_controllers && pSDL_IsGameController(index) + && joystick_type != SDL_JOYSTICK_TYPE_WHEEL + && joystick_type != SDL_JOYSTICK_TYPE_FLIGHT_STICK) controller = pSDL_GameControllerOpen(index);
if (controller) product = pSDL_GameControllerName(controller);
From: Rémi Bernon rbernon@codeweavers.com
It causes a 30s delay with some devices, lets assume that it will later work without the delay. --- dlls/winebus.sys/bus_udev.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-)
diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 65cabff09e4..3facaa0241e 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -549,7 +549,6 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_device *dev, struct lnxev_info *info) { struct input_absinfo abs_info[ABS_CNT]; - struct ff_effect effect; USHORT count = 0; USAGE usages[16]; int i; @@ -599,21 +598,8 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d impl->haptic_effect_id = -1; for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i) impl->effect_ids[i] = -1;
- if (test_bit(info->ff, FF_RUMBLE)) - { - effect.id = -1; - effect.type = FF_RUMBLE; - effect.replay.length = 0; - effect.u.rumble.strong_magnitude = 0; - effect.u.rumble.weak_magnitude = 0; - - if (ioctl(impl->base.device_fd, EVIOCSFF, &effect) == -1) - WARN("couldn't allocate rumble effect for haptics: %d %s\n", errno, strerror(errno)); - else if (!hid_device_add_haptics(iface)) - return FALSE; - else - impl->haptic_effect_id = effect.id; - } + if (test_bit(info->ff, FF_RUMBLE) && !hid_device_add_haptics(iface)) + return STATUS_NO_MEMORY;
for (i = 0; i < FF_MAX; ++i) if (test_bit(info->ff, i)) break; if (i != FF_MAX) @@ -761,7 +747,7 @@ static NTSTATUS lnxev_device_haptics_start(struct unix_device *iface, UINT durat effect.u.rumble.strong_magnitude = rumble_intensity; effect.u.rumble.weak_magnitude = buzz_intensity;
- if (ioctl(impl->base.device_fd, EVIOCSFF, &effect) == -1) + if (effect.id == -1 || ioctl(impl->base.device_fd, EVIOCSFF, &effect) == -1) { effect.id = -1; if (ioctl(impl->base.device_fd, EVIOCSFF, &effect) == 1)
From: Rémi Bernon rbernon@codeweavers.com
Some controllers block on ioctl and write calls, and this may propagate up to the frontend through various blocking calls, including when the frontend API is using async I/O because of how Wine implements async I/O for device objects. --- dlls/winebus.sys/bus_udev.c | 110 +++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 44 deletions(-)
diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 3facaa0241e..4f929d0cb45 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -199,7 +199,10 @@ struct lnxev_device int button_count; BOOL is_gamepad;
- int haptic_effect_id; + pthread_cond_t haptics_cond; + pthread_t haptics_thread; + struct ff_effect haptics; + int effect_ids[256]; LONG effect_flags; }; @@ -595,7 +598,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d return STATUS_NO_MEMORY; }
- impl->haptic_effect_id = -1; + impl->haptics.id = -1; for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i) impl->effect_ids[i] = -1;
if (test_bit(info->ff, FF_RUMBLE) && !hid_device_add_haptics(iface)) @@ -694,11 +697,53 @@ static void lnxev_device_destroy(struct unix_device *iface) udev_device_unref(impl->base.udev_device); }
+static void *lnxev_device_haptics_thread(void *args) +{ + struct lnxev_device *impl = lnxev_impl_from_unix_device(args); + struct ff_effect effect = {0}; + + pthread_mutex_lock(&udev_cs); + + for (;;) + { + while (!memcmp(&effect, &impl->haptics, sizeof(effect))) + pthread_cond_wait(&impl->haptics_cond, &udev_cs); + if (impl->haptics.type == (__u16)-1) break; + + effect = impl->haptics; + pthread_mutex_unlock(&udev_cs); + + if (effect.type && (effect.id == -1 || ioctl(impl->base.device_fd, EVIOCSFF, &effect) == -1)) + { + effect.id = -1; + ioctl(impl->base.device_fd, EVIOCSFF, &effect); + } + + if (effect.id != -1) + { + struct input_event event = {.type = EV_FF, .code = effect.id, .value = !!effect.type}; + write(impl->base.device_fd, &event, sizeof(event)); + } + + pthread_mutex_lock(&udev_cs); + impl->haptics.id = effect.id; + } + + pthread_mutex_unlock(&udev_cs); + return NULL; +} + static NTSTATUS lnxev_device_start(struct unix_device *iface) { + struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); + pthread_mutex_lock(&udev_cs); start_polling_device(iface); + impl->haptics.type = 0; pthread_mutex_unlock(&udev_cs); + + pthread_cond_init(&impl->haptics_cond, NULL); + pthread_create(&impl->haptics_thread, NULL, lnxev_device_haptics_thread, iface); return STATUS_SUCCESS; }
@@ -709,7 +754,12 @@ static void lnxev_device_stop(struct unix_device *iface) pthread_mutex_lock(&udev_cs); stop_polling_device(iface); list_remove(&impl->base.unix_device.entry); + impl->haptics.type = -1; pthread_mutex_unlock(&udev_cs); + pthread_cond_signal(&impl->haptics_cond); + + pthread_join(impl->haptics_thread, NULL); + pthread_cond_destroy(&impl->haptics_cond); }
static void lnxev_device_read_report(struct unix_device *iface) @@ -733,39 +783,17 @@ static NTSTATUS lnxev_device_haptics_start(struct unix_device *iface, UINT durat USHORT left_intensity, USHORT right_intensity) { struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); - struct ff_effect effect = - { - .id = impl->haptic_effect_id, - .type = FF_RUMBLE, - }; - struct input_event event;
TRACE("iface %p, duration_ms %u, rumble_intensity %u, buzz_intensity %u, left_intensity %u, right_intensity %u.\n", iface, duration_ms, rumble_intensity, buzz_intensity, left_intensity, right_intensity);
- effect.replay.length = duration_ms; - effect.u.rumble.strong_magnitude = rumble_intensity; - effect.u.rumble.weak_magnitude = buzz_intensity; - - if (effect.id == -1 || ioctl(impl->base.device_fd, EVIOCSFF, &effect) == -1) - { - effect.id = -1; - if (ioctl(impl->base.device_fd, EVIOCSFF, &effect) == 1) - { - WARN("couldn't re-allocate rumble effect for haptics: %d %s\n", errno, strerror(errno)); - return STATUS_UNSUCCESSFUL; - } - impl->haptic_effect_id = effect.id; - } - - event.type = EV_FF; - event.code = effect.id; - event.value = 1; - if (write(impl->base.device_fd, &event, sizeof(event)) == -1) - { - WARN("couldn't start haptics rumble effect: %d %s\n", errno, strerror(errno)); - return STATUS_UNSUCCESSFUL; - } + pthread_mutex_lock(&udev_cs); + impl->haptics.type = FF_RUMBLE; + impl->haptics.replay.length = duration_ms; + impl->haptics.u.rumble.strong_magnitude = rumble_intensity; + impl->haptics.u.rumble.weak_magnitude = buzz_intensity; + pthread_mutex_unlock(&udev_cs); + pthread_cond_signal(&impl->haptics_cond);
return STATUS_SUCCESS; } @@ -773,22 +801,16 @@ static NTSTATUS lnxev_device_haptics_start(struct unix_device *iface, UINT durat static NTSTATUS lnxev_device_haptics_stop(struct unix_device *iface) { struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); - struct ff_effect effect = - { - .id = impl->haptic_effect_id, - .type = FF_RUMBLE, - }; - struct input_event event;
TRACE("iface %p.\n", iface);
- if (effect.id == -1) return STATUS_SUCCESS; - - event.type = EV_FF; - event.code = effect.id; - event.value = 0; - if (write(impl->base.device_fd, &event, sizeof(event)) == -1) - WARN("couldn't stop haptics rumble effect: %d %s\n", errno, strerror(errno)); + pthread_mutex_lock(&udev_cs); + impl->haptics.type = 0; + impl->haptics.replay.length = 0; + impl->haptics.u.rumble.strong_magnitude = 0; + impl->haptics.u.rumble.weak_magnitude = 0; + pthread_mutex_unlock(&udev_cs); + pthread_cond_signal(&impl->haptics_cond);
return STATUS_SUCCESS; }
This merge request was approved by Rémi Bernon.