Add support for keyboard input that is not normalized to US English layout

This is used by the soft keyboards on Android and iOS
This commit is contained in:
Cameron Gutman 2023-04-09 11:55:03 -05:00
parent ae7ae8a870
commit 4e04604696
5 changed files with 37 additions and 20 deletions

View File

@ -59,8 +59,22 @@ namespace input {
gamepad_mask[id] = false; gamepad_mask[id] = false;
} }
typedef uint32_t key_press_id_t;
key_press_id_t
make_kpid(uint16_t vk, uint8_t flags) {
return (key_press_id_t) vk << 8 | flags;
}
uint16_t
vk_from_kpid(key_press_id_t kpid) {
return kpid >> 8;
}
uint8_t
flags_from_kpid(key_press_id_t kpid) {
return kpid & 0xFF;
}
static task_pool_util::TaskPool::task_id_t key_press_repeat_id {}; static task_pool_util::TaskPool::task_id_t key_press_repeat_id {};
static std::unordered_map<short, bool> key_press {}; static std::unordered_map<key_press_id_t, bool> key_press {};
static std::array<std::uint8_t, 5> mouse_press {}; static std::array<std::uint8_t, 5> mouse_press {};
static platf::input_t platf_input; static platf::input_t platf_input;
@ -449,16 +463,16 @@ namespace input {
} }
void void
repeat_key(short key_code) { repeat_key(uint16_t key_code, uint8_t flags) {
// If key no longer pressed, stop repeating // If key no longer pressed, stop repeating
if (!key_press[key_code]) { if (!key_press[make_kpid(key_code, flags)]) {
key_press_repeat_id = nullptr; key_press_repeat_id = nullptr;
return; return;
} }
platf::keyboard(platf_input, map_keycode(key_code), false); platf::keyboard(platf_input, map_keycode(key_code), false, flags);
key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_period, key_code).task_id; key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_period, key_code, flags).task_id;
} }
void void
@ -470,7 +484,7 @@ namespace input {
auto release = util::endian::little(packet->header.magic) == KEY_UP_EVENT_MAGIC; auto release = util::endian::little(packet->header.magic) == KEY_UP_EVENT_MAGIC;
auto keyCode = packet->keyCode & 0x00FF; auto keyCode = packet->keyCode & 0x00FF;
auto &pressed = key_press[keyCode]; auto &pressed = key_press[make_kpid(keyCode, packet->flags)];
if (!pressed) { if (!pressed) {
if (!release) { if (!release) {
// A new key has been pressed down, we need to check for key combo's // A new key has been pressed down, we need to check for key combo's
@ -484,7 +498,7 @@ namespace input {
} }
if (config::input.key_repeat_delay.count() > 0) { if (config::input.key_repeat_delay.count() > 0) {
key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_delay, keyCode).task_id; key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_delay, keyCode, packet->flags).task_id;
} }
} }
else { else {
@ -500,7 +514,7 @@ namespace input {
pressed = !release; pressed = !release;
update_shortcutFlags(&input->shortcutFlags, map_keycode(keyCode), release); update_shortcutFlags(&input->shortcutFlags, map_keycode(keyCode), release);
platf::keyboard(platf_input, map_keycode(keyCode), release); platf::keyboard(platf_input, map_keycode(keyCode), release, packet->flags);
} }
void void
@ -731,7 +745,7 @@ namespace input {
} }
for (auto &kp : key_press) { for (auto &kp : key_press) {
platf::keyboard(platf_input, kp.first & 0x00FF, true); platf::keyboard(platf_input, vk_from_kpid(kp.first) & 0x00FF, true, flags_from_kpid(kp.first));
key_press[kp.first] = false; key_press[kp.first] = false;
} }
}); });

View File

@ -436,7 +436,7 @@ namespace platf {
void void
hscroll(input_t &input, int distance); hscroll(input_t &input, int distance);
void void
keyboard(input_t &input, uint16_t modcode, bool release); keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags);
void void
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state); gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state);
void void

View File

@ -1337,14 +1337,15 @@ namespace platf {
* @param input The input_t instance to use. * @param input The input_t instance to use.
* @param modcode The moonlight key code. * @param modcode The moonlight key code.
* @param release Whether the event was a press (false) or a release (true) * @param release Whether the event was a press (false) or a release (true)
* @param flags SS_KBE_FLAG_* values
* *
* EXAMPLES: * EXAMPLES:
* ```cpp * ```cpp
* x_keyboard(input, 0x5A, false); // Press Z * x_keyboard(input, 0x5A, false, 0); // Press Z
* ``` * ```
*/ */
static void static void
x_keyboard(input_t &input, uint16_t modcode, bool release) { x_keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
#ifdef SUNSHINE_BUILD_X11 #ifdef SUNSHINE_BUILD_X11
auto keycode = keysym(modcode); auto keycode = keysym(modcode);
if (keycode.keysym == UNKNOWN) { if (keycode.keysym == UNKNOWN) {
@ -1371,17 +1372,18 @@ namespace platf {
* @param input The input_t instance to use. * @param input The input_t instance to use.
* @param modcode The moonlight key code. * @param modcode The moonlight key code.
* @param release Whether the event was a press (false) or a release (true) * @param release Whether the event was a press (false) or a release (true)
* @param flags SS_KBE_FLAG_* values
* *
* EXAMPLES: * EXAMPLES:
* ```cpp * ```cpp
* keyboard(input, 0x5A, false); // Press Z * keyboard(input, 0x5A, false, 0); // Press Z
* ``` * ```
*/ */
void void
keyboard(input_t &input, uint16_t modcode, bool release) { keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
auto keyboard = ((input_raw_t *) input.get())->keyboard_input.get(); auto keyboard = ((input_raw_t *) input.get())->keyboard_input.get();
if (!keyboard) { if (!keyboard) {
x_keyboard(input, modcode, release); x_keyboard(input, modcode, release, flags);
return; return;
} }

View File

@ -230,7 +230,7 @@ const KeyCodeMap kKeyCodesMap[] = {
} }
void void
keyboard(input_t &input, uint16_t modcode, bool release) { keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
auto key = keysym(modcode); auto key = keysym(modcode);
BOOST_LOG(debug) << "got keycode: 0x"sv << std::hex << modcode << ", translated to: 0x" << std::hex << key << ", release:" << release; BOOST_LOG(debug) << "got keycode: 0x"sv << std::hex << modcode << ", translated to: 0x" << std::hex << key << ", release:" << release;

View File

@ -338,15 +338,16 @@ namespace platf {
} }
void void
keyboard(input_t &input, uint16_t modcode, bool release) { keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
auto raw = (input_raw_t *) input.get(); auto raw = (input_raw_t *) input.get();
INPUT i {}; INPUT i {};
i.type = INPUT_KEYBOARD; i.type = INPUT_KEYBOARD;
auto &ki = i.ki; auto &ki = i.ki;
// For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/ // If the client did not normalize this VK code to a US English layout, we can't accurately convert it to a scancode.
if (modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE && raw->keyboard_layout != NULL) { if (!(flags & SS_KBE_FLAG_NON_NORMALIZED) && modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE && raw->keyboard_layout != NULL) {
// For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/
ki.wScan = MapVirtualKeyEx(modcode, MAPVK_VK_TO_VSC, raw->keyboard_layout); ki.wScan = MapVirtualKeyEx(modcode, MAPVK_VK_TO_VSC, raw->keyboard_layout);
} }
@ -355,7 +356,7 @@ namespace platf {
ki.dwFlags = KEYEVENTF_SCANCODE; ki.dwFlags = KEYEVENTF_SCANCODE;
} }
else { else {
// If there is no scancode mapping, send it as a regular VK event. // If there is no scancode mapping or it's non-normalized, send it as a regular VK event.
ki.wVk = modcode; ki.wVk = modcode;
} }