diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 47fe8f8..8f387dc 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -1,16 +1,16 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright 2011 Pierre Ossman for Cendio AB - * + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, @@ -662,20 +662,16 @@ int DesktopWindow::fltkHandle(int event, Fl_Window *win) if (dw && fullscreenSystemKeys) { switch (event) { + // Focus might not stay with us just because we have grabbed the + // keyboard. E.g. we might have sub windows, or we're not using + // all monitors and the user clicked on another application. + // Make sure we update our grabs with the focus changes. 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; } @@ -729,6 +725,23 @@ void DesktopWindow::fullscreen_on() fullscreen(); } +#if !defined(WIN32) && !defined(__APPLE__) +Bool eventIsFocusWithSerial(Display *display, XEvent *event, XPointer arg) +{ + unsigned long serial; + + serial = *(unsigned long*)arg; + + if (event->xany.serial != serial) + return False; + + if ((event->type != FocusIn) && (event->type != FocusOut)) + return False; + + return True; +} +#endif + void DesktopWindow::grabKeyboard() { // Grabbing the keyboard is fairly safe as FLTK reroutes events to the @@ -739,19 +752,24 @@ void DesktopWindow::grabKeyboard() #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, fullScreenAllMonitors); if (ret != 0) vlog.error(_("Failure grabbing keyboard")); #else int ret; + XEvent xev; + unsigned long serial; + + serial = XNextRequest(fl_display); + ret = XGrabKeyboard(fl_display, fl_xid(this), True, GrabModeAsync, GrabModeAsync, CurrentTime); if (ret) { @@ -774,6 +792,16 @@ void DesktopWindow::grabKeyboard() None, None, CurrentTime); if (ret) vlog.error(_("Failure grabbing mouse")); + + // Xorg 1.20+ generates FocusIn/FocusOut even when there is no actual + // change of focus. This causes us to get stuck in an endless loop + // grabbing and ungrabbing the keyboard. Avoid this by filtering out + // any focus events generated by XGrabKeyboard(). + XSync(fl_display, False); + while (XCheckIfEvent(fl_display, &xev, &eventIsFocusWithSerial, + (XPointer)&serial) == True) { + vlog.debug("Ignored synthetic focus event cause by grab change"); + } #endif } @@ -791,8 +819,20 @@ void DesktopWindow::ungrabKeyboard() if (Fl::grab()) return; + XEvent xev; + unsigned long serial; + + serial = XNextRequest(fl_display); + XUngrabPointer(fl_display, fl_event_time); XUngrabKeyboard(fl_display, fl_event_time); + + // See grabKeyboard() + XSync(fl_display, False); + while (XCheckIfEvent(fl_display, &xev, &eventIsFocusWithSerial, + (XPointer)&serial) == True) { + vlog.debug("Ignored synthetic focus event cause by grab change"); + } #endif }