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})
#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;
setName(name);
+ OptionsDialog::addCallback(handleOptions, this);
+
show();
// The window manager might give us an initial window size that is different
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
}
}
+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
+}
// 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;
};
acceptClipboardCheckbox->value(acceptClipboard);
sendClipboardCheckbox->value(sendClipboard);
sendPrimaryCheckbox->value(sendPrimary);
+ systemKeysCheckbox->value(fullscreenSystemKeys);
menuKeyChoice->value(0);
acceptClipboard.setParam(acceptClipboardCheckbox->value());
sendClipboard.setParam(sendClipboardCheckbox->value());
sendPrimary.setParam(sendPrimaryCheckbox->value());
+ fullscreenSystemKeys.setParam(systemKeysCheckbox->value());
if (menuKeyChoice->value() == 0)
menuKey.setParam("");
_("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);
Fl_Check_Button *acceptClipboardCheckbox;
Fl_Check_Button *sendClipboardCheckbox;
Fl_Check_Button *sendPrimaryCheckbox;
+ Fl_Check_Button *systemKeysCheckbox;
Fl_Choice *menuKeyChoice;
/* Misc. */
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();
--- /dev/null
+/* 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
--- /dev/null
+/* 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];
+}
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);
+
extern rfb::StringParameter menuKey;
+extern rfb::BoolParameter fullscreenSystemKeys;
+
#endif
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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