[PATCH v7 0/3] MR9789: winebus: Use smallest report size for joystick axis
Some games don't handle 32-bit sized joystick axes well. Assetto Corsa Rally, which was released a month ago as an early access and is still v0.1, is a recent example. The proof of concept 16-bit patch resolves the problem for many users and hardware on Linux, (https://github.com/ValveSoftware/Proton/issues/9220#issuecomment-3647591465) and users with open-source hardware confirmed the behavior of the game on Windows. Since it's the case that the game is non-compliant while the wine is perfectly compliant, we might be able to push and wait for the developer to release the fix, but I believe that the fix will also help to improve compatibility for some old, non-maintained, non-compliant games. -- v7: winebus: Use smallest report size for unsigned axis too winebus: Use 16-bit relative axis range for bus_sdl winebus: Use smallest report size for joystick axis https://gitlab.winehq.org/wine/wine/-/merge_requests/9789
From: SeongChan Lee <foriequal@gmail.com> --- dlls/winebus.sys/hid.c | 68 ++++++++++++++++++++++++++------- dlls/winebus.sys/unix_private.h | 4 +- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 46ad0c69065..dcd8dc072c7 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -256,7 +256,14 @@ BOOL hid_device_add_hatswitch(struct unix_device *iface, INT count) return TRUE; } -static BOOL hid_device_add_axis_count(struct unix_device *iface, BOOL rel, BYTE count, +static BYTE hid_device_determine_axis_size(LONG min, LONG max) +{ + if (min >= -128 && max <= 127) return 8; + if (min >= -32768 && max <= 32767) return 16; + return 32; +} + +static BOOL hid_device_add_axis_count(struct unix_device *iface, BOOL rel, BYTE size, BYTE count, USAGE usage_page, const USAGE *usages) { struct hid_device_state *state = &iface->hid_device_state; @@ -268,13 +275,22 @@ static BOOL hid_device_add_axis_count(struct unix_device *iface, BOOL rel, BYTE ERR("axes should be added before buttons or hatswitches!\n"); else if ((state->bit_size % 8)) ERR("axes should be byte aligned, missing padding!\n"); - else if (state->bit_size + 32 * count > 0x80000) + else if (size != 8 && size != 16 && size != 32) + ERR("unsupported axis size, not one of 8, 16, 32!\n"); + else if (state->bit_size + size * count > 0x80000) ERR("report size overflow, too many elements!\n"); + else if (state->abs_axis_count + state->rel_axis_count + count > ARRAY_SIZE(state->axis_byte_offsets)) + ERR("axis usage overflow, too many elements!\n"); else if (rel) { - if (!state->rel_axis_count) state->rel_axis_start = offset; + for (i = 0; i < count; ++i) + { + int axis = state->abs_axis_count + state->rel_axis_count + i; + state->axis_byte_offsets[axis] = offset + i * size / 8; + state->axis_sizes[axis] = size; + } state->rel_axis_count += count; - state->bit_size += 32 * count; + state->bit_size += size * count; return TRUE; } else @@ -286,13 +302,15 @@ static BOOL hid_device_add_axis_count(struct unix_device *iface, BOOL rel, BYTE } for (i = 0; i < count; ++i) { + state->axis_byte_offsets[state->abs_axis_count + i] = offset + i * size / 8; + state->axis_sizes[state->abs_axis_count + i] = size; + state->abs_axis_usages[state->abs_axis_count + i].UsagePage = usage_page; state->abs_axis_usages[state->abs_axis_count + i].Usage = usages[i]; } - if (!state->abs_axis_count) state->abs_axis_start = offset; state->abs_axis_count += count; - state->bit_size += 32 * count; + state->bit_size += size * count; return TRUE; } @@ -312,17 +330,18 @@ BOOL hid_device_add_axes(struct unix_device *iface, BYTE count, USAGE usage_page { END_COLLECTION, }; + BYTE size = hid_device_determine_axis_size(min, max); const BYTE template[] = { LOGICAL_MINIMUM(4, min), LOGICAL_MAXIMUM(4, max), - REPORT_SIZE(1, 32), + REPORT_SIZE(1, size), REPORT_COUNT(1, count), INPUT(1, Data|Var|(rel ? Rel : Abs)), }; int i; - if (!hid_device_add_axis_count(iface, rel, count, usage_page, usages)) + if (!hid_device_add_axis_count(iface, rel, size, count, usage_page, usages)) return FALSE; if (!hid_report_descriptor_append(desc, template_begin, sizeof(template_begin))) @@ -1457,26 +1476,47 @@ void *hid_device_create(const struct hid_device_vtbl *vtbl, SIZE_T size) #ifdef WORDS_BIGENDIAN # define LE_ULONG(x) RtlUlongByteSwap((ULONG)(x)) +# define LE_USHORT(x) RtlUshortByteSwap((USHORT)(x)) #else # define LE_ULONG(x) ((ULONG)(x)) +# define LE_USHORT(x) ((USHORT)(x)) #endif +static BOOL hid_device_set_axis(struct hid_device_state* state, ULONG axis, LONG value) +{ + USHORT offset = state->axis_byte_offsets[axis]; + BYTE size = state->axis_sizes[axis]; + switch (size) + { + case 8: + *(state->report_buf + offset) = (BYTE)value; + break; + case 16: + *(USHORT *)(state->report_buf + offset) = LE_USHORT(value); + break; + case 32: + *(ULONG *)(state->report_buf + offset) = LE_ULONG(value); + break; + default: + return FALSE; + } + + return TRUE; +} + BOOL hid_device_set_abs_axis(struct unix_device *iface, ULONG index, LONG value) { struct hid_device_state *state = &iface->hid_device_state; - ULONG offset = state->abs_axis_start + index * 4; if (index >= state->abs_axis_count) return FALSE; - *(ULONG *)(state->report_buf + offset) = LE_ULONG(value); - return TRUE; + return hid_device_set_axis(state, index, value); } BOOL hid_device_set_rel_axis(struct unix_device *iface, ULONG index, LONG value) { struct hid_device_state *state = &iface->hid_device_state; - ULONG offset = state->rel_axis_start + index * 4; + ULONG axis = state->abs_axis_count + index; if (index >= state->rel_axis_count) return FALSE; - *(ULONG *)(state->report_buf + offset) = LE_ULONG(value); - return TRUE; + return hid_device_set_axis(state, axis, value); } BOOL hid_device_set_button(struct unix_device *iface, ULONG index, BOOL is_set) diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 11328caee08..c39c8787247 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -190,10 +190,10 @@ struct hid_physical struct hid_device_state { ULONG bit_size; + USHORT axis_byte_offsets[64]; + BYTE axis_sizes[64]; USAGE_AND_PAGE abs_axis_usages[32]; - USHORT abs_axis_start; USHORT abs_axis_count; - USHORT rel_axis_start; USHORT rel_axis_count; USHORT hatswitch_start; USHORT hatswitch_count; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9789
From: SeongChan Lee <foriequal@gmail.com> --- dlls/winebus.sys/bus_sdl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index bc7904059ed..0048dea8712 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -373,7 +373,7 @@ static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface, cons for (i = 0; i < ball_count; i++) { if (!hid_device_add_axes(iface, 2, relative_axis_usages[2 * i].UsagePage, - &relative_axis_usages[2 * i].Usage, TRUE, INT32_MIN, INT32_MAX)) + &relative_axis_usages[2 * i].Usage, TRUE, INT16_MIN, INT16_MAX)) return STATUS_NO_MEMORY; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9789
From: SeongChan Lee <foriequal@gmail.com> --- dlls/winebus.sys/hid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index dcd8dc072c7..66ea37f077c 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -258,8 +258,8 @@ BOOL hid_device_add_hatswitch(struct unix_device *iface, INT count) static BYTE hid_device_determine_axis_size(LONG min, LONG max) { - if (min >= -128 && max <= 127) return 8; - if (min >= -32768 && max <= 32767) return 16; + if ((min >= 0 && max <= 255) || (min >= -128 && max <= 127)) return 8; + if ((min >= 0 && max <= 65535) || (min >= -32768 && max <= 32767)) return 16; return 32; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9789
Hi, @rbernon. Can I get a review for this MR and !9796? I've been using this patch and https://gitlab.winehq.org/wine/wine/-/merge_requests/9796 for a while with the Thrustmaster T300RS, and it's been fine with a few games (Assetto Corsa Rally, Dirt Rally 2.0, BeamNG). I can wait for more since the game that I've wanted to fix has added 32-bit axis support, and they're preparing Wine 11 release. Or I can send this patch to Proton to see if it's working fine and upstream this again after that. Please let me know if there's anything I could do. Thank you. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9789#note_126210
Rémi Bernon (@rbernon) commented about dlls/winebus.sys/hid.c:
static BYTE hid_device_determine_axis_size(LONG min, LONG max) { - if (min >= -128 && max <= 127) return 8; - if (min >= -32768 && max <= 32767) return 16; + if ((min >= 0 && max <= 255) || (min >= -128 && max <= 127)) return 8; + if ((min >= 0 && max <= 65535) || (min >= -32768 && max <= 32767)) return 16;
This should probably be squashed with the first commit -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9789#note_126438
participants (3)
-
Rémi Bernon (@rbernon) -
SeongChan Lee -
SeongChan Lee (@foriequal0)