Implement support for grabbing the keyboard when in full screen mode.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4449 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 5628142..ce9e9ab 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -22,11 +22,22 @@
 #include <string.h>
 
 #include <FL/Fl_Scroll.H>
+#include <FL/x.H>
 
 #include <rfb/LogWriter.h>
 
 #include "DesktopWindow.h"
+#include "OptionsDialog.h"
 #include "i18n.h"
+#include "parameters.h"
+
+#ifdef WIN32
+#include "win32.h"
+#endif
+
+#ifdef __APPLE__
+#include "cocoa.h"
+#endif
 
 using namespace rfb;
 
@@ -56,6 +67,8 @@
 
   setName(name);
 
+  OptionsDialog::addCallback(handleOptions, this);
+
   show();
 
   // The window manager might give us an initial window size that is different
@@ -68,6 +81,12 @@
 
 DesktopWindow::~DesktopWindow()
 {
+  // Unregister all timeouts in case they get a change tro trigger
+  // again later when this object is already gone.
+  Fl::remove_timeout(handleGrab, this);
+
+  OptionsDialog::removeCallback(handleOptions);
+
   // FLTK automatically deletes all child widgets, so we shouldn't touch
   // them ourselves here
 }
@@ -152,7 +171,132 @@
 }
 
 
+int DesktopWindow::handle(int event)
+{
+  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 (!fullscreenSystemKeys)
+      break;
+
+    if (fullscreen_active())
+      grabKeyboard();
+    else
+      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
+    // shortcut dispatching. Try to make things sane again...
+    if (Fl::focus() == NULL) {
+      take_focus();
+      Fl::handle(FL_KEYDOWN, this);
+    }
+    return 1;
+  }
+
+  return Fl_Window::handle(event);
+}
+
+
+void DesktopWindow::grabKeyboard()
+{
+  // Grabbing the keyboard is fairly safe as FLTK reroutes events to the
+  // correct widget regardless of which low level window got the system
+  // event.
+
+  // FIXME: Push this stuff into FLTK.
+
+#if defined(WIN32)
+  int ret;
+  
+  ret = win32_enable_lowlevel_keyboard(fl_xid(this));
+  if (ret != 0)
+    vlog.error(_("Failure grabbing keyboard"));
+#elif defined(__APPLE__)
+  int ret;
+  
+  ret = cocoa_capture_display(this);
+  if (ret != 0)
+    vlog.error(_("Failure grabbing keyboard"));
+#else
+  int ret;
+
+  ret = XGrabKeyboard(fl_display, fl_xid(this), True,
+                      GrabModeAsync, GrabModeAsync, fl_event_time);
+  if (ret) {
+    if (ret == AlreadyGrabbed) {
+      // It seems like we can race with the WM in some cases.
+      // Try again in a bit.
+      if (!Fl::has_timeout(handleGrab, this))
+        Fl::add_timeout(0.500, handleGrab, this);
+    } else {
+      vlog.error(_("Failure grabbing keyboard"));
+    }
+  }
+#endif
+}
+
+
+void DesktopWindow::ungrabKeyboard()
+{
+  Fl::remove_timeout(handleGrab, this);
+
+#if defined(WIN32)
+  win32_disable_lowlevel_keyboard(fl_xid(this));
+#elif defined(__APPLE__)
+  cocoa_release_display(this);
+#else
+  // FLTK has a grab so lets not mess with it
+  if (Fl::grab())
+    return;
+
+  XUngrabKeyboard(fl_display, fl_event_time);
+#endif
+}
+
+
+void DesktopWindow::handleGrab(void *data)
+{
+  DesktopWindow *self = (DesktopWindow*)data;
+
+  assert(self);
+
+#ifdef HAVE_FLTK_FULLSCREEN
+  if (!fullscreenSystemKeys)
+    return;
+  if (!self->fullscreen_active())
+    return;
+
+  self->grabKeyboard();
+#endif
+}
+
+
 void DesktopWindow::handleClose(Fl_Widget *wnd, void *data)
 {
   exit_vncviewer();
 }
+
+
+void DesktopWindow::handleOptions(void *data)
+{
+  DesktopWindow *self = (DesktopWindow*)data;
+
+#ifdef HAVE_FLTK_FULLSCREEN
+  if (self->fullscreen_active() && fullscreenSystemKeys)
+    self->grabKeyboard();
+  else
+    self->ungrabKeyboard();
+#endif
+}