blob: 6ff631c7f87781ef1c9b78d9f5c05296f51ce404 [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 Åstrandd62482e2011-08-02 08:33:27 +000079 } else
Pierre Ossman63ca58e2011-05-26 14:59:32 +000080#endif
Peter Åstrandd62482e2011-08-02 08:33:27 +000081 {
82 // If we are creating a window which is equal to the size on the
83 // screen on X11, many WMs will treat this as a legacy fullscreen
84 // request. This is not what we want. Besides, it doesn't really
85 // make sense to try to create a window which is larger than the
86 // available work space.
87 size(__rfbmin(w, Fl::w()), __rfbmin(h, Fl::h()));
88 }
Pierre Ossman63ca58e2011-05-26 14:59:32 +000089
Pierre Ossman5156d5e2011-03-09 09:42:34 +000090 show();
Pierre Ossman3f6c4d02011-04-15 14:09:09 +000091
92 // The window manager might give us an initial window size that is different
93 // than the one we requested, and in those cases we need to manually adjust
94 // the scroll widget for things to behave sanely.
95 if ((w != this->w()) || (h != this->h()))
96 scroll->size(this->w(), this->h());
Pierre Ossman5156d5e2011-03-09 09:42:34 +000097}
98
99
100DesktopWindow::~DesktopWindow()
101{
Pierre Ossman407a5c32011-05-26 14:48:29 +0000102 // Unregister all timeouts in case they get a change tro trigger
103 // again later when this object is already gone.
104 Fl::remove_timeout(handleGrab, this);
105
106 OptionsDialog::removeCallback(handleOptions);
107
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000108 // FLTK automatically deletes all child widgets, so we shouldn't touch
109 // them ourselves here
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000110}
111
112
113void DesktopWindow::setServerPF(const rfb::PixelFormat& pf)
114{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000115 viewport->setServerPF(pf);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000116}
117
118
119const rfb::PixelFormat &DesktopWindow::getPreferredPF()
120{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000121 return viewport->getPreferredPF();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000122}
123
124
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000125void DesktopWindow::setName(const char *name)
126{
127 CharArray windowNameStr;
128 windowNameStr.replaceBuf(new char[256]);
129
130 snprintf(windowNameStr.buf, 256, _("TigerVNC: %.240s"), name);
131
132 copy_label(windowNameStr.buf);
133}
134
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000135
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000136void DesktopWindow::setColourMapEntries(int firstColour, int nColours,
137 rdr::U16* rgbs)
138{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000139 viewport->setColourMapEntries(firstColour, nColours, rgbs);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000140}
141
142
143// Copy the areas of the framebuffer that have been changed (damaged)
144// to the displayed window.
145
146void DesktopWindow::updateWindow()
147{
Pierre Ossmand50b3d12011-04-15 07:46:56 +0000148 viewport->updateWindow();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000149}
150
151
Pierre Ossman6455d852011-06-01 09:33:00 +0000152void DesktopWindow::resizeFramebuffer(int new_w, int new_h)
153{
154 if ((new_w == viewport->w()) && (new_h == viewport->h()))
155 return;
156
157 // Turn off size limitations for a bit while we juggle things around
158 size_range(100, 100, 0, 0);
159
160 // If we're letting the viewport match the window perfectly, then
161 // keep things that way for the new size, otherwise just keep things
162 // like they are.
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000163#ifdef HAVE_FLTK_FULLSCREEN
164 if (!fullscreen_active()) {
165#endif
Pierre Ossman6455d852011-06-01 09:33:00 +0000166 if ((w() == viewport->w()) && (h() == viewport->h()))
167 size(new_w, new_h);
168 else {
Pierre Ossman6455d852011-06-01 09:33:00 +0000169 // Make sure the window isn't too big
170 if ((w() > new_w) || (h() > new_h))
171 size(__rfbmin(w(), new_w), __rfbmin(h(), new_h));
Pierre Ossman6455d852011-06-01 09:33:00 +0000172 }
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000173#ifdef HAVE_FLTK_FULLSCREEN
174 }
175#endif
Pierre Ossman6455d852011-06-01 09:33:00 +0000176
177 viewport->size(new_w, new_h);
178
179 // We might not resize the main window, so we need to manually call this
180 // to make sure the viewport is centered.
181 repositionViewport();
182
183 // Update allowed resize range
Peter Åstrandadf5e252011-07-19 09:28:39 +0000184#ifdef HAVE_FLTK_FULLSCREEN
185 if (!fullscreen_active())
186#endif
Pierre Ossman6455d852011-06-01 09:33:00 +0000187 size_range(100, 100, new_w, new_h);
188
189 // repositionViewport() makes sure the scroll widget notices any changes
190 // in position, but it might be just the size that changes so we also
191 // need a poke here as well.
192 redraw();
193}
194
195
Pierre Ossman835b4ef2011-06-08 17:02:36 +0000196void DesktopWindow::setCursor(int width, int height, const Point& hotspot,
197 void* data, void* mask)
198{
199 viewport->setCursor(width, height, hotspot, data, mask);
200}
201
202
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000203void DesktopWindow::resize(int x, int y, int w, int h)
204{
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000205 Fl_Window::resize(x, y, w, h);
Pierre Ossman6455d852011-06-01 09:33:00 +0000206
207 // Deal with some scrolling corner cases
208 repositionViewport();
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000209}
210
211
Pierre Ossman407a5c32011-05-26 14:48:29 +0000212int DesktopWindow::handle(int event)
213{
214 switch (event) {
215#ifdef HAVE_FLTK_FULLSCREEN
Pierre Ossman407a5c32011-05-26 14:48:29 +0000216 case FL_FULLSCREEN:
Peter Åstrand17067b12011-07-18 07:45:14 +0000217 if (event == FL_FULLSCREEN) {
Pierre Ossman105738c2011-05-26 14:57:25 +0000218 fullScreen.setParam(fullscreen_active());
Peter Åstrand17067b12011-07-18 07:45:14 +0000219 if (!fullscreen_active()) {
220 size_range(100, 100, viewport->w(), viewport->h());
Peter Åstrand1d03cbc2011-08-01 10:34:38 +0000221 size(viewport->w(), viewport->h());
Peter Åstrand17067b12011-07-18 07:45:14 +0000222 } else {
223 // We need to turn off the size limitations for proper
224 // fullscreen support, but in case fullscreen is activated via
225 // the WM, this is a bit of a problem. In practice, it seems to
226 // work to change the size limits after we have recieved the
227 // FL_FULLSCREEN event, at least with my Metacity.
228 size_range(100, 100, 0, 0);
229 }
230 }
Pierre Ossman1870ab12011-05-26 14:53:49 +0000231
Pierre Ossman407a5c32011-05-26 14:48:29 +0000232 if (!fullscreenSystemKeys)
233 break;
234
235 if (fullscreen_active())
236 grabKeyboard();
237 else
238 ungrabKeyboard();
239
240 break;
Pierre Ossman407a5c32011-05-26 14:48:29 +0000241#endif
242 case FL_SHORTCUT:
243 // Sometimes the focus gets out of whack and we fall through to the
244 // shortcut dispatching. Try to make things sane again...
245 if (Fl::focus() == NULL) {
246 take_focus();
247 Fl::handle(FL_KEYDOWN, this);
248 }
249 return 1;
250 }
251
252 return Fl_Window::handle(event);
253}
254
255
Pierre Ossmana4f0f182011-06-14 13:36:57 +0000256int DesktopWindow::fltkHandle(int event, Fl_Window *win)
257{
258 int ret;
259
260 ret = Fl::handle_(event, win);
261
262#ifdef HAVE_FLTK_FULLSCREEN
263 // This is hackish and the result of the dodgy focus handling in FLTK.
264 // The basic problem is that FLTK's view of focus and the system's tend
265 // to differ, and as a result we do not see all the FL_FOCUS events we
266 // need. Fortunately we can grab them here...
267
268 DesktopWindow *dw = dynamic_cast<DesktopWindow*>(win);
269
270 if (dw && fullscreenSystemKeys) {
271 switch (event) {
272 case FL_FOCUS:
273 // FIXME: We reassert the keyboard grabbing on focus as FLTK there are
274 // some issues we need to work around:
275 // a) Fl::grab(0) on X11 will release the keyboard grab for us.
276 // b) Gaining focus on the system level causes FLTK to switch
277 // window level on OS X.
278 if (dw->fullscreen_active())
279 dw->grabKeyboard();
280 break;
281
282 case FL_UNFOCUS:
283 // FIXME: We need to relinquish control when the entire window loses
284 // focus as it is very tied to this specific window on some
285 // platforms and we want to be able to open subwindows.
286 dw->ungrabKeyboard();
287 break;
288 }
289 }
290#endif
291
292 return ret;
293}
294
295
Pierre Ossman407a5c32011-05-26 14:48:29 +0000296void DesktopWindow::grabKeyboard()
297{
298 // Grabbing the keyboard is fairly safe as FLTK reroutes events to the
299 // correct widget regardless of which low level window got the system
300 // event.
301
302 // FIXME: Push this stuff into FLTK.
303
304#if defined(WIN32)
305 int ret;
306
307 ret = win32_enable_lowlevel_keyboard(fl_xid(this));
308 if (ret != 0)
309 vlog.error(_("Failure grabbing keyboard"));
310#elif defined(__APPLE__)
311 int ret;
312
313 ret = cocoa_capture_display(this);
314 if (ret != 0)
315 vlog.error(_("Failure grabbing keyboard"));
316#else
317 int ret;
318
319 ret = XGrabKeyboard(fl_display, fl_xid(this), True,
Peter Åstrandf52860b2011-07-18 07:42:16 +0000320 GrabModeAsync, GrabModeAsync, CurrentTime);
Pierre Ossman407a5c32011-05-26 14:48:29 +0000321 if (ret) {
322 if (ret == AlreadyGrabbed) {
323 // It seems like we can race with the WM in some cases.
324 // Try again in a bit.
325 if (!Fl::has_timeout(handleGrab, this))
326 Fl::add_timeout(0.500, handleGrab, this);
327 } else {
328 vlog.error(_("Failure grabbing keyboard"));
329 }
330 }
331#endif
332}
333
334
335void DesktopWindow::ungrabKeyboard()
336{
337 Fl::remove_timeout(handleGrab, this);
338
339#if defined(WIN32)
340 win32_disable_lowlevel_keyboard(fl_xid(this));
341#elif defined(__APPLE__)
342 cocoa_release_display(this);
343#else
344 // FLTK has a grab so lets not mess with it
345 if (Fl::grab())
346 return;
347
348 XUngrabKeyboard(fl_display, fl_event_time);
349#endif
350}
351
352
353void DesktopWindow::handleGrab(void *data)
354{
355 DesktopWindow *self = (DesktopWindow*)data;
356
357 assert(self);
358
359#ifdef HAVE_FLTK_FULLSCREEN
360 if (!fullscreenSystemKeys)
361 return;
362 if (!self->fullscreen_active())
363 return;
364
365 self->grabKeyboard();
366#endif
367}
368
369
Pierre Ossman6455d852011-06-01 09:33:00 +0000370void DesktopWindow::repositionViewport()
371{
372 int new_x, new_y;
373
374 // Deal with some scrolling corner cases:
375 //
376 // a) If the window is larger then the viewport, center the viewport.
377 // b) If the window is smaller than the viewport, make sure there is
378 // no wasted space on the sides.
379 //
380 // FIXME: Doesn't compensate for scroll widget size properly.
381
382 new_x = viewport->x();
383 new_y = viewport->y();
384
385 if (w() > viewport->w())
386 new_x = (w() - viewport->w()) / 2;
387 else {
388 if (viewport->x() > 0)
389 new_x = 0;
390 else if (w() > (viewport->x() + viewport->w()))
391 new_x = w() - viewport->w();
392 }
393
394 // Same thing for y axis
395 if (h() > viewport->h())
396 new_y = (h() - viewport->h()) / 2;
397 else {
398 if (viewport->y() > 0)
399 new_y = 0;
400 else if (h() > (viewport->y() + viewport->h()))
401 new_y = h() - viewport->h();
402 }
403
404 if ((new_x != viewport->x()) || (new_y != viewport->y())) {
405 viewport->position(new_x, new_y);
406
407 // The scroll widget does not notice when you move around child widgets,
408 // so redraw everything to make sure things update.
409 redraw();
410 }
411}
412
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000413void DesktopWindow::handleClose(Fl_Widget *wnd, void *data)
414{
415 exit_vncviewer();
416}
Pierre Ossman407a5c32011-05-26 14:48:29 +0000417
418
419void DesktopWindow::handleOptions(void *data)
420{
421 DesktopWindow *self = (DesktopWindow*)data;
422
423#ifdef HAVE_FLTK_FULLSCREEN
424 if (self->fullscreen_active() && fullscreenSystemKeys)
425 self->grabKeyboard();
426 else
427 self->ungrabKeyboard();
Pierre Ossman91911642011-05-26 14:57:51 +0000428
Peter Åstrand17067b12011-07-18 07:45:14 +0000429 if (fullScreen && !self->fullscreen_active()) {
430 // Some WMs (Metacity) apparently requires that the size limits
431 // are removed before fullscreen
432 self->size_range(100, 100, 0, 0);
Pierre Ossman91911642011-05-26 14:57:51 +0000433 self->fullscreen();
Peter Åstrand17067b12011-07-18 07:45:14 +0000434 } else if (!fullScreen && self->fullscreen_active())
Pierre Ossman91911642011-05-26 14:57:51 +0000435 self->fullscreen_off();
Pierre Ossman407a5c32011-05-26 14:48:29 +0000436#endif
437}