diff options
author | Pierre Ossman <ossman@cendio.se> | 2011-04-15 07:46:56 +0000 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2011-04-15 07:46:56 +0000 |
commit | d50b3d137b14c9111c9c789a9acb4d1d67802400 (patch) | |
tree | 94675ead37f01e2f613e1dc901db5f5309a01fb7 /vncviewer | |
parent | a64515286b115ba425297012bfb268d22cd5763c (diff) | |
download | tigervnc-d50b3d137b14c9111c9c789a9acb4d1d67802400.tar.gz tigervnc-d50b3d137b14c9111c9c789a9acb4d1d67802400.zip |
Split out the graphics and input handling to a separate widget in preparation
for things like scroll bars.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4371 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'vncviewer')
-rw-r--r-- | vncviewer/CMakeLists.txt | 1 | ||||
-rw-r--r-- | vncviewer/DesktopWindow.cxx | 430 | ||||
-rw-r--r-- | vncviewer/DesktopWindow.h | 62 | ||||
-rw-r--r-- | vncviewer/Viewport.cxx | 489 | ||||
-rw-r--r-- | vncviewer/Viewport.h | 118 |
5 files changed, 624 insertions, 476 deletions
diff --git a/vncviewer/CMakeLists.txt b/vncviewer/CMakeLists.txt index e1bda9da..43157100 100644 --- a/vncviewer/CMakeLists.txt +++ b/vncviewer/CMakeLists.txt @@ -5,6 +5,7 @@ set(VNCVIEWER_SOURCES CConn.cxx DesktopWindow.cxx UserDialog.cxx + Viewport.cxx parameters.cxx keysym2ucs.c vncviewer.cxx) diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 189b5210..deae16ec 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -21,35 +21,13 @@ #include <stdio.h> #include <string.h> -#include <FL/fl_draw.H> - -#include <rfb/CMsgWriter.h> #include <rfb/LogWriter.h> -// FLTK can pull in the X11 headers on some systems -#ifndef XK_VoidSymbol -#define XK_MISCELLANY -#define XK_XKB_KEYS -#include <rfb/keysymdef.h> -#endif - #include "DesktopWindow.h" -#include "CConn.h" #include "i18n.h" -#include "parameters.h" -#include "keysym2ucs.h" - -// FLTK STR #2599 must be fixed for proper dead keys support -#ifndef HAVE_FLTK_DEAD_KEYS -#define event_compose_symbol event_text -#endif using namespace rfb; -#ifdef __APPLE__ -extern "C" const char *osx_event_string(void); -#endif - extern void exit_vncviewer(); static rfb::LogWriter vlog("DesktopWindow"); @@ -57,56 +35,34 @@ static rfb::LogWriter vlog("DesktopWindow"); DesktopWindow::DesktopWindow(int w, int h, const char *name, const rfb::PixelFormat& serverPF, CConn* cc_) - : Fl_Window(w, h), cc(cc_), frameBuffer(NULL), pixelTrans(NULL), - lastPointerPos(0, 0), lastButtonMask(0) + : Fl_Window(w, h) { + viewport = new Viewport(w, h, serverPF, cc_); + callback(handleClose, this); setName(name); - frameBuffer = new ManagedPixelBuffer(getPreferredPF(), w, h); - assert(frameBuffer); - - setServerPF(serverPF); - show(); } DesktopWindow::~DesktopWindow() { - // Unregister all timeouts in case they get a change tro trigger - // again later when this object is already gone. - Fl::remove_timeout(handleUpdateTimeout, this); - Fl::remove_timeout(handleColourMap, this); - Fl::remove_timeout(handlePointerTimeout, this); - - delete frameBuffer; - - if (pixelTrans) - delete pixelTrans; + // FLTK automatically deletes all child widgets, so we shouldn't touch + // them ourselves here } void DesktopWindow::setServerPF(const rfb::PixelFormat& pf) { - if (pixelTrans) - delete pixelTrans; - pixelTrans = NULL; - - if (pf.equal(getPreferredPF())) - return; - - pixelTrans = new PixelTransformer(); - pixelTrans->init(pf, &colourMap, getPreferredPF()); + viewport->setServerPF(pf); } const rfb::PixelFormat &DesktopWindow::getPreferredPF() { - static PixelFormat prefPF(32, 24, false, true, 255, 255, 255, 0, 8, 16); - - return prefPF; + return viewport->getPreferredPF(); } @@ -128,18 +84,11 @@ void DesktopWindow::setName(const char *name) copy_label(windowNameStr.buf); } -// setColourMapEntries() changes some of the entries in the colourmap. -// Unfortunately these messages are often sent one at a time, so we delay the -// settings taking effect by 100ms. This is because recalculating the internal -// translation table can be expensive. + void DesktopWindow::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs) { - for (int i = 0; i < nColours; i++) - colourMap.set(firstColour+i, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); - - if (!Fl::has_timeout(handleColourMap, this)) - Fl::add_timeout(0.100, handleColourMap, this); + viewport->setColourMapEntries(firstColour, nColours, rgbs); } @@ -148,370 +97,11 @@ void DesktopWindow::setColourMapEntries(int firstColour, int nColours, void DesktopWindow::updateWindow() { - Rect r; - - Fl::remove_timeout(handleUpdateTimeout, this); - - r = damage.get_bounding_rect(); - Fl_Window::damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height()); - - damage.clear(); + viewport->updateWindow(); } -void DesktopWindow::draw() -{ - int X, Y, W, H; - - int pixel_bytes, stride_bytes; - const uchar *buf_start; - - // Check what actually needs updating - fl_clip_box(0, 0, w(), h(), X, Y, W, H); - if ((W == 0) || (H == 0)) - return; - - pixel_bytes = frameBuffer->getPF().bpp/8; - stride_bytes = pixel_bytes * frameBuffer->getStride(); - buf_start = frameBuffer->data + - pixel_bytes * X + - stride_bytes * Y; - - // FIXME: Check how efficient this thing really is - fl_draw_image(buf_start, X, Y, W, H, pixel_bytes, stride_bytes); -} - - -int DesktopWindow::handle(int event) -{ - int buttonMask, wheelMask; - DownMap::const_iterator iter; - - switch (event) { - case FL_PUSH: - case FL_RELEASE: - case FL_DRAG: - case FL_MOVE: - case FL_MOUSEWHEEL: - buttonMask = 0; - if (Fl::event_button1()) - buttonMask |= 1; - if (Fl::event_button2()) - buttonMask |= 2; - if (Fl::event_button3()) - buttonMask |= 4; - - if (event == FL_MOUSEWHEEL) { - if (Fl::event_dy() < 0) - wheelMask = 8; - else - wheelMask = 16; - - // A quick press of the wheel "button", followed by a immediate - // release below - handlePointerEvent(Point(Fl::event_x(), Fl::event_y()), - buttonMask | wheelMask); - } - - handlePointerEvent(Point(Fl::event_x(), Fl::event_y()), buttonMask); - return 1; - - case FL_FOCUS: - // Yes, we would like some focus please! - return 1; - - case FL_UNFOCUS: - // Release all keys that were pressed as that generally makes most - // sense (e.g. Alt+Tab where we only see the Alt press) - for (iter = downKeySym.begin();iter != downKeySym.end();++iter) - cc->writer()->keyEvent(iter->second, false); - downKeySym.clear(); - return 1; - - case FL_KEYDOWN: - handleKeyEvent(Fl::event_key(), Fl::event_compose_symbol(), true); - return 1; - - case FL_KEYUP: - handleKeyEvent(Fl::event_key(), Fl::event_compose_symbol(), false); - return 1; - } - - return Fl_Window::handle(event); -} - - -void DesktopWindow::handleUpdateTimeout(void *data) -{ - DesktopWindow *self = (DesktopWindow *)data; - - assert(self); - - self->updateWindow(); -} - - -void DesktopWindow::handleColourMap(void *data) -{ - DesktopWindow *self = (DesktopWindow *)data; - - assert(self); - - if (self->pixelTrans != NULL) - self->pixelTrans->setColourMapEntries(0, 0); - - self->Fl_Window::damage(FL_DAMAGE_ALL); -} - void DesktopWindow::handleClose(Fl_Widget *wnd, void *data) { exit_vncviewer(); } - - -void DesktopWindow::handlePointerEvent(const rfb::Point& pos, int buttonMask) -{ - if (!viewOnly) { - if (pointerEventInterval == 0 || buttonMask != lastButtonMask) { - cc->writer()->pointerEvent(pos, buttonMask); - } else { - if (!Fl::has_timeout(handlePointerTimeout, this)) - Fl::add_timeout((double)pointerEventInterval/1000.0, - handlePointerTimeout, this); - } - lastPointerPos = pos; - lastButtonMask = buttonMask; - } -} - - -void DesktopWindow::handlePointerTimeout(void *data) -{ - DesktopWindow *self = (DesktopWindow *)data; - - assert(self); - - self->cc->writer()->pointerEvent(self->lastPointerPos, self->lastButtonMask); -} - - -rdr::U32 DesktopWindow::translateKeyEvent(int keyCode, const char *keyText) -{ - unsigned ucs; - - // First check for function keys - if ((keyCode > FL_F) && (keyCode <= FL_F_Last)) - return XK_F1 + (keyCode - FL_F - 1); - - // Numpad numbers - if ((keyCode >= (FL_KP + '0')) && (keyCode <= (FL_KP + '9'))) - return XK_KP_0 + (keyCode - (FL_KP + '0')); - - // Then other special keys - switch (keyCode) { - case FL_BackSpace: - return XK_BackSpace; - case FL_Tab: - return XK_Tab; - case FL_Enter: - return XK_Return; - case FL_Pause: - return XK_Pause; - case FL_Scroll_Lock: - return XK_Scroll_Lock; - case FL_Escape: - return XK_Escape; - case FL_Home: - return XK_Home; - case FL_Left: - return XK_Left; - case FL_Up: - return XK_Up; - case FL_Right: - return XK_Right; - case FL_Down: - return XK_Down; - case FL_Page_Up: - return XK_Page_Up; - case FL_Page_Down: - return XK_Page_Down; - case FL_End: - return XK_End; - case FL_Print: - return XK_Print; - case FL_Insert: - return XK_Insert; - case FL_Menu: - return XK_Menu; - case FL_Help: - return XK_Help; - case FL_Num_Lock: - return XK_Num_Lock; - case FL_Shift_L: - return XK_Shift_L; - case FL_Shift_R: - return XK_Shift_R; - case FL_Control_L: - return XK_Control_L; - case FL_Control_R: - return XK_Control_R; - case FL_Caps_Lock: - return XK_Caps_Lock; - case FL_Meta_L: - return XK_Super_L; - case FL_Meta_R: - return XK_Super_R; - case FL_Alt_L: - return XK_Alt_L; - case FL_Alt_R: - return XK_Alt_R; - case FL_Delete: - return XK_Delete; - case FL_KP_Enter: - return XK_KP_Enter; - case FL_KP + '=': - return XK_KP_Equal; - case FL_KP + '*': - return XK_KP_Multiply; - case FL_KP + '+': - return XK_KP_Add; - case FL_KP + ',': - return XK_KP_Separator; - case FL_KP + '-': - return XK_KP_Subtract; - case FL_KP + '.': - return XK_KP_Decimal; - case FL_KP + '/': - return XK_KP_Divide; - case XK_ISO_Level3_Shift: - // FLTK tends to let this one leak through on X11... - return XK_ISO_Level3_Shift; - } - - // Ctrl and Cmd tend to fudge input handling, so we need to cheat here - if (Fl::event_state() & (FL_COMMAND | FL_CTRL)) { -#ifdef WIN32 - BYTE keystate[256]; - WCHAR wbuf[8]; - int ret; - - static char buf[32]; - - switch (fl_msg.message) { - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - // Most buttons end up here when Ctrl is pressed. Here we can pretend - // that Ctrl isn't pressed, and do a character lookup. - GetKeyboardState(keystate); - keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; - - ret = ToUnicode(fl_msg.wParam, 0, keystate, wbuf, sizeof(wbuf)/sizeof(wbuf[0]), 0); - if (ret != 0) { - // -1 means one dead character - ret = abs(ret); - wbuf[ret] = 0x0000; - - if (fl_utf8fromwc(buf, sizeof(buf), wbuf, ret) >= sizeof(buf)) { - vlog.error(_("Out of buffer space whilst converting key event")); - return XK_VoidSymbol; - } - - keyText = buf; - } - break; - case WM_CHAR: - case WM_SYSCHAR: - // Windows doesn't seem to have any sanity when it comes to control - // characters. We assume that Ctrl-A through Ctrl-Z range maps to - // the VK_A through VK_Z keys, and just let the rest fall through. - if ((fl_msg.wParam < 0x01) || (fl_msg.wParam > 0x1a)) - break; - - // Pretend that Ctrl isn't pressed, and do a character lookup. - GetKeyboardState(keystate); - keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; - - // Ctrl-A is 0x01 and VK_A is 0x41, so add 0x40 for the conversion - ret = ToUnicode(fl_msg.wParam + 0x40, 0, keystate, wbuf, sizeof(wbuf)/sizeof(wbuf[0]), 0); - if (ret != 0) { - // -1 means one dead character - ret = abs(ret); - wbuf[ret] = 0x0000; - - if (fl_utf8fromwc(buf, sizeof(buf), wbuf, ret) >= sizeof(buf)) { - vlog.error(_("Out of buffer space whilst converting key event")); - return XK_VoidSymbol; - } - - keyText = buf; - } - break; - default: - // Not sure how we ended up here. Do nothing... - break; - } -#elif defined(__APPLE__) - keyText = osx_event_string(); -#else - char buf[16]; - KeySym sym; - - XLookupString((XKeyEvent*)fl_xevent, buf, sizeof(buf), &sym, NULL); - - return sym; -#endif - } - - // Unknown special key? - if (keyText[0] == '\0') { - vlog.error(_("Unknown FLTK key code %d (0x%04x)"), keyCode, keyCode); - return XK_VoidSymbol; - } - - // Look up the symbol the key produces and translate that from Unicode - // to a X11 keysym. - if (fl_utf_nb_char((const unsigned char*)keyText, strlen(keyText)) != 1) { - vlog.error(_("Multiple characters given for key code %d (0x%04x): '%s'"), - keyCode, keyCode, keyText); - return XK_VoidSymbol; - } - - ucs = fl_utf8decode(keyText, NULL, NULL); - return ucs2keysym(ucs); -} - - -void DesktopWindow::handleKeyEvent(int keyCode, const char *keyText, bool down) -{ - rdr::U32 keySym; - - if (viewOnly) - return; - - // Because of the way keyboards work, we cannot expect to have the same - // symbol on release as when pressed. This breaks the VNC protocol however, - // so we need to keep track of what keysym a key _code_ generated on press - // and send the same on release. - if (!down) { - DownMap::iterator iter; - - iter = downKeySym.find(keyCode); - if (iter == downKeySym.end()) { - vlog.error(_("Unexpected release of FLTK key code %d (0x%04x)"), keyCode, keyCode); - return; - } - - cc->writer()->keyEvent(iter->second, false); - - downKeySym.erase(iter); - - return; - } - - keySym = translateKeyEvent(keyCode, keyText); - if (keySym == XK_VoidSymbol) - return; - - downKeySym[keyCode] = keySym; - cc->writer()->keyEvent(keySym, down); -} diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h index bf54c211..39fb9e7b 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -26,10 +26,8 @@ #include <FL/Fl_Window.H> #include <rfb/Rect.h> -#include <rfb/Region.h> -#include <rfb/Timer.h> -#include <rfb/PixelBuffer.h> -#include <rfb/PixelTransformer.h> + +#include "Viewport.h" class CConn; @@ -58,68 +56,20 @@ public: void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); void fillRect(const rfb::Rect& r, rfb::Pixel pix) { - if (pixelTrans) { - rfb::Pixel pix2; - pixelTrans->translatePixels(&pix, &pix2, 1); - pix = pix2; - } - - frameBuffer->fillRect(r, pix); - damageRect(r); + viewport->fillRect(r, pix); } void imageRect(const rfb::Rect& r, void* pixels) { - if (pixelTrans) - pixelTrans->translateRect(pixels, r.width(), - rfb::Rect(0, 0, r.width(), r.height()), - frameBuffer->data, frameBuffer->getStride(), - r.tl); - else - frameBuffer->imageRect(r, pixels); - damageRect(r); + viewport->imageRect(r, pixels); } void copyRect(const rfb::Rect& r, int srcX, int srcY) { - frameBuffer->copyRect(r, rfb::Point(r.tl.x-srcX, r.tl.y-srcY)); - damageRect(r); + viewport->copyRect(r, srcX, srcY); } - // Fl_Window callback methods - void draw(); - int handle(int event); - private: - - void damageRect(const rfb::Rect& r) { - damage.assign_union(rfb::Region(r)); - if (!Fl::has_timeout(handleUpdateTimeout, this)) - Fl::add_timeout(0.100, handleUpdateTimeout, this); - }; - - static void handleUpdateTimeout(void *data); - static void handleColourMap(void *data); - static void handleClose(Fl_Widget *wnd, void *data); - void handlePointerEvent(const rfb::Point& pos, int buttonMask); - static void handlePointerTimeout(void *data); - - rdr::U32 translateKeyEvent(int keyCode, const char *keyText); - void handleKeyEvent(int keyCode, const char *keyText, bool down); - private: - CConn* cc; - - rfb::ManagedPixelBuffer* frameBuffer; - - rfb::PixelTransformer *pixelTrans; - rfb::SimpleColourMap colourMap; - - rfb::Region damage; - - rfb::Point lastPointerPos; - int lastButtonMask; - - typedef std::map<int, rdr::U32> DownMap; - DownMap downKeySym; + Viewport *viewport; }; #endif diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx new file mode 100644 index 00000000..6525b7c6 --- /dev/null +++ b/vncviewer/Viewport.cxx @@ -0,0 +1,489 @@ +/* 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 <assert.h> +#include <stdio.h> +#include <string.h> + +#include <FL/fl_draw.H> + +#include <rfb/CMsgWriter.h> +#include <rfb/LogWriter.h> + +// FLTK can pull in the X11 headers on some systems +#ifndef XK_VoidSymbol +#define XK_MISCELLANY +#define XK_XKB_KEYS +#include <rfb/keysymdef.h> +#endif + +#include "Viewport.h" +#include "CConn.h" +#include "i18n.h" +#include "parameters.h" +#include "keysym2ucs.h" + +// FLTK STR #2599 must be fixed for proper dead keys support +#ifndef HAVE_FLTK_DEAD_KEYS +#define event_compose_symbol event_text +#endif + +using namespace rfb; + +#ifdef __APPLE__ +extern "C" const char *osx_event_string(void); +#endif + +extern void exit_vncviewer(); + +static rfb::LogWriter vlog("Viewport"); + +Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_) + : Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL), pixelTrans(NULL), + lastPointerPos(0, 0), lastButtonMask(0) +{ + frameBuffer = new ManagedPixelBuffer(getPreferredPF(), w, h); + assert(frameBuffer); + + setServerPF(serverPF); +} + + +Viewport::~Viewport() +{ + // Unregister all timeouts in case they get a change tro trigger + // again later when this object is already gone. + Fl::remove_timeout(handleUpdateTimeout, this); + Fl::remove_timeout(handleColourMap, this); + Fl::remove_timeout(handlePointerTimeout, this); + + delete frameBuffer; + + if (pixelTrans) + delete pixelTrans; +} + + +void Viewport::setServerPF(const rfb::PixelFormat& pf) +{ + if (pixelTrans) + delete pixelTrans; + pixelTrans = NULL; + + if (pf.equal(getPreferredPF())) + return; + + pixelTrans = new PixelTransformer(); + pixelTrans->init(pf, &colourMap, getPreferredPF()); +} + + +const rfb::PixelFormat &Viewport::getPreferredPF() +{ + static PixelFormat prefPF(32, 24, false, true, 255, 255, 255, 0, 8, 16); + + return prefPF; +} + + +// setColourMapEntries() changes some of the entries in the colourmap. +// Unfortunately these messages are often sent one at a time, so we delay the +// settings taking effect by 100ms. This is because recalculating the internal +// translation table can be expensive. +void Viewport::setColourMapEntries(int firstColour, int nColours, + rdr::U16* rgbs) +{ + for (int i = 0; i < nColours; i++) + colourMap.set(firstColour+i, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); + + if (!Fl::has_timeout(handleColourMap, this)) + Fl::add_timeout(0.100, handleColourMap, this); +} + + +// Copy the areas of the framebuffer that have been changed (damaged) +// to the displayed window. + +void Viewport::updateWindow() +{ + Rect r; + + Fl::remove_timeout(handleUpdateTimeout, this); + + r = damage.get_bounding_rect(); + Fl_Widget::damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height()); + + damage.clear(); +} + + +void Viewport::draw() +{ + int X, Y, W, H; + + int pixel_bytes, stride_bytes; + const uchar *buf_start; + + // Check what actually needs updating + fl_clip_box(0, 0, w(), h(), X, Y, W, H); + if ((W == 0) || (H == 0)) + return; + + pixel_bytes = frameBuffer->getPF().bpp/8; + stride_bytes = pixel_bytes * frameBuffer->getStride(); + buf_start = frameBuffer->data + + pixel_bytes * X + + stride_bytes * Y; + + // FIXME: Check how efficient this thing really is + fl_draw_image(buf_start, X, Y, W, H, pixel_bytes, stride_bytes); +} + + +int Viewport::handle(int event) +{ + int buttonMask, wheelMask; + DownMap::const_iterator iter; + + switch (event) { + case FL_ENTER: + // Yes, we would like some pointer events please! + return 1; + case FL_PUSH: + case FL_RELEASE: + case FL_DRAG: + case FL_MOVE: + case FL_MOUSEWHEEL: + buttonMask = 0; + if (Fl::event_button1()) + buttonMask |= 1; + if (Fl::event_button2()) + buttonMask |= 2; + if (Fl::event_button3()) + buttonMask |= 4; + + if (event == FL_MOUSEWHEEL) { + if (Fl::event_dy() < 0) + wheelMask = 8; + else + wheelMask = 16; + + // A quick press of the wheel "button", followed by a immediate + // release below + handlePointerEvent(Point(Fl::event_x(), Fl::event_y()), + buttonMask | wheelMask); + } + + handlePointerEvent(Point(Fl::event_x(), Fl::event_y()), buttonMask); + return 1; + + case FL_FOCUS: + // Yes, we would like some focus please! + return 1; + + case FL_UNFOCUS: + // Release all keys that were pressed as that generally makes most + // sense (e.g. Alt+Tab where we only see the Alt press) + for (iter = downKeySym.begin();iter != downKeySym.end();++iter) + cc->writer()->keyEvent(iter->second, false); + downKeySym.clear(); + return 1; + + case FL_KEYDOWN: + handleKeyEvent(Fl::event_key(), Fl::event_compose_symbol(), true); + return 1; + + case FL_KEYUP: + handleKeyEvent(Fl::event_key(), Fl::event_compose_symbol(), false); + return 1; + } + + return Fl_Widget::handle(event); +} + + +void Viewport::handleUpdateTimeout(void *data) +{ + Viewport *self = (Viewport *)data; + + assert(self); + + self->updateWindow(); +} + + +void Viewport::handleColourMap(void *data) +{ + Viewport *self = (Viewport *)data; + + assert(self); + + if (self->pixelTrans != NULL) + self->pixelTrans->setColourMapEntries(0, 0); + + self->Fl_Widget::damage(FL_DAMAGE_ALL); +} + + +void Viewport::handlePointerEvent(const rfb::Point& pos, int buttonMask) +{ + if (!viewOnly) { + if (pointerEventInterval == 0 || buttonMask != lastButtonMask) { + cc->writer()->pointerEvent(pos, buttonMask); + } else { + if (!Fl::has_timeout(handlePointerTimeout, this)) + Fl::add_timeout((double)pointerEventInterval/1000.0, + handlePointerTimeout, this); + } + lastPointerPos = pos; + lastButtonMask = buttonMask; + } +} + + +void Viewport::handlePointerTimeout(void *data) +{ + Viewport *self = (Viewport *)data; + + assert(self); + + self->cc->writer()->pointerEvent(self->lastPointerPos, self->lastButtonMask); +} + + +rdr::U32 Viewport::translateKeyEvent(int keyCode, const char *keyText) +{ + unsigned ucs; + + // First check for function keys + if ((keyCode > FL_F) && (keyCode <= FL_F_Last)) + return XK_F1 + (keyCode - FL_F - 1); + + // Numpad numbers + if ((keyCode >= (FL_KP + '0')) && (keyCode <= (FL_KP + '9'))) + return XK_KP_0 + (keyCode - (FL_KP + '0')); + + // Then other special keys + switch (keyCode) { + case FL_BackSpace: + return XK_BackSpace; + case FL_Tab: + return XK_Tab; + case FL_Enter: + return XK_Return; + case FL_Pause: + return XK_Pause; + case FL_Scroll_Lock: + return XK_Scroll_Lock; + case FL_Escape: + return XK_Escape; + case FL_Home: + return XK_Home; + case FL_Left: + return XK_Left; + case FL_Up: + return XK_Up; + case FL_Right: + return XK_Right; + case FL_Down: + return XK_Down; + case FL_Page_Up: + return XK_Page_Up; + case FL_Page_Down: + return XK_Page_Down; + case FL_End: + return XK_End; + case FL_Print: + return XK_Print; + case FL_Insert: + return XK_Insert; + case FL_Menu: + return XK_Menu; + case FL_Help: + return XK_Help; + case FL_Num_Lock: + return XK_Num_Lock; + case FL_Shift_L: + return XK_Shift_L; + case FL_Shift_R: + return XK_Shift_R; + case FL_Control_L: + return XK_Control_L; + case FL_Control_R: + return XK_Control_R; + case FL_Caps_Lock: + return XK_Caps_Lock; + case FL_Meta_L: + return XK_Super_L; + case FL_Meta_R: + return XK_Super_R; + case FL_Alt_L: + return XK_Alt_L; + case FL_Alt_R: + return XK_Alt_R; + case FL_Delete: + return XK_Delete; + case FL_KP_Enter: + return XK_KP_Enter; + case FL_KP + '=': + return XK_KP_Equal; + case FL_KP + '*': + return XK_KP_Multiply; + case FL_KP + '+': + return XK_KP_Add; + case FL_KP + ',': + return XK_KP_Separator; + case FL_KP + '-': + return XK_KP_Subtract; + case FL_KP + '.': + return XK_KP_Decimal; + case FL_KP + '/': + return XK_KP_Divide; + case XK_ISO_Level3_Shift: + // FLTK tends to let this one leak through on X11... + return XK_ISO_Level3_Shift; + } + + // Ctrl and Cmd tend to fudge input handling, so we need to cheat here + if (Fl::event_state() & (FL_COMMAND | FL_CTRL)) { +#ifdef WIN32 + BYTE keystate[256]; + WCHAR wbuf[8]; + int ret; + + static char buf[32]; + + switch (fl_msg.message) { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + // Most buttons end up here when Ctrl is pressed. Here we can pretend + // that Ctrl isn't pressed, and do a character lookup. + GetKeyboardState(keystate); + keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; + + ret = ToUnicode(fl_msg.wParam, 0, keystate, wbuf, sizeof(wbuf)/sizeof(wbuf[0]), 0); + if (ret != 0) { + // -1 means one dead character + ret = abs(ret); + wbuf[ret] = 0x0000; + + if (fl_utf8fromwc(buf, sizeof(buf), wbuf, ret) >= sizeof(buf)) { + vlog.error(_("Out of buffer space whilst converting key event")); + return XK_VoidSymbol; + } + + keyText = buf; + } + break; + case WM_CHAR: + case WM_SYSCHAR: + // Windows doesn't seem to have any sanity when it comes to control + // characters. We assume that Ctrl-A through Ctrl-Z range maps to + // the VK_A through VK_Z keys, and just let the rest fall through. + if ((fl_msg.wParam < 0x01) || (fl_msg.wParam > 0x1a)) + break; + + // Pretend that Ctrl isn't pressed, and do a character lookup. + GetKeyboardState(keystate); + keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; + + // Ctrl-A is 0x01 and VK_A is 0x41, so add 0x40 for the conversion + ret = ToUnicode(fl_msg.wParam + 0x40, 0, keystate, wbuf, sizeof(wbuf)/sizeof(wbuf[0]), 0); + if (ret != 0) { + // -1 means one dead character + ret = abs(ret); + wbuf[ret] = 0x0000; + + if (fl_utf8fromwc(buf, sizeof(buf), wbuf, ret) >= sizeof(buf)) { + vlog.error(_("Out of buffer space whilst converting key event")); + return XK_VoidSymbol; + } + + keyText = buf; + } + break; + default: + // Not sure how we ended up here. Do nothing... + break; + } +#elif defined(__APPLE__) + keyText = osx_event_string(); +#else + char buf[16]; + KeySym sym; + + XLookupString((XKeyEvent*)fl_xevent, buf, sizeof(buf), &sym, NULL); + + return sym; +#endif + } + + // Unknown special key? + if (keyText[0] == '\0') { + vlog.error(_("Unknown FLTK key code %d (0x%04x)"), keyCode, keyCode); + return XK_VoidSymbol; + } + + // Look up the symbol the key produces and translate that from Unicode + // to a X11 keysym. + if (fl_utf_nb_char((const unsigned char*)keyText, strlen(keyText)) != 1) { + vlog.error(_("Multiple characters given for key code %d (0x%04x): '%s'"), + keyCode, keyCode, keyText); + return XK_VoidSymbol; + } + + ucs = fl_utf8decode(keyText, NULL, NULL); + return ucs2keysym(ucs); +} + + +void Viewport::handleKeyEvent(int keyCode, const char *keyText, bool down) +{ + rdr::U32 keySym; + + if (viewOnly) + return; + + // Because of the way keyboards work, we cannot expect to have the same + // symbol on release as when pressed. This breaks the VNC protocol however, + // so we need to keep track of what keysym a key _code_ generated on press + // and send the same on release. + if (!down) { + DownMap::iterator iter; + + iter = downKeySym.find(keyCode); + if (iter == downKeySym.end()) { + vlog.error(_("Unexpected release of FLTK key code %d (0x%04x)"), keyCode, keyCode); + return; + } + + cc->writer()->keyEvent(iter->second, false); + + downKeySym.erase(iter); + + return; + } + + keySym = translateKeyEvent(keyCode, keyText); + if (keySym == XK_VoidSymbol) + return; + + downKeySym[keyCode] = keySym; + cc->writer()->keyEvent(keySym, down); +} diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h new file mode 100644 index 00000000..d8a584ae --- /dev/null +++ b/vncviewer/Viewport.h @@ -0,0 +1,118 @@ +/* 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 __VIEWPORT_H__ +#define __VIEWPORT_H__ + +#include <map> + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> + +#include <rfb/Rect.h> +#include <rfb/Region.h> +#include <rfb/Timer.h> +#include <rfb/PixelBuffer.h> +#include <rfb/PixelTransformer.h> + +class CConn; + +class Viewport : public Fl_Widget { +public: + + Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_); + ~Viewport(); + + // PixelFormat of incoming write operations + void setServerPF(const rfb::PixelFormat& pf); + // Most efficient format (from Viewport's point of view) + const rfb::PixelFormat &getPreferredPF(); + + // Flush updates to screen + void updateWindow(); + + // Methods forwarded from CConn + void setName(const char *name); + + void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); + + void fillRect(const rfb::Rect& r, rfb::Pixel pix) { + if (pixelTrans) { + rfb::Pixel pix2; + pixelTrans->translatePixels(&pix, &pix2, 1); + pix = pix2; + } + + frameBuffer->fillRect(r, pix); + damageRect(r); + } + void imageRect(const rfb::Rect& r, void* pixels) { + if (pixelTrans) + pixelTrans->translateRect(pixels, r.width(), + rfb::Rect(0, 0, r.width(), r.height()), + frameBuffer->data, frameBuffer->getStride(), + r.tl); + else + frameBuffer->imageRect(r, pixels); + damageRect(r); + } + void copyRect(const rfb::Rect& r, int srcX, int srcY) { + frameBuffer->copyRect(r, rfb::Point(r.tl.x-srcX, r.tl.y-srcY)); + damageRect(r); + } + + // Fl_Widget callback methods + void draw(); + int handle(int event); + +private: + + void damageRect(const rfb::Rect& r) { + damage.assign_union(rfb::Region(r)); + if (!Fl::has_timeout(handleUpdateTimeout, this)) + Fl::add_timeout(0.100, handleUpdateTimeout, this); + }; + + static void handleUpdateTimeout(void *data); + static void handleColourMap(void *data); + + void handlePointerEvent(const rfb::Point& pos, int buttonMask); + static void handlePointerTimeout(void *data); + + rdr::U32 translateKeyEvent(int keyCode, const char *keyText); + void handleKeyEvent(int keyCode, const char *keyText, bool down); + +private: + CConn* cc; + + rfb::ManagedPixelBuffer* frameBuffer; + + rfb::PixelTransformer *pixelTrans; + rfb::SimpleColourMap colourMap; + + rfb::Region damage; + + rfb::Point lastPointerPos; + int lastButtonMask; + + typedef std::map<int, rdr::U32> DownMap; + DownMap downKeySym; +}; + +#endif |