diff --git a/src/include/libvirtualhid/types.hpp b/src/include/libvirtualhid/types.hpp index a471d51..e120519 100644 --- a/src/include/libvirtualhid/types.hpp +++ b/src/include/libvirtualhid/types.hpp @@ -789,12 +789,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. The Linux uinput and XTest backends map these + * editing actions to native keys (for example `KEY_COPY` and `XF86Copy`). + * Windows and macOS do not currently implement these semantic keycodes. + */ + 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 bf58868..d15fc83 100644 --- a/src/platform/linux/uhid_backend.cpp +++ b/src/platform/linux/uhid_backend.cpp @@ -52,6 +52,7 @@ #if defined(LIBVIRTUALHID_HAVE_XTEST) #include #include + #include #include #include #endif @@ -649,7 +650,7 @@ namespace lvh::detail { } int key_code_to_linux(KeyboardKeyCode key_code) { - static constexpr std::array, 48> special_keys {{ + static constexpr std::array, 52> special_keys {{ {0x08, KEY_BACKSPACE}, {0x09, KEY_TAB}, {0x0D, KEY_ENTER}, @@ -698,6 +699,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()) { @@ -2335,7 +2340,7 @@ namespace lvh::detail { #if defined(LIBVIRTUALHID_HAVE_XTEST) KeySym key_code_to_keysym(KeyboardKeyCode key_code) { - static constexpr std::array, 46> special_keysyms {{ + static constexpr std::array, 50> special_keysyms {{ {0x08, XK_BackSpace}, {0x09, XK_Tab}, {0x0D, XK_Return}, @@ -2382,6 +2387,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 93f0d86..ae06648 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 @@ -122,6 +123,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); } @@ -1592,6 +1597,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);