2222#include < array>
2323#include < chrono>
2424#include < cstdint>
25- #include < iomanip >
25+ #include < format >
2626#include < memory>
2727#include < set>
28- #include < sstream>
2928#include < string>
3029#include < utility>
3130#include < vector>
@@ -192,7 +191,7 @@ namespace {
192191 * @brief One modifier the client can report, with the keys that hold it down.
193192 */
194193 struct modifier_t {
195- std:: uint8_t bit; // /< Modifier bit carried in the keyboard packet.
194+ unsigned bit; // /< Modifier bit carried in the keyboard packet.
196195 std::uint16_t left; // /< Left-hand virtual-key code.
197196 std::uint16_t right; // /< Right-hand virtual-key code.
198197 std::uint16_t generic; // /< Side-agnostic virtual-key code.
@@ -224,27 +223,59 @@ namespace {
224223 * @return Virtual-key code the client sends.
225224 */
226225 std::uint16_t key_for_side (const modifier_t &modifier, side_e side) {
226+ using enum side_e;
227227 switch (side) {
228- case side_e:: left:
228+ case left:
229229 return modifier.left ;
230- case side_e:: right:
230+ case right:
231231 return modifier.right ;
232- case side_e:: generic:
232+ case generic:
233233 default :
234234 return modifier.generic ;
235235 }
236236 }
237237
238+ /* *
239+ * @brief One modifier combination the client holds down, in press order.
240+ */
241+ struct held_modifiers_t {
242+ std::vector<std::pair<std::uint16_t , unsigned >> keys; // /< Key code and the mask reported with it.
243+ unsigned claimed = 0 ; // /< Full mask once every key is down.
244+ std::string label; // /< Combination name for assertion traces.
245+ };
246+
247+ /* *
248+ * @brief Select the modifier keys named by a combination bitmask.
249+ *
250+ * Each key carries the mask accumulated so far, which matches a client that presses the
251+ * modifiers one at a time.
252+ *
253+ * @param combination Bit per entry of the modifier table.
254+ * @param side Which physical side the client reports.
255+ * @return Keys to hold, the resulting mask, and a trace label.
256+ */
257+ held_modifiers_t select_modifiers (unsigned combination, side_e side) {
258+ held_modifiers_t held;
259+ for (std::size_t index = 0 ; index < modifiers.size (); ++index) {
260+ if (!(combination & (1u << index))) {
261+ continue ;
262+ }
263+ held.claimed |= modifiers[index].bit ;
264+ held.keys .emplace_back (key_for_side (modifiers[index], side), held.claimed );
265+ held.label += (held.label .empty () ? " " : " +" );
266+ held.label += modifiers[index].name ;
267+ }
268+ return held;
269+ }
270+
238271 /* *
239272 * @brief Render a value as a hexadecimal literal for assertion traces.
240273 *
241274 * @param value Value to render.
242275 * @return Hexadecimal literal.
243276 */
244277 std::string hex (unsigned value) {
245- std::ostringstream out;
246- out << " 0x" << std::uppercase << std::hex << value;
247- return out.str ();
278+ return std::format (" 0x{:X}" , value);
248279 }
249280
250281 /* *
@@ -258,11 +289,7 @@ namespace {
258289 * @return Token describing the event.
259290 */
260291 std::string describe (const input::testing::keyboard_event_t &event) {
261- std::ostringstream token;
262- token << (event.release ? ' -' : ' +' )
263- << " 0x" << std::uppercase << std::hex << std::setw (2 ) << std::setfill (' 0' )
264- << event.key_code ;
265- return token.str ();
292+ return std::format (" {}0x{:02X}" , event.release ? ' -' : ' +' , event.key_code );
266293 }
267294
268295 /* *
@@ -315,7 +342,7 @@ namespace {
315342 });
316343
317344 static int session = 0 ;
318- stream_ = input::alloc (std::make_shared<safe::mail_raw_t >(), " keyboard-test-" + std::to_string ( ++session));
345+ stream_ = input::alloc (std::make_shared<safe::mail_raw_t >(), std::format ( " keyboard-test-{} " , ++session));
319346 ASSERT_NE (stream_, nullptr );
320347 }
321348
@@ -338,8 +365,14 @@ namespace {
338365 * @param client_modifiers Modifier bitmask the client reports as held.
339366 * @param flags Bit flags carried by the packet.
340367 */
341- void press (std::uint16_t key_code, std::uint8_t client_modifiers = 0 , std::uint8_t flags = 0 ) {
342- input::testing::send_keyboard_packet (stream_, key_code, client_modifiers, flags, false );
368+ void press (std::uint16_t key_code, unsigned client_modifiers = 0 , unsigned flags = 0 ) {
369+ input::testing::send_keyboard_packet (
370+ stream_,
371+ key_code,
372+ static_cast <std::uint8_t >(client_modifiers),
373+ static_cast <std::uint8_t >(flags),
374+ false
375+ );
343376 }
344377
345378 /* *
@@ -349,8 +382,14 @@ namespace {
349382 * @param client_modifiers Modifier bitmask the client reports as held.
350383 * @param flags Bit flags carried by the packet.
351384 */
352- void release (std::uint16_t key_code, std::uint8_t client_modifiers = 0 , std::uint8_t flags = 0 ) {
353- input::testing::send_keyboard_packet (stream_, key_code, client_modifiers, flags, true );
385+ void release (std::uint16_t key_code, unsigned client_modifiers = 0 , unsigned flags = 0 ) {
386+ input::testing::send_keyboard_packet (
387+ stream_,
388+ key_code,
389+ static_cast <std::uint8_t >(client_modifiers),
390+ static_cast <std::uint8_t >(flags),
391+ true
392+ );
354393 }
355394
356395 /* *
@@ -386,9 +425,8 @@ namespace {
386425 return *context_->keyboard ;
387426 }
388427
389- std::shared_ptr<input::input_t > stream_; // /< Stream input state under test.
390-
391428 private:
429+ std::shared_ptr<input::input_t > stream_; // /< Stream input state under test.
392430 std::vector<input::testing::keyboard_event_t > events_; // /< Keyboard output recorded for the current test.
393431 platf::virtualhid::input_context_t *context_ = nullptr ; // /< Fake input context installed in the global backend.
394432 config::input_t original_input_; // /< Input configuration restored after each test.
@@ -469,35 +507,22 @@ TEST_F(KeyboardPassthroughTest, HoldsEveryModifierCombinationWithoutSyntheticInj
469507 // Bit 0 selects shift, bit 1 ctrl, bit 2 alt, bit 3 meta: all 15 non-empty combinations of
470508 // single modifiers, pairs, triples, and the full quad.
471509 for (unsigned combination = 1 ; combination < (1u << modifiers.size ()); ++combination) {
472- // Each held key is reported with the modifiers accumulated so far, matching a client
473- // that presses them one at a time.
474- std::vector<std::pair<std::uint16_t , std::uint8_t >> held;
475- std::uint8_t claimed = 0 ;
476- std::string label;
477- for (std::size_t index = 0 ; index < modifiers.size (); ++index) {
478- if (!(combination & (1u << index))) {
479- continue ;
480- }
481- claimed |= modifiers[index].bit ;
482- held.emplace_back (key_for_side (modifiers[index], side), claimed);
483- label += (label.empty () ? " " : " +" );
484- label += modifiers[index].name ;
485- }
486- SCOPED_TRACE (label + " (side " + std::to_string (static_cast <int >(side)) + " )" );
510+ const auto held = select_modifiers (combination, side);
511+ SCOPED_TRACE (std::format (" {} (side {})" , held.label , static_cast <int >(side)));
487512
488513 std::vector<std::string> expected;
489- for (const auto &[key_code, reported] : held) {
514+ for (const auto &[key_code, reported] : held. keys ) {
490515 press (key_code, reported);
491516 expected.push_back (pressed (key_code));
492517 }
493518
494- press (VKEY_A , claimed);
495- release (VKEY_A , claimed);
519+ press (VKEY_A , held. claimed );
520+ release (VKEY_A , held. claimed );
496521 expected.push_back (pressed (VKEY_A ));
497522 expected.push_back (released (VKEY_A ));
498523
499- for (auto key = held.rbegin (); key != held.rend (); ++key) {
500- release (key->first , claimed);
524+ for (auto key = held.keys . rbegin (); key != held. keys .rend (); ++key) {
525+ release (key->first , held. claimed );
501526 expected.push_back (released (key->first ));
502527 }
503528
@@ -510,7 +535,7 @@ TEST_F(KeyboardPassthroughTest, InjectsSyntheticModifiersTheClientHoldsWithoutSe
510535 // Moonlight can report a modifier without ever sending its key event, for example when the
511536 // client's own compositor swallowed it. Sunshine wraps the key in a real modifier press.
512537 struct injectable_t {
513- std:: uint8_t bit;
538+ unsigned bit;
514539 std::uint16_t key_code;
515540 };
516541
@@ -522,7 +547,7 @@ TEST_F(KeyboardPassthroughTest, InjectsSyntheticModifiersTheClientHoldsWithoutSe
522547
523548 for (unsigned combination = 1 ; combination < (1u << injectable.size ()); ++combination) {
524549 std::vector<std::string> expected;
525- std:: uint8_t claimed = 0 ;
550+ unsigned claimed = 0 ;
526551 for (std::size_t index = 0 ; index < injectable.size (); ++index) {
527552 if (combination & (1u << index)) {
528553 claimed |= injectable[index].bit ;
@@ -799,9 +824,9 @@ TEST_F(KeyboardPassthroughTest, ReleasesHeldKeysAsThemselvesWithoutKeybindings)
799824
800825 // key_press is an unordered_map, so the release order is unspecified.
801826 auto released_keys = taken ();
802- std::sort (released_keys. begin (), released_keys. end () );
803- std::vector<std::string> expected {released (VKEY_LMENU ), released (VKEY_A )};
804- std::sort (expected. begin (), expected. end () );
827+ std::ranges:: sort (released_keys);
828+ std::vector expected {released (VKEY_LMENU ), released (VKEY_A )};
829+ std::ranges:: sort (expected);
805830 EXPECT_EQ (released_keys, expected);
806831}
807832
0 commit comments