From c1d51031ef2c8926777e45f596a49eae4a3ff328 Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Thu, 23 Jan 2020 14:28:56 -0600 Subject: [PATCH] xinput: Implement XInputGetKeystroke. Signed-off-by: Andrew Eikum Signed-off-by: Alexandre Julliard --- dlls/xinput1_3/xinput_main.c | 216 ++++++++++++++++++++++++++++++-- dlls/xinput1_3/xinput_private.h | 1 + 2 files changed, 209 insertions(+), 8 deletions(-) diff --git a/dlls/xinput1_3/xinput_main.c b/dlls/xinput1_3/xinput_main.c index 5485ae8d2d1..63b7dd7f0b5 100644 --- a/dlls/xinput1_3/xinput_main.c +++ b/dlls/xinput1_3/xinput_main.c @@ -202,19 +202,219 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetStateEx(DWORD index, XINPUT_STATE* state return xinput_get_state(index, state); } -DWORD WINAPI DECLSPEC_HOTPATCH XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke) +static const int JS_STATE_OFF = 0; +static const int JS_STATE_LOW = 1; +static const int JS_STATE_HIGH = 2; + +static int joystick_state(const SHORT value) { - static int warn_once; + if (value > 20000) + return JS_STATE_HIGH; + if (value < -20000) + return JS_STATE_LOW; + return JS_STATE_OFF; +} - if (!warn_once++) - FIXME("(index %u, reserved %u, keystroke %p) Stub!\n", index, reserved, keystroke); +static WORD js_vk_offs(const int x, const int y) +{ + if (y == JS_STATE_OFF) + { + /*if (x == JS_STATE_OFF) shouldn't get here */ + if (x == JS_STATE_LOW) return 3; /* LEFT */ + /*if (x == JS_STATE_HIGH)*/ return 2; /* RIGHT */ + } + if (y == JS_STATE_HIGH) + { + if (x == JS_STATE_OFF) return 0; /* UP */ + if (x == JS_STATE_LOW) return 4; /* UPLEFT */ + /*if (x == JS_STATE_HIGH)*/ return 5; /* UPRIGHT */ + } + /*if (y == JS_STATE_LOW)*/ + { + if (x == JS_STATE_OFF) return 1; /* DOWN */ + if (x == JS_STATE_LOW) return 7; /* DOWNLEFT */ + /*if (x == JS_STATE_HIGH)*/ return 6; /* DOWNRIGHT */ + } +} - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].platform_private) +static DWORD check_joystick_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke, + const SHORT *cur_x, const SHORT *cur_y, SHORT *last_x, SHORT *last_y, + const WORD base_vk) +{ + int cur_vk = 0, cur_x_st, cur_y_st; + int last_vk = 0, last_x_st, last_y_st; + + cur_x_st = joystick_state(*cur_x); + cur_y_st = joystick_state(*cur_y); + if (cur_x_st || cur_y_st) + cur_vk = base_vk + js_vk_offs(cur_x_st, cur_y_st); + + last_x_st = joystick_state(*last_x); + last_y_st = joystick_state(*last_y); + if (last_x_st || last_y_st) + last_vk = base_vk + js_vk_offs(last_x_st, last_y_st); + + if (cur_vk != last_vk) + { + if (last_vk) + { + /* joystick was set, and now different. send a KEYUP event, and set + * last pos to centered, so the appropriate KEYDOWN event will be + * sent on the next call. */ + keystroke->VirtualKey = last_vk; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = XINPUT_KEYSTROKE_KEYUP; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + + *last_x = 0; + *last_y = 0; + + return ERROR_SUCCESS; + } + + /* joystick was unset, send KEYDOWN. */ + keystroke->VirtualKey = cur_vk; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + + *last_x = *cur_x; + *last_y = *cur_y; + + return ERROR_SUCCESS; + } + + *last_x = *cur_x; + *last_y = *cur_y; + + return ERROR_EMPTY; +} + +static BOOL trigger_is_on(const BYTE value) +{ + return value > 30; +} + +static DWORD check_for_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke) +{ + xinput_controller *device = &controllers[index]; + const XINPUT_GAMEPAD *cur; + DWORD ret = ERROR_EMPTY; + int i; + + static const struct { + int mask; + WORD vk; + } buttons[] = { + { XINPUT_GAMEPAD_DPAD_UP, VK_PAD_DPAD_UP }, + { XINPUT_GAMEPAD_DPAD_DOWN, VK_PAD_DPAD_DOWN }, + { XINPUT_GAMEPAD_DPAD_LEFT, VK_PAD_DPAD_LEFT }, + { XINPUT_GAMEPAD_DPAD_RIGHT, VK_PAD_DPAD_RIGHT }, + { XINPUT_GAMEPAD_START, VK_PAD_START }, + { XINPUT_GAMEPAD_BACK, VK_PAD_BACK }, + { XINPUT_GAMEPAD_LEFT_THUMB, VK_PAD_LTHUMB_PRESS }, + { XINPUT_GAMEPAD_RIGHT_THUMB, VK_PAD_RTHUMB_PRESS }, + { XINPUT_GAMEPAD_LEFT_SHOULDER, VK_PAD_LSHOULDER }, + { XINPUT_GAMEPAD_RIGHT_SHOULDER, VK_PAD_RSHOULDER }, + { XINPUT_GAMEPAD_A, VK_PAD_A }, + { XINPUT_GAMEPAD_B, VK_PAD_B }, + { XINPUT_GAMEPAD_X, VK_PAD_X }, + { XINPUT_GAMEPAD_Y, VK_PAD_Y }, + /* note: guide button does not send an event */ + }; + + if (!verify_and_lock_device(device)) return ERROR_DEVICE_NOT_CONNECTED; - return ERROR_NOT_SUPPORTED; + cur = &device->state.Gamepad; + + /*** buttons ***/ + for (i = 0; i < ARRAY_SIZE(buttons); ++i) + { + if ((cur->wButtons & buttons[i].mask) ^ (device->last_keystroke.wButtons & buttons[i].mask)) + { + keystroke->VirtualKey = buttons[i].vk; + keystroke->Unicode = 0; /* unused */ + if (cur->wButtons & buttons[i].mask) + { + keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN; + device->last_keystroke.wButtons |= buttons[i].mask; + } + else + { + keystroke->Flags = XINPUT_KEYSTROKE_KEYUP; + device->last_keystroke.wButtons &= ~buttons[i].mask; + } + keystroke->UserIndex = index; + keystroke->HidCode = 0; + ret = ERROR_SUCCESS; + goto done; + } + } + + /*** triggers ***/ + if (trigger_is_on(cur->bLeftTrigger) ^ trigger_is_on(device->last_keystroke.bLeftTrigger)) + { + keystroke->VirtualKey = VK_PAD_LTRIGGER; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = trigger_is_on(cur->bLeftTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + device->last_keystroke.bLeftTrigger = cur->bLeftTrigger; + ret = ERROR_SUCCESS; + goto done; + } + + if (trigger_is_on(cur->bRightTrigger) ^ trigger_is_on(device->last_keystroke.bRightTrigger)) + { + keystroke->VirtualKey = VK_PAD_RTRIGGER; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = trigger_is_on(cur->bRightTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + device->last_keystroke.bRightTrigger = cur->bRightTrigger; + ret = ERROR_SUCCESS; + goto done; + } + + /*** joysticks ***/ + ret = check_joystick_keystroke(index, keystroke, &cur->sThumbLX, &cur->sThumbLY, + &device->last_keystroke.sThumbLX, + &device->last_keystroke.sThumbLY, VK_PAD_LTHUMB_UP); + if (ret == ERROR_SUCCESS) + goto done; + + ret = check_joystick_keystroke(index, keystroke, &cur->sThumbRX, &cur->sThumbRY, + &device->last_keystroke.sThumbRX, + &device->last_keystroke.sThumbRY, VK_PAD_RTHUMB_UP); + if (ret == ERROR_SUCCESS) + goto done; + +done: + unlock_device(device); + + return ret; +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke) +{ + TRACE("(index %u, reserved %u, keystroke %p)\n", index, reserved, keystroke); + + if (index >= XUSER_MAX_COUNT && index != XUSER_INDEX_ANY) + return ERROR_BAD_ARGUMENTS; + + if (index == XUSER_INDEX_ANY) + { + int i; + for (i = 0; i < XUSER_MAX_COUNT; ++i) + if (check_for_keystroke(i, keystroke) == ERROR_SUCCESS) + return ERROR_SUCCESS; + return ERROR_EMPTY; + } + + return check_for_keystroke(index, keystroke); } DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES* capabilities) diff --git a/dlls/xinput1_3/xinput_private.h b/dlls/xinput1_3/xinput_private.h index f3456cd8e7c..7de9a2a2e4d 100644 --- a/dlls/xinput1_3/xinput_private.h +++ b/dlls/xinput1_3/xinput_private.h @@ -23,6 +23,7 @@ typedef struct _xinput_controller XINPUT_CAPABILITIES caps; void *platform_private; /* non-NULL when device is valid; validity may be read without holding crit */ XINPUT_STATE state; + XINPUT_GAMEPAD last_keystroke; XINPUT_VIBRATION vibration; } xinput_controller;