diff --git a/src/include/libvirtualhid/types.hpp b/src/include/libvirtualhid/types.hpp index 7f3bc1c..6948fc7 100644 --- a/src/include/libvirtualhid/types.hpp +++ b/src/include/libvirtualhid/types.hpp @@ -714,12 +714,29 @@ namespace lvh { /** * @brief Keyboard key code accepted by the keyboard event model. * - * The initial Linux backend treats this as a Windows virtual-key code so - * streaming hosts can pass common client key codes without exposing platform - * backends. Backends translate this value to their native key representation. + * Values in the low byte (0x00-0xFF) are interpreted as Windows virtual-key + * codes so streaming hosts can pass common client key codes without exposing + * platform backends. Values at or above 0x0100 are semantic keycodes defined + * in @ref keyboard_key_codes that have no Windows virtual-key equivalent. + * Backends translate the value to their native key representation. */ using KeyboardKeyCode = std::uint16_t; + /** + * @brief Semantic keyboard keycodes that have no Windows virtual-key equivalent. + * + * Windows virtual-key codes occupy the low byte (0x00-0xFF), so these reserved + * values start at 0x0100. They request a platform's native editing action key; + * backends translate them to the appropriate native key (for example the Linux + * `KEY_COPY` evdev code or the X11 `XF86Copy` keysym). + */ + namespace keyboard_key_codes { + inline constexpr KeyboardKeyCode undo = 0x0100; ///< Undo editing action. + inline constexpr KeyboardKeyCode cut = 0x0101; ///< Cut editing action. + inline constexpr KeyboardKeyCode copy = 0x0102; ///< Copy editing action. + inline constexpr KeyboardKeyCode paste = 0x0103; ///< Paste editing action. + } // namespace keyboard_key_codes + /** * @brief Keyboard key transition. */ diff --git a/src/platform/linux/uhid_backend.cpp b/src/platform/linux/uhid_backend.cpp index 8f01d32..4697ea0 100644 --- a/src/platform/linux/uhid_backend.cpp +++ b/src/platform/linux/uhid_backend.cpp @@ -53,6 +53,7 @@ #if defined(LIBVIRTUALHID_HAVE_XTEST) #include #include + #include #include #include #endif @@ -994,7 +995,7 @@ namespace lvh::detail { } int key_code_to_linux(KeyboardKeyCode key_code) { - static constexpr std::array, 47> special_keys {{ + static constexpr std::array, 51> special_keys {{ {0x08, KEY_BACKSPACE}, {0x09, KEY_TAB}, {0x0D, KEY_ENTER}, @@ -1042,6 +1043,10 @@ namespace lvh::detail { {0xDD, KEY_RIGHTBRACE}, {0xDE, KEY_APOSTROPHE}, {0xE2, KEY_102ND}, + {keyboard_key_codes::undo, KEY_UNDO}, + {keyboard_key_codes::cut, KEY_CUT}, + {keyboard_key_codes::copy, KEY_COPY}, + {keyboard_key_codes::paste, KEY_PASTE}, }}; if (const auto linux_key = mapped_keyboard_code(key_code, special_keys); linux_key.has_value()) { @@ -2512,7 +2517,7 @@ namespace lvh::detail { #if defined(LIBVIRTUALHID_HAVE_XTEST) KeySym key_code_to_keysym(KeyboardKeyCode key_code) { - static constexpr std::array, 45> special_keysyms {{ + static constexpr std::array, 49> special_keysyms {{ {0x08, XK_BackSpace}, {0x09, XK_Tab}, {0x0D, XK_Return}, @@ -2558,6 +2563,10 @@ namespace lvh::detail { {0xDC, XK_backslash}, {0xDD, XK_bracketright}, {0xDE, XK_apostrophe}, + {keyboard_key_codes::undo, XK_Undo}, + {keyboard_key_codes::cut, XF86XK_Cut}, + {keyboard_key_codes::copy, XF86XK_Copy}, + {keyboard_key_codes::paste, XF86XK_Paste}, }}; if (const auto keysym = mapped_keyboard_code(key_code, special_keysyms); keysym.has_value()) { diff --git a/tests/unit/test_linux_backend.cpp b/tests/unit/test_linux_backend.cpp index e3761f3..33040e1 100644 --- a/tests/unit/test_linux_backend.cpp +++ b/tests/unit/test_linux_backend.cpp @@ -19,6 +19,7 @@ #include #if defined(LIBVIRTUALHID_HAVE_XTEST) #include + #include #endif // lib includes @@ -107,6 +108,10 @@ TEST_F(LinuxBackendTest, TranslatesKeyboardKeys) { EXPECT_EQ(lvh::detail::test::linux_key_code(0x70), KEY_F1); EXPECT_EQ(lvh::detail::test::linux_key_code(0x7B), KEY_F12); EXPECT_EQ(lvh::detail::test::linux_key_code(0x87), KEY_F24); + EXPECT_EQ(lvh::detail::test::linux_key_code(lvh::keyboard_key_codes::undo), KEY_UNDO); + EXPECT_EQ(lvh::detail::test::linux_key_code(lvh::keyboard_key_codes::cut), KEY_CUT); + EXPECT_EQ(lvh::detail::test::linux_key_code(lvh::keyboard_key_codes::copy), KEY_COPY); + EXPECT_EQ(lvh::detail::test::linux_key_code(lvh::keyboard_key_codes::paste), KEY_PASTE); EXPECT_EQ(lvh::detail::test::linux_key_code(0), -1); EXPECT_EQ(lvh::detail::test::linux_key_code(0x88), -1); } @@ -1382,6 +1387,10 @@ TEST_F(LinuxBackendTest, XTestFallbackCoversKeyboardAndMousePaths) { EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(0x6F), XK_KP_Divide); EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(0x70), XK_F1); EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(0x87), XK_F24); + EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(lvh::keyboard_key_codes::undo), XK_Undo); + EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(lvh::keyboard_key_codes::cut), XF86XK_Cut); + EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(lvh::keyboard_key_codes::copy), XF86XK_Copy); + EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(lvh::keyboard_key_codes::paste), XF86XK_Paste); EXPECT_EQ(lvh::detail::test::linux_xtest_keysym(0x88), 0UL); EXPECT_EQ(lvh::detail::test::linux_xtest_mouse_button(lvh::MouseButton::left), 1);