From db906564835dd4ea8db5146d33afd21c5f7955b7 Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Fri, 8 Apr 2022 09:56:27 +0200 Subject: [PATCH] vbox6: sync capslock state on boot / LED change Thanks to Peter for the initial patch. --- repos/ports/src/virtualbox6/main.cc | 57 ++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/repos/ports/src/virtualbox6/main.cc b/repos/ports/src/virtualbox6/main.cc index 0bbd2cd2df..77c0f0e3c5 100644 --- a/repos/ports/src/virtualbox6/main.cc +++ b/repos/ports/src/virtualbox6/main.cc @@ -210,7 +210,9 @@ struct Main : Event_handler Signal_handler
_capslock_handler { _env.ep(), *this, &Main::_handle_capslock }; - void _handle_capslock(); + void _handle_capslock() { Libc::with_libc([&] { _sync_capslock(); }); } + + void _sync_capslock(); struct Capslock { @@ -238,6 +240,9 @@ struct Main : Event_handler bool update_from_rom() { + if (mode != Mode::ROM) + return false; + _rom->update(); bool const rom = _rom->xml().attribute_value("enabled", _guest); @@ -245,14 +250,11 @@ struct Main : Event_handler bool trigger = false; /* - * If guest didn't respond with led change last time, we have to - * trigger CapsLock change - mainly assuming that guest don't use the - * led to externalize its internal state. + * Trigger CapsLock change whenever the ROM state changes. This + * helps with guests that do not use the keyboard led to indicate + * the CapsLock state. */ - if (rom != _host && _host != _guest) - trigger = true; - - if (rom != _guest) + if (rom != _host || rom != _guest) trigger = true; /* remember last seen host capslock state */ @@ -361,6 +363,7 @@ struct Main : Event_handler event_types.push_back(VBoxEventType_OnMousePointerShapeChanged); event_types.push_back(VBoxEventType_OnKeyboardLedsChanged); event_types.push_back(VBoxEventType_OnStateChanged); + event_types.push_back(VBoxEventType_OnAdditionsStateChanged); ievent_source->RegisterListener(listener, ComSafeArrayAsInParam(event_types), true); } @@ -377,13 +380,12 @@ struct Main : Event_handler }; -void Main::_handle_capslock() +/* must be called in Libc::with_libc() context */ +void Main::_sync_capslock() { if (_capslock.update_from_rom()) { - Libc::with_libc([&] { - _input_adapter.handle_input_event(Input::Event { Input::Press { Input::KEY_CAPSLOCK } }); - _input_adapter.handle_input_event(Input::Event { Input::Release { Input::KEY_CAPSLOCK } }); - }); + _input_adapter.handle_input_event(Input::Event { Input::Press { Input::KEY_CAPSLOCK } }); + _input_adapter.handle_input_event(Input::Event { Input::Release { Input::KEY_CAPSLOCK } }); } } @@ -470,10 +472,18 @@ void Main::handle_vbox_event(VBoxEventType_T ev_type, IEvent &ev) case VBoxEventType_OnKeyboardLedsChanged: { + /* + * Use CapsLock LED as indicator for guest assumption about the + * state and optionally resync to host state. This is required + * because the guest may try to switch CapsLock (off) on its own, + * e.g. during startup. + */ + ComPtr led_ev = &ev; BOOL capslock; led_ev->COMGETTER(CapsLock)(&capslock); _capslock.update_guest(!!capslock); + _sync_capslock(); } break; case VBoxEventType_OnStateChanged: @@ -487,6 +497,27 @@ void Main::handle_vbox_event(VBoxEventType_T ev_type, IEvent &ev) } break; + case VBoxEventType_OnAdditionsStateChanged: + { + /* + * Try to sync initial CapsLock state when starting a guest OS. + * Usually this is only a problem when CapsLock is already on + * during startup, because the guest will assume it's off or + * deliberately clear the CapsLock state during boot. + * + * Ideally this should only be done once, after the guest is ready + * to process the CapsLock key but before it's ready for login. The + * OnAdditionsStateChanged event will fire a few times during boot, + * but maybe not when we really need it to. Maybe there is a better + * event to listen to, once the guest additions are fulling + * working, like VBoxEventType_OnGuestSessionRegistered. + * + * For a list of "VBoxEventType_..." events see + * virtualbox6_sdk/sdk/bindings/xpcom/include/VirtualBox_XPCOM.h + */ + _sync_capslock(); + } break; + default: /* ignore other events */ break; } }