winebus.sys: Factor out setting HID report values.

Signed-off-by: Rémi Bernon <rbernon@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Rémi Bernon 2021-09-23 08:55:27 +02:00 committed by Alexandre Julliard
parent c6df23fed3
commit 9cf1e8353c
4 changed files with 172 additions and 216 deletions

View File

@ -50,12 +50,6 @@
#include "wine/unicode.h"
#include "hidusage.h"
#ifdef WORDS_BIGENDIAN
# define LE_DWORD(x) RtlUlongByteSwap(x)
#else
# define LE_DWORD(x) (x)
#endif
#include "unix_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
@ -142,64 +136,23 @@ static struct sdl_device *find_device_from_id(SDL_JoystickID id)
return NULL;
}
static void set_button_value(struct sdl_device *impl, int index, int value)
static void set_hat_value(struct unix_device *iface, int index, int value)
{
struct hid_device_state *state = &impl->unix_device.hid_device_state;
USHORT offset = state->button_start;
int byte_index = offset + index / 8;
int bit_index = index % 8;
BYTE mask = 1 << bit_index;
if (index >= state->button_count) return;
if (value) state->report_buf[byte_index] |= mask;
else state->report_buf[byte_index] &= ~mask;
}
static void set_axis_value(struct sdl_device *impl, int index, short value)
{
struct hid_device_state *state = &impl->unix_device.hid_device_state;
USHORT offset = state->abs_axis_start;
if (index >= state->abs_axis_count) return;
offset += index * sizeof(DWORD);
*(DWORD *)&state->report_buf[offset] = LE_DWORD(value);
}
static void set_ball_value(struct sdl_device *impl, int index, int value1, int value2)
{
struct hid_device_state *state = &impl->unix_device.hid_device_state;
USHORT offset = state->rel_axis_start;
if (index >= state->rel_axis_count) return;
offset += index * sizeof(DWORD);
*(DWORD *)&state->report_buf[offset] = LE_DWORD(value1);
*(DWORD *)&state->report_buf[offset + sizeof(DWORD)] = LE_DWORD(value2);
}
static void set_hat_value(struct sdl_device *impl, int index, int value)
{
struct hid_device_state *state = &impl->unix_device.hid_device_state;
USHORT offset = state->hatswitch_start;
unsigned char val;
if (index >= state->hatswitch_count) return;
offset += index;
LONG x = 0, y = 0;
switch (value)
{
/* 8 1 2
* 7 0 3
* 6 5 4 */
case SDL_HAT_CENTERED: val = 0; break;
case SDL_HAT_UP: val = 1; break;
case SDL_HAT_RIGHTUP: val = 2; break;
case SDL_HAT_RIGHT: val = 3; break;
case SDL_HAT_RIGHTDOWN: val = 4; break;
case SDL_HAT_DOWN: val = 5; break;
case SDL_HAT_LEFTDOWN: val = 6; break;
case SDL_HAT_LEFT: val = 7; break;
case SDL_HAT_LEFTUP: val = 8; break;
default: return;
case SDL_HAT_CENTERED: break;
case SDL_HAT_UP: y = 1; break;
case SDL_HAT_RIGHTUP: y = x = 1; break;
case SDL_HAT_RIGHT: x = 1; break;
case SDL_HAT_RIGHTDOWN: x = 1; y = -1; break;
case SDL_HAT_DOWN: y = -1; break;
case SDL_HAT_LEFTDOWN: x = y = -1; break;
case SDL_HAT_LEFT: x = -1; break;
case SDL_HAT_LEFTUP: x = -1; y = 1; break;
}
state->report_buf[offset] = val;
hid_device_set_hatswitch_x(iface, index, x);
hid_device_set_hatswitch_y(iface, index, y);
}
static BOOL descriptor_add_haptic(struct sdl_device *impl)
@ -286,40 +239,13 @@ static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface)
/* Initialize axis in the report */
for (i = 0; i < axis_count; i++)
set_axis_value(impl, i, pSDL_JoystickGetAxis(impl->sdl_joystick, i));
hid_device_set_abs_axis(iface, i, pSDL_JoystickGetAxis(impl->sdl_joystick, i));
for (i = 0; i < hat_count; i++)
set_hat_value(impl, i, pSDL_JoystickGetHat(impl->sdl_joystick, i));
set_hat_value(iface, i, pSDL_JoystickGetHat(impl->sdl_joystick, i));
return STATUS_SUCCESS;
}
static SHORT compose_dpad_value(SDL_GameController *joystick)
{
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP))
{
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
return SDL_HAT_RIGHTUP;
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT))
return SDL_HAT_LEFTUP;
return SDL_HAT_UP;
}
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN))
{
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
return SDL_HAT_RIGHTDOWN;
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT))
return SDL_HAT_LEFTDOWN;
return SDL_HAT_DOWN;
}
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
return SDL_HAT_RIGHT;
if (pSDL_GameControllerGetButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT))
return SDL_HAT_LEFT;
return SDL_HAT_CENTERED;
}
static NTSTATUS build_controller_report_descriptor(struct unix_device *iface)
{
static const USAGE left_axis_usages[] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y};
@ -358,8 +284,15 @@ static NTSTATUS build_controller_report_descriptor(struct unix_device *iface)
/* Initialize axis in the report */
for (i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
set_axis_value(impl, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i));
set_hat_value(impl, 0, compose_dpad_value(impl->sdl_controller));
hid_device_set_abs_axis(iface, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i));
if (pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_UP))
hid_device_set_hatswitch_y(iface, 0, -1);
if (pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN))
hid_device_set_hatswitch_y(iface, 0, +1);
if (pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT))
hid_device_set_hatswitch_x(iface, 0, -1);
if (pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
hid_device_set_hatswitch_x(iface, 0, +1);
return STATUS_SUCCESS;
}
@ -471,7 +404,7 @@ static BOOL set_report_from_joystick_event(struct sdl_device *impl, SDL_Event *e
{
SDL_JoyButtonEvent *ie = &event->jbutton;
set_button_value(impl, ie->button, ie->state);
hid_device_set_button(iface, ie->button, ie->state);
bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
break;
}
@ -479,18 +412,16 @@ static BOOL set_report_from_joystick_event(struct sdl_device *impl, SDL_Event *e
{
SDL_JoyAxisEvent *ie = &event->jaxis;
if (ie->axis < 6)
{
set_axis_value(impl, ie->axis, ie->value);
bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
}
hid_device_set_abs_axis(iface, ie->axis, ie->value);
bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
break;
}
case SDL_JOYBALLMOTION:
{
SDL_JoyBallEvent *ie = &event->jball;
set_ball_value(impl, ie->ball, ie->xrel, ie->yrel);
hid_device_set_rel_axis(iface, 2 * ie->ball, ie->xrel);
hid_device_set_rel_axis(iface, 2 * ie->ball + 1, ie->yrel);
bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
break;
}
@ -498,7 +429,7 @@ static BOOL set_report_from_joystick_event(struct sdl_device *impl, SDL_Event *e
{
SDL_JoyHatEvent *ie = &event->jhat;
set_hat_value(impl, ie->hat, ie->value);
set_hat_value(iface, ie->hat, ie->value);
bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
break;
}
@ -524,10 +455,16 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event
switch ((button = ie->button))
{
case SDL_CONTROLLER_BUTTON_DPAD_UP:
hid_device_set_hatswitch_y(iface, 0, ie->state ? -1 : 0);
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
hid_device_set_hatswitch_y(iface, 0, ie->state ? +1 : 0);
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
hid_device_set_hatswitch_x(iface, 0, ie->state ? -1 : 0);
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
set_hat_value(impl, 0, compose_dpad_value(impl->sdl_controller));
hid_device_set_hatswitch_x(iface, 0, ie->state ? +1 : 0);
break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = 4; break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = 5; break;
@ -538,7 +475,7 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event
case SDL_CONTROLLER_BUTTON_GUIDE: button = 10; break;
}
set_button_value(impl, button, ie->state);
hid_device_set_button(iface, button, ie->state);
bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
break;
}
@ -546,7 +483,7 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event
{
SDL_ControllerAxisEvent *ie = &event->caxis;
set_axis_value(impl, ie->axis, ie->value);
hid_device_set_abs_axis(iface, ie->axis, ie->value);
bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
break;
}

View File

@ -126,7 +126,6 @@ struct lnxev_device
BYTE rel_map[HID_REL_MAX];
BYTE hat_map[8];
BYTE button_map[KEY_MAX];
int hat_values[8];
};
static inline struct base_device *impl_from_unix_device(struct unix_device *iface)
@ -325,89 +324,6 @@ static const BYTE* what_am_I(struct udev_device *dev)
return Unknown;
}
static void set_button_value(struct lnxev_device *impl, int index, int value)
{
struct hid_device_state *state = &impl->base.unix_device.hid_device_state;
USHORT offset = state->button_start;
int byte_index = offset + index / 8;
int bit_index = index % 8;
BYTE mask = 1 << bit_index;
if (index >= state->button_count) return;
if (value) state->report_buf[byte_index] |= mask;
else state->report_buf[byte_index] &= ~mask;
}
static void set_abs_axis_value(struct lnxev_device *impl, int code, int value)
{
struct hid_device_state *state = &impl->base.unix_device.hid_device_state;
USHORT offset, index;
/* check for hatswitches */
if (code <= ABS_HAT3Y && code >= ABS_HAT0X)
{
index = code - ABS_HAT0X;
impl->hat_values[index] = value;
if ((code - ABS_HAT0X) % 2)
index--;
/* 8 1 2
* 7 0 3
* 6 5 4 */
if (impl->hat_values[index] == 0)
{
if (impl->hat_values[index+1] == 0)
value = 0;
else if (impl->hat_values[index+1] < 0)
value = 1;
else
value = 5;
}
else if (impl->hat_values[index] > 0)
{
if (impl->hat_values[index+1] == 0)
value = 3;
else if (impl->hat_values[index+1] < 0)
value = 2;
else
value = 4;
}
else
{
if (impl->hat_values[index+1] == 0)
value = 7;
else if (impl->hat_values[index+1] < 0)
value = 8;
else
value = 6;
}
index = impl->hat_map[index];
offset = state->hatswitch_start;
if (index >= state->hatswitch_count) return;
offset += index;
state->report_buf[offset] = value;
}
else if (code < HID_ABS_MAX && ABS_TO_HID_MAP[code][0] != 0)
{
index = impl->abs_map[code];
offset = state->abs_axis_start;
if (index >= state->abs_axis_count) return;
offset += index * sizeof(DWORD);
*(DWORD *)&state->report_buf[offset] = LE_DWORD(value);
}
}
static void set_rel_axis_value(struct lnxev_device *impl, int code, int value)
{
struct hid_device_state *state = &impl->base.unix_device.hid_device_state;
USHORT offset, index;
if (code < HID_REL_MAX && REL_TO_HID_MAP[code][0] != 0)
{
index = impl->rel_map[code];
offset = state->rel_axis_start;
if (index >= state->rel_axis_count) return;
offset += index * sizeof(DWORD);
*(DWORD *)&state->report_buf[offset] = LE_DWORD(value);
}
}
static INT count_buttons(int device_fd, BYTE *map)
{
int i;
@ -506,12 +422,11 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d
}
hat_count = 0;
for (i = ABS_HAT0X; i <=ABS_HAT3X; i+=2)
for (i = ABS_HAT0X; i <= ABS_HAT3X; i += 2)
{
if (!test_bit(absbits, i)) continue;
impl->hat_map[i - ABS_HAT0X] = hat_count++;
impl->hat_values[i - ABS_HAT0X] = 0;
impl->hat_values[i - ABS_HAT0X + 1] = 0;
impl->hat_map[i - ABS_HAT0X] = hat_count;
impl->hat_map[i - ABS_HAT0X + 1] = hat_count++;
}
if (hat_count && !hid_device_add_hatswitch(iface, hat_count))
@ -527,8 +442,15 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d
/* Initialize axis in the report */
for (i = 0; i < HID_ABS_MAX; i++)
if (test_bit(absbits, i))
set_abs_axis_value(impl, i, abs_info[i].value);
{
if (!test_bit(absbits, i)) continue;
if (i < ABS_HAT0X || i > ABS_HAT3Y)
hid_device_set_abs_axis(iface, impl->abs_map[i], abs_info[i].value);
else if ((i - ABS_HAT0X) % 2)
hid_device_set_hatswitch_y(iface, impl->hat_map[i - ABS_HAT0X], abs_info[i].value);
else
hid_device_set_hatswitch_x(iface, impl->hat_map[i - ABS_HAT0X], abs_info[i].value);
}
return STATUS_SUCCESS;
}
@ -536,33 +458,39 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d
static BOOL set_report_from_event(struct unix_device *iface, struct input_event *ie)
{
struct lnxev_device *impl = lnxev_impl_from_unix_device(iface);
switch(ie->type)
switch (ie->type)
{
#ifdef EV_SYN
case EV_SYN:
switch (ie->code)
{
case SYN_REPORT: return hid_device_sync_report(iface);
case SYN_DROPPED: hid_device_drop_report(iface); break;
}
return FALSE;
case EV_SYN:
switch (ie->code)
{
case SYN_REPORT: return hid_device_sync_report(iface);
case SYN_DROPPED: hid_device_drop_report(iface); break;
}
return FALSE;
#endif
#ifdef EV_MSC
case EV_MSC:
return FALSE;
case EV_MSC:
return FALSE;
#endif
case EV_KEY:
set_button_value(impl, impl->button_map[ie->code], ie->value);
return FALSE;
case EV_ABS:
set_abs_axis_value(impl, ie->code, ie->value);
return FALSE;
case EV_REL:
set_rel_axis_value(impl, ie->code, ie->value);
return FALSE;
default:
ERR("TODO: Process Report (%i, %i)\n",ie->type, ie->code);
return FALSE;
case EV_KEY:
hid_device_set_button(iface, impl->button_map[ie->code], ie->value);
return FALSE;
case EV_ABS:
if (ie->code < ABS_HAT0X || ie->code > ABS_HAT3Y)
hid_device_set_abs_axis(iface, impl->abs_map[ie->code], ie->value);
else if ((ie->code - ABS_HAT0X) % 2)
hid_device_set_hatswitch_y(iface, impl->hat_map[ie->code - ABS_HAT0X], ie->value);
else
hid_device_set_hatswitch_x(iface, impl->hat_map[ie->code - ABS_HAT0X], ie->value);
return FALSE;
case EV_REL:
hid_device_set_rel_axis(iface, impl->rel_map[ie->code], ie->value);
return FALSE;
default:
ERR("TODO: Process Report (%i, %i)\n",ie->type, ie->code);
return FALSE;
}
}
#endif
@ -791,8 +719,6 @@ static void lnxev_device_read_report(struct unix_device *iface)
struct input_event ie;
int size;
if (!state->report_buf || !state->report_len) return;
size = read(impl->base.device_fd, &ie, sizeof(ie));
if (size == -1)
TRACE_(hid_report)("Read failed. Likely an unplugged device\n");

View File

@ -369,6 +369,93 @@ void *hid_device_create(const struct hid_device_vtbl *vtbl, SIZE_T size)
return impl;
}
#ifdef WORDS_BIGENDIAN
# define LE_ULONG(x) RtlUlongByteSwap((ULONG)(x))
#else
# define LE_ULONG(x) ((ULONG)(x))
#endif
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;
}
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;
if (index > state->rel_axis_count) return FALSE;
*(ULONG *)(state->report_buf + offset) = LE_ULONG(value);
return TRUE;
}
BOOL hid_device_set_button(struct unix_device *iface, ULONG index, BOOL is_set)
{
struct hid_device_state *state = &iface->hid_device_state;
ULONG offset = state->button_start + (index / 8);
BYTE mask = (1 << (index % 8));
if (index > state->button_count) return FALSE;
if (is_set) state->report_buf[offset] |= mask;
else state->report_buf[offset] &= ~mask;
return TRUE;
}
/* hatswitch x / y vs value:
* -1 x +1
* +-------->
* -1 | 8 1 2
* y | 7 0 3
* +1 | 6 5 4
* v
*/
static void hatswitch_decompose(BYTE value, LONG *x, LONG *y)
{
*x = *y = 0;
if (value == 8 || value == 1 || value == 2) *y = -1;
if (value == 6 || value == 5 || value == 4) *y = +1;
if (value == 8 || value == 7 || value == 6) *x = -1;
if (value == 2 || value == 3 || value == 4) *x = +1;
}
static void hatswitch_compose(LONG x, LONG y, BYTE *value)
{
if (x == 0 && y == 0) *value = 0;
else if (x == 0 && y < 0) *value = 1;
else if (x > 0 && y < 0) *value = 2;
else if (x > 0 && y == 0) *value = 3;
else if (x > 0 && y > 0) *value = 4;
else if (x == 0 && y > 0) *value = 5;
else if (x < 0 && y > 0) *value = 6;
else if (x < 0 && y == 0) *value = 7;
else if (x < 0 && y < 0) *value = 8;
}
BOOL hid_device_set_hatswitch_x(struct unix_device *iface, ULONG index, LONG new_x)
{
struct hid_device_state *state = &iface->hid_device_state;
ULONG offset = state->hatswitch_start + index;
LONG x, y;
if (index > state->hatswitch_count) return FALSE;
hatswitch_decompose(state->report_buf[offset], &x, &y);
hatswitch_compose(new_x, y, &state->report_buf[offset]);
return TRUE;
}
BOOL hid_device_set_hatswitch_y(struct unix_device *iface, ULONG index, LONG new_y)
{
struct hid_device_state *state = &iface->hid_device_state;
ULONG offset = state->hatswitch_start + index;
LONG x, y;
if (index > state->hatswitch_count) return FALSE;
hatswitch_decompose(state->report_buf[offset], &x, &y);
hatswitch_compose(x, new_y, &state->report_buf[offset]);
return TRUE;
}
BOOL hid_device_sync_report(struct unix_device *iface)
{
BOOL dropped;

View File

@ -119,6 +119,12 @@ extern BOOL hid_device_add_axes(struct unix_device *iface, BYTE count, USAGE usa
extern BOOL hid_device_add_haptics(struct unix_device *iface) DECLSPEC_HIDDEN;
extern BOOL hid_device_set_abs_axis(struct unix_device *iface, ULONG index, LONG value) DECLSPEC_HIDDEN;
extern BOOL hid_device_set_rel_axis(struct unix_device *iface, ULONG index, LONG value) DECLSPEC_HIDDEN;
extern BOOL hid_device_set_button(struct unix_device *iface, ULONG index, BOOL is_set) DECLSPEC_HIDDEN;
extern BOOL hid_device_set_hatswitch_x(struct unix_device *iface, ULONG index, LONG new_x) DECLSPEC_HIDDEN;
extern BOOL hid_device_set_hatswitch_y(struct unix_device *iface, ULONG index, LONG new_y) DECLSPEC_HIDDEN;
extern BOOL hid_device_sync_report(struct unix_device *iface) DECLSPEC_HIDDEN;
extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN;