Implement a more robust method to handle the keyboard grab/focus voodoo as
it didn't catch some cases on OS X.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4496 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 52725ed..456e372 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -69,6 +69,9 @@
 
   OptionsDialog::addCallback(handleOptions, this);
 
+  // Hack. See below...
+  Fl::event_dispatch(&fltkHandle);
+
 #ifdef HAVE_FLTK_FULLSCREEN
   if (fullScreen)
     fullscreen();
@@ -197,9 +200,6 @@
 {
   switch (event) {
 #ifdef HAVE_FLTK_FULLSCREEN
-  case FL_FOCUS:
-    // FIXME: We reassert the keyboard grabbing on focus/unfocus as FLTK
-    //        releases the grab when someone calls Fl::grab(0)
   case FL_FULLSCREEN:
     if (event == FL_FULLSCREEN)
       fullScreen.setParam(fullscreen_active());
@@ -213,12 +213,6 @@
       ungrabKeyboard();
 
     break;
-  case FL_UNFOCUS:
-    // FIXME: We need to relinquish control when the entire window loses
-    //        focus as it seems to interfere with the WM:s ability to handle
-    //        interactions with popups' window decorations.
-    ungrabKeyboard();
-    break;
 #endif
   case FL_SHORTCUT:
     // Sometimes the focus gets out of whack and we fall through to the
@@ -234,6 +228,46 @@
 }
 
 
+int DesktopWindow::fltkHandle(int event, Fl_Window *win)
+{
+  int ret;
+
+  ret = Fl::handle_(event, win);
+
+#ifdef HAVE_FLTK_FULLSCREEN
+  // This is hackish and the result of the dodgy focus handling in FLTK.
+  // The basic problem is that FLTK's view of focus and the system's tend
+  // to differ, and as a result we do not see all the FL_FOCUS events we
+  // need. Fortunately we can grab them here...
+
+  DesktopWindow *dw = dynamic_cast<DesktopWindow*>(win);
+
+  if (dw && fullscreenSystemKeys) {
+    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();
+      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();
+      break;
+    }
+  }
+#endif
+
+  return ret;
+}
+
+
 void DesktopWindow::grabKeyboard()
 {
   // Grabbing the keyboard is fairly safe as FLTK reroutes events to the
diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h
index 2be0768..0848306 100644
--- a/vncviewer/DesktopWindow.h
+++ b/vncviewer/DesktopWindow.h
@@ -72,6 +72,8 @@
   int handle(int event);
 
 private:
+  static int fltkHandle(int event, Fl_Window *win);
+
   void grabKeyboard();
   void ungrabKeyboard();
 
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 139eb9a..ae9aa10 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -740,13 +740,6 @@
   const Fl_Menu_Item *m;
   char buffer[1024];
 
-  // FIXME: Hacky workaround for the fact that menus grab and ungrab the
-  //        keyboard. Releasing focus here triggers some events on ungrab
-  //        that DesktopWindow can catch and trigger some voodoo.
-  //        See DesktopWindow::handle().
-  if (window()->contains(Fl::focus()))
-    Fl::focus(NULL);
-
   // Make sure the menu is reset to its initial state between goes or
   // it will start up highlighting the previously selected entry.
   contextMenu->value(-1);
diff --git a/vncviewer/cocoa.mm b/vncviewer/cocoa.mm
index 589da2d..6300d3c 100644
--- a/vncviewer/cocoa.mm
+++ b/vncviewer/cocoa.mm
@@ -22,19 +22,24 @@
 
 #import <Cocoa/Cocoa.h>
 
+static bool captured = false;
+
 int cocoa_capture_display(Fl_Window *win)
 {
   NSWindow *nsw;
 
   nsw = (NSWindow*)fl_xid(win);
 
-  // Already captured?
+  if (!captured) {
+    if (CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess)
+      return 1;
+
+    captured = true;
+  }
+
   if ([nsw level] == CGShieldingWindowLevel())
     return 0;
 
-  if (CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess)
-    return 1;
-
   [nsw setLevel:CGShieldingWindowLevel()];
 
   return 0;
@@ -45,7 +50,10 @@
   NSWindow *nsw;
   int newlevel;
 
-  CGDisplayRelease(kCGDirectMainDisplay);
+  if (captured)
+    CGDisplayRelease(kCGDirectMainDisplay);
+
+  captured = false;
 
   nsw = (NSWindow*)fl_xid(win);