blob: 91019516e70578cc5f144f9df1056d3537201656 [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
176 size_range(100, 100, new_w, new_h);
177
178 // repositionViewport() makes sure the scroll widget notices any changes
179 // in position, but it might be just the size that changes so we also
180 // need a poke here as well.
181 redraw();
182}
183
184
Pierre Ossman835b4ef2011-06-08 17:02:36 +0000185void DesktopWindow::setCursor(int width, int height, const Point& hotspot,
186 void* data, void* mask)
187{
188 viewport->setCursor(width, height, hotspot, data, mask);
189}
190
191
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000192void DesktopWindow::resize(int x, int y, int w, int h)
193{
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000194 Fl_Window::resize(x, y, w, h);
Pierre Ossman6455d852011-06-01 09:33:00 +0000195
196 // Deal with some scrolling corner cases
197 repositionViewport();
Pierre Ossman4ae229f2011-04-15 12:58:31 +0000198}
199
200
Pierre Ossman407a5c32011-05-26 14:48:29 +0000201int DesktopWindow::handle(int event)
202{
203 switch (event) {
204#ifdef HAVE_FLTK_FULLSCREEN
Pierre Ossman407a5c32011-05-26 14:48:29 +0000205 case FL_FULLSCREEN:
Peter Åstrand17067b12011-07-18 07:45:14 +0000206 if (event == FL_FULLSCREEN) {
Pierre Ossman105738c2011-05-26 14:57:25 +0000207 fullScreen.setParam(fullscreen_active());
Peter Åstrand17067b12011-07-18 07:45:14 +0000208 if (!fullscreen_active()) {
209 size_range(100, 100, viewport->w(), viewport->h());
210 } else {
211 // We need to turn off the size limitations for proper
212 // fullscreen support, but in case fullscreen is activated via
213 // the WM, this is a bit of a problem. In practice, it seems to
214 // work to change the size limits after we have recieved the
215 // FL_FULLSCREEN event, at least with my Metacity.
216 size_range(100, 100, 0, 0);
217 }
218 }
Pierre Ossman1870ab12011-05-26 14:53:49 +0000219
Pierre Ossman407a5c32011-05-26 14:48:29 +0000220 if (!fullscreenSystemKeys)
221 break;
222
223 if (fullscreen_active())
224 grabKeyboard();
225 else
226 ungrabKeyboard();
227
228 break;
Pierre Ossman407a5c32011-05-26 14:48:29 +0000229#endif
230 case FL_SHORTCUT:
231 // Sometimes the focus gets out of whack and we fall through to the
232 // shortcut dispatching. Try to make things sane again...
233 if (Fl::focus() == NULL) {
234 take_focus();
235 Fl::handle(FL_KEYDOWN, this);
236 }
237 return 1;
238 }
239
240 return Fl_Window::handle(event);
241}
242
243
Pierre Ossmana4f0f182011-06-14 13:36:57 +0000244int DesktopWindow::fltkHandle(int event, Fl_Window *win)
245{
246 int ret;
247
248 ret = Fl::handle_(event, win);
249
250#ifdef HAVE_FLTK_FULLSCREEN
251 // This is hackish and the result of the dodgy focus handling in FLTK.
252 // The basic problem is that FLTK's view of focus and the system's tend
253 // to differ, and as a result we do not see all the FL_FOCUS events we
254 // need. Fortunately we can grab them here...
255
256 DesktopWindow *dw = dynamic_cast<DesktopWindow*>(win);
257
258 if (dw && fullscreenSystemKeys) {
259 switch (event) {
260 case FL_FOCUS:
261 // FIXME: We reassert the keyboard grabbing on focus as FLTK there are
262 // some issues we need to work around:
263 // a) Fl::grab(0) on X11 will release the keyboard grab for us.
264 // b) Gaining focus on the system level causes FLTK to switch
265 // window level on OS X.
266 if (dw->fullscreen_active())
267 dw->grabKeyboard();
268 break;
269
270 case FL_UNFOCUS:
271 // FIXME: We need to relinquish control when the entire window loses
272 // focus as it is very tied to this specific window on some
273 // platforms and we want to be able to open subwindows.
274 dw->ungrabKeyboard();
275 break;
276 }
277 }
278#endif
279
280 return ret;
281}
282
283
Pierre Ossman407a5c32011-05-26 14:48:29 +0000284void DesktopWindow::grabKeyboard()
285{
286 // Grabbing the keyboard is fairly safe as FLTK reroutes events to the
287 // correct widget regardless of which low level window got the system
288 // event.
289
290 // FIXME: Push this stuff into FLTK.
291
292#if defined(WIN32)
293 int ret;
294
295 ret = win32_enable_lowlevel_keyboard(fl_xid(this));
296 if (ret != 0)
297 vlog.error(_("Failure grabbing keyboard"));
298#elif defined(__APPLE__)
299 int ret;
300
301 ret = cocoa_capture_display(this);
302 if (ret != 0)
303 vlog.error(_("Failure grabbing keyboard"));
304#else
305 int ret;
306
307 ret = XGrabKeyboard(fl_display, fl_xid(this), True,
Peter Åstrandf52860b2011-07-18 07:42:16 +0000308 GrabModeAsync, GrabModeAsync, CurrentTime);
Pierre Ossman407a5c32011-05-26 14:48:29 +0000309 if (ret) {
310 if (ret == AlreadyGrabbed) {
311 // It seems like we can race with the WM in some cases.
312 // Try again in a bit.
313 if (!Fl::has_timeout(handleGrab, this))
314 Fl::add_timeout(0.500, handleGrab, this);
315 } else {
316 vlog.error(_("Failure grabbing keyboard"));
317 }
318 }
319#endif
320}
321
322
323void DesktopWindow::ungrabKeyboard()
324{
325 Fl::remove_timeout(handleGrab, this);
326
327#if defined(WIN32)
328 win32_disable_lowlevel_keyboard(fl_xid(this));
329#elif defined(__APPLE__)
330 cocoa_release_display(this);
331#else
332 // FLTK has a grab so lets not mess with it
333 if (Fl::grab())
334 return;
335
336 XUngrabKeyboard(fl_display, fl_event_time);
337#endif
338}
339
340
341void DesktopWindow::handleGrab(void *data)
342{
343 DesktopWindow *self = (DesktopWindow*)data;
344
345 assert(self);
346
347#ifdef HAVE_FLTK_FULLSCREEN
348 if (!fullscreenSystemKeys)
349 return;
350 if (!self->fullscreen_active())
351 return;
352
353 self->grabKeyboard();
354#endif
355}
356
357
Pierre Ossman6455d852011-06-01 09:33:00 +0000358void DesktopWindow::repositionViewport()
359{
360 int new_x, new_y;
361
362 // Deal with some scrolling corner cases:
363 //
364 // a) If the window is larger then the viewport, center the viewport.
365 // b) If the window is smaller than the viewport, make sure there is
366 // no wasted space on the sides.
367 //
368 // FIXME: Doesn't compensate for scroll widget size properly.
369
370 new_x = viewport->x();
371 new_y = viewport->y();
372
373 if (w() > viewport->w())
374 new_x = (w() - viewport->w()) / 2;
375 else {
376 if (viewport->x() > 0)
377 new_x = 0;
378 else if (w() > (viewport->x() + viewport->w()))
379 new_x = w() - viewport->w();
380 }
381
382 // Same thing for y axis
383 if (h() > viewport->h())
384 new_y = (h() - viewport->h()) / 2;
385 else {
386 if (viewport->y() > 0)
387 new_y = 0;
388 else if (h() > (viewport->y() + viewport->h()))
389 new_y = h() - viewport->h();
390 }
391
392 if ((new_x != viewport->x()) || (new_y != viewport->y())) {
393 viewport->position(new_x, new_y);
394
395 // The scroll widget does not notice when you move around child widgets,
396 // so redraw everything to make sure things update.
397 redraw();
398 }
399}
400
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000401void DesktopWindow::handleClose(Fl_Widget *wnd, void *data)
402{
403 exit_vncviewer();
404}
Pierre Ossman407a5c32011-05-26 14:48:29 +0000405
406
407void DesktopWindow::handleOptions(void *data)
408{
409 DesktopWindow *self = (DesktopWindow*)data;
410
411#ifdef HAVE_FLTK_FULLSCREEN
412 if (self->fullscreen_active() && fullscreenSystemKeys)
413 self->grabKeyboard();
414 else
415 self->ungrabKeyboard();
Pierre Ossman91911642011-05-26 14:57:51 +0000416
Peter Åstrand17067b12011-07-18 07:45:14 +0000417 if (fullScreen && !self->fullscreen_active()) {
418 // Some WMs (Metacity) apparently requires that the size limits
419 // are removed before fullscreen
420 self->size_range(100, 100, 0, 0);
Pierre Ossman91911642011-05-26 14:57:51 +0000421 self->fullscreen();
Peter Åstrand17067b12011-07-18 07:45:14 +0000422 } else if (!fullScreen && self->fullscreen_active())
Pierre Ossman91911642011-05-26 14:57:51 +0000423 self->fullscreen_off();
Pierre Ossman407a5c32011-05-26 14:48:29 +0000424#endif
425}