From 1668cfaef2fe7effb38f311d232d4f57a8703d11 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 10 Dec 2016 17:13:40 +0100 Subject: [PATCH] Sync LED state when gaining focus The state might have changed when we didn't have focus. Get everything back in sync once we're back in control. --- vncviewer/DesktopWindow.cxx | 32 +++++++++------ vncviewer/Viewport.cxx | 81 +++++++++++++++++++++++++++++++++++++ vncviewer/Viewport.h | 2 + vncviewer/cocoa.h | 3 ++ vncviewer/cocoa.mm | 27 +++++++++++++ 5 files changed, 133 insertions(+), 12 deletions(-) diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 3973cd66..897f9557 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -666,23 +666,31 @@ int DesktopWindow::fltkHandle(int event, Fl_Window *win) DesktopWindow *dw = dynamic_cast(win); - if (dw && fullscreenSystemKeys) { + if (dw) { switch (event) { case FL_FOCUS: - // FIXME: We reassert the keyboard grabbing on focus as FLTK there are - // some issues we need to work around: - // a) Fl::grab(0) on X11 will release the keyboard grab for us. - // b) Gaining focus on the system level causes FLTK to switch - // window level on OS X. - if (dw->fullscreen_active()) - dw->grabKeyboard(); + if (fullscreenSystemKeys) { + // FIXME: We reassert the keyboard grabbing on focus as FLTK there are + // some issues we need to work around: + // a) Fl::grab(0) on X11 will release the keyboard grab for us. + // b) Gaining focus on the system level causes FLTK to switch + // window level on OS X. + if (dw->fullscreen_active()) + dw->grabKeyboard(); + } + + // We may have gotten our lock keys out of sync with the server + // whilst we didn't have focus. Try to sort this out. + dw->viewport->pushLEDState(); break; case FL_UNFOCUS: - // FIXME: We need to relinquish control when the entire window loses - // focus as it is very tied to this specific window on some - // platforms and we want to be able to open subwindows. - dw->ungrabKeyboard(); + if (fullscreenSystemKeys) { + // FIXME: We need to relinquish control when the entire window loses + // focus as it is very tied to this specific window on some + // platforms and we want to be able to open subwindows. + dw->ungrabKeyboard(); + } break; } } diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index c0bbd0dd..bfadd4c0 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -329,6 +329,87 @@ void Viewport::setLEDState(unsigned int state) #endif } +void Viewport::pushLEDState() +{ + unsigned int state; + + // Server support? + if (cc->cp.ledState() == ledUnknown) + return; + + state = 0; + +#if defined(WIN32) + if (GetKeyState(VK_CAPITAL) & 0x1) + state |= ledCapsLock; + if (GetKeyState(VK_NUMLOCK) & 0x1) + state |= ledNumLock; + if (GetKeyState(VK_SCROLL) & 0x1) + state |= ledScrollLock; +#elif defined(__APPLE__) + int ret; + bool on; + + ret = cocoa_get_caps_lock_state(&on); + if (ret != 0) { + vlog.error(_("Failed to get keyboard LED state: %d"), ret); + return; + } + if (on) + state |= ledCapsLock; + + ret = cocoa_get_num_lock_state(&on); + if (ret != 0) { + vlog.error(_("Failed to get keyboard LED state: %d"), ret); + return; + } + if (on) + state |= ledNumLock; + + // No support for Scroll Lock // + state |= (cc->cp.ledState() & ledScrollLock); + +#else + unsigned int mask; + + Status status; + XkbStateRec xkbState; + + status = XkbGetState(fl_display, XkbUseCoreKbd, &xkbState); + if (status != Success) { + vlog.error(_("Failed to get keyboard LED state: %d"), status); + return; + } + + if (xkbState.locked_mods & LockMask) + state |= ledCapsLock; + + mask = getModifierMask(XK_Num_Lock); + if (xkbState.locked_mods & mask) + state |= ledNumLock; + + mask = getModifierMask(XK_Scroll_Lock); + if (xkbState.locked_mods & mask) + state |= ledScrollLock; +#endif + + if ((state & ledCapsLock) != (cc->cp.ledState() & ledCapsLock)) { + vlog.debug("Inserting fake CapsLock to get in sync with server"); + handleKeyPress(fakeKeyBase + 100, XK_Caps_Lock); + handleKeyRelease(fakeKeyBase + 100); + } + if ((state & ledNumLock) != (cc->cp.ledState() & ledNumLock)) { + vlog.debug("Inserting fake NumLock to get in sync with server"); + handleKeyPress(fakeKeyBase + 101, XK_Num_Lock); + handleKeyRelease(fakeKeyBase + 101); + } + if ((state & ledScrollLock) != (cc->cp.ledState() & ledScrollLock)) { + vlog.debug("Inserting fake ScrollLock to get in sync with server"); + handleKeyPress(fakeKeyBase + 102, XK_Scroll_Lock); + handleKeyRelease(fakeKeyBase + 102); + } +} + void Viewport::draw(Surface* dst) { diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h index 652feb45..31158c2d 100644 --- a/vncviewer/Viewport.h +++ b/vncviewer/Viewport.h @@ -49,6 +49,8 @@ public: // Change client LED state void setLEDState(unsigned int state); + // Change server LED state + void pushLEDState(); void draw(Surface* dst); diff --git a/vncviewer/cocoa.h b/vncviewer/cocoa.h index b5d5f763..a7138026 100644 --- a/vncviewer/cocoa.h +++ b/vncviewer/cocoa.h @@ -36,4 +36,7 @@ int cocoa_event_keysym(const void *event); int cocoa_set_caps_lock_state(bool on); int cocoa_set_num_lock_state(bool on); +int cocoa_get_caps_lock_state(bool *on); +int cocoa_get_num_lock_state(bool *on); + #endif diff --git a/vncviewer/cocoa.mm b/vncviewer/cocoa.mm index 6483291c..e26851d6 100644 --- a/vncviewer/cocoa.mm +++ b/vncviewer/cocoa.mm @@ -447,6 +447,23 @@ static int cocoa_set_modifier_lock_state(int modifier, bool on) return KERN_SUCCESS; } +static int cocoa_get_modifier_lock_state(int modifier, bool *on) +{ + kern_return_t ret; + io_connect_t ioc; + + ret = cocoa_open_hid(&ioc); + if (ret != KERN_SUCCESS) + return ret; + + ret = IOHIDGetModifierLockState(ioc, modifier, on); + IOServiceClose(ioc); + if (ret != KERN_SUCCESS) + return ret; + + return KERN_SUCCESS; +} + int cocoa_set_caps_lock_state(bool on) { return cocoa_set_modifier_lock_state(kIOHIDCapsLockState, on); @@ -456,3 +473,13 @@ int cocoa_set_num_lock_state(bool on) { return cocoa_set_modifier_lock_state(kIOHIDNumLockState, on); } + +int cocoa_get_caps_lock_state(bool *on) +{ + return cocoa_get_modifier_lock_state(kIOHIDCapsLockState, on); +} + +int cocoa_get_num_lock_state(bool *on) +{ + return cocoa_get_modifier_lock_state(kIOHIDNumLockState, on); +} -- 2.39.5