diff --git a/src/moonlight-protocol/moonlight/control.hpp b/src/moonlight-protocol/moonlight/control.hpp index bb05bad1..71212d7d 100644 --- a/src/moonlight-protocol/moonlight/control.hpp +++ b/src/moonlight-protocol/moonlight/control.hpp @@ -98,6 +98,21 @@ enum CONTROLLER_BTN : unsigned int { Y = 0x8000 }; +enum KEYBOARD_MODIFIERS : char { + NONE = 0x00, + SHIFT = 0x01, + CTRL = 0x02, + ALT = 0x04, + META = 0x08 +}; + +enum MOONLIGHT_MODIFIERS : short { + M_SHIFT = 0x10, + M_CTRL = 0x11, + M_ALT = 0xA4, + M_META = 0x5B +}; + // make sure these structs are allocated in 1-byte blocks so the data aligns // right #pragma pack(push, 1) @@ -141,7 +156,7 @@ struct MOUSE_HSCROLL_PACKET : INPUT_PKT { struct KEYBOARD_PACKET : INPUT_PKT { unsigned char flags; short key_code; - unsigned char modifiers; + char modifiers; short zero1; }; diff --git a/src/moonlight-server/control/input_handler.cpp b/src/moonlight-server/control/input_handler.cpp index 072e1f8b..34650f7d 100644 --- a/src/moonlight-server/control/input_handler.cpp +++ b/src/moonlight-server/control/input_handler.cpp @@ -298,7 +298,29 @@ void keyboard_key(const KEYBOARD_PACKET &pkt, state::StreamSession &session) { short moonlight_key = (short)boost::endian::little_to_native(pkt.key_code) & (short)0x7fff; if (session.keyboard->has_value()) { if (pkt.type == KEY_PRESS) { + // Press the virtual modifiers + if (pkt.modifiers & KEYBOARD_MODIFIERS::SHIFT && moonlight_key != M_SHIFT) + std::visit([](auto &keyboard) { keyboard.press(M_SHIFT); }, session.keyboard->value()); + if (pkt.modifiers & KEYBOARD_MODIFIERS::CTRL && moonlight_key != M_CTRL) + std::visit([](auto &keyboard) { keyboard.press(M_CTRL); }, session.keyboard->value()); + if (pkt.modifiers & KEYBOARD_MODIFIERS::ALT && moonlight_key != M_ALT) + std::visit([](auto &keyboard) { keyboard.press(M_ALT); }, session.keyboard->value()); + if (pkt.modifiers & KEYBOARD_MODIFIERS::META && moonlight_key != M_META) + std::visit([](auto &keyboard) { keyboard.press(M_META); }, session.keyboard->value()); + + // Press the actual key std::visit([moonlight_key](auto &keyboard) { keyboard.press(moonlight_key); }, session.keyboard->value()); + + // Release the virtual modifiers + if (pkt.modifiers & KEYBOARD_MODIFIERS::SHIFT && moonlight_key != M_SHIFT) + std::visit([](auto &keyboard) { keyboard.release(M_SHIFT); }, session.keyboard->value()); + if (pkt.modifiers & KEYBOARD_MODIFIERS::CTRL && moonlight_key != M_CTRL) + std::visit([](auto &keyboard) { keyboard.release(M_CTRL); }, session.keyboard->value()); + if (pkt.modifiers & KEYBOARD_MODIFIERS::ALT && moonlight_key != M_ALT) + std::visit([](auto &keyboard) { keyboard.release(M_ALT); }, session.keyboard->value()); + if (pkt.modifiers & KEYBOARD_MODIFIERS::META && moonlight_key != M_META) + std::visit([](auto &keyboard) { keyboard.release(M_META); }, session.keyboard->value()); + } else { std::visit([moonlight_key](auto &keyboard) { keyboard.release(moonlight_key); }, session.keyboard->value()); } diff --git a/tests/platforms/linux/wayland-display.cpp b/tests/platforms/linux/wayland-display.cpp index e6e4af55..fc04c2b5 100644 --- a/tests/platforms/linux/wayland-display.cpp +++ b/tests/platforms/linux/wayland-display.cpp @@ -124,6 +124,32 @@ TEST_CASE("Wayland virtual inputs", "[WAYLAND]") { REQUIRE(!k_ev->pressed); } + { // Testing modifiers + auto press_SHIFT_A = + pkts::KEYBOARD_PACKET{.key_code = boost::endian::native_to_little((short)0x41), .modifiers = pkts::SHIFT}; + press_SHIFT_A.type = pkts::KEY_PRESS; + control::handle_input(session, {}, &press_SHIFT_A); + wl_display_roundtrip(wd.get()); + + auto k_ev = kb_events_q->pop(); + // Press SHIFT + REQUIRE(k_ev.has_value()); + REQUIRE(k_ev->keycode == 42); + REQUIRE(k_ev->pressed); + + // Press A + k_ev = kb_events_q->pop(); + REQUIRE(k_ev.has_value()); + REQUIRE(k_ev->keycode == 30); + REQUIRE(k_ev->pressed); + + // Release SHIFT + k_ev = kb_events_q->pop(); + REQUIRE(k_ev.has_value()); + REQUIRE(k_ev->keycode == 42); + REQUIRE(!k_ev->pressed); + } + // Mouse tests: scroll { short scroll_amt = 10;