blob: 102e0b1c847ac5ff3207fdaf178199dce93e6a02 [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.
155 if ((w() == viewport->w()) && (h() == viewport->h()))
156 size(new_w, new_h);
157 else {
158#ifdef HAVE_FLTK_FULLSCREEN
159 if (!fullscreen_active()) {
160#endif
161 // 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));
164#ifdef HAVE_FLTK_FULLSCREEN
165 }
166#endif
167 }
168
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());
213 } else {
214 // We need to turn off the size limitations for proper
215 // fullscreen support, but in case fullscreen is activated via
216 // the WM, this is a bit of a problem. In practice, it seems to
217 // work to change the size limits after we have recieved the
218 // FL_FULLSCREEN event, at least with my Metacity.
219 size_range(100, 100, 0, 0);
220 }
221 }
Pierre Ossman1870ab12011-05-26 14:53:49 +0000222
Pierre Ossman407a5c32011-05-26 14:48:29 +0000223 if (!fullscreenSystemKeys)
224 break;
225
226 if (fullscreen_active())
227 grabKeyboard();
228 else
229 ungrabKeyboard();
230
231 break;
Pierre Ossman407a5c32011-05-26 14:48:29 +0000232#endif
233 case FL_SHORTCUT:
234 // Sometimes the focus gets out of whack and we fall through to the
235 // shortcut dispatching. Try to make things sane again...
236 if (Fl::focus() == NULL) {
237 take_focus();
238 Fl::handle(FL_KEYDOWN, this);
239 }
240 return 1;
241 }
242
243 return Fl_Window::handle(event);
244}
245
246
Pierre Ossmana4f0f182011-06-14 13:36:57 +0000247int DesktopWindow::fltkHandle(int event, Fl_Window *win)
248{
249 int ret;
250
251 ret = Fl::handle_(event, win);
252
253#ifdef HAVE_FLTK_FULLSCREEN
254 // This is hackish and the result of the dodgy focus handling in FLTK.
255 // The basic problem is that FLTK's view of focus and the system's tend
256 // to differ, and as a result we do not see all the FL_FOCUS events we
257 // need. Fortunately we can grab them here...
258
259 DesktopWindow *dw = dynamic_cast<DesktopWindow*>(win);
260
261 if (dw && fullscreenSystemKeys) {
262 switch (event) {
263 case FL_FOCUS:
264 // FIXME: We reassert the keyboard grabbing on focus as FLTK there are
265 // some issues we need to work around:
266 // a) Fl::grab(0) on X11 will release the keyboard grab for us.
267 // b) Gaining focus on the system level causes FLTK to switch
268 // window level on OS X.
269 if (dw->fullscreen_active())
270 dw->grabKeyboard();
271 break;
272
273 case FL_UNFOCUS:
274 // FIXME: We need to relinquish control when the entire window loses
275 // focus as it is very tied to this specific window on some
276 // platforms and we want to be able to open subwindows.
277 dw->ungrabKeyboard();
278 break;
279 }
280 }
281#endif
282
283 return ret;
284}
285
286
Pierre Ossman407a5c32011-05-26 14:48:29 +0000287void DesktopWindow::grabKeyboard()
288{
289 // Grabbing the keyboard is fairly safe as FLTK reroutes events to the
290 // correct widget regardless of which low level window got the system
291 // event.
292
293 // FIXME: Push this stuff into FLTK.
294
295#if defined(WIN32)
296 int ret;
297
298 ret = win32_enable_lowlevel_keyboard(fl_xid(this));
299 if (ret != 0)
300 vlog.error(_("Failure grabbing keyboard"));
301#elif defined(__APPLE__)
302 int ret;
303
304 ret = cocoa_capture_display(this);
305 if (ret != 0)
306 vlog.error(_("Failure grabbing keyboard"));
307#else
308 int ret;
309
310 ret = XGrabKeyboard(fl_display, fl_xid(this), True,
Peter Åstrandf52860b2011-07-18 07:42:16 +0000311 GrabModeAsync, GrabModeAsync, CurrentTime);
Pierre Ossman407a5c32011-05-26 14:48:29 +0000312 if (ret) {
313 if (ret == AlreadyGrabbed) {
314 // It seems like we can race with the WM in some cases.
315 // Try again in a bit.
316 if (!Fl::has_timeout(handleGrab, this))
317 Fl::add_timeout(0.500, handleGrab, this);
318 } else {
319 vlog.error(_("Failure grabbing keyboard"));
320 }
321 }
322#endif
323}
324
325
326void DesktopWindow::ungrabKeyboard()
327{
328 Fl::remove_timeout(handleGrab, this);
329
330#if defined(WIN32)
331 win32_disable_lowlevel_keyboard(fl_xid(this));
332#elif defined(__APPLE__)
333 cocoa_release_display(this);
334#else
335 // FLTK has a grab so lets not mess with it
336 if (Fl::grab())
337 return;
338
339 XUngrabKeyboard(fl_display, fl_event_time);
340#endif
341}
342
343
344void DesktopWindow::handleGrab(void *data)
345{
346 DesktopWindow *self = (DesktopWindow*)data;
347
348 assert(self);
349
350#ifdef HAVE_FLTK_FULLSCREEN
351 if (!fullscreenSystemKeys)
352 return;
353 if (!self->fullscreen_active())
354 return;
355
356 self->grabKeyboard();
357#endif
358}
359
360
Pierre Ossman6455d852011-06-01 09:33:00 +0000361void DesktopWindow::repositionViewport()
362{
363 int new_x, new_y;
364
365 // Deal with some scrolling corner cases:
366 //
367 // a) If the window is larger then the viewport, center the viewport.
368 // b) If the window is smaller than the viewport, make sure there is
369 // no wasted space on the sides.
370 //
371 // FIXME: Doesn't compensate for scroll widget size properly.
372
373 new_x = viewport->x();
374 new_y = viewport->y();
375
376 if (w() > viewport->w())
377 new_x = (w() - viewport->w()) / 2;
378 else {
379 if (viewport->x() > 0)
380 new_x = 0;
381 else if (w() > (viewport->x() + viewport->w()))
382 new_x = w() - viewport->w();
383 }
384
385 // Same thing for y axis
386 if (h() > viewport->h())
387 new_y = (h() - viewport->h()) / 2;
388 else {
389 if (viewport->y() > 0)
390 new_y = 0;
391 else if (h() > (viewport->y() + viewport->h()))
392 new_y = h() - viewport->h();
393 }
394
395 if ((new_x != viewport->x()) || (new_y != viewport->y())) {
396 viewport->position(new_x, new_y);
397
398 // The scroll widget does not notice when you move around child widgets,
399 // so redraw everything to make sure things update.
400 redraw();
401 }
402}
403
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000404void DesktopWindow::handleClose(Fl_Widget *wnd, void *data)
405{
406 exit_vncviewer();
407}
Pierre Ossman407a5c32011-05-26 14:48:29 +0000408
409
410void DesktopWindow::handleOptions(void *data)
411{
412 DesktopWindow *self = (DesktopWindow*)data;
413
414#ifdef HAVE_FLTK_FULLSCREEN
415 if (self->fullscreen_active() && fullscreenSystemKeys)
416 self->grabKeyboard();
417 else
418 self->ungrabKeyboard();
Pierre Ossman91911642011-05-26 14:57:51 +0000419
Peter Åstrand17067b12011-07-18 07:45:14 +0000420 if (fullScreen && !self->fullscreen_active()) {
421 // Some WMs (Metacity) apparently requires that the size limits
422 // are removed before fullscreen
423 self->size_range(100, 100, 0, 0);
Pierre Ossman91911642011-05-26 14:57:51 +0000424 self->fullscreen();
Peter Åstrand17067b12011-07-18 07:45:14 +0000425 } else if (!fullScreen && self->fullscreen_active())
Pierre Ossman91911642011-05-26 14:57:51 +0000426 self->fullscreen_off();
Pierre Ossman407a5c32011-05-26 14:48:29 +0000427#endif
428}