[WinKey] Rebindable keyboard controls.

This commit is contained in:
Enrico Pozzobon 2021-09-20 11:23:27 +02:00 committed by Rick Gibbed
parent 5384e0e174
commit 5e31429128
3 changed files with 206 additions and 196 deletions

View File

@ -0,0 +1,38 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
// This is a partial file designed to be included by other files when
// constructing various tables.
// clang-format off
XE_HID_WINKEY_BINDING(DpadLeft, "DPAD_LEFT" , keybind_dpad_left , "^A" )
XE_HID_WINKEY_BINDING(DpadRight, "DPAD_RIGHT" , keybind_dpad_right , "^D" )
XE_HID_WINKEY_BINDING(DpadDown, "DPAD_DOWN" , keybind_dpad_down , "^S" )
XE_HID_WINKEY_BINDING(DpadUp, "DPAD_UP" , keybind_dpad_up , "^W" )
XE_HID_WINKEY_BINDING(LThumbLeft, "LEFT_THUMB_LEFT" , keybind_left_thumb_left , "_A" )
XE_HID_WINKEY_BINDING(LThumbRight, "LEFT_THUMB_RIGHT" , keybind_left_thumb_right , "_D" )
XE_HID_WINKEY_BINDING(LThumbDown, "LEFT_THUMB_DOWN" , keybind_left_thumb_down , "_S" )
XE_HID_WINKEY_BINDING(LThumbUp, "LEFT_THUMB_UP" , keybind_left_thumb_up , "_W" )
XE_HID_WINKEY_BINDING(LThumbPress, "LEFT_THUMB_PRESSED" , keybind_left_thumb , "F" )
XE_HID_WINKEY_BINDING(RThumbUp, "RIGHT_THUMB_UP" , keybind_right_thumb_up , "0x26")
XE_HID_WINKEY_BINDING(RThumbLeft, "RIGHT_THUMB_DOWN" , keybind_right_thumb_down , "0x28")
XE_HID_WINKEY_BINDING(RThumbRight, "RIGHT_THUMB_RIGHT" , keybind_right_thumb_right, "0x27")
XE_HID_WINKEY_BINDING(RThumbLeft, "RIGHT_THUMB_LEFT" , keybind_right_thumb_left , "0x25")
XE_HID_WINKEY_BINDING(RThumbPress, "RIGHT_THUMB_PRESSED", keybind_right_thumb , "K" )
XE_HID_WINKEY_BINDING(X, "X" , keybind_x , "L" )
XE_HID_WINKEY_BINDING(B, "B" , keybind_b , "0xDE")
XE_HID_WINKEY_BINDING(A, "A" , keybind_a , "0xBA")
XE_HID_WINKEY_BINDING(Y, "Y" , keybind_y , "P" )
XE_HID_WINKEY_BINDING(LTrigger, "LEFT_TRIGGER" , keybind_left_trigger , "Q I" )
XE_HID_WINKEY_BINDING(RTrigger, "RIGHT_TRIGGER" , keybind_right_trigger , "E O" )
XE_HID_WINKEY_BINDING(Back, "BACK" , keybind_back , "Z" )
XE_HID_WINKEY_BINDING(Start, "START" , keybind_start , "X" )
XE_HID_WINKEY_BINDING(LShoulder, "LEFT_SHOULDER" , keybind_left_shoulder , "1" )
XE_HID_WINKEY_BINDING(RShoulder, "RIGHT_SHOULDER" , keybind_right_shoulder , "3" )
// clang-format on

View File

@ -2,23 +2,84 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/hid/winkey/winkey_input_driver.h"
#include "xenia/base/logging.h"
#include "xenia/base/platform_win.h"
#include "xenia/hid/hid_flags.h"
#include "xenia/hid/input_system.h"
#include "xenia/ui/virtual_key.h"
#include "xenia/ui/window.h"
#define XE_HID_WINKEY_BINDING(button, description, cvar_name, \
cvar_default_value) \
DEFINE_string(cvar_name, cvar_default_value, \
"List of keys to bind to " description \
", separated by spaces", \
"HID.WinKey")
#include "winkey_binding_table.inc"
#undef XE_HID_WINKEY_BINDING
namespace xe {
namespace hid {
namespace winkey {
bool __inline IsKeyToggled(uint8_t key) {
return (GetKeyState(key) & 0x1) == 0x1;
}
bool __inline IsKeyDown(uint8_t key) {
return (GetAsyncKeyState(key) & 0x8000) == 0x8000;
}
bool __inline IsKeyDown(ui::VirtualKey virtual_key) {
return IsKeyDown(static_cast<uint8_t>(virtual_key));
}
void WinKeyInputDriver::ParseKeyBinding(ui::VirtualKey output_key,
const std::string_view description,
const std::string_view source_tokens) {
for (const std::string_view source_token :
utf8::split(source_tokens, " ", true)) {
KeyBinding key_binding;
key_binding.output_key = output_key;
std::string_view token = source_token;
if (utf8::starts_with(token, "_")) {
key_binding.lowercase = true;
token = token.substr(1);
} else if (utf8::starts_with(token, "^")) {
key_binding.uppercase = true;
token = token.substr(1);
}
if (utf8::starts_with(token, "0x")) {
token = token.substr(2);
key_binding.input_key = static_cast<ui::VirtualKey>(
string_util::from_string<uint16_t>(token, true));
} else if (token.size() == 1 && (token[0] >= 'A' && token[0] <= 'Z') ||
(token[0] >= '0' && token[0] <= '9')) {
key_binding.input_key = static_cast<ui::VirtualKey>(token[0]);
}
if (key_binding.input_key == ui::VirtualKey::kNone) {
XELOGW("winkey: failed to parse binding \"{}\" for controller input {}.",
source_token, description);
continue;
}
key_bindings_.push_back(key_binding);
XELOGI("winkey: \"{}\" binds key 0x{:X} to controller input {}.",
source_token, key_binding.input_key, description);
}
}
WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
: InputDriver(window), packet_number_(1) {
// Register a key listener.
@ -50,6 +111,13 @@ WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
key.repeat_count = evt->repeat_count();
key_events_.push(key);
});
#define XE_HID_WINKEY_BINDING(button, description, cvar_name, \
cvar_default_value) \
ParseKeyBinding(xe::ui::VirtualKey::kXInputPad##button, description, \
cvars::cvar_name);
#include "winkey_binding_table.inc"
#undef XE_HID_WINKEY_BINDING
}
WinKeyInputDriver::~WinKeyInputDriver() = default;
@ -78,9 +146,6 @@ X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
return X_ERROR_SUCCESS;
}
#define IS_KEY_TOGGLED(key) ((GetKeyState(key) & 0x1) == 0x1)
#define IS_KEY_DOWN(key) ((GetAsyncKeyState(key) & 0x8000) == 0x8000)
X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
X_INPUT_STATE* out_state) {
if (user_index != 0) {
@ -98,114 +163,86 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
int16_t thumb_ry = 0;
if (window()->has_focus() && is_active()) {
if (IS_KEY_TOGGLED(VK_CAPITAL) || IS_KEY_DOWN(VK_SHIFT)) {
// dpad toggled
if (IS_KEY_DOWN('A')) {
// A
buttons |= 0x0004; // XINPUT_GAMEPAD_DPAD_LEFT
bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT);
for (const KeyBinding& b : key_bindings_) {
if (((b.lowercase == b.uppercase) || (b.lowercase && !capital) ||
(b.uppercase && capital)) &&
IsKeyDown(b.input_key)) {
switch (b.output_key) {
case ui::VirtualKey::kXInputPadA:
buttons |= 0x1000; // XINPUT_GAMEPAD_A
break;
case ui::VirtualKey::kXInputPadY:
buttons |= 0x8000; // XINPUT_GAMEPAD_Y
break;
case ui::VirtualKey::kXInputPadB:
buttons |= 0x2000; // XINPUT_GAMEPAD_B
break;
case ui::VirtualKey::kXInputPadX:
buttons |= 0x4000; // XINPUT_GAMEPAD_X
break;
case ui::VirtualKey::kXInputPadDpadLeft:
buttons |= 0x0004; // XINPUT_GAMEPAD_DPAD_LEFT
break;
case ui::VirtualKey::kXInputPadDpadRight:
buttons |= 0x0008; // XINPUT_GAMEPAD_DPAD_RIGHT
break;
case ui::VirtualKey::kXInputPadDpadDown:
buttons |= 0x0002; // XINPUT_GAMEPAD_DPAD_DOWN
break;
case ui::VirtualKey::kXInputPadDpadUp:
buttons |= 0x0001; // XINPUT_GAMEPAD_DPAD_UP
break;
case ui::VirtualKey::kXInputPadRThumbPress:
buttons |= 0x0080; // XINPUT_GAMEPAD_RIGHT_THUMB
break;
case ui::VirtualKey::kXInputPadLThumbPress:
buttons |= 0x0040; // XINPUT_GAMEPAD_LEFT_THUMB
break;
case ui::VirtualKey::kXInputPadBack:
buttons |= 0x0020; // XINPUT_GAMEPAD_BACK
break;
case ui::VirtualKey::kXInputPadStart:
buttons |= 0x0010; // XINPUT_GAMEPAD_START
break;
case ui::VirtualKey::kXInputPadLShoulder:
buttons |= 0x0100; // XINPUT_GAMEPAD_LEFT_SHOULDER
break;
case ui::VirtualKey::kXInputPadRShoulder:
buttons |= 0x0200; // XINPUT_GAMEPAD_RIGHT_SHOULDER
break;
case ui::VirtualKey::kXInputPadLTrigger:
left_trigger = 0xFF;
break;
case ui::VirtualKey::kXInputPadRTrigger:
right_trigger = 0xFF;
break;
case ui::VirtualKey::kXInputPadLThumbLeft:
thumb_lx += SHRT_MIN;
break;
case ui::VirtualKey::kXInputPadLThumbRight:
thumb_lx += SHRT_MAX;
break;
case ui::VirtualKey::kXInputPadLThumbDown:
thumb_ly += SHRT_MIN;
break;
case ui::VirtualKey::kXInputPadLThumbUp:
thumb_ly += SHRT_MAX;
break;
case ui::VirtualKey::kXInputPadRThumbUp:
thumb_ry += SHRT_MAX;
break;
case ui::VirtualKey::kXInputPadRThumbDown:
thumb_ry += SHRT_MIN;
break;
case ui::VirtualKey::kXInputPadRThumbRight:
thumb_rx += SHRT_MAX;
break;
case ui::VirtualKey::kXInputPadRThumbLeft:
thumb_rx += SHRT_MIN;
break;
}
}
if (IS_KEY_DOWN('D')) {
// D
buttons |= 0x0008; // XINPUT_GAMEPAD_DPAD_RIGHT
}
if (IS_KEY_DOWN('S')) {
// S
buttons |= 0x0002; // XINPUT_GAMEPAD_DPAD_DOWN
}
if (IS_KEY_DOWN('W')) {
// W
buttons |= 0x0001; // XINPUT_GAMEPAD_DPAD_UP
}
} else {
// left stick
if (IS_KEY_DOWN('A')) {
// A
thumb_lx += SHRT_MIN;
}
if (IS_KEY_DOWN('D')) {
// D
thumb_lx += SHRT_MAX;
}
if (IS_KEY_DOWN('S')) {
// S
thumb_ly += SHRT_MIN;
}
if (IS_KEY_DOWN('W')) {
// W
thumb_ly += SHRT_MAX;
}
}
if (IS_KEY_DOWN('F')) {
// F
buttons |= 0x0040; // XINPUT_GAMEPAD_LEFT_THUMB
}
// Right stick
if (IS_KEY_DOWN(VK_UP)) {
// Up
thumb_ry += SHRT_MAX;
}
if (IS_KEY_DOWN(VK_DOWN)) {
// Down
thumb_ry += SHRT_MIN;
}
if (IS_KEY_DOWN(VK_RIGHT)) {
// Right
thumb_rx += SHRT_MAX;
}
if (IS_KEY_DOWN(VK_LEFT)) {
// Left
thumb_rx += SHRT_MIN;
}
if (IS_KEY_DOWN('L')) {
// L
buttons |= 0x4000; // XINPUT_GAMEPAD_X
}
if (IS_KEY_DOWN(VK_OEM_7)) {
// '
buttons |= 0x2000; // XINPUT_GAMEPAD_B
}
if (IS_KEY_DOWN(VK_OEM_1)) {
// ;
buttons |= 0x1000; // XINPUT_GAMEPAD_A
}
if (IS_KEY_DOWN('P')) {
// P
buttons |= 0x8000; // XINPUT_GAMEPAD_Y
}
if (IS_KEY_DOWN('K')) {
// K
buttons |= 0x0080; // XINPUT_GAMEPAD_RIGHT_THUMB
}
if (IS_KEY_DOWN('Q') || IS_KEY_DOWN('I')) {
// Q / I
left_trigger = 0xFF;
}
if (IS_KEY_DOWN('E') || IS_KEY_DOWN('O')) {
// E / O
right_trigger = 0xFF;
}
if (IS_KEY_DOWN('Z')) {
// Z
buttons |= 0x0020; // XINPUT_GAMEPAD_BACK
}
if (IS_KEY_DOWN('X')) {
// X
buttons |= 0x0010; // XINPUT_GAMEPAD_START
}
if (IS_KEY_DOWN('1')) {
// 1
buttons |= 0x0100; // XINPUT_GAMEPAD_LEFT_SHOULDER
}
if (IS_KEY_DOWN('3')) {
// 3
buttons |= 0x0200; // XINPUT_GAMEPAD_RIGHT_SHOULDER
}
}
@ -259,90 +296,13 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
key_events_.pop();
}
switch (evt.virtual_key) {
case ui::VirtualKey::kOem1: // ;
xinput_virtual_key = ui::VirtualKey::kXInputPadA;
break;
case ui::VirtualKey::kOem7: // '
xinput_virtual_key = ui::VirtualKey::kXInputPadB;
break;
case ui::VirtualKey::kL:
xinput_virtual_key = ui::VirtualKey::kXInputPadX;
break;
case ui::VirtualKey::kP:
xinput_virtual_key = ui::VirtualKey::kXInputPadY;
break;
case ui::VirtualKey::k3:
xinput_virtual_key = ui::VirtualKey::kXInputPadRShoulder;
break;
case ui::VirtualKey::k1:
xinput_virtual_key = ui::VirtualKey::kXInputPadLShoulder;
break;
case ui::VirtualKey::kQ:
case ui::VirtualKey::kI:
xinput_virtual_key = ui::VirtualKey::kXInputPadLTrigger;
break;
case ui::VirtualKey::kE:
case ui::VirtualKey::kO:
xinput_virtual_key = ui::VirtualKey::kXInputPadRTrigger;
break;
case ui::VirtualKey::kX:
xinput_virtual_key = ui::VirtualKey::kXInputPadStart;
break;
case ui::VirtualKey::kZ:
xinput_virtual_key = ui::VirtualKey::kXInputPadBack;
break;
case ui::VirtualKey::kUp:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbUp;
break;
case ui::VirtualKey::kDown:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbDown;
break;
case ui::VirtualKey::kRight:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbRight;
break;
case ui::VirtualKey::kLeft:
xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbLeft;
break;
default:
// TODO(DrChat): Some other way to toggle this...
if (IS_KEY_TOGGLED(VK_CAPITAL) || IS_KEY_DOWN(VK_SHIFT)) {
// D-pad toggled.
switch (evt.virtual_key) {
case ui::VirtualKey::kW:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadUp;
break;
case ui::VirtualKey::kS:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadDown;
break;
case ui::VirtualKey::kA:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadLeft;
break;
case ui::VirtualKey::kD:
xinput_virtual_key = ui::VirtualKey::kXInputPadDpadRight;
break;
default:
break;
}
} else {
// Left thumbstick.
switch (evt.virtual_key) {
case ui::VirtualKey::kW:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbUp;
break;
case ui::VirtualKey::kS:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbDown;
break;
case ui::VirtualKey::kA:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbLeft;
break;
case ui::VirtualKey::kD:
xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbRight;
break;
default:
break;
}
}
bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT);
for (const KeyBinding& b : key_bindings_) {
if (b.input_key == evt.virtual_key &&
((b.lowercase == b.uppercase) || (b.lowercase && !capital) ||
(b.uppercase && capital))) {
xinput_virtual_key = b.output_key;
}
}
if (xinput_virtual_key != ui::VirtualKey::kNone) {

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -42,10 +42,22 @@ class WinKeyInputDriver : public InputDriver {
bool prev_state = false; // down(true) or up(false)
};
struct KeyBinding {
ui::VirtualKey input_key = ui::VirtualKey::kNone;
ui::VirtualKey output_key = ui::VirtualKey::kNone;
bool uppercase = false;
bool lowercase = false;
};
xe::global_critical_region global_critical_region_;
std::queue<KeyEvent> key_events_;
std::vector<KeyBinding> key_bindings_;
uint32_t packet_number_;
void ParseKeyBinding(ui::VirtualKey virtual_key,
const std::string_view description,
const std::string_view binding);
};
} // namespace winkey