blob: 1693ab5530244e8cf50a5d3866aef235594393f5 [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
Peter Åstrandc359f362011-08-23 12:04:46 +000020#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
Pierre Ossman5156d5e2011-03-09 09:42:34 +000024#include <assert.h>
25#include <stdio.h>
26#include <string.h>
27
Pierre Ossman5156d5e2011-03-09 09:42:34 +000028#include <rfb/LogWriter.h>
Pierre Ossmanff473402012-07-04 11:27:47 +000029#include <rfb/CMsgWriter.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000030
31#include "DesktopWindow.h"
Pierre Ossman407a5c32011-05-26 14:48:29 +000032#include "OptionsDialog.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000033#include "i18n.h"
Pierre Ossman407a5c32011-05-26 14:48:29 +000034#include "parameters.h"
Pierre Ossman39ceb502011-07-12 15:54:25 +000035#include "vncviewer.h"
Pierre Ossmanff473402012-07-04 11:27:47 +000036#include "CConn.h"
Pierre Ossman407a5c32011-05-26 14:48:29 +000037
DRCb65bb932011-06-24 03:17:00 +000038#include <FL/Fl_Scroll.H>
39#include <FL/x.H>
40
Pierre Ossman407a5c32011-05-26 14:48:29 +000041#ifdef WIN32
42#include "win32.h"
43#endif
44
45#ifdef __APPLE__
46#include "cocoa.h"
47#endif
Pierre Ossman89f868a2011-04-11 11:59:31 +000048
Pierre Ossman5156d5e2011-03-09 09:42:34 +000049using namespace rfb;
50
Pierre Ossman5156d5e2011-03-09 09:42:34 +000051static rfb::LogWriter vlog("DesktopWindow");
52
53DesktopWindow::DesktopWindow(int w, int h, const char *name,
54 const rfb::PixelFormat& serverPF,
55 CConn* cc_)
Pierre Ossman4c4b66f2012-08-23 14:53:36 +000056 : Fl_Window(w, h), cc(cc_), firstUpdate(true),
57 delayedFullscreen(false), delayedDesktopSize(false)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000058{
Pierre Ossman4ae229f2011-04-15 12:58:31 +000059 Fl_Scroll *scroll = new Fl_Scroll(0, 0, w, h);
60 scroll->color(FL_BLACK);
61
62 // Automatically adjust the scroll box to the window
63 resizable(scroll);
64
Pierre Ossmanff473402012-07-04 11:27:47 +000065 viewport = new Viewport(w, h, serverPF, cc);
Pierre Ossmand50b3d12011-04-15 07:46:56 +000066
Pierre Ossman4ae229f2011-04-15 12:58:31 +000067 scroll->end();
68
Pierre Ossman5156d5e2011-03-09 09:42:34 +000069 callback(handleClose, this);
70
71 setName(name);
72
Pierre Ossman407a5c32011-05-26 14:48:29 +000073 OptionsDialog::addCallback(handleOptions, this);
74
Pierre Ossmana4f0f182011-06-14 13:36:57 +000075 // Hack. See below...
76 Fl::event_dispatch(&fltkHandle);
77
Pierre Ossman63ca58e2011-05-26 14:59:32 +000078#ifdef HAVE_FLTK_FULLSCREEN
Pierre Ossman4c4b66f2012-08-23 14:53:36 +000079 if (fullScreen) {
80 // Hack: Window managers seem to be rather crappy at respecting
81 // fullscreen hints on initial windows. So on X11 we'll have to
82 // wait until after we've been mapped.
83#if defined(WIN32) || defined(__APPLE__)
Pierre Ossmanaae38912012-07-13 11:22:55 +000084 fullscreen_on();
Pierre Ossman4c4b66f2012-08-23 14:53:36 +000085#else
86 delayedFullscreen = true;
87#endif
88 } else
Pierre Ossman63ca58e2011-05-26 14:59:32 +000089#endif
Peter Åstrandd62482e2011-08-02 08:33:27 +000090 {
Peter Åstrandb182a9e2012-08-27 07:28:08 +000091
92 int geom_x = 0, geom_y = 0;
93 if (geometry.hasBeenSet()) {
94 int matched;
95 matched = sscanf(geometry.getValueStr(), "+%d+%d", &geom_x, &geom_y);
96 if (matched == 2) {
97 force_position(1);
98 } else {
99 int geom_w, geom_h;
100 matched = sscanf(geometry.getValueStr(), "%dx%d+%d+%d", &geom_w, &geom_h, &geom_x, &geom_y);
101 switch (matched) {
102 case 4:
103 force_position(1);
104 /* fall through */
105 case 2:
106 w = geom_w;
107 h = geom_h;
108 default:
109 vlog.error("Invalid geometry specified!");
110 }
111 }
112 }
113
Peter Åstrandd62482e2011-08-02 08:33:27 +0000114 // If we are creating a window which is equal to the size on the
115 // screen on X11, many WMs will treat this as a legacy fullscreen
116 // request. This is not what we want. Besides, it doesn't really
117 // make sense to try to create a window which is larger than the
118 // available work space.
Peter Åstrandb182a9e2012-08-27 07:28:08 +0000119 w = __rfbmin(w, Fl::w());
120 h = __rfbmin(h, Fl::h());
121
122 if (force_position()) {
123 resize(geom_x, geom_y, w, h);
124 } else {
125 size(w, h);
126 }
Peter Åstrandd62482e2011-08-02 08:33:27 +0000127 }
Pierre Ossman63ca58e2011-05-26 14:59:32 +0000128
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000129 show();
Pierre Ossman3f6c4d02011-04-15 14:09:09 +0000130
Peter Åstrand49b11572012-08-01 08:09:09 +0000131 // Unfortunately, current FLTK does not allow us to set the
132 // maximized property before showing the window. See STR #2083 and
133 // STR #2178
134 if (maximize) {
135 maximizeWindow();
136 }
137
Pierre Ossman3f6c4d02011-04-15 14:09:09 +0000138 // The window manager might give us an initial window size that is different
139 // than the one we requested, and in those cases we need to manually adjust
140 // the scroll widget for things to behave sanely.
141 if ((w != this->w()) || (h != this->h()))
142 scroll->size(this->w(), this->h());
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000143
144#ifdef HAVE_FLTK_FULLSCREEN
145 if (delayedFullscreen) {
146 // Hack: Fullscreen requests may be ignored, so we need a timeout for
147 // when we should stop waiting. We also really need to wait for the
148 // resize, which can come after the fullscreen event.
149 Fl::add_timeout(0.5, handleFullscreenTimeout, this);
150 fullscreen_on();
151 }
152#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000153}
154
155
156DesktopWindow::~DesktopWindow()
157{
Pierre Ossman407a5c32011-05-26 14:48:29 +0000158 // Unregister all timeouts in case they get a change tro trigger
159 // again later when this object is already gone.
160 Fl::remove_timeout(handleGrab, this);
Pierre Ossmanff473402012-07-04 11:27:47 +0000161 Fl::remove_timeout(handleResizeTimeout, this);
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000162 Fl::remove_timeout(handleFullscreenTimeout, this);
Pierre Ossman407a5c32011-05-26 14:48:29 +0000163
164 OptionsDialog::removeCallback(handleOptions);
165
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000166 // FLTK automatically deletes all child widgets, so we shouldn't touch
167 // them ourselves here
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000168}
169
170
171void DesktopWindow::setServerPF(const rfb::PixelFormat& pf)
172{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000173 viewport->setServerPF(pf);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000174}
175
176
177const rfb::PixelFormat &DesktopWindow::getPreferredPF()
178{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000179 return viewport->getPreferredPF();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000180}
181
182
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000183void DesktopWindow::setName(const char *name)
184{
185 CharArray windowNameStr;
186 windowNameStr.replaceBuf(new char[256]);
187
Pierre Ossman27820ba2011-09-30 12:54:24 +0000188 snprintf(windowNameStr.buf, 256, "%.240s - TigerVNC", name);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000189
190 copy_label(windowNameStr.buf);
191}
192
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000193
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000194void DesktopWindow::setColourMapEntries(int firstColour, int nColours,
195 rdr::U16* rgbs)
196{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000197 viewport->setColourMapEntries(firstColour, nColours, rgbs);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000198}
199
200
201// Copy the areas of the framebuffer that have been changed (damaged)
202// to the displayed window.
203
204void DesktopWindow::updateWindow()
205{
Pierre Ossmanff473402012-07-04 11:27:47 +0000206 if (firstUpdate) {
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000207 if (cc->cp.supportsSetDesktopSize) {
208 // Hack: Wait until we're in the proper mode and position until
209 // resizing things, otherwise we might send the wrong thing.
210 if (delayedFullscreen)
211 delayedDesktopSize = true;
212 else
213 handleDesktopSize();
214 }
Pierre Ossmanff473402012-07-04 11:27:47 +0000215 firstUpdate = false;
216 }
217
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000218 viewport->updateWindow();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000219}
220
221
Pierre Ossman6455d852011-06-01 09:33:00 +0000222void DesktopWindow::resizeFramebuffer(int new_w, int new_h)
223{
224 if ((new_w == viewport->w()) && (new_h == viewport->h()))
225 return;
226
Pierre Ossman6455d852011-06-01 09:33:00 +0000227 // If we're letting the viewport match the window perfectly, then
228 // keep things that way for the new size, otherwise just keep things
229 // like they are.
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000230#ifdef HAVE_FLTK_FULLSCREEN
231 if (!fullscreen_active()) {
232#endif
Pierre Ossman29e4a162011-11-21 14:03:31 +0000233 if ((w() == viewport->w()) && (h() == viewport->h()))
234 size(new_w, new_h);
235 else {
236 // Make sure the window isn't too big. We do this manually because
237 // we have to disable the window size restriction (and it isn't
238 // entirely trustworthy to begin with).
Pierre Ossman6455d852011-06-01 09:33:00 +0000239 if ((w() > new_w) || (h() > new_h))
240 size(__rfbmin(w(), new_w), __rfbmin(h(), new_h));
Pierre Ossman29e4a162011-11-21 14:03:31 +0000241 }
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000242#ifdef HAVE_FLTK_FULLSCREEN
243 }
244#endif
Pierre Ossman6455d852011-06-01 09:33:00 +0000245
246 viewport->size(new_w, new_h);
247
248 // We might not resize the main window, so we need to manually call this
249 // to make sure the viewport is centered.
250 repositionViewport();
251
Pierre Ossman6455d852011-06-01 09:33:00 +0000252 // repositionViewport() makes sure the scroll widget notices any changes
253 // in position, but it might be just the size that changes so we also
254 // need a poke here as well.
255 redraw();
256}
257
258
Pierre Ossman835b4ef2011-06-08 17:02:36 +0000259void DesktopWindow::setCursor(int width, int height, const Point& hotspot,
260 void* data, void* mask)
261{
262 viewport->setCursor(width, height, hotspot, data, mask);
263}
264
265
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000266void DesktopWindow::resize(int x, int y, int w, int h)
267{
Pierre Ossman9f32e3c2012-08-23 14:40:52 +0000268 bool resizing;
269
270 if ((this->w() != w) || (this->h() != h))
271 resizing = true;
272 else
273 resizing = false;
274
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000275 Fl_Window::resize(x, y, w, h);
Pierre Ossman6455d852011-06-01 09:33:00 +0000276
Pierre Ossman9f32e3c2012-08-23 14:40:52 +0000277 if (resizing) {
278 // Try to get the remote size to match our window size, provided
279 // the following conditions are true:
280 //
281 // a) The user has this feature turned on
282 // b) The server supports it
283 // c) We're not still waiting for a chance to handle DesktopSize
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000284 // d) We're not still waiting for startup fullscreen to kick in
Pierre Ossman9f32e3c2012-08-23 14:40:52 +0000285 //
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000286 if (not firstUpdate and not delayedFullscreen and
287 ::remoteResize and cc->cp.supportsSetDesktopSize) {
Pierre Ossman9f32e3c2012-08-23 14:40:52 +0000288 // We delay updating the remote desktop as we tend to get a flood
289 // of resize events as the user is dragging the window.
290 Fl::remove_timeout(handleResizeTimeout, this);
291 Fl::add_timeout(0.5, handleResizeTimeout, this);
292 }
Pierre Ossmanff473402012-07-04 11:27:47 +0000293
Pierre Ossman9f32e3c2012-08-23 14:40:52 +0000294 // Deal with some scrolling corner cases
295 repositionViewport();
296 }
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000297}
298
299
Pierre Ossman407a5c32011-05-26 14:48:29 +0000300int DesktopWindow::handle(int event)
301{
302 switch (event) {
303#ifdef HAVE_FLTK_FULLSCREEN
Pierre Ossman407a5c32011-05-26 14:48:29 +0000304 case FL_FULLSCREEN:
Pierre Ossman29e4a162011-11-21 14:03:31 +0000305 fullScreen.setParam(fullscreen_active());
306
Pierre Ossman407a5c32011-05-26 14:48:29 +0000307 if (!fullscreenSystemKeys)
308 break;
309
310 if (fullscreen_active())
311 grabKeyboard();
312 else
313 ungrabKeyboard();
314
315 break;
Pierre Ossman407a5c32011-05-26 14:48:29 +0000316#endif
317 case FL_SHORTCUT:
318 // Sometimes the focus gets out of whack and we fall through to the
319 // shortcut dispatching. Try to make things sane again...
320 if (Fl::focus() == NULL) {
321 take_focus();
322 Fl::handle(FL_KEYDOWN, this);
323 }
324 return 1;
325 }
326
327 return Fl_Window::handle(event);
328}
329
330
Pierre Ossmana4f0f182011-06-14 13:36:57 +0000331int DesktopWindow::fltkHandle(int event, Fl_Window *win)
332{
333 int ret;
334
335 ret = Fl::handle_(event, win);
336
337#ifdef HAVE_FLTK_FULLSCREEN
338 // This is hackish and the result of the dodgy focus handling in FLTK.
339 // The basic problem is that FLTK's view of focus and the system's tend
340 // to differ, and as a result we do not see all the FL_FOCUS events we
341 // need. Fortunately we can grab them here...
342
343 DesktopWindow *dw = dynamic_cast<DesktopWindow*>(win);
344
345 if (dw && fullscreenSystemKeys) {
346 switch (event) {
347 case FL_FOCUS:
348 // FIXME: We reassert the keyboard grabbing on focus as FLTK there are
349 // some issues we need to work around:
350 // a) Fl::grab(0) on X11 will release the keyboard grab for us.
351 // b) Gaining focus on the system level causes FLTK to switch
352 // window level on OS X.
353 if (dw->fullscreen_active())
354 dw->grabKeyboard();
355 break;
356
357 case FL_UNFOCUS:
358 // FIXME: We need to relinquish control when the entire window loses
359 // focus as it is very tied to this specific window on some
360 // platforms and we want to be able to open subwindows.
361 dw->ungrabKeyboard();
362 break;
363 }
364 }
365#endif
366
367 return ret;
368}
369
370
Pierre Ossmanaae38912012-07-13 11:22:55 +0000371void DesktopWindow::fullscreen_on()
372{
373#ifdef HAVE_FLTK_FULLSCREEN
374#ifdef HAVE_FLTK_FULLSCREEN_SCREENS
375 if (not fullScreenAllMonitors)
376 fullscreen_screens(-1, -1, -1, -1);
377 else {
378 int top, bottom, left, right;
379 int top_y, bottom_y, left_x, right_x;
380
381 int sx, sy, sw, sh;
382
383 top = bottom = left = right = 0;
384
385 Fl::screen_xywh(sx, sy, sw, sh, 0);
386 top_y = sy;
387 bottom_y = sy + sh;
388 left_x = sx;
389 right_x = sx + sw;
390
391 for (int i = 1;i < Fl::screen_count();i++) {
392 Fl::screen_xywh(sx, sy, sw, sh, i);
393 if (sy < top_y) {
394 top = i;
395 top_y = sy;
396 }
397 if ((sy + sh) > bottom_y) {
398 bottom = i;
399 bottom_y = sy + sh;
400 }
401 if (sx < left_x) {
402 left = i;
403 left_x = sx;
404 }
405 if ((sx + sw) > right_x) {
406 right = i;
407 right_x = sx + sw;
408 }
409 }
410
411 fullscreen_screens(top, bottom, left, right);
412 }
413#endif // HAVE_FLTK_FULLSCREEN_SCREENS
414
415 fullscreen();
416#endif // HAVE_FLTK_FULLSCREEN
417}
418
Pierre Ossman407a5c32011-05-26 14:48:29 +0000419void DesktopWindow::grabKeyboard()
420{
421 // Grabbing the keyboard is fairly safe as FLTK reroutes events to the
422 // correct widget regardless of which low level window got the system
423 // event.
424
425 // FIXME: Push this stuff into FLTK.
426
427#if defined(WIN32)
428 int ret;
429
430 ret = win32_enable_lowlevel_keyboard(fl_xid(this));
431 if (ret != 0)
432 vlog.error(_("Failure grabbing keyboard"));
433#elif defined(__APPLE__)
434 int ret;
435
436 ret = cocoa_capture_display(this);
437 if (ret != 0)
438 vlog.error(_("Failure grabbing keyboard"));
439#else
440 int ret;
441
442 ret = XGrabKeyboard(fl_display, fl_xid(this), True,
Peter Åstrandf52860b2011-07-18 07:42:16 +0000443 GrabModeAsync, GrabModeAsync, CurrentTime);
Pierre Ossman407a5c32011-05-26 14:48:29 +0000444 if (ret) {
445 if (ret == AlreadyGrabbed) {
446 // It seems like we can race with the WM in some cases.
447 // Try again in a bit.
448 if (!Fl::has_timeout(handleGrab, this))
449 Fl::add_timeout(0.500, handleGrab, this);
450 } else {
451 vlog.error(_("Failure grabbing keyboard"));
452 }
453 }
Pierre Ossman53fd5442011-11-17 10:19:19 +0000454
455 // We also need to grab the pointer as some WMs like to grab buttons
456 // combined with modifies (e.g. Alt+Button0 in metacity).
457 ret = XGrabPointer(fl_display, fl_xid(this), True,
458 ButtonPressMask|ButtonReleaseMask|
459 ButtonMotionMask|PointerMotionMask,
460 GrabModeAsync, GrabModeAsync,
461 None, None, CurrentTime);
462 if (ret)
463 vlog.error(_("Failure grabbing mouse"));
Pierre Ossman407a5c32011-05-26 14:48:29 +0000464#endif
465}
466
467
468void DesktopWindow::ungrabKeyboard()
469{
470 Fl::remove_timeout(handleGrab, this);
471
472#if defined(WIN32)
473 win32_disable_lowlevel_keyboard(fl_xid(this));
474#elif defined(__APPLE__)
475 cocoa_release_display(this);
476#else
477 // FLTK has a grab so lets not mess with it
478 if (Fl::grab())
479 return;
480
Pierre Ossman53fd5442011-11-17 10:19:19 +0000481 XUngrabPointer(fl_display, fl_event_time);
Pierre Ossman407a5c32011-05-26 14:48:29 +0000482 XUngrabKeyboard(fl_display, fl_event_time);
483#endif
484}
485
486
487void DesktopWindow::handleGrab(void *data)
488{
489 DesktopWindow *self = (DesktopWindow*)data;
490
491 assert(self);
492
493#ifdef HAVE_FLTK_FULLSCREEN
494 if (!fullscreenSystemKeys)
495 return;
496 if (!self->fullscreen_active())
497 return;
498
499 self->grabKeyboard();
500#endif
501}
502
503
Peter Åstrand49b11572012-08-01 08:09:09 +0000504#define _NET_WM_STATE_ADD 1 /* add/set property */
505void DesktopWindow::maximizeWindow()
506{
507#if defined(WIN32)
508 WINDOWPLACEMENT wp;
509 wp.length = sizeof(WINDOWPLACEMENT);
510 GetWindowPlacement(fl_xid(this), &wp);
511 wp.showCmd = SW_MAXIMIZE;
512 SetWindowPlacement(fl_xid(this), &wp);
513#elif defined(__APPLE__)
514 /* OS X is somewhat strange and does not really have a concept of a
515 maximized window, so we can simply resize the window to the workarea */
516 int X, Y, W, H;
517 Fl::screen_work_area(X, Y, W, H, this->x(), this->y());
518 size(W, H);
519#else
520 // X11
521 fl_open_display();
522 Atom net_wm_state = XInternAtom (fl_display, "_NET_WM_STATE", 0);
523 Atom net_wm_state_maximized_vert = XInternAtom (fl_display, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
524 Atom net_wm_state_maximized_horz = XInternAtom (fl_display, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
525
526 XEvent e;
527 e.xany.type = ClientMessage;
528 e.xany.window = fl_xid(this);
529 e.xclient.message_type = net_wm_state;
530 e.xclient.format = 32;
531 e.xclient.data.l[0] = _NET_WM_STATE_ADD;
532 e.xclient.data.l[1] = net_wm_state_maximized_vert;
533 e.xclient.data.l[2] = net_wm_state_maximized_horz;
534 e.xclient.data.l[3] = 0;
535 e.xclient.data.l[4] = 0;
536 XSendEvent(fl_display, RootWindow(fl_display, fl_screen), 0, SubstructureNotifyMask | SubstructureRedirectMask, &e);
537#endif
538}
539
540
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000541void DesktopWindow::handleDesktopSize()
542{
543 int width, height;
544
545 if (sscanf(desktopSize.getValueStr(), "%dx%d", &width, &height) != 2)
546 return;
547
548 remoteResize(width, height);
549}
550
551
Pierre Ossmanff473402012-07-04 11:27:47 +0000552void DesktopWindow::handleResizeTimeout(void *data)
553{
554 DesktopWindow *self = (DesktopWindow *)data;
555
556 assert(self);
557
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000558 self->remoteResize(self->w(), self->h());
Pierre Ossmanff473402012-07-04 11:27:47 +0000559}
560
561
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000562void DesktopWindow::remoteResize(int width, int height)
Pierre Ossmanff473402012-07-04 11:27:47 +0000563{
Pierre Ossmanff473402012-07-04 11:27:47 +0000564 ScreenSet layout;
Pierre Ossmanaae38912012-07-13 11:22:55 +0000565 ScreenSet::iterator iter;
Pierre Ossmanff473402012-07-04 11:27:47 +0000566
Pierre Ossmanaae38912012-07-13 11:22:55 +0000567#ifdef HAVE_FLTK_FULLSCREEN
Pierre Ossmanf44f6c02012-07-20 12:39:27 +0000568 if (!fullscreen_active() || (width > w()) || (height > h())) {
Pierre Ossmanaae38912012-07-13 11:22:55 +0000569#endif
Pierre Ossmanf44f6c02012-07-20 12:39:27 +0000570 // In windowed mode (or the framebuffer is so large that we need
571 // to scroll) we just report a single virtual screen that covers
572 // the entire framebuffer.
Pierre Ossmanff473402012-07-04 11:27:47 +0000573
Pierre Ossmanaae38912012-07-13 11:22:55 +0000574 layout = cc->cp.screenLayout;
Pierre Ossmanff473402012-07-04 11:27:47 +0000575
Pierre Ossmanaae38912012-07-13 11:22:55 +0000576 // Not sure why we have no screens, but adding a new one should be
577 // safe as there is nothing to conflict with...
578 if (layout.num_screens() == 0)
579 layout.add_screen(rfb::Screen());
580 else if (layout.num_screens() != 1) {
581 // More than one screen. Remove all but the first (which we
582 // assume is the "primary").
Pierre Ossmanff473402012-07-04 11:27:47 +0000583
Pierre Ossmanaae38912012-07-13 11:22:55 +0000584 while (true) {
585 iter = layout.begin();
586 ++iter;
Pierre Ossmanff473402012-07-04 11:27:47 +0000587
Pierre Ossmanaae38912012-07-13 11:22:55 +0000588 if (iter == layout.end())
589 break;
590
591 layout.remove_screen(iter->id);
592 }
593 }
594
595 // Resize the remaining single screen to the complete framebuffer
596 layout.begin()->dimensions.tl.x = 0;
597 layout.begin()->dimensions.tl.y = 0;
598 layout.begin()->dimensions.br.x = width;
599 layout.begin()->dimensions.br.y = height;
600#ifdef HAVE_FLTK_FULLSCREEN
601 } else {
602 int i;
603 rdr::U32 id;
604 int sx, sy, sw, sh;
Pierre Ossmanf44f6c02012-07-20 12:39:27 +0000605 Rect viewport_rect, screen_rect;
Pierre Ossmanaae38912012-07-13 11:22:55 +0000606
607 // In full screen we report all screens that are fully covered.
608
Pierre Ossmanf44f6c02012-07-20 12:39:27 +0000609 viewport_rect.setXYWH(x() + (w() - width)/2, y() + (h() - height)/2,
610 width, height);
Pierre Ossman93d2d922012-07-20 12:32:52 +0000611
Pierre Ossmanaae38912012-07-13 11:22:55 +0000612 // If we can find a matching screen in the existing set, we use
613 // that, otherwise we create a brand new screen.
614 //
615 // FIXME: We should really track screens better so we can handle
616 // a resized one.
617 //
618 for (i = 0;i < Fl::screen_count();i++) {
619 Fl::screen_xywh(sx, sy, sw, sh, i);
620
Pierre Ossman93d2d922012-07-20 12:32:52 +0000621 // Check that the screen is fully inside the framebuffer
622 screen_rect.setXYWH(sx, sy, sw, sh);
Pierre Ossmanf44f6c02012-07-20 12:39:27 +0000623 if (!screen_rect.enclosed_by(viewport_rect))
Pierre Ossman93d2d922012-07-20 12:32:52 +0000624 continue;
625
Pierre Ossmanf44f6c02012-07-20 12:39:27 +0000626 // Adjust the coordinates so they are relative to our viewport
627 sx -= viewport_rect.tl.x;
628 sy -= viewport_rect.tl.y;
629
Pierre Ossmanaae38912012-07-13 11:22:55 +0000630 // Look for perfectly matching existing screen...
631 for (iter = cc->cp.screenLayout.begin();
632 iter != cc->cp.screenLayout.end(); ++iter) {
633 if ((iter->dimensions.tl.x == sx) &&
634 (iter->dimensions.tl.y == sy) &&
635 (iter->dimensions.width() == sw) &&
636 (iter->dimensions.height() == sh))
637 break;
638 }
639
640 // Found it?
641 if (iter != cc->cp.screenLayout.end()) {
642 layout.add_screen(*iter);
643 continue;
644 }
645
646 // Need to add a new one, which means we need to find an unused id
647 while (true) {
648 id = rand();
649 for (iter = cc->cp.screenLayout.begin();
650 iter != cc->cp.screenLayout.end(); ++iter) {
651 if (iter->id == id)
652 break;
653 }
654
655 if (iter == cc->cp.screenLayout.end())
656 break;
657 }
658
659 layout.add_screen(rfb::Screen(id, sx, sy, sw, sh, 0));
Pierre Ossmanff473402012-07-04 11:27:47 +0000660 }
Pierre Ossman72b4adf2012-07-20 14:11:26 +0000661
662 // If the viewport doesn't match a physical screen, then we might
663 // end up with no screens in the layout. Add a fake one...
664 if (layout.num_screens() == 0)
665 layout.add_screen(rfb::Screen(0, 0, 0, width, height, 0));
Pierre Ossmanff473402012-07-04 11:27:47 +0000666 }
Pierre Ossmanaae38912012-07-13 11:22:55 +0000667#endif
Pierre Ossmanff473402012-07-04 11:27:47 +0000668
669 // Do we actually change anything?
670 if ((width == cc->cp.width) &&
671 (height == cc->cp.height) &&
672 (layout == cc->cp.screenLayout))
673 return;
674
Pierre Ossmanaae38912012-07-13 11:22:55 +0000675 vlog.debug("Requesting framebuffer resize from %dx%d to %dx%d (%d screens)",
676 cc->cp.width, cc->cp.height, width, height, layout.num_screens());
677
678 if (!layout.validate(width, height)) {
679 vlog.error("Invalid screen layout computed for resize request!");
Pierre Ossmanaae38912012-07-13 11:22:55 +0000680 return;
681 }
Pierre Ossmanff473402012-07-04 11:27:47 +0000682
683 cc->writer()->writeSetDesktopSize(width, height, layout);
684}
685
686
Pierre Ossman6455d852011-06-01 09:33:00 +0000687void DesktopWindow::repositionViewport()
688{
689 int new_x, new_y;
690
691 // Deal with some scrolling corner cases:
692 //
693 // a) If the window is larger then the viewport, center the viewport.
694 // b) If the window is smaller than the viewport, make sure there is
695 // no wasted space on the sides.
696 //
697 // FIXME: Doesn't compensate for scroll widget size properly.
698
699 new_x = viewport->x();
700 new_y = viewport->y();
701
702 if (w() > viewport->w())
703 new_x = (w() - viewport->w()) / 2;
704 else {
705 if (viewport->x() > 0)
706 new_x = 0;
707 else if (w() > (viewport->x() + viewport->w()))
708 new_x = w() - viewport->w();
709 }
710
711 // Same thing for y axis
712 if (h() > viewport->h())
713 new_y = (h() - viewport->h()) / 2;
714 else {
715 if (viewport->y() > 0)
716 new_y = 0;
717 else if (h() > (viewport->y() + viewport->h()))
718 new_y = h() - viewport->h();
719 }
720
721 if ((new_x != viewport->x()) || (new_y != viewport->y())) {
722 viewport->position(new_x, new_y);
723
724 // The scroll widget does not notice when you move around child widgets,
725 // so redraw everything to make sure things update.
726 redraw();
727 }
728}
729
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000730void DesktopWindow::handleClose(Fl_Widget *wnd, void *data)
731{
732 exit_vncviewer();
733}
Pierre Ossman407a5c32011-05-26 14:48:29 +0000734
735
736void DesktopWindow::handleOptions(void *data)
737{
738 DesktopWindow *self = (DesktopWindow*)data;
739
740#ifdef HAVE_FLTK_FULLSCREEN
741 if (self->fullscreen_active() && fullscreenSystemKeys)
742 self->grabKeyboard();
743 else
744 self->ungrabKeyboard();
Pierre Ossman91911642011-05-26 14:57:51 +0000745
Pierre Ossmanff473402012-07-04 11:27:47 +0000746 if (fullScreen && !self->fullscreen_active())
Pierre Ossmanaae38912012-07-13 11:22:55 +0000747 self->fullscreen_on();
Pierre Ossmanff473402012-07-04 11:27:47 +0000748 else if (!fullScreen && self->fullscreen_active())
Pierre Ossman91911642011-05-26 14:57:51 +0000749 self->fullscreen_off();
Pierre Ossman407a5c32011-05-26 14:48:29 +0000750#endif
751}
Pierre Ossman4c4b66f2012-08-23 14:53:36 +0000752
753void DesktopWindow::handleFullscreenTimeout(void *data)
754{
755 DesktopWindow *self = (DesktopWindow *)data;
756
757 assert(self);
758
759 self->delayedFullscreen = false;
760
761 if (self->delayedDesktopSize) {
762 self->handleDesktopSize();
763 self->delayedDesktopSize = false;
764 }
765}