]> source.dussan.org Git - tigervnc.git/commitdiff
Handle macOS keyboard stealing
authorPierre Ossman <ossman@cendio.se>
Tue, 14 Dec 2021 16:06:36 +0000 (17:06 +0100)
committerPierre Ossman <ossman@cendio.se>
Tue, 13 Dec 2022 13:52:13 +0000 (14:52 +0100)
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.

vncviewer/Viewport.cxx
vncviewer/Viewport.h
vncviewer/cocoa.h
vncviewer/cocoa.mm

index 81ad98272695bb015427357c5cc93cb0be591148..83872ced76bb0384c310d969c625daf7b3850ee6 100644 (file)
@@ -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;
 
@@ -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;
@@ -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;
 
index 19def92c09b87103e69e5729ca0eb0efd0915888..a42b06c6832161feea1513c21b648ba7bcea7357 100644 (file)
@@ -84,6 +84,8 @@ private:
   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);
 
index ca17ddf9da66658002e1e4f05bcc5f000d9a0a86..63b2a53509398ad101f6614e241768505465c282 100644 (file)
@@ -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);
index cf9c9a8e22e811900d99ac50a9d07b9309d94113..e8b202d6f2e690a387481441a5535ff0e4d82322 100644 (file)
@@ -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;
@@ -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;