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/Viewport.cxx | |
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/Viewport.cxx')
-rw-r--r-- | vncviewer/Viewport.cxx | 489 |
1 files changed, 489 insertions, 0 deletions
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); +} |