blob: 113b88010165c154e7b31ce861456d6724db531c [file] [log] [blame]
Pierre Ossman5156d5e2011-03-09 09:42:34 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
3 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20#include <assert.h>
21#include <stdio.h>
22#include <string.h>
23
24#include <FL/fl_draw.H>
25
26#include <rfb/CMsgWriter.h>
27#include <rfb/LogWriter.h>
28
Pierre Ossmand014d052011-03-09 13:28:12 +000029// FLTK can pull in the X11 headers on some systems
30#ifndef XK_VoidSymbol
31#define XK_MISCELLANY
32#include <rfb/keysymdef.h>
33#endif
34
Pierre Ossman5156d5e2011-03-09 09:42:34 +000035#include "DesktopWindow.h"
36#include "CConn.h"
37#include "i18n.h"
38#include "parameters.h"
Pierre Ossmand014d052011-03-09 13:28:12 +000039#include "keysym2ucs.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000040
Pierre Ossman89f868a2011-04-11 11:59:31 +000041// FLTK STR #2599 must be fixed for proper dead keys support
42#ifndef HAVE_FLTK_DEAD_KEYS
43#define event_compose_symbol event_text
44#endif
45
Pierre Ossman5156d5e2011-03-09 09:42:34 +000046using namespace rfb;
47
48extern void exit_vncviewer();
49
50static rfb::LogWriter vlog("DesktopWindow");
51
52DesktopWindow::DesktopWindow(int w, int h, const char *name,
53 const rfb::PixelFormat& serverPF,
54 CConn* cc_)
Pierre Ossmanc266e5a2011-03-09 10:24:12 +000055 : Fl_Window(w, h), cc(cc_), frameBuffer(NULL), pixelTrans(NULL),
56 lastPointerPos(0, 0), lastButtonMask(0)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000057{
58 callback(handleClose, this);
59
60 setName(name);
61
62 frameBuffer = new ManagedPixelBuffer(getPreferredPF(), w, h);
63 assert(frameBuffer);
64
65 setServerPF(serverPF);
66
67 show();
68}
69
70
71DesktopWindow::~DesktopWindow()
72{
Pierre Ossman3d5d8a02011-03-09 11:53:08 +000073 // Unregister all timeouts in case they get a change tro trigger
74 // again later when this object is already gone.
75 Fl::remove_timeout(handleUpdateTimeout, this);
76 Fl::remove_timeout(handleColourMap, this);
77 Fl::remove_timeout(handlePointerTimeout, this);
78
Pierre Ossman5156d5e2011-03-09 09:42:34 +000079 delete frameBuffer;
80
81 if (pixelTrans)
82 delete pixelTrans;
83}
84
85
86void DesktopWindow::setServerPF(const rfb::PixelFormat& pf)
87{
88 if (pixelTrans)
89 delete pixelTrans;
90 pixelTrans = NULL;
91
92 if (pf.equal(getPreferredPF()))
93 return;
94
95 pixelTrans = new PixelTransformer();
96 pixelTrans->init(pf, &colourMap, getPreferredPF());
97}
98
99
100const rfb::PixelFormat &DesktopWindow::getPreferredPF()
101{
102 static PixelFormat prefPF(32, 24, false, true, 255, 255, 255, 0, 8, 16);
103
104 return prefPF;
105}
106
107
108// Cursor stuff
109
110void DesktopWindow::setCursor(int width, int height, const Point& hotspot,
111 void* data, void* mask)
112{
113}
114
115
116void DesktopWindow::setName(const char *name)
117{
118 CharArray windowNameStr;
119 windowNameStr.replaceBuf(new char[256]);
120
121 snprintf(windowNameStr.buf, 256, _("TigerVNC: %.240s"), name);
122
123 copy_label(windowNameStr.buf);
124}
125
126// setColourMapEntries() changes some of the entries in the colourmap.
127// Unfortunately these messages are often sent one at a time, so we delay the
128// settings taking effect by 100ms. This is because recalculating the internal
129// translation table can be expensive.
130void DesktopWindow::setColourMapEntries(int firstColour, int nColours,
131 rdr::U16* rgbs)
132{
133 for (int i = 0; i < nColours; i++)
134 colourMap.set(firstColour+i, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
135
136 if (!Fl::has_timeout(handleColourMap, this))
137 Fl::add_timeout(0.100, handleColourMap, this);
138}
139
140
141// Copy the areas of the framebuffer that have been changed (damaged)
142// to the displayed window.
143
144void DesktopWindow::updateWindow()
145{
146 Rect r;
147
148 Fl::remove_timeout(handleUpdateTimeout, this);
149
150 r = damage.get_bounding_rect();
151 Fl_Window::damage(FL_DAMAGE_USER1, r.tl.x, r.tl.y, r.width(), r.height());
152
153 damage.clear();
154}
155
156
157void DesktopWindow::draw()
158{
159 int X, Y, W, H;
160
161 int pixel_bytes, stride_bytes;
162 const uchar *buf_start;
163
164 // Check what actually needs updating
165 fl_clip_box(0, 0, w(), h(), X, Y, W, H);
166 if ((W == 0) || (H == 0))
167 return;
168
169 pixel_bytes = frameBuffer->getPF().bpp/8;
170 stride_bytes = pixel_bytes * frameBuffer->getStride();
171 buf_start = frameBuffer->data +
172 pixel_bytes * X +
173 stride_bytes * Y;
174
175 // FIXME: Check how efficient this thing really is
176 fl_draw_image(buf_start, X, Y, W, H, pixel_bytes, stride_bytes);
177}
178
179
Pierre Ossmanc266e5a2011-03-09 10:24:12 +0000180int DesktopWindow::handle(int event)
181{
182 int buttonMask, wheelMask;
183
184 switch (event) {
185 case FL_PUSH:
186 case FL_RELEASE:
187 case FL_DRAG:
188 case FL_MOVE:
189 case FL_MOUSEWHEEL:
190 buttonMask = 0;
191 if (Fl::event_button1())
192 buttonMask |= 1;
193 if (Fl::event_button2())
194 buttonMask |= 2;
195 if (Fl::event_button3())
196 buttonMask |= 4;
197
198 if (event == FL_MOUSEWHEEL) {
199 if (Fl::event_dy() < 0)
200 wheelMask = 8;
201 else
202 wheelMask = 16;
203
204 // A quick press of the wheel "button", followed by a immediate
205 // release below
206 handlePointerEvent(Point(Fl::event_x(), Fl::event_y()),
207 buttonMask | wheelMask);
208 }
209
210 handlePointerEvent(Point(Fl::event_x(), Fl::event_y()), buttonMask);
211 return 1;
Pierre Ossmand014d052011-03-09 13:28:12 +0000212
213 case FL_FOCUS:
214 // Yes, we would like some focus please!
215 return 1;
216
217 case FL_KEYDOWN:
Pierre Ossman89f868a2011-04-11 11:59:31 +0000218 handleKeyEvent(Fl::event_key(), Fl::event_compose_symbol(), true);
Pierre Ossmand014d052011-03-09 13:28:12 +0000219 return 1;
220
221 case FL_KEYUP:
Pierre Ossman89f868a2011-04-11 11:59:31 +0000222 handleKeyEvent(Fl::event_key(), Fl::event_compose_symbol(), false);
Pierre Ossmand014d052011-03-09 13:28:12 +0000223 return 1;
Pierre Ossmanc266e5a2011-03-09 10:24:12 +0000224 }
225
226 return Fl_Window::handle(event);
227}
228
229
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000230void DesktopWindow::handleUpdateTimeout(void *data)
231{
232 DesktopWindow *self = (DesktopWindow *)data;
233
234 assert(self);
235
236 self->updateWindow();
237}
238
239
240void DesktopWindow::handleColourMap(void *data)
241{
242 DesktopWindow *self = (DesktopWindow *)data;
243
244 assert(self);
245
246 if (self->pixelTrans != NULL)
247 self->pixelTrans->setColourMapEntries(0, 0);
248
249 self->Fl_Window::damage(FL_DAMAGE_ALL);
250}
251
252void DesktopWindow::handleClose(Fl_Widget *wnd, void *data)
253{
254 exit_vncviewer();
255}
Pierre Ossmanc266e5a2011-03-09 10:24:12 +0000256
257
258void DesktopWindow::handlePointerEvent(const rfb::Point& pos, int buttonMask)
259{
260 if (!viewOnly) {
261 if (pointerEventInterval == 0 || buttonMask != lastButtonMask) {
262 cc->writer()->pointerEvent(pos, buttonMask);
263 } else {
264 if (!Fl::has_timeout(handlePointerTimeout, this))
265 Fl::add_timeout((double)pointerEventInterval/1000.0,
266 handlePointerTimeout, this);
267 }
268 lastPointerPos = pos;
269 lastButtonMask = buttonMask;
270 }
271}
272
273
274void DesktopWindow::handlePointerTimeout(void *data)
275{
276 DesktopWindow *self = (DesktopWindow *)data;
277
278 assert(self);
279
Pierre Ossmane21ae3d2011-03-09 11:44:24 +0000280 self->cc->writer()->pointerEvent(self->lastPointerPos, self->lastButtonMask);
Pierre Ossmanc266e5a2011-03-09 10:24:12 +0000281}
Pierre Ossmand014d052011-03-09 13:28:12 +0000282
Pierre Ossman98486c12011-03-10 11:39:42 +0000283
284rdr::U32 DesktopWindow::translateKeyEvent(int keyCode, const char *keyText)
285{
286 unsigned ucs;
287
288 // First check for function keys
Pierre Ossman70f32462011-03-10 11:57:03 +0000289 if ((keyCode > FL_F) && (keyCode <= FL_F_Last))
290 return XK_F1 + (keyCode - FL_F - 1);
Pierre Ossman98486c12011-03-10 11:39:42 +0000291
Pierre Ossman381e5462011-03-10 11:56:17 +0000292 // Numpad numbers
293 if ((keyCode >= (FL_KP + '0')) && (keyCode <= (FL_KP + '9')))
294 return XK_KP_0 + (keyCode - (FL_KP + '0'));
295
Pierre Ossman98486c12011-03-10 11:39:42 +0000296 // Then other special keys
297 switch (keyCode) {
298 case FL_BackSpace:
299 return XK_BackSpace;
300 case FL_Tab:
301 return XK_Tab;
302 case FL_Enter:
303 return XK_Return;
304 case FL_Pause:
305 return XK_Pause;
306 case FL_Scroll_Lock:
307 return XK_Scroll_Lock;
308 case FL_Escape:
309 return XK_Escape;
310 case FL_Home:
311 return XK_Home;
312 case FL_Left:
313 return XK_Left;
314 case FL_Up:
315 return XK_Up;
316 case FL_Right:
317 return XK_Right;
318 case FL_Down:
319 return XK_Down;
320 case FL_Page_Up:
321 return XK_Page_Up;
322 case FL_Page_Down:
323 return XK_Page_Down;
324 case FL_End:
325 return XK_End;
326 case FL_Print:
327 return XK_Print;
328 case FL_Insert:
329 return XK_Insert;
330 case FL_Menu:
331 return XK_Menu;
332 case FL_Help:
333 return XK_Help;
334 case FL_Num_Lock:
335 return XK_Num_Lock;
336 case FL_Shift_L:
337 return XK_Shift_L;
338 case FL_Shift_R:
339 return XK_Shift_R;
340 case FL_Control_L:
341 return XK_Control_L;
342 case FL_Control_R:
343 return XK_Control_R;
344 case FL_Caps_Lock:
345 return XK_Caps_Lock;
346 case FL_Meta_L:
347 return XK_Super_L;
348 case FL_Meta_R:
349 return XK_Super_R;
350 case FL_Alt_L:
351 return XK_Alt_L;
352 case FL_Alt_R:
353 return XK_Alt_R;
354 case FL_Delete:
355 return XK_Delete;
Pierre Ossman381e5462011-03-10 11:56:17 +0000356 case FL_KP_Enter:
357 return XK_KP_Enter;
358 case FL_KP + '=':
359 return XK_KP_Equal;
360 case FL_KP + '*':
361 return XK_KP_Multiply;
362 case FL_KP + '+':
363 return XK_KP_Add;
364 case FL_KP + ',':
365 return XK_KP_Separator;
366 case FL_KP + '-':
367 return XK_KP_Subtract;
368 case FL_KP + '.':
369 return XK_KP_Decimal;
370 case FL_KP + '/':
371 return XK_KP_Divide;
Pierre Ossman98486c12011-03-10 11:39:42 +0000372 }
373
374 // Unknown special key?
375 if (keyText[0] == '\0') {
376 vlog.error(_("Unknown FLTK key code %d (0x%04x)"), keyCode, keyCode);
377 return XK_VoidSymbol;
378 }
379
380 // Look up the symbol the key produces and translate that from Unicode
381 // to a X11 keysym.
382 if (fl_utf_nb_char((const unsigned char*)keyText, strlen(keyText)) != 1) {
383 vlog.error(_("Multiple characters given for key code %d (0x%04x): '%s'"),
384 keyCode, keyCode, keyText);
385 return XK_VoidSymbol;
386 }
387
388 ucs = fl_utf8decode(keyText, NULL, NULL);
389 return ucs2keysym(ucs);
390}
391
392
Pierre Ossmand014d052011-03-09 13:28:12 +0000393void DesktopWindow::handleKeyEvent(int keyCode, const char *keyText, bool down)
394{
395 rdr::U32 keySym;
396
397 if (viewOnly)
398 return;
399
400 if (keyCode > 0xFFFF) {
401 vlog.error(_("Too large FLTK key code %d (0x%08x)"), keyCode, keyCode);
402 return;
403 }
404
405 // Because of the way keyboards work, we cannot expect to have the same
406 // symbol on release as when pressed. This breaks the VNC protocol however,
407 // so we need to keep track of what keysym a key _code_ generated on press
408 // and send the same on release.
409 if (!down) {
410 cc->writer()->keyEvent(downKeySym[keyCode], false);
411 return;
412 }
413
Pierre Ossman98486c12011-03-10 11:39:42 +0000414 keySym = translateKeyEvent(keyCode, keyText);
415 if (keySym == XK_VoidSymbol)
416 return;
Pierre Ossmand014d052011-03-09 13:28:12 +0000417
418 downKeySym[keyCode] = keySym;
419 cc->writer()->keyEvent(keySym, down);
420}