blob: a7373ffcbe96884c03534140ba5fc453139557b6 [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
Pierre Ossman5156d5e2011-03-09 09:42:34 +000024#include <rfb/LogWriter.h>
25
26#include "DesktopWindow.h"
Pierre Ossman407a5c32011-05-26 14:48:29 +000027#include "OptionsDialog.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000028#include "i18n.h"
Pierre Ossman407a5c32011-05-26 14:48:29 +000029#include "parameters.h"
Pierre Ossman39ceb502011-07-12 15:54:25 +000030#include "vncviewer.h"
Pierre Ossman407a5c32011-05-26 14:48:29 +000031
DRCb65bb932011-06-24 03:17:00 +000032#include <FL/Fl_Scroll.H>
33#include <FL/x.H>
34
Pierre Ossman407a5c32011-05-26 14:48:29 +000035#ifdef WIN32
36#include "win32.h"
37#endif
38
39#ifdef __APPLE__
40#include "cocoa.h"
41#endif
Pierre Ossman89f868a2011-04-11 11:59:31 +000042
Pierre Ossman5156d5e2011-03-09 09:42:34 +000043using namespace rfb;
44
Pierre Ossman5156d5e2011-03-09 09:42:34 +000045static rfb::LogWriter vlog("DesktopWindow");
46
47DesktopWindow::DesktopWindow(int w, int h, const char *name,
48 const rfb::PixelFormat& serverPF,
49 CConn* cc_)
Pierre Ossmand50b3d12011-04-15 07:46:56 +000050 : Fl_Window(w, h)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000051{
Pierre Ossman4ae229f2011-04-15 12:58:31 +000052 // Allow resize
Pierre Ossman343e2e02011-04-15 13:00:12 +000053 size_range(100, 100, w, h);
Pierre Ossman4ae229f2011-04-15 12:58:31 +000054
55 Fl_Scroll *scroll = new Fl_Scroll(0, 0, w, h);
56 scroll->color(FL_BLACK);
57
58 // Automatically adjust the scroll box to the window
59 resizable(scroll);
60
Pierre Ossmand50b3d12011-04-15 07:46:56 +000061 viewport = new Viewport(w, h, serverPF, cc_);
62
Pierre Ossman4ae229f2011-04-15 12:58:31 +000063 scroll->end();
64
Pierre Ossman5156d5e2011-03-09 09:42:34 +000065 callback(handleClose, this);
66
67 setName(name);
68
Pierre Ossman407a5c32011-05-26 14:48:29 +000069 OptionsDialog::addCallback(handleOptions, this);
70
Pierre Ossmana4f0f182011-06-14 13:36:57 +000071 // Hack. See below...
72 Fl::event_dispatch(&fltkHandle);
73
Pierre Ossman63ca58e2011-05-26 14:59:32 +000074#ifdef HAVE_FLTK_FULLSCREEN
Peter Åstrand17067b12011-07-18 07:45:14 +000075 if (fullScreen) {
76 // See comment in DesktopWindow::handleOptions
77 size_range(100, 100, 0, 0);
Pierre Ossman63ca58e2011-05-26 14:59:32 +000078 fullscreen();
Peter Åstrand17067b12011-07-18 07:45:14 +000079 }
Pierre Ossman63ca58e2011-05-26 14:59:32 +000080#endif
81
Pierre Ossman5156d5e2011-03-09 09:42:34 +000082 show();
Pierre Ossman3f6c4d02011-04-15 14:09:09 +000083
84 // The window manager might give us an initial window size that is different
85 // than the one we requested, and in those cases we need to manually adjust
86 // the scroll widget for things to behave sanely.
87 if ((w != this->w()) || (h != this->h()))
88 scroll->size(this->w(), this->h());
Pierre Ossman5156d5e2011-03-09 09:42:34 +000089}
90
91
92DesktopWindow::~DesktopWindow()
93{
Pierre Ossman407a5c32011-05-26 14:48:29 +000094 // Unregister all timeouts in case they get a change tro trigger
95 // again later when this object is already gone.
96 Fl::remove_timeout(handleGrab, this);
97
98 OptionsDialog::removeCallback(handleOptions);
99
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000100 // FLTK automatically deletes all child widgets, so we shouldn't touch
101 // them ourselves here
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000102}
103
104
105void DesktopWindow::setServerPF(const rfb::PixelFormat& pf)
106{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000107 viewport->setServerPF(pf);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000108}
109
110
111const rfb::PixelFormat &DesktopWindow::getPreferredPF()
112{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000113 return viewport->getPreferredPF();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000114}
115
116
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000117void DesktopWindow::setName(const char *name)
118{
119 CharArray windowNameStr;
120 windowNameStr.replaceBuf(new char[256]);
121
122 snprintf(windowNameStr.buf, 256, _("TigerVNC: %.240s"), name);
123
124 copy_label(windowNameStr.buf);
125}
126
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000127
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000128void DesktopWindow::setColourMapEntries(int firstColour, int nColours,
129 rdr::U16* rgbs)
130{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000131 viewport->setColourMapEntries(firstColour, nColours, rgbs);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000132}
133
134
135// Copy the areas of the framebuffer that have been changed (damaged)
136// to the displayed window.
137
138void DesktopWindow::updateWindow()
139{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000140 viewport->updateWindow();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000141}
142
143
Pierre Ossman6455d852011-06-01 09:33:00 +0000144void DesktopWindow::resizeFramebuffer(int new_w, int new_h)
145{
146 if ((new_w == viewport->w()) && (new_h == viewport->h()))
147 return;
148
149 // Turn off size limitations for a bit while we juggle things around
150 size_range(100, 100, 0, 0);
151
152 // If we're letting the viewport match the window perfectly, then
153 // keep things that way for the new size, otherwise just keep things
154 // like they are.
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000155#ifdef HAVE_FLTK_FULLSCREEN
156 if (!fullscreen_active()) {
157#endif
Pierre Ossman6455d852011-06-01 09:33:00 +0000158 if ((w() == viewport->w()) && (h() == viewport->h()))
159 size(new_w, new_h);
160 else {
Pierre Ossman6455d852011-06-01 09:33:00 +0000161 // Make sure the window isn't too big
162 if ((w() > new_w) || (h() > new_h))
163 size(__rfbmin(w(), new_w), __rfbmin(h(), new_h));
Pierre Ossman6455d852011-06-01 09:33:00 +0000164 }
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000165#ifdef HAVE_FLTK_FULLSCREEN
166 }
167#endif
Pierre Ossman6455d852011-06-01 09:33:00 +0000168
169 viewport->size(new_w, new_h);
170
171 // We might not resize the main window, so we need to manually call this
172 // to make sure the viewport is centered.
173 repositionViewport();
174
175 // Update allowed resize range
Peter Åstrandadf5e252011-07-19 09:28:39 +0000176#ifdef HAVE_FLTK_FULLSCREEN
177 if (!fullscreen_active())
178#endif
Pierre Ossman6455d852011-06-01 09:33:00 +0000179 size_range(100, 100, new_w, new_h);
180
181 // repositionViewport() makes sure the scroll widget notices any changes
182 // in position, but it might be just the size that changes so we also
183 // need a poke here as well.
184 redraw();
185}
186
187
Pierre Ossman835b4ef2011-06-08 17:02:36 +0000188void DesktopWindow::setCursor(int width, int height, const Point& hotspot,
189 void* data, void* mask)
190{
191 viewport->setCursor(width, height, hotspot, data, mask);
192}
193
194
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000195void DesktopWindow::resize(int x, int y, int w, int h)
196{
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000197 Fl_Window::resize(x, y, w, h);
Pierre Ossman6455d852011-06-01 09:33:00 +0000198
199 // Deal with some scrolling corner cases
200 repositionViewport();
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000201}
202
203
Pierre Ossman407a5c32011-05-26 14:48:29 +0000204int DesktopWindow::handle(int event)
205{
206 switch (event) {
207#ifdef HAVE_FLTK_FULLSCREEN
Pierre Ossman407a5c32011-05-26 14:48:29 +0000208 case FL_FULLSCREEN:
Peter Åstrand17067b12011-07-18 07:45:14 +0000209 if (event == FL_FULLSCREEN) {
Pierre Ossman105738c2011-05-26 14:57:25 +0000210 fullScreen.setParam(fullscreen_active());
Peter Åstrand17067b12011-07-18 07:45:14 +0000211 if (!fullscreen_active()) {
212 size_range(100, 100, viewport->w(), viewport->h());
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000213 size(viewport->w(), viewport->h());
Peter Åstrand17067b12011-07-18 07:45:14 +0000214 } else {
215 // We need to turn off the size limitations for proper
216 // fullscreen support, but in case fullscreen is activated via
217 // the WM, this is a bit of a problem. In practice, it seems to
218 // work to change the size limits after we have recieved the
219 // FL_FULLSCREEN event, at least with my Metacity.
220 size_range(100, 100, 0, 0);
221 }
222 }
Pierre Ossman1870ab12011-05-26 14:53:49 +0000223
Pierre Ossman407a5c32011-05-26 14:48:29 +0000224 if (!fullscreenSystemKeys)
225 break;
226
227 if (fullscreen_active())
228 grabKeyboard();
229 else
230 ungrabKeyboard();
231
232 break;
Pierre Ossman407a5c32011-05-26 14:48:29 +0000233#endif
234 case FL_SHORTCUT:
235 // Sometimes the focus gets out of whack and we fall through to the
236 // shortcut dispatching. Try to make things sane again...
237 if (Fl::focus() == NULL) {
238 take_focus();
239 Fl::handle(FL_KEYDOWN, this);
240 }
241 return 1;
242 }
243
244 return Fl_Window::handle(event);
245}
246
247
Pierre Ossmana4f0f182011-06-14 13:36:57 +0000248int DesktopWindow::fltkHandle(int event, Fl_Window *win)
249{
250 int ret;
251
252 ret = Fl::handle_(event, win);
253
254#ifdef HAVE_FLTK_FULLSCREEN
255 // This is hackish and the result of the dodgy focus handling in FLTK.
256 // The basic problem is that FLTK's view of focus and the system's tend
257 // to differ, and as a result we do not see all the FL_FOCUS events we
258 // need. Fortunately we can grab them here...
259
260 DesktopWindow *dw = dynamic_cast<DesktopWindow*>(win);
261
262 if (dw && fullscreenSystemKeys) {
263 switch (event) {
264 case FL_FOCUS:
265 // FIXME: We reassert the keyboard grabbing on focus as FLTK there are
266 // some issues we need to work around:
267 // a) Fl::grab(0) on X11 will release the keyboard grab for us.
268 // b) Gaining focus on the system level causes FLTK to switch
269 // window level on OS X.
270 if (dw->fullscreen_active())
271 dw->grabKeyboard();
272 break;
273
274 case FL_UNFOCUS:
275 // FIXME: We need to relinquish control when the entire window loses
276 // focus as it is very tied to this specific window on some
277 // platforms and we want to be able to open subwindows.
278 dw->ungrabKeyboard();
279 break;
280 }
281 }
282#endif
283
284 return ret;
285}
286
287
Pierre Ossman407a5c32011-05-26 14:48:29 +0000288void DesktopWindow::grabKeyboard()
289{
290 // Grabbing the keyboard is fairly safe as FLTK reroutes events to the
291 // correct widget regardless of which low level window got the system
292 // event.
293
294 // FIXME: Push this stuff into FLTK.
295
296#if defined(WIN32)
297 int ret;
298
299 ret = win32_enable_lowlevel_keyboard(fl_xid(this));
300 if (ret != 0)
301 vlog.error(_("Failure grabbing keyboard"));
302#elif defined(__APPLE__)
303 int ret;
304
305 ret = cocoa_capture_display(this);
306 if (ret != 0)
307 vlog.error(_("Failure grabbing keyboard"));
308#else
309 int ret;
310
311 ret = XGrabKeyboard(fl_display, fl_xid(this), True,
Peter Åstrandf52860b2011-07-18 07:42:16 +0000312 GrabModeAsync, GrabModeAsync, CurrentTime);
Pierre Ossman407a5c32011-05-26 14:48:29 +0000313 if (ret) {
314 if (ret == AlreadyGrabbed) {
315 // It seems like we can race with the WM in some cases.
316 // Try again in a bit.
317 if (!Fl::has_timeout(handleGrab, this))
318 Fl::add_timeout(0.500, handleGrab, this);
319 } else {
320 vlog.error(_("Failure grabbing keyboard"));
321 }
322 }
323#endif
324}
325
326
327void DesktopWindow::ungrabKeyboard()
328{
329 Fl::remove_timeout(handleGrab, this);
330
331#if defined(WIN32)
332 win32_disable_lowlevel_keyboard(fl_xid(this));
333#elif defined(__APPLE__)
334 cocoa_release_display(this);
335#else
336 // FLTK has a grab so lets not mess with it
337 if (Fl::grab())
338 return;
339
340 XUngrabKeyboard(fl_display, fl_event_time);
341#endif
342}
343
344
345void DesktopWindow::handleGrab(void *data)
346{
347 DesktopWindow *self = (DesktopWindow*)data;
348
349 assert(self);
350
351#ifdef HAVE_FLTK_FULLSCREEN
352 if (!fullscreenSystemKeys)
353 return;
354 if (!self->fullscreen_active())
355 return;
356
357 self->grabKeyboard();
358#endif
359}
360
361
Pierre Ossman6455d852011-06-01 09:33:00 +0000362void DesktopWindow::repositionViewport()
363{
364 int new_x, new_y;
365
366 // Deal with some scrolling corner cases:
367 //
368 // a) If the window is larger then the viewport, center the viewport.
369 // b) If the window is smaller than the viewport, make sure there is
370 // no wasted space on the sides.
371 //
372 // FIXME: Doesn't compensate for scroll widget size properly.
373
374 new_x = viewport->x();
375 new_y = viewport->y();
376
377 if (w() > viewport->w())
378 new_x = (w() - viewport->w()) / 2;
379 else {
380 if (viewport->x() > 0)
381 new_x = 0;
382 else if (w() > (viewport->x() + viewport->w()))
383 new_x = w() - viewport->w();
384 }
385
386 // Same thing for y axis
387 if (h() > viewport->h())
388 new_y = (h() - viewport->h()) / 2;
389 else {
390 if (viewport->y() > 0)
391 new_y = 0;
392 else if (h() > (viewport->y() + viewport->h()))
393 new_y = h() - viewport->h();
394 }
395
396 if ((new_x != viewport->x()) || (new_y != viewport->y())) {
397 viewport->position(new_x, new_y);
398
399 // The scroll widget does not notice when you move around child widgets,
400 // so redraw everything to make sure things update.
401 redraw();
402 }
403}
404
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000405void DesktopWindow::handleClose(Fl_Widget *wnd, void *data)
406{
407 exit_vncviewer();
408}
Pierre Ossman407a5c32011-05-26 14:48:29 +0000409
410
411void DesktopWindow::handleOptions(void *data)
412{
413 DesktopWindow *self = (DesktopWindow*)data;
414
415#ifdef HAVE_FLTK_FULLSCREEN
416 if (self->fullscreen_active() && fullscreenSystemKeys)
417 self->grabKeyboard();
418 else
419 self->ungrabKeyboard();
Pierre Ossman91911642011-05-26 14:57:51 +0000420
Peter Åstrand17067b12011-07-18 07:45:14 +0000421 if (fullScreen && !self->fullscreen_active()) {
422 // Some WMs (Metacity) apparently requires that the size limits
423 // are removed before fullscreen
424 self->size_range(100, 100, 0, 0);
Pierre Ossman91911642011-05-26 14:57:51 +0000425 self->fullscreen();
Peter Åstrand17067b12011-07-18 07:45:14 +0000426 } else if (!fullScreen && self->fullscreen_active())
Pierre Ossman91911642011-05-26 14:57:51 +0000427 self->fullscreen_off();
Pierre Ossman407a5c32011-05-26 14:48:29 +0000428#endif
429}