Skip to content

Commit

Permalink
Handle macOS keyboard stealing
Browse files Browse the repository at this point in the history
The system steals keyboard events for certain system keyboard shortcuts,
e.g. Cmd+Tab. Unfortunately this isn't considered a focus loss, so we
don't realise we've lost a few keyboard events and can end up in a
confused state.

Fortunately it is possible to detect when this happens and reset the
keyboard state, just like we do when focus is lost.
  • Loading branch information
CendioOssman committed Dec 13, 2022
1 parent 946fb2b commit 4f6d489
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 4 deletions.
20 changes: 16 additions & 4 deletions vncviewer/Viewport.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -643,10 +643,9 @@ int Viewport::handle(int event)
return 1;

case FL_UNFOCUS:
// Release all keys that were pressed as that generally makes most
// sense (e.g. Alt+Tab where we only see the Alt press)
while (!downKeySym.empty())
handleKeyRelease(downKeySym.begin()->first);
// We won't get more key events, so reset our knowledge about keys
resetKeyboard();

Fl::enable_im();
return 1;

Expand Down Expand Up @@ -823,6 +822,13 @@ void Viewport::handlePointerTimeout(void *data)
}


void Viewport::resetKeyboard()
{
while (!downKeySym.empty())
handleKeyRelease(downKeySym.begin()->first);
}


void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym)
{
static bool menuRecursion = false;
Expand Down Expand Up @@ -1125,6 +1131,12 @@ int Viewport::handleSystemEvent(void *event, void *data)
return 1;
}
#elif defined(__APPLE__)
// Special event that means we temporarily lost some input
if (cocoa_is_keyboard_sync(event)) {
self->resetKeyboard();
return 1;
}

if (cocoa_is_keyboard_event(event)) {
int keyCode;

Expand Down
2 changes: 2 additions & 0 deletions vncviewer/Viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ class Viewport : public Fl_Widget, public EmulateMB {
void handlePointerEvent(const rfb::Point& pos, int buttonMask);
static void handlePointerTimeout(void *data);

void resetKeyboard();

void handleKeyPress(int keyCode, rdr::U32 keySym);
void handleKeyRelease(int keyCode);

Expand Down
1 change: 1 addition & 0 deletions vncviewer/cocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ CGColorSpaceRef cocoa_win_color_space(Fl_Window *win);
bool cocoa_win_is_zoomed(Fl_Window *win);
void cocoa_win_zoom(Fl_Window *win);

int cocoa_is_keyboard_sync(const void *event);
int cocoa_is_keyboard_event(const void *event);

int cocoa_is_key_press(const void *event);
Expand Down
21 changes: 21 additions & 0 deletions vncviewer/cocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,25 @@ void cocoa_win_zoom(Fl_Window *win)
[nsw zoom:nsw];
}

int cocoa_is_keyboard_sync(const void *event)
{
const NSEvent* nsevent = (const NSEvent*)event;

assert(event);

// If we get a NSFlagsChanged event with key code 0 then this isn't
// an actual keyboard event but rather the system trying to sync up
// modifier state after it has stolen input for some reason (e.g.
// Cmd+Tab)

if ([nsevent type] != NSFlagsChanged)
return 0;
if ([nsevent keyCode] != 0)
return 0;

return 1;
}

int cocoa_is_keyboard_event(const void *event)
{
NSEvent *nsevent;
Expand All @@ -185,6 +204,8 @@ int cocoa_is_keyboard_event(const void *event)
case NSKeyDown:
case NSKeyUp:
case NSFlagsChanged:
if (cocoa_is_keyboard_sync(event))
return 0;
return 1;
default:
return 0;
Expand Down

0 comments on commit 4f6d489

Please sign in to comment.