vbox6: sync capslock state on boot / LED change

Thanks to Peter for the initial patch.
This commit is contained in:
Christian Helmuth 2022-04-08 09:56:27 +02:00
parent 9965b2b72a
commit db90656483

View File

@ -210,7 +210,9 @@ struct Main : Event_handler
Signal_handler<Main> _capslock_handler { _env.ep(), *this, &Main::_handle_capslock }; Signal_handler<Main> _capslock_handler { _env.ep(), *this, &Main::_handle_capslock };
void _handle_capslock(); void _handle_capslock() { Libc::with_libc([&] { _sync_capslock(); }); }
void _sync_capslock();
struct Capslock struct Capslock
{ {
@ -238,6 +240,9 @@ struct Main : Event_handler
bool update_from_rom() bool update_from_rom()
{ {
if (mode != Mode::ROM)
return false;
_rom->update(); _rom->update();
bool const rom = _rom->xml().attribute_value("enabled", _guest); bool const rom = _rom->xml().attribute_value("enabled", _guest);
@ -245,14 +250,11 @@ struct Main : Event_handler
bool trigger = false; bool trigger = false;
/* /*
* If guest didn't respond with led change last time, we have to * Trigger CapsLock change whenever the ROM state changes. This
* trigger CapsLock change - mainly assuming that guest don't use the * helps with guests that do not use the keyboard led to indicate
* led to externalize its internal state. * the CapsLock state.
*/ */
if (rom != _host && _host != _guest) if (rom != _host || rom != _guest)
trigger = true;
if (rom != _guest)
trigger = true; trigger = true;
/* remember last seen host capslock state */ /* 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_OnMousePointerShapeChanged);
event_types.push_back(VBoxEventType_OnKeyboardLedsChanged); event_types.push_back(VBoxEventType_OnKeyboardLedsChanged);
event_types.push_back(VBoxEventType_OnStateChanged); event_types.push_back(VBoxEventType_OnStateChanged);
event_types.push_back(VBoxEventType_OnAdditionsStateChanged);
ievent_source->RegisterListener(listener, ComSafeArrayAsInParam(event_types), true); 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()) { 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::Press { Input::KEY_CAPSLOCK } }); _input_adapter.handle_input_event(Input::Event { Input::Release { 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: 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<IKeyboardLedsChangedEvent> led_ev = &ev; ComPtr<IKeyboardLedsChangedEvent> led_ev = &ev;
BOOL capslock; BOOL capslock;
led_ev->COMGETTER(CapsLock)(&capslock); led_ev->COMGETTER(CapsLock)(&capslock);
_capslock.update_guest(!!capslock); _capslock.update_guest(!!capslock);
_sync_capslock();
} break; } break;
case VBoxEventType_OnStateChanged: case VBoxEventType_OnStateChanged:
@ -487,6 +497,27 @@ void Main::handle_vbox_event(VBoxEventType_T ev_type, IEvent &ev)
} break; } 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; default: /* ignore other events */ break;
} }
} }