xenia-canary/src/xenia/hid/winkey/winkey_input_driver.cc

438 lines
11 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 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/cvar.h"
#include "xenia/base/platform_win.h"
#include "xenia/hid/hid_flags.h"
#include "xenia/hid/input_system.h"
#include "xenia/ui/window.h"
DEFINE_bool(keyboard_passthru, false,
"Maybe useful for debug games, disables keyboard->gamepad "
"emulation and forwards exact keyboard events to game (note that "
"Xenia keybinds, eg. H to show FPS, will still be in effect!)",
"HID");
namespace xe {
namespace hid {
namespace winkey {
WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
: InputDriver(window), packet_number_(1) {
// Register a key listener.
window_->on_key_down.AddListener([this](ui::KeyEvent* evt) {
auto global_lock = global_critical_region_.Acquire();
KeyEvent key;
key.vkey = evt->key_code();
key.transition = true;
key.prev_state = evt->prev_state();
key.repeat_count = evt->repeat_count();
key_events_.push(key);
});
window_->on_key_up.AddListener([this](ui::KeyEvent* evt) {
auto global_lock = global_critical_region_.Acquire();
KeyEvent key;
key.vkey = evt->key_code();
key.transition = false;
key.prev_state = evt->prev_state();
key.repeat_count = evt->repeat_count();
key_events_.push(key);
});
}
WinKeyInputDriver::~WinKeyInputDriver() = default;
X_STATUS WinKeyInputDriver::Setup(
std::vector<std::unique_ptr<hid::InputDriver>>& drivers) {
int index = 0;
X_INPUT_STATE state;
// Search already added drivers for an index that none of them can use
while (index < 4) {
bool not_connected = false;
for (auto& driver : drivers) {
if (driver.get()->GetState(index, &state) !=
X_ERROR_DEVICE_NOT_CONNECTED) {
not_connected = false;
break;
}
not_connected = true;
}
if (not_connected) {
user_index_ = index;
return X_STATUS_SUCCESS;
}
index++;
}
// All indexes are in use...
user_index_ = -1;
// Return error so this driver won't get used
return X_ERROR_DEVICE_NOT_CONNECTED;
}
X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
X_INPUT_CAPABILITIES* out_caps) {
if (user_index != user_index_) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
// TODO(benvanik): confirm with a real XInput controller.
out_caps->type = 0x01; // XINPUT_DEVTYPE_GAMEPAD
out_caps->sub_type = 0x01; // XINPUT_DEVSUBTYPE_GAMEPAD
out_caps->flags = 0;
out_caps->gamepad.buttons = 0xFFFF;
out_caps->gamepad.left_trigger = 0xFF;
out_caps->gamepad.right_trigger = 0xFF;
out_caps->gamepad.thumb_lx = (int16_t)0xFFFFu;
out_caps->gamepad.thumb_ly = (int16_t)0xFFFFu;
out_caps->gamepad.thumb_rx = (int16_t)0xFFFFu;
out_caps->gamepad.thumb_ry = (int16_t)0xFFFFu;
out_caps->vibration.left_motor_speed = 0;
out_caps->vibration.right_motor_speed = 0;
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 != user_index_ || cvars::keyboard_passthru) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
packet_number_++;
uint16_t buttons = 0;
uint8_t left_trigger = 0;
uint8_t right_trigger = 0;
int16_t thumb_lx = 0;
int16_t thumb_ly = 0;
int16_t thumb_rx = 0;
int16_t thumb_ry = 0;
if (window_->has_focus()) {
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
}
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
}
}
out_state->packet_number = packet_number_;
out_state->gamepad.buttons = buttons;
out_state->gamepad.left_trigger = left_trigger;
out_state->gamepad.right_trigger = right_trigger;
out_state->gamepad.thumb_lx = thumb_lx;
out_state->gamepad.thumb_ly = thumb_ly;
out_state->gamepad.thumb_rx = thumb_rx;
out_state->gamepad.thumb_ry = thumb_ry;
return X_ERROR_SUCCESS;
}
X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
X_INPUT_VIBRATION* vibration) {
if (user_index != user_index_) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
return X_ERROR_SUCCESS;
}
X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
X_INPUT_KEYSTROKE* out_keystroke) {
if (!cvars::keyboard_passthru && user_index != user_index_) {
return X_ERROR_DEVICE_NOT_CONNECTED;
}
X_RESULT result = X_ERROR_EMPTY;
uint16_t virtual_key = 0;
uint16_t unicode = 0;
uint16_t keystroke_flags = 0;
uint8_t hid_code = 0;
// Pop from the queue.
KeyEvent evt;
{
auto global_lock = global_critical_region_.Acquire();
if (key_events_.empty()) {
// No keys!
return X_ERROR_EMPTY;
}
evt = key_events_.front();
key_events_.pop();
}
if (cvars::keyboard_passthru) {
virtual_key = evt.vkey;
} else {
// TODO(DrChat): Some other way to toggle this...
if (IS_KEY_TOGGLED(VK_CAPITAL)) {
// dpad toggled
if (evt.vkey == (0x41)) {
// A
virtual_key = 0x5812; // VK_PAD_DPAD_LEFT
} else if (evt.vkey == (0x44)) {
// D
virtual_key = 0x5813; // VK_PAD_DPAD_RIGHT
} else if (evt.vkey == (0x53)) {
// S
virtual_key = 0x5811; // VK_PAD_DPAD_DOWN
} else if (evt.vkey == (0x57)) {
// W
virtual_key = 0x5810; // VK_PAD_DPAD_UP
}
} else {
// left stick
if (evt.vkey == (0x57)) {
// W
virtual_key = 0x5820; // VK_PAD_LTHUMB_UP
}
if (evt.vkey == (0x53)) {
// S
virtual_key = 0x5821; // VK_PAD_LTHUMB_DOWN
}
if (evt.vkey == (0x44)) {
// D
virtual_key = 0x5822; // VK_PAD_LTHUMB_RIGHT
}
if (evt.vkey == (0x41)) {
// A
virtual_key = 0x5823; // VK_PAD_LTHUMB_LEFT
}
}
// Right stick
if (evt.vkey == (0x26)) {
// Up
virtual_key = 0x5830;
}
if (evt.vkey == (0x28)) {
// Down
virtual_key = 0x5831;
}
if (evt.vkey == (0x27)) {
// Right
virtual_key = 0x5832;
}
if (evt.vkey == (0x25)) {
// Left
virtual_key = 0x5833;
}
if (evt.vkey == (0x4C)) {
// L
virtual_key = 0x5802; // VK_PAD_X
} else if (evt.vkey == (VK_OEM_7)) {
// '
virtual_key = 0x5801; // VK_PAD_B
} else if (evt.vkey == (VK_OEM_1)) {
// ;
virtual_key = 0x5800; // VK_PAD_A
} else if (evt.vkey == (0x50)) {
// P
virtual_key = 0x5803; // VK_PAD_Y
}
if (evt.vkey == (0x58)) {
// X
virtual_key = 0x5814; // VK_PAD_START
}
if (evt.vkey == (0x5A)) {
// Z
virtual_key = 0x5815; // VK_PAD_BACK
}
if (evt.vkey == (0x51) || evt.vkey == (0x49)) {
// Q / I
virtual_key = 0x5806; // VK_PAD_LTRIGGER
}
if (evt.vkey == (0x45) || evt.vkey == (0x4F)) {
// E / O
virtual_key = 0x5807; // VK_PAD_RTRIGGER
}
if (evt.vkey == (0x31)) {
// 1
virtual_key = 0x5805; // VK_PAD_LSHOULDER
}
if (evt.vkey == (0x33)) {
// 3
virtual_key = 0x5804; // VK_PAD_RSHOULDER
}
}
if (!cvars::keyboard_passthru) {
if (virtual_key != 0) {
if (evt.transition == true) {
keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN
} else if (evt.transition == false) {
keystroke_flags |= 0x0002; // XINPUT_KEYSTROKE_KEYUP
}
if (evt.prev_state == evt.transition) {
keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT
}
result = X_ERROR_SUCCESS;
}
} else {
// Handle keydown/keyup for VK_SHIFT
if (virtual_key == VK_SHIFT) {
if (evt.transition == false) {
keystroke_flags |= 0x0002;
} else if (evt.transition == true) {
keystroke_flags |= 0x0001;
}
}
// Only handle keydown for other keys, since some stupid games will count
// keyup as another keypress (seen in SR1..)
if (virtual_key != 0 && virtual_key != 0x10) {
if (evt.transition) {
keystroke_flags |= 0x0001;
}
}
if (keystroke_flags != 0) {
result = X_ERROR_SUCCESS;
}
}
if (!result) {
out_keystroke->virtual_key = virtual_key;
out_keystroke->unicode = unicode;
out_keystroke->flags = keystroke_flags;
out_keystroke->user_index = user_index_;
out_keystroke->hid_code = hid_code;
} else {
memset(out_keystroke, 0, sizeof(X_INPUT_KEYSTROKE));
}
// X_ERROR_EMPTY if no new keys
// X_ERROR_DEVICE_NOT_CONNECTED if no device
// X_ERROR_SUCCESS if key
return result;
}
} // namespace winkey
} // namespace hid
} // namespace xe