/** * FreeRDP: A Remote Desktop Protocol Implementation * FreeRDP SDL keyboard helper * * Copyright 2022 Armin Novak * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "sdl_input.hpp" #include "sdl_disp.hpp" #include "sdl_context.hpp" #include "sdl_utils.hpp" #include "sdl_prefs.hpp" #include "sdl_touch.hpp" #include #include #include #include #include #include #include typedef struct { Uint32 sdl; const char* sdl_name; UINT32 rdp; const char* rdp_name; } scancode_entry_t; #define STR(x) #x #define ENTRY(x, y) { x, STR(x), y, #y } static const scancode_entry_t map[] = { ENTRY(SDL_SCANCODE_UNKNOWN, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_A, RDP_SCANCODE_KEY_A), ENTRY(SDL_SCANCODE_B, RDP_SCANCODE_KEY_B), ENTRY(SDL_SCANCODE_C, RDP_SCANCODE_KEY_C), ENTRY(SDL_SCANCODE_D, RDP_SCANCODE_KEY_D), ENTRY(SDL_SCANCODE_E, RDP_SCANCODE_KEY_E), ENTRY(SDL_SCANCODE_F, RDP_SCANCODE_KEY_F), ENTRY(SDL_SCANCODE_G, RDP_SCANCODE_KEY_G), ENTRY(SDL_SCANCODE_H, RDP_SCANCODE_KEY_H), ENTRY(SDL_SCANCODE_I, RDP_SCANCODE_KEY_I), ENTRY(SDL_SCANCODE_J, RDP_SCANCODE_KEY_J), ENTRY(SDL_SCANCODE_K, RDP_SCANCODE_KEY_K), ENTRY(SDL_SCANCODE_L, RDP_SCANCODE_KEY_L), ENTRY(SDL_SCANCODE_M, RDP_SCANCODE_KEY_M), ENTRY(SDL_SCANCODE_N, RDP_SCANCODE_KEY_N), ENTRY(SDL_SCANCODE_O, RDP_SCANCODE_KEY_O), ENTRY(SDL_SCANCODE_P, RDP_SCANCODE_KEY_P), ENTRY(SDL_SCANCODE_Q, RDP_SCANCODE_KEY_Q), ENTRY(SDL_SCANCODE_R, RDP_SCANCODE_KEY_R), ENTRY(SDL_SCANCODE_S, RDP_SCANCODE_KEY_S), ENTRY(SDL_SCANCODE_T, RDP_SCANCODE_KEY_T), ENTRY(SDL_SCANCODE_U, RDP_SCANCODE_KEY_U), ENTRY(SDL_SCANCODE_V, RDP_SCANCODE_KEY_V), ENTRY(SDL_SCANCODE_W, RDP_SCANCODE_KEY_W), ENTRY(SDL_SCANCODE_X, RDP_SCANCODE_KEY_X), ENTRY(SDL_SCANCODE_Y, RDP_SCANCODE_KEY_Y), ENTRY(SDL_SCANCODE_Z, RDP_SCANCODE_KEY_Z), ENTRY(SDL_SCANCODE_1, RDP_SCANCODE_KEY_1), ENTRY(SDL_SCANCODE_2, RDP_SCANCODE_KEY_2), ENTRY(SDL_SCANCODE_3, RDP_SCANCODE_KEY_3), ENTRY(SDL_SCANCODE_4, RDP_SCANCODE_KEY_4), ENTRY(SDL_SCANCODE_5, RDP_SCANCODE_KEY_5), ENTRY(SDL_SCANCODE_6, RDP_SCANCODE_KEY_6), ENTRY(SDL_SCANCODE_7, RDP_SCANCODE_KEY_7), ENTRY(SDL_SCANCODE_8, RDP_SCANCODE_KEY_8), ENTRY(SDL_SCANCODE_9, RDP_SCANCODE_KEY_9), ENTRY(SDL_SCANCODE_0, RDP_SCANCODE_KEY_0), ENTRY(SDL_SCANCODE_RETURN, RDP_SCANCODE_RETURN), ENTRY(SDL_SCANCODE_ESCAPE, RDP_SCANCODE_ESCAPE), ENTRY(SDL_SCANCODE_BACKSPACE, RDP_SCANCODE_BACKSPACE), ENTRY(SDL_SCANCODE_TAB, RDP_SCANCODE_TAB), ENTRY(SDL_SCANCODE_SPACE, RDP_SCANCODE_SPACE), ENTRY(SDL_SCANCODE_MINUS, RDP_SCANCODE_OEM_MINUS), ENTRY(SDL_SCANCODE_CAPSLOCK, RDP_SCANCODE_CAPSLOCK), ENTRY(SDL_SCANCODE_F1, RDP_SCANCODE_F1), ENTRY(SDL_SCANCODE_F2, RDP_SCANCODE_F2), ENTRY(SDL_SCANCODE_F3, RDP_SCANCODE_F3), ENTRY(SDL_SCANCODE_F4, RDP_SCANCODE_F4), ENTRY(SDL_SCANCODE_F5, RDP_SCANCODE_F5), ENTRY(SDL_SCANCODE_F6, RDP_SCANCODE_F6), ENTRY(SDL_SCANCODE_F7, RDP_SCANCODE_F7), ENTRY(SDL_SCANCODE_F8, RDP_SCANCODE_F8), ENTRY(SDL_SCANCODE_F9, RDP_SCANCODE_F9), ENTRY(SDL_SCANCODE_F10, RDP_SCANCODE_F10), ENTRY(SDL_SCANCODE_F11, RDP_SCANCODE_F11), ENTRY(SDL_SCANCODE_F12, RDP_SCANCODE_F12), ENTRY(SDL_SCANCODE_F13, RDP_SCANCODE_F13), ENTRY(SDL_SCANCODE_F14, RDP_SCANCODE_F14), ENTRY(SDL_SCANCODE_F15, RDP_SCANCODE_F15), ENTRY(SDL_SCANCODE_F16, RDP_SCANCODE_F16), ENTRY(SDL_SCANCODE_F17, RDP_SCANCODE_F17), ENTRY(SDL_SCANCODE_F18, RDP_SCANCODE_F18), ENTRY(SDL_SCANCODE_F19, RDP_SCANCODE_F19), ENTRY(SDL_SCANCODE_F20, RDP_SCANCODE_F20), ENTRY(SDL_SCANCODE_F21, RDP_SCANCODE_F21), ENTRY(SDL_SCANCODE_F22, RDP_SCANCODE_F22), ENTRY(SDL_SCANCODE_F23, RDP_SCANCODE_F23), ENTRY(SDL_SCANCODE_F24, RDP_SCANCODE_F24), ENTRY(SDL_SCANCODE_NUMLOCKCLEAR, RDP_SCANCODE_NUMLOCK), ENTRY(SDL_SCANCODE_KP_DIVIDE, RDP_SCANCODE_DIVIDE), ENTRY(SDL_SCANCODE_KP_MULTIPLY, RDP_SCANCODE_MULTIPLY), ENTRY(SDL_SCANCODE_KP_MINUS, RDP_SCANCODE_SUBTRACT), ENTRY(SDL_SCANCODE_KP_PLUS, RDP_SCANCODE_ADD), ENTRY(SDL_SCANCODE_KP_ENTER, RDP_SCANCODE_RETURN_KP), ENTRY(SDL_SCANCODE_KP_1, RDP_SCANCODE_NUMPAD1), ENTRY(SDL_SCANCODE_KP_2, RDP_SCANCODE_NUMPAD2), ENTRY(SDL_SCANCODE_KP_3, RDP_SCANCODE_NUMPAD3), ENTRY(SDL_SCANCODE_KP_4, RDP_SCANCODE_NUMPAD4), ENTRY(SDL_SCANCODE_KP_5, RDP_SCANCODE_NUMPAD5), ENTRY(SDL_SCANCODE_KP_6, RDP_SCANCODE_NUMPAD6), ENTRY(SDL_SCANCODE_KP_7, RDP_SCANCODE_NUMPAD7), ENTRY(SDL_SCANCODE_KP_8, RDP_SCANCODE_NUMPAD8), ENTRY(SDL_SCANCODE_KP_9, RDP_SCANCODE_NUMPAD9), ENTRY(SDL_SCANCODE_KP_0, RDP_SCANCODE_NUMPAD0), ENTRY(SDL_SCANCODE_KP_PERIOD, RDP_SCANCODE_DECIMAL), ENTRY(SDL_SCANCODE_LCTRL, RDP_SCANCODE_LCONTROL), ENTRY(SDL_SCANCODE_LSHIFT, RDP_SCANCODE_LSHIFT), ENTRY(SDL_SCANCODE_LALT, RDP_SCANCODE_LMENU), ENTRY(SDL_SCANCODE_LGUI, RDP_SCANCODE_LWIN), ENTRY(SDL_SCANCODE_RCTRL, RDP_SCANCODE_RCONTROL), ENTRY(SDL_SCANCODE_RSHIFT, RDP_SCANCODE_RSHIFT), ENTRY(SDL_SCANCODE_RALT, RDP_SCANCODE_RMENU), ENTRY(SDL_SCANCODE_RGUI, RDP_SCANCODE_RWIN), ENTRY(SDL_SCANCODE_MODE, RDP_SCANCODE_APPS), ENTRY(SDL_SCANCODE_MUTE, RDP_SCANCODE_VOLUME_MUTE), ENTRY(SDL_SCANCODE_VOLUMEUP, RDP_SCANCODE_VOLUME_UP), ENTRY(SDL_SCANCODE_VOLUMEDOWN, RDP_SCANCODE_VOLUME_DOWN), ENTRY(SDL_SCANCODE_GRAVE, RDP_SCANCODE_OEM_3), ENTRY(SDL_SCANCODE_COMMA, RDP_SCANCODE_OEM_COMMA), ENTRY(SDL_SCANCODE_PERIOD, RDP_SCANCODE_OEM_PERIOD), ENTRY(SDL_SCANCODE_SLASH, RDP_SCANCODE_OEM_2), ENTRY(SDL_SCANCODE_BACKSLASH, RDP_SCANCODE_OEM_5), ENTRY(SDL_SCANCODE_SCROLLLOCK, RDP_SCANCODE_SCROLLLOCK), ENTRY(SDL_SCANCODE_INSERT, RDP_SCANCODE_INSERT), ENTRY(SDL_SCANCODE_PRINTSCREEN, RDP_SCANCODE_PRINTSCREEN), ENTRY(SDL_SCANCODE_HOME, RDP_SCANCODE_HOME), ENTRY(SDL_SCANCODE_DELETE, RDP_SCANCODE_DELETE), ENTRY(SDL_SCANCODE_RIGHT, RDP_SCANCODE_RIGHT), ENTRY(SDL_SCANCODE_LEFT, RDP_SCANCODE_LEFT), ENTRY(SDL_SCANCODE_DOWN, RDP_SCANCODE_DOWN), ENTRY(SDL_SCANCODE_UP, RDP_SCANCODE_UP), ENTRY(SDL_SCANCODE_SEMICOLON, RDP_SCANCODE_OEM_1), ENTRY(SDL_SCANCODE_PAUSE, RDP_SCANCODE_PAUSE), ENTRY(SDL_SCANCODE_PAGEUP, RDP_SCANCODE_PRIOR), ENTRY(SDL_SCANCODE_END, RDP_SCANCODE_END), ENTRY(SDL_SCANCODE_PAGEDOWN, RDP_SCANCODE_NEXT), ENTRY(SDL_SCANCODE_MEDIA_NEXT_TRACK, RDP_SCANCODE_MEDIA_NEXT_TRACK), ENTRY(SDL_SCANCODE_MEDIA_PREVIOUS_TRACK, RDP_SCANCODE_MEDIA_PREV_TRACK), ENTRY(SDL_SCANCODE_MEDIA_STOP, RDP_SCANCODE_MEDIA_STOP), ENTRY(SDL_SCANCODE_MEDIA_PLAY, RDP_SCANCODE_MEDIA_PLAY_PAUSE), ENTRY(SDL_SCANCODE_MUTE, RDP_SCANCODE_VOLUME_MUTE), ENTRY(SDL_SCANCODE_MEDIA_SELECT, RDP_SCANCODE_LAUNCH_MEDIA_SELECT), ENTRY(SDL_SCANCODE_SYSREQ, RDP_SCANCODE_SYSREQ), ENTRY(SDL_SCANCODE_LEFTBRACKET, RDP_SCANCODE_OEM_4), ENTRY(SDL_SCANCODE_RIGHTBRACKET, RDP_SCANCODE_OEM_6), ENTRY(SDL_SCANCODE_APOSTROPHE, RDP_SCANCODE_OEM_7), ENTRY(SDL_SCANCODE_NONUSBACKSLASH, RDP_SCANCODE_OEM_102), ENTRY(SDL_SCANCODE_SLEEP, RDP_SCANCODE_SLEEP), ENTRY(SDL_SCANCODE_EQUALS, RDP_SCANCODE_OEM_PLUS), ENTRY(SDL_SCANCODE_KP_COMMA, RDP_SCANCODE_DECIMAL), ENTRY(SDL_SCANCODE_FIND, RDP_SCANCODE_BROWSER_SEARCH), ENTRY(SDL_SCANCODE_RETURN2, RDP_SCANCODE_RETURN_KP), ENTRY(SDL_SCANCODE_AC_SEARCH, RDP_SCANCODE_BROWSER_SEARCH), ENTRY(SDL_SCANCODE_AC_HOME, RDP_SCANCODE_BROWSER_HOME), ENTRY(SDL_SCANCODE_AC_BACK, RDP_SCANCODE_BROWSER_BACK), ENTRY(SDL_SCANCODE_AC_FORWARD, RDP_SCANCODE_BROWSER_FORWARD), ENTRY(SDL_SCANCODE_AC_STOP, RDP_SCANCODE_BROWSER_STOP), ENTRY(SDL_SCANCODE_NONUSHASH, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_APPLICATION, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_POWER, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_EQUALS, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_EXECUTE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_HELP, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_MENU, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_SELECT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_STOP, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_AGAIN, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_UNDO, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_CUT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_COPY, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_PASTE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_EQUALSAS400, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL1, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL2, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL3, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL4, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL5, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL6, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL7, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL8, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_INTERNATIONAL9, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG1, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG2, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG3, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG4, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG5, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG6, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG7, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG8, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_LANG9, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_ALTERASE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_CANCEL, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_CLEAR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_PRIOR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_SEPARATOR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_OUT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_OPER, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_CLEARAGAIN, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_CRSEL, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_EXSEL, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_00, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_000, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_THOUSANDSSEPARATOR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_DECIMALSEPARATOR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_CURRENCYUNIT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_CURRENCYSUBUNIT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_LEFTPAREN, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_RIGHTPAREN, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_LEFTBRACE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_RIGHTBRACE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_TAB, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_BACKSPACE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_A, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_B, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_C, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_D, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_E, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_F, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_XOR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_POWER, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_PERCENT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_LESS, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_GREATER, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_AMPERSAND, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_DBLAMPERSAND, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_VERTICALBAR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_DBLVERTICALBAR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_COLON, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_HASH, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_SPACE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_AT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_EXCLAM, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_MEMSTORE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_MEMRECALL, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_MEMCLEAR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_MEMADD, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_MEMSUBTRACT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_MEMMULTIPLY, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_MEMDIVIDE, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_PLUSMINUS, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_CLEAR, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_CLEARENTRY, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_BINARY, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_OCTAL, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_KP_DECIMAL, RDP_SCANCODE_DECIMAL), ENTRY(SDL_SCANCODE_KP_HEXADECIMAL, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_AC_REFRESH, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_AC_BOOKMARKS, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_MEDIA_EJECT, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_MEDIA_REWIND, RDP_SCANCODE_UNKNOWN), ENTRY(SDL_SCANCODE_MEDIA_FAST_FORWARD, RDP_SCANCODE_UNKNOWN) }; [[nodiscard]] static UINT32 sdl_get_kbd_flags() { UINT32 flags = 0; SDL_Keymod mod = SDL_GetModState(); if ((mod & SDL_KMOD_NUM) != 0) flags |= KBD_SYNC_NUM_LOCK; if ((mod & SDL_KMOD_CAPS) != 0) flags |= KBD_SYNC_CAPS_LOCK; if ((mod & SDL_KMOD_SCROLL) != 0) flags |= KBD_SYNC_SCROLL_LOCK; // TODO: KBD_SYNC_KANA_LOCK return flags; } bool sdlInput::keyboard_sync_state() { const UINT32 syncFlags = sdl_get_kbd_flags(); return freerdp_input_send_synchronize_event(_sdl->context()->input, syncFlags); } bool sdlInput::keyboard_focus_in() { auto input = _sdl->context()->input; WINPR_ASSERT(input); auto syncFlags = sdl_get_kbd_flags(); if (!freerdp_input_send_focus_in_event(input, WINPR_ASSERTING_INT_CAST(uint16_t, syncFlags))) return false; /* finish with a mouse pointer position like mstsc.exe if required */ // TODO: fullscreen/remote app float fx = 0.0f; float fy = 0.0f; SDL_GetMouseState(&fx, &fy); auto w = SDL_GetMouseFocus(); const auto& pos = _sdl->screenToPixel(SDL_GetWindowID(w), SDL_FPoint{ fx, fy }); return freerdp_client_send_button_event(_sdl->common(), FALSE, PTR_FLAGS_MOVE, static_cast(pos.x), static_cast(pos.y)); } /* This function is called to update the keyboard indicator LED */ BOOL sdlInput::keyboard_set_indicators(rdpContext* context, UINT16 led_flags) { WINPR_UNUSED(context); SDL_Keymod state = SDL_KMOD_NONE; if ((led_flags & KBD_SYNC_NUM_LOCK) != 0) state |= SDL_KMOD_NUM; if ((led_flags & KBD_SYNC_CAPS_LOCK) != 0) state |= SDL_KMOD_CAPS; if ((led_flags & KBD_SYNC_SCROLL_LOCK) != 0) state |= SDL_KMOD_SCROLL; if ((led_flags & KBD_SYNC_KANA_LOCK) != 0) state |= SDL_KMOD_LEVEL5; SDL_SetModState(state); return TRUE; } /* This function is called to set the IME state */ BOOL sdlInput::keyboard_set_ime_status(rdpContext* context, UINT16 imeId, UINT32 imeState, UINT32 imeConvMode) { auto sdl = reinterpret_cast(context); if (!context) return FALSE; WLog_Print(sdl->getWLog(), WLOG_WARN, "KeyboardSetImeStatus(unitId=%04" PRIx16 ", imeState=%08" PRIx32 ", imeConvMode=%08" PRIx32 ") ignored", imeId, imeState, imeConvMode); return TRUE; } [[nodiscard]] static const std::map& getSdlMap() { static std::map s_map = { { "KMOD_LSHIFT", SDL_KMOD_LSHIFT }, { "KMOD_RSHIFT", SDL_KMOD_RSHIFT }, { "KMOD_LCTRL", SDL_KMOD_LCTRL }, { "KMOD_RCTRL", SDL_KMOD_RCTRL }, { "KMOD_LALT", SDL_KMOD_LALT }, { "KMOD_RALT", SDL_KMOD_RALT }, { "KMOD_LGUI", SDL_KMOD_LGUI }, { "KMOD_RGUI", SDL_KMOD_RGUI }, { "KMOD_NUM", SDL_KMOD_NUM }, { "KMOD_CAPS", SDL_KMOD_CAPS }, { "KMOD_MODE", SDL_KMOD_MODE }, { "KMOD_SCROLL", SDL_KMOD_SCROLL }, { "KMOD_CTRL", SDL_KMOD_CTRL }, { "KMOD_SHIFT", SDL_KMOD_SHIFT }, { "KMOD_ALT", SDL_KMOD_ALT }, { "KMOD_GUI", SDL_KMOD_GUI }, { "KMOD_NONE", SDL_KMOD_NONE }, { "SDL_KMOD_LSHIFT", SDL_KMOD_LSHIFT }, { "SDL_KMOD_RSHIFT", SDL_KMOD_RSHIFT }, { "SDL_KMOD_LCTRL", SDL_KMOD_LCTRL }, { "SDL_KMOD_RCTRL", SDL_KMOD_RCTRL }, { "SDL_KMOD_LALT", SDL_KMOD_LALT }, { "SDL_KMOD_RALT", SDL_KMOD_RALT }, { "SDL_KMOD_LGUI", SDL_KMOD_LGUI }, { "SDL_KMOD_RGUI", SDL_KMOD_RGUI }, { "SDL_KMOD_NUM", SDL_KMOD_NUM }, { "SDL_KMOD_CAPS", SDL_KMOD_CAPS }, { "SDL_KMOD_MODE", SDL_KMOD_MODE }, { "SDL_KMOD_SCROLL", SDL_KMOD_SCROLL }, { "SDL_KMOD_CTRL", SDL_KMOD_CTRL }, { "SDL_KMOD_SHIFT", SDL_KMOD_SHIFT }, { "SDL_KMOD_ALT", SDL_KMOD_ALT }, { "SDL_KMOD_GUI", SDL_KMOD_GUI }, { "SDL_KMOD_NONE", SDL_KMOD_NONE } }; return s_map; } [[nodiscard]] static std::string modbyvalue(uint32_t val) { for (const auto& v : getSdlMap()) { if (v.second == val) { return v.first; } } return "KMOD_UNKNONW"; } [[nodiscard]] static std::string masktostr(uint32_t mask) { if (mask == 0) return ""; std::string str = "<"; for (uint32_t x = 0; x < 32; x++) { uint32_t cmask = 1 << x; if ((mask & cmask) != 0) { auto s = modbyvalue(cmask); if (str.size() > 1) { str += ">+<"; } str += s; } } str += ">"; return str; } bool sdlInput::prefToEnabled() { bool enabled = true; const auto& m = getSdlMap(); for (const auto& val : SdlPref::instance()->get_array("SDL_KeyModMask", { "KMOD_RSHIFT" })) { auto it = m.find(val); if (it != m.end()) { if (it->second == SDL_KMOD_NONE) enabled = false; } else { WLog_Print(_sdl->getWLog(), WLOG_WARN, "Invalid config::SDL_KeyModMask entry value '%s', disabling hotkeys", val.c_str()); enabled = false; } } return enabled; } uint32_t sdlInput::prefToMask() { const auto& m = getSdlMap(); uint32_t mod = SDL_KMOD_NONE; for (const auto& val : SdlPref::instance()->get_array("SDL_KeyModMask", { "KMOD_RSHIFT" })) { auto it = m.find(val); if (it != m.end()) mod |= it->second; } return mod; } [[nodiscard]] static const char* sdl_scancode_name(Uint32 scancode) { for (const auto& cur : map) { if (cur.sdl == scancode) return cur.sdl_name; } return "SDL_SCANCODE_UNKNOWN"; } [[nodiscard]] static Uint32 sdl_scancode_val(const char* scancodeName) { for (const auto& cur : map) { if (strcmp(cur.sdl_name, scancodeName) == 0) return cur.sdl; } return SDL_SCANCODE_UNKNOWN; } #if defined(WITH_DEBUG_SDL_KBD_EVENTS) [[nodiscard]] static const char* sdl_rdp_scancode_name(UINT32 scancode) { for (const auto& cur : map) { if (cur.rdp == scancode) return cur.rdp_name; } return "RDP_SCANCODE_UNKNOWN"; } [[nodiscard]] static UINT32 sdl_rdp_scancode_val(const char* scancodeName) { for (const auto& cur : map) { if (strcmp(cur.rdp_name, scancodeName) == 0) return cur.rdp; } return RDP_SCANCODE_UNKNOWN; } #endif UINT32 sdlInput::scancode_to_rdp(Uint32 scancode) { UINT32 rdp = RDP_SCANCODE_UNKNOWN; for (const auto& cur : map) { if (cur.sdl == scancode) { rdp = cur.rdp; break; } } #if defined(WITH_DEBUG_SDL_KBD_EVENTS) auto code = static_cast(scancode); WLog_Print(_sdl->getWLog(), WLOG_DEBUG, "got %s [%s] -> [%s]", SDL_GetScancodeName(code), sdl_scancode_name(scancode), sdl_rdp_scancode_name(rdp)); #endif return rdp; } uint32_t sdlInput::prefKeyValue(const std::string& key, uint32_t fallback) { auto item = SdlPref::instance()->get_string(key); if (item.empty()) return fallback; auto val = sdl_scancode_val(item.c_str()); if (val == SDL_SCANCODE_UNKNOWN) return fallback; return val; } std::list sdlInput::tokenize(const std::string& data, const std::string& delimiter) { size_t lastpos = 0; size_t pos = 0; std::list list; while ((pos = data.find(delimiter, lastpos)) != std::string::npos) { auto token = data.substr(lastpos, pos); lastpos = pos + 1; list.push_back(std::move(token)); } auto token = data.substr(lastpos); list.push_back(std::move(token)); return list; } bool sdlInput::extract(const std::string& token, uint32_t& key, uint32_t& value) { return freerdp_extract_key_value(token.c_str(), &key, &value); } bool sdlInput::handleEvent(const SDL_KeyboardEvent& ev) { const UINT32 rdp_scancode = scancode_to_rdp(ev.scancode); const SDL_Keymod mods = SDL_GetModState(); if (_hotkeysEnabled && (mods & _hotkeyModmask) == _hotkeyModmask) { if (ev.type == SDL_EVENT_KEY_DOWN) { if (ev.scancode == _hotkeyFullscreen) { WLog_Print(_sdl->getWLog(), WLOG_INFO, "%s+<%s> pressed, toggling fullscreen state", masktostr(_hotkeyModmask).c_str(), sdl_scancode_name(_hotkeyFullscreen)); if (!keyboard_sync_state()) return false; return _sdl->toggleFullscreen(); } if (ev.scancode == _hotkeyResizable) { WLog_Print(_sdl->getWLog(), WLOG_INFO, "%s+<%s> pressed, toggling resizeable state", masktostr(_hotkeyModmask).c_str(), sdl_scancode_name(_hotkeyResizable)); if (!keyboard_sync_state()) return false; return _sdl->toggleResizeable(); } if (ev.scancode == _hotkeyGrab) { WLog_Print(_sdl->getWLog(), WLOG_INFO, "%s+<%s> pressed, toggling grab state", masktostr(_hotkeyModmask).c_str(), sdl_scancode_name(_hotkeyGrab)); if (!keyboard_sync_state()) return false; return keyboard_grab(ev.windowID, !_sdl->grabKeyboard()); } if (ev.scancode == _hotkeyDisconnect) { WLog_Print(_sdl->getWLog(), WLOG_INFO, "%s+<%s> pressed, disconnecting RDP session", masktostr(_hotkeyModmask).c_str(), sdl_scancode_name(_hotkeyDisconnect)); if (!keyboard_sync_state()) return false; freerdp_abort_connect_context(_sdl->context()); return true; } if (ev.scancode == _hotkeyMinimize) { WLog_Print(_sdl->getWLog(), WLOG_INFO, "%s+<%s> pressed, minimizing client", masktostr(_hotkeyModmask).c_str(), sdl_scancode_name(_hotkeyMinimize)); if (!keyboard_sync_state()) return false; return _sdl->setMinimized(); } } } #if defined(WITH_DEBUG_KBD) { const BOOL ex = RDP_SCANCODE_EXTENDED(rdp_scancode); const DWORD sc = RDP_SCANCODE_CODE(rdp_scancode); WLog_Print(_sdl->getWLog(), WLOG_DEBUG, "SDL keycode: %02" PRIX32 " -> rdp code: [%04" PRIx16 "] %02" PRIX8 "%s", ev.scancode, rdp_scancode, sc, ex ? " extended" : ""); } #endif auto scancode = freerdp_keyboard_remap_key(_remapTable, rdp_scancode); #if defined(WITH_DEBUG_KBD) { const BOOL ex = RDP_SCANCODE_EXTENDED(scancode); const DWORD sc = RDP_SCANCODE_CODE(scancode); WLog_Print(_sdl->getWLog(), WLOG_DEBUG, "SDL keycode: %02" PRIX32 " -> remapped rdp code: [%04" PRIx16 "] %02" PRIX8 "%s", ev.scancode, scancode, sc, ex ? " extended" : ""); } #endif return freerdp_input_send_keyboard_event_ex(_sdl->context()->input, ev.type == SDL_EVENT_KEY_DOWN, ev.repeat, scancode); } bool sdlInput::keyboard_grab(Uint32 windowID, bool enable) { const auto window = _sdl->getWindowForId(windowID); if (!window) return false; auto settings = _sdl->context()->settings; auto kbd_enabled = freerdp_settings_get_bool(settings, FreeRDP_GrabKeyboard); auto status = enable && kbd_enabled; if (!_sdl->setGrabKeyboard(status)) WLog_Print(_sdl->getWLog(), WLOG_WARN, "Failed to ungrab keyboard"); return window->grabKeyboard(status); } bool sdlInput::mouse_focus(Uint32 windowID) { if (_lastWindowID != windowID) { _lastWindowID = windowID; auto window = _sdl->getWindowForId(windowID); if (!window) return false; window->raise(); } return true; } bool sdlInput::mouse_grab(Uint32 windowID, bool enable) { auto window = _sdl->getWindowForId(windowID); if (!window) return false; if (!_sdl->setGrabMouse(enable)) WLog_Print(_sdl->getWLog(), WLOG_WARN, "Failed to ungrab mouse"); return window->grabMouse(enable); } sdlInput::sdlInput(SdlContext* sdl) : _sdl(sdl), _lastWindowID(UINT32_MAX), _hotkeysEnabled(prefToEnabled()), _hotkeyModmask(prefToMask()) { _hotkeyFullscreen = prefKeyValue("SDL_Fullscreen", SDL_SCANCODE_RETURN); _hotkeyResizable = prefKeyValue("SDL_Resizeable", SDL_SCANCODE_R); _hotkeyGrab = prefKeyValue("SDL_Grab", SDL_SCANCODE_G); _hotkeyDisconnect = prefKeyValue("SDL_Disconnect", SDL_SCANCODE_D); _hotkeyMinimize = prefKeyValue("SDL_Minimize", SDL_SCANCODE_M); } sdlInput::~sdlInput() { freerdp_keyboard_remap_free(_remapTable); } bool sdlInput::initialize() { auto settings = _sdl->context()->settings; WINPR_ASSERT(settings); WINPR_ASSERT(!_remapTable); { auto list = freerdp_settings_get_string(settings, FreeRDP_KeyboardRemappingList); _remapTable = freerdp_keyboard_remap_string_to_list(list); if (!_remapTable) return false; } if (freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout) == 0) { DWORD KeyboardLayout = 0; freerdp_detect_keyboard_layout_from_system_locale(&KeyboardLayout); if (KeyboardLayout == 0) KeyboardLayout = ENGLISH_UNITED_STATES; if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, KeyboardLayout)) return false; } return true; }