Browse Source

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
tags/v1.1.90
Pierre Ossman 13 years ago
parent
commit
407a5c3ce6

+ 8
- 0
vncviewer/CMakeLists.txt View File

@@ -13,6 +13,14 @@ set(VNCVIEWER_SOURCES
keysym2ucs.c
vncviewer.cxx)

if(WIN32)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} win32.c)
endif()

if(APPLE)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} cocoa.mm)
endif()

add_executable(vncviewer ${VNCVIEWER_SOURCES})

target_link_libraries(vncviewer rfb network rdr os Xregion ${FLTK_LIBRARIES} ${GETTEXT_LIBRARIES})

+ 144
- 0
vncviewer/DesktopWindow.cxx View File

@@ -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 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name,

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(int w, int h, const char *name,

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 @@ void DesktopWindow::resize(int x, int y, int w, int h)
}


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
}

+ 9
- 0
vncviewer/DesktopWindow.h View File

@@ -68,9 +68,18 @@ public:
// Fl_Window callback methods
void resize(int x, int y, int w, int h);

int handle(int event);

private:
void grabKeyboard();
void ungrabKeyboard();

static void handleGrab(void *data);

static void handleClose(Fl_Widget *wnd, void *data);

static void handleOptions(void *data);

private:
Viewport *viewport;
};

+ 8
- 0
vncviewer/OptionsDialog.cxx View File

@@ -259,6 +259,7 @@ void OptionsDialog::loadOptions(void)
acceptClipboardCheckbox->value(acceptClipboard);
sendClipboardCheckbox->value(sendClipboard);
sendPrimaryCheckbox->value(sendPrimary);
systemKeysCheckbox->value(fullscreenSystemKeys);

menuKeyChoice->value(0);

@@ -352,6 +353,7 @@ void OptionsDialog::storeOptions(void)
acceptClipboard.setParam(acceptClipboardCheckbox->value());
sendClipboard.setParam(sendClipboardCheckbox->value());
sendPrimary.setParam(sendPrimaryCheckbox->value());
fullscreenSystemKeys.setParam(systemKeysCheckbox->value());

if (menuKeyChoice->value() == 0)
menuKey.setParam("");
@@ -683,6 +685,12 @@ void OptionsDialog::createInputPage(int tx, int ty, int tw, int th)
_("Send primary selection and cut buffer as clipboard")));
ty += CHECK_HEIGHT + TIGHT_MARGIN;

systemKeysCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
CHECK_MIN_WIDTH,
CHECK_HEIGHT,
_("Pass system keys directly to server (full screen)")));
ty += CHECK_HEIGHT + TIGHT_MARGIN;

menuKeyChoice = new Fl_Choice(LBLLEFT(tx, ty, 150, CHOICE_HEIGHT, _("Menu key")));

menuKeyChoice->add(_("None"), 0, NULL, (void*)0, FL_MENU_DIVIDER);

+ 1
- 0
vncviewer/OptionsDialog.h View File

@@ -105,6 +105,7 @@ protected:
Fl_Check_Button *acceptClipboardCheckbox;
Fl_Check_Button *sendClipboardCheckbox;
Fl_Check_Button *sendPrimaryCheckbox;
Fl_Check_Button *systemKeysCheckbox;
Fl_Choice *menuKeyChoice;

/* Misc. */

+ 7
- 0
vncviewer/Viewport.cxx View File

@@ -605,6 +605,13 @@ void Viewport::popupContextMenu()
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);

contextMenu->position(Fl::event_x(), Fl::event_y());

m = contextMenu->popup();

+ 25
- 0
vncviewer/cocoa.h View File

@@ -0,0 +1,25 @@
/* Copyright 2011 Pierre Ossman <ossman@cendio.se> 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,
* USA.
*/

#ifndef __VNCVIEWER_COCOA_H__
#define __VNCVIEWER_COCOA_H__

int cocoa_capture_display(Fl_Window *win);
void cocoa_release_display(Fl_Window *win);

#endif

+ 69
- 0
vncviewer/cocoa.mm View File

@@ -0,0 +1,69 @@
/* Copyright 2011 Pierre Ossman <ossman@cendio.se> 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,
* USA.
*/

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/x.H>

#import <Cocoa/Cocoa.h>

int cocoa_capture_display(Fl_Window *win)
{
NSWindow *nsw;

nsw = (NSWindow*)fl_xid(win);

// Already captured?
if ([nsw level] == CGShieldingWindowLevel())
return 0;

if (CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess)
return 1;

[nsw setLevel:CGShieldingWindowLevel()];

return 0;
}

void cocoa_release_display(Fl_Window *win)
{
NSWindow *nsw;
int newlevel;

CGDisplayRelease(kCGDirectMainDisplay);

nsw = (NSWindow*)fl_xid(win);

// Someone else has already changed the level of this window
if ([nsw level] != CGShieldingWindowLevel())
return;

// FIXME: Store the previous level somewhere so we don't have to hard
// code a level here.
#ifdef HAVE_FLTK_FULLSCREEN
if (win->fullscreen_active() && win->contains(Fl::focus()))
newlevel = NSStatusWindowLevel;
else
#endif
newlevel = NSNormalWindowLevel;

// Only change if different as the level change also moves the window
// to the top of that level.
if ([nsw level] != newlevel)
[nsw setLevel:newlevel];
}

+ 5
- 0
vncviewer/parameters.cxx View File

@@ -88,3 +88,8 @@ BoolParameter sendPrimary("SendPrimary",
StringParameter menuKey("MenuKey", "The key which brings up the popup menu",
"F8");

BoolParameter fullscreenSystemKeys("FullscreenSystemKeys",
"Pass special keys (like Alt+Tab) directly "
"to the server when in full screen mode.",
true);


+ 2
- 0
vncviewer/parameters.h View File

@@ -50,4 +50,6 @@ extern rfb::BoolParameter sendPrimary;

extern rfb::StringParameter menuKey;

extern rfb::BoolParameter fullscreenSystemKeys;

#endif

+ 115
- 0
vncviewer/win32.c View File

@@ -0,0 +1,115 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011 Pierre Ossman <ossman@cendio.se> 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,
* USA.
*/

#include <windows.h>

static HANDLE thread;
static DWORD thread_id;

static HHOOK hook = 0;
static HWND target_wnd = 0;

static int is_system_hotkey(int vkCode) {
switch (vkCode) {
case VK_LWIN:
case VK_RWIN:
case VK_SNAPSHOT:
return 1;
case VK_TAB:
if (GetAsyncKeyState(VK_MENU) & 0x8000)
return 1;
case VK_ESCAPE:
if (GetAsyncKeyState(VK_MENU) & 0x8000)
return 1;
if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
return 1;
}
return 0;
}

static LRESULT CALLBACK keyboard_hook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0) {
KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam;

// Grabbing everything seems to mess up some keyboard state that
// FLTK relies on, so just grab the keys that we normally cannot.
if (is_system_hotkey(msgInfo->vkCode)) {
PostMessage(target_wnd, wParam, msgInfo->vkCode,
(msgInfo->scanCode & 0xff) << 16 |
(msgInfo->flags & 0xff) << 24);
return 1;
}
}

return CallNextHookEx(hook, nCode, wParam, lParam);
}

static DWORD WINAPI keyboard_thread(LPVOID data)
{
MSG msg;

target_wnd = (HWND)data;

// Make sure a message queue is created
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD);

hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook, GetModuleHandle(0), 0);
// If something goes wrong then there is not much we can do.
// Just sit around and wait for WM_QUIT...

while (GetMessage(&msg, NULL, 0, 0));

if (hook)
UnhookWindowsHookEx(hook);

target_wnd = 0;

return 0;
}

int win32_enable_lowlevel_keyboard(HWND hwnd)
{
// Only one target at a time for now
if (thread != NULL) {
if (hwnd == target_wnd)
return 0;

return 1;
}

// We create a separate thread as it is crucial that hooks are processed
// in a timely manner.
thread = CreateThread(NULL, 0, keyboard_thread, hwnd, 0, &thread_id);
if (thread == NULL)
return 1;

return 0;
}

void win32_disable_lowlevel_keyboard(HWND hwnd)
{
if (hwnd != target_wnd)
return;

PostThreadMessage(thread_id, WM_QUIT, 0, 0);

CloseHandle(thread);
thread = NULL;
}

+ 30
- 0
vncviewer/win32.h View File

@@ -0,0 +1,30 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011 Pierre Ossman <ossman@cendio.se> 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,
* USA.
*/

#ifndef __VNCVIEWER_WIN32_H__
#define __VNCVIEWER_WIN32_H__

extern "C" {

int win32_enable_lowlevel_keyboard(HWND hwnd);
void win32_disable_lowlevel_keyboard(HWND hwnd);

};

#endif

Loading…
Cancel
Save