1 /*
2 * GTK VNC Widget
3 *
4 * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
5 * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.0 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <config.h>
23 #include <locale.h>
24
25 #include "vncdisplay.h"
26 #include "vncconnection.h"
27 #include "vncutil.h"
28 #include "vncmarshal.h"
29 #include "vncdisplaykeymap.h"
30 #include "vncdisplayenums.h"
31 #include "vnccairoframebuffer.h"
32
33 #include <gtk/gtk.h>
34 #include <glib/gi18n.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <gdk-pixbuf/gdk-pixbuf.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42
43 #ifdef G_OS_WIN32
44 #include <windows.h>
45 #include <gdk/gdkwin32.h>
46 #ifndef MAPVK_VK_TO_VSC /* may be undefined in older mingw-headers */
47 #define MAPVK_VK_TO_VSC 0
48 #endif
49 #endif
50
51 #define VNC_DISPLAY_GET_PRIVATE(obj) \
52 (G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_DISPLAY, VncDisplayPrivate))
53
54 struct _VncDisplayPrivate
55 {
56 GdkCursor *null_cursor;
57 GdkCursor *remote_cursor;
58
59 VncConnection *conn;
60 VncCairoFramebuffer *fb;
61 cairo_surface_t *fbCache; /* Cache on server display */
62
63 VncDisplayDepthColor depth;
64
65 gboolean in_pointer_grab;
66 gboolean in_keyboard_grab;
67
68 guint down_keyval[16];
69 guint down_scancode[16];
70
71 int button_mask;
72 int last_x;
73 int last_y;
74
75 gboolean absolute;
76
77 gboolean grab_pointer;
78 gboolean grab_keyboard;
79 gboolean local_pointer;
80 gboolean read_only;
81 gboolean allow_lossy;
82 gboolean allow_scaling;
83 gboolean shared_flag;
84 gboolean force_size;
85 gboolean smoothing;
86
87 GSList *preferable_auths;
88 GSList *preferable_vencrypt_subauths;
89 size_t keycode_maplen;
90 const guint16 *keycode_map;
91
92 gboolean vncgrabpending; /* Key sequence detected, waiting for release */
93 VncGrabSequence *vncgrabseq; /* the configured key sequence */
94 gboolean *vncactiveseq; /* the currently pressed keys */
95
96 #ifdef WIN32
97 HHOOK keyboard_hook;
98 #endif
99 };
100
101 G_DEFINE_TYPE(VncDisplay, vnc_display, GTK_TYPE_DRAWING_AREA)
102
103 /* Properties */
104 enum
105 {
106 PROP_0,
107 PROP_POINTER_LOCAL,
108 PROP_POINTER_GRAB,
109 PROP_KEYBOARD_GRAB,
110 PROP_READ_ONLY,
111 PROP_WIDTH,
112 PROP_HEIGHT,
113 PROP_NAME,
114 PROP_LOSSY_ENCODING,
115 PROP_SCALING,
116 PROP_SHARED_FLAG,
117 PROP_FORCE_SIZE,
118 PROP_SMOOTHING,
119 PROP_DEPTH,
120 PROP_GRAB_KEYS,
121 PROP_CONNECTION,
122 };
123
124 /* Signals */
125 typedef enum
126 {
127 VNC_POINTER_GRAB,
128 VNC_POINTER_UNGRAB,
129 VNC_KEYBOARD_GRAB,
130 VNC_KEYBOARD_UNGRAB,
131
132 VNC_CONNECTED,
133 VNC_INITIALIZED,
134 VNC_DISCONNECTED,
135 VNC_AUTH_CREDENTIAL,
136
137 VNC_DESKTOP_RESIZE,
138
139 VNC_AUTH_FAILURE,
140 VNC_AUTH_UNSUPPORTED,
141
142 VNC_SERVER_CUT_TEXT,
143 VNC_BELL,
144 VNC_ERROR,
145
146 LAST_SIGNAL
147 } vnc_display_signals;
148
149
150 /* Some compatibility defines to let us build on both Gtk2 and Gtk3 */
151 #if GTK_CHECK_VERSION (2, 91, 0)
152
gdk_drawable_get_size(GdkWindow * w,gint * ww,gint * wh)153 static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
154 {
155 *ww = gdk_window_get_width(w);
156 *wh = gdk_window_get_height(w);
157 }
158
159 #define GtkObject GtkWidget
160 #define GtkObjectClass GtkWidgetClass
161 #define GTK_OBJECT_CLASS(c) GTK_WIDGET_CLASS(c)
162 #define gdk_cursor_unref(c) g_object_unref(c)
163 #endif
164
165
166 static guint signals[LAST_SIGNAL] = { 0, 0, 0, 0,
167 0, 0, 0, 0,
168 0, 0, 0, 0, 0,};
169
vnc_debug_option_arg(const gchar * option_name G_GNUC_UNUSED,const gchar * value G_GNUC_UNUSED,gpointer data G_GNUC_UNUSED,GError ** error G_GNUC_UNUSED)170 static gboolean vnc_debug_option_arg(const gchar *option_name G_GNUC_UNUSED,
171 const gchar *value G_GNUC_UNUSED,
172 gpointer data G_GNUC_UNUSED,
173 GError **error G_GNUC_UNUSED)
174 {
175 vnc_util_set_debug(TRUE);
176 return TRUE;
177 }
178
179 static const GOptionEntry gtk_vnc_args[] =
180 {
181 { "gtk-vnc-debug", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
182 vnc_debug_option_arg, N_("Enables debug output"), 0 },
183 { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, 0 }
184 };
185
186
187 static void
vnc_display_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)188 vnc_display_get_property (GObject *object,
189 guint prop_id,
190 GValue *value,
191 GParamSpec *pspec)
192 {
193 VncDisplay *vnc = VNC_DISPLAY (object);
194
195 switch (prop_id)
196 {
197 case PROP_POINTER_LOCAL:
198 g_value_set_boolean (value, vnc->priv->local_pointer);
199 break;
200 case PROP_POINTER_GRAB:
201 g_value_set_boolean (value, vnc->priv->grab_pointer);
202 break;
203 case PROP_KEYBOARD_GRAB:
204 g_value_set_boolean (value, vnc->priv->grab_keyboard);
205 break;
206 case PROP_READ_ONLY:
207 g_value_set_boolean (value, vnc->priv->read_only);
208 break;
209 case PROP_WIDTH:
210 g_value_set_int (value, vnc_display_get_width (vnc));
211 break;
212 case PROP_HEIGHT:
213 g_value_set_int (value, vnc_display_get_height (vnc));
214 break;
215 case PROP_NAME:
216 g_value_set_string (value, vnc_display_get_name (vnc));
217 break;
218 case PROP_LOSSY_ENCODING:
219 g_value_set_boolean (value, vnc->priv->allow_lossy);
220 break;
221 case PROP_SCALING:
222 g_value_set_boolean (value, vnc->priv->allow_scaling);
223 break;
224 case PROP_SHARED_FLAG:
225 g_value_set_boolean (value, vnc->priv->shared_flag);
226 break;
227 case PROP_FORCE_SIZE:
228 g_value_set_boolean (value, vnc->priv->force_size);
229 break;
230 case PROP_SMOOTHING:
231 g_value_set_boolean (value, vnc->priv->smoothing);
232 break;
233 case PROP_DEPTH:
234 g_value_set_enum (value, vnc->priv->depth);
235 break;
236 case PROP_GRAB_KEYS:
237 g_value_set_boxed(value, vnc->priv->vncgrabseq);
238 break;
239 case PROP_CONNECTION:
240 g_value_set_object(value, vnc->priv->conn);
241 break;
242 default:
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244 break;
245 }
246 }
247
248 static void
vnc_display_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)249 vnc_display_set_property (GObject *object,
250 guint prop_id,
251 const GValue *value,
252 GParamSpec *pspec)
253 {
254 VncDisplay *vnc = VNC_DISPLAY (object);
255
256 switch (prop_id)
257 {
258 case PROP_POINTER_LOCAL:
259 vnc_display_set_pointer_local (vnc, g_value_get_boolean (value));
260 break;
261 case PROP_POINTER_GRAB:
262 vnc_display_set_pointer_grab (vnc, g_value_get_boolean (value));
263 break;
264 case PROP_KEYBOARD_GRAB:
265 vnc_display_set_keyboard_grab (vnc, g_value_get_boolean (value));
266 break;
267 case PROP_READ_ONLY:
268 vnc_display_set_read_only (vnc, g_value_get_boolean (value));
269 break;
270 case PROP_LOSSY_ENCODING:
271 vnc_display_set_lossy_encoding (vnc, g_value_get_boolean (value));
272 break;
273 case PROP_SCALING:
274 vnc_display_set_scaling (vnc, g_value_get_boolean (value));
275 break;
276 case PROP_SHARED_FLAG:
277 vnc_display_set_shared_flag (vnc, g_value_get_boolean (value));
278 break;
279 case PROP_FORCE_SIZE:
280 vnc_display_set_force_size (vnc, g_value_get_boolean (value));
281 break;
282 case PROP_SMOOTHING:
283 vnc_display_set_smoothing (vnc, g_value_get_boolean (value));
284 break;
285 case PROP_DEPTH:
286 vnc_display_set_depth (vnc, g_value_get_enum (value));
287 break;
288 case PROP_GRAB_KEYS:
289 vnc_display_set_grab_keys(vnc, g_value_get_boxed(value));
290 break;
291 default:
292 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
293 break;
294 }
295 }
296
297
298 /**
299 * vnc_display_new:
300 *
301 * Create a new widget capable of connecting to a VNC server
302 * and displaying its contents
303 *
304 * The widget will initially be in a disconnected state
305 *
306 * Returns: (transfer full): the new VNC display widget
307 */
vnc_display_new(void)308 GtkWidget *vnc_display_new(void)
309 {
310 return GTK_WIDGET(g_object_new(VNC_TYPE_DISPLAY, NULL));
311 }
312
create_null_cursor(void)313 static GdkCursor *create_null_cursor(void)
314 {
315 GdkCursor *cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
316
317 return cursor;
318 }
319
320 #ifdef G_OS_WIN32
321 static HWND win32_window = NULL;
322
keyboard_hook_cb(int code,WPARAM wparam,LPARAM lparam)323 static LRESULT CALLBACK keyboard_hook_cb(int code, WPARAM wparam, LPARAM lparam)
324 {
325 if (win32_window && code == HC_ACTION && wparam != WM_KEYUP) {
326 KBDLLHOOKSTRUCT *hooked = (KBDLLHOOKSTRUCT*)lparam;
327 DWORD dwmsg = (hooked->flags << 24) | (hooked->scanCode << 16) | 1;
328
329 if (hooked->vkCode == VK_NUMLOCK || hooked->vkCode == VK_RSHIFT) {
330 dwmsg &= ~(1 << 24);
331 SendMessage(win32_window, wparam, hooked->vkCode, dwmsg);
332 }
333 switch (hooked->vkCode) {
334 case VK_CAPITAL:
335 case VK_SCROLL:
336 case VK_NUMLOCK:
337 case VK_LSHIFT:
338 case VK_RSHIFT:
339 case VK_RCONTROL:
340 case VK_LMENU:
341 case VK_RMENU:
342 break;
343 case VK_LCONTROL:
344 /* When pressing AltGr, an extra VK_LCONTROL with a special
345 * scancode with bit 9 set is sent. Let's ignore the extra
346 * VK_LCONTROL, as that will make AltGr misbehave. */
347 if (hooked->scanCode & 0x200)
348 return 1;
349 break;
350 default:
351 SendMessage(win32_window, wparam, hooked->vkCode, dwmsg);
352 return 1;
353 }
354 }
355 return CallNextHookEx(NULL, code, wparam, lparam);
356 }
357 #endif
358
setup_surface_cache(VncDisplay * dpy,cairo_t * crWin,int w,int h)359 static void setup_surface_cache(VncDisplay *dpy, cairo_t *crWin, int w, int h)
360 {
361 VncDisplayPrivate *priv = dpy->priv;
362 cairo_surface_t *win = cairo_get_target(crWin);
363 cairo_t *crCache;
364
365 if (priv->fbCache)
366 return;
367
368 /* Creates a Pixmap on the X11 server matching the Window */
369 priv->fbCache = cairo_surface_create_similar(win,
370 CAIRO_CONTENT_COLOR,
371 w, h);
372 crCache = cairo_create(priv->fbCache);
373
374 /* Copy our local framebuffer contents to the Pixmap */
375 cairo_set_source_surface(crCache,
376 vnc_cairo_framebuffer_get_surface(priv->fb),
377 0,
378 0);
379 cairo_paint(crCache);
380
381 cairo_destroy(crCache);
382 }
383
draw_event(GtkWidget * widget,cairo_t * cr)384 static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
385 {
386 VncDisplay *obj = VNC_DISPLAY(widget);
387 VncDisplayPrivate *priv = obj->priv;
388 int ww, wh;
389 int mx = 0, my = 0;
390 int fbw = 0, fbh = 0;
391
392 if (priv->fb) {
393 fbw = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
394 fbh = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
395
396 setup_surface_cache(obj, cr, fbw, fbh);
397 }
398
399 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
400
401 if (ww > fbw)
402 mx = (ww - fbw) / 2;
403 if (wh > fbh)
404 my = (wh - fbh) / 2;
405
406 /* If we don't have a pixmap, or we're not scaling, then
407 we need to fill with background color */
408 if (!priv->fb ||
409 !priv->allow_scaling) {
410 cairo_rectangle(cr, 0, 0, ww, wh);
411 /* Optionally cut out the inner area where the pixmap
412 will be drawn. This avoids 'flashing' since we're
413 not double-buffering. Note we're using the undocumented
414 behaviour of drawing the rectangle from right to left
415 to cut out the whole */
416 if (priv->fb)
417 cairo_rectangle(cr, mx + fbw, my,
418 -1 * fbw, fbh);
419 cairo_fill(cr);
420 }
421
422 /* Draw the VNC display */
423 if (priv->fb) {
424 if (priv->allow_scaling) {
425 double sx, sy;
426 /* Scale to fill window */
427 sx = (double)ww / (double)fbw;
428 sy = (double)wh / (double)fbh;
429 cairo_scale(cr, sx, sy);
430 cairo_set_source_surface(cr,
431 priv->fbCache,
432 0,
433 0);
434 if (!priv->smoothing) {
435 cairo_pattern_set_filter(cairo_get_source(cr),
436 CAIRO_FILTER_NEAREST);
437 }
438 } else {
439 cairo_set_source_surface(cr,
440 priv->fbCache,
441 mx,
442 my);
443 }
444 cairo_paint(cr);
445 }
446
447 return TRUE;
448 }
449
450
451 #if !GTK_CHECK_VERSION (2, 91, 0)
expose_event(GtkWidget * widget,GdkEventExpose * expose)452 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose)
453 {
454 VncDisplay *obj = VNC_DISPLAY(widget);
455 cairo_t *cr;
456 gboolean ret;
457
458 cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(obj)));
459 cairo_rectangle(cr,
460 expose->area.x,
461 expose->area.y,
462 expose->area.width,
463 expose->area.height);
464 cairo_clip(cr);
465
466 ret = draw_event(widget, cr);
467
468 cairo_destroy(cr);
469
470 return ret;
471 }
472 #endif
473
474 #if !GTK_CHECK_VERSION(3, 0, 0)
do_keyboard_grab_all(GdkWindow * window)475 static void do_keyboard_grab_all(GdkWindow *window)
476 {
477 if (window == NULL)
478 return;
479
480 gdk_keyboard_grab(window,
481 FALSE,
482 GDK_CURRENT_TIME);
483 }
do_keyboard_ungrab_all(GdkWindow * window G_GNUC_UNUSED)484 static void do_keyboard_ungrab_all(GdkWindow *window G_GNUC_UNUSED)
485 {
486 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
487 }
do_pointer_grab_all(GdkWindow * window,GdkCursor * cursor)488 static void do_pointer_grab_all(GdkWindow *window,
489 GdkCursor *cursor)
490 {
491 if (window == NULL)
492 return;
493
494 gdk_pointer_grab(window,
495 FALSE, /* All events to come to our window directly */
496 GDK_POINTER_MOTION_MASK |
497 GDK_BUTTON_PRESS_MASK |
498 GDK_BUTTON_RELEASE_MASK |
499 GDK_BUTTON_MOTION_MASK |
500 GDK_SCROLL_MASK,
501 NULL, /* Allow cursor to move over entire desktop */
502 cursor,
503 GDK_CURRENT_TIME);
504 }
do_pointer_ungrab_all(GdkWindow * window G_GNUC_UNUSED)505 static void do_pointer_ungrab_all(GdkWindow *window G_GNUC_UNUSED)
506 {
507 gdk_pointer_ungrab(GDK_CURRENT_TIME);
508 }
509 #else
do_keyboard_grab_all(GdkWindow * window)510 static void do_keyboard_grab_all(GdkWindow *window)
511 {
512 GdkDeviceManager *mgr = gdk_display_get_device_manager(gdk_window_get_display(window));
513 GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
514 GList *tmp = devices;
515 while (tmp) {
516 GdkDevice *dev = tmp->data;
517 if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD)
518 gdk_device_grab(dev,
519 window,
520 GDK_OWNERSHIP_NONE,
521 FALSE,
522 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
523 NULL,
524 GDK_CURRENT_TIME);
525 tmp = tmp->next;
526 }
527 g_list_free(devices);
528 }
529
do_keyboard_ungrab_all(GdkWindow * window)530 static void do_keyboard_ungrab_all(GdkWindow *window)
531 {
532 GdkDeviceManager *mgr = gdk_display_get_device_manager(gdk_window_get_display(window));
533 GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
534 GList *tmp = devices;
535 while (tmp) {
536 GdkDevice *dev = tmp->data;
537 if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD)
538 gdk_device_ungrab(dev,
539 GDK_CURRENT_TIME);
540 tmp = tmp->next;
541 }
542 g_list_free(devices);
543 }
544
do_pointer_grab_all(GdkWindow * window,GdkCursor * cursor)545 static void do_pointer_grab_all(GdkWindow *window,
546 GdkCursor *cursor)
547 {
548 GdkDeviceManager *mgr = gdk_display_get_device_manager(gdk_window_get_display(window));
549 GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
550 GList *tmp = devices;
551 while (tmp) {
552 GdkDevice *dev = tmp->data;
553 if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE)
554 gdk_device_grab(dev,
555 window,
556 GDK_OWNERSHIP_NONE,
557 FALSE, /* All events to come to our window directly */
558 GDK_POINTER_MOTION_MASK |
559 GDK_BUTTON_PRESS_MASK |
560 GDK_BUTTON_RELEASE_MASK |
561 GDK_BUTTON_MOTION_MASK |
562 GDK_SCROLL_MASK,
563 cursor,
564 GDK_CURRENT_TIME);
565 tmp = tmp->next;
566 }
567 g_list_free(devices);
568 }
569
do_pointer_ungrab_all(GdkWindow * window)570 static void do_pointer_ungrab_all(GdkWindow *window)
571 {
572 GdkDeviceManager *mgr = gdk_display_get_device_manager(gdk_window_get_display(window));
573 GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
574 GList *tmp = devices;
575 while (tmp) {
576 GdkDevice *dev = tmp->data;
577 if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE)
578 gdk_device_ungrab(dev,
579 GDK_CURRENT_TIME);
580 tmp = tmp->next;
581 }
582 g_list_free(devices);
583 }
584 #endif
585
do_keyboard_grab(VncDisplay * obj,gboolean quiet)586 static void do_keyboard_grab(VncDisplay *obj, gboolean quiet)
587 {
588 VncDisplayPrivate *priv = obj->priv;
589
590 #ifdef G_OS_WIN32
591 if (priv->keyboard_hook == NULL)
592 priv->keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_cb,
593 GetModuleHandle(NULL), 0);
594 g_warn_if_fail(priv->keyboard_hook != NULL);
595 #endif
596
597 do_keyboard_grab_all(gtk_widget_get_window(GTK_WIDGET(obj)));
598
599 priv->in_keyboard_grab = TRUE;
600 if (!quiet)
601 g_signal_emit(obj, signals[VNC_KEYBOARD_GRAB], 0);
602 }
603
604
do_keyboard_ungrab(VncDisplay * obj,gboolean quiet)605 static void do_keyboard_ungrab(VncDisplay *obj, gboolean quiet)
606 {
607 VncDisplayPrivate *priv = obj->priv;
608
609 do_keyboard_ungrab_all(gtk_widget_get_window(GTK_WIDGET(obj)));
610
611 #ifdef G_OS_WIN32
612 if (priv->keyboard_hook != NULL) {
613 UnhookWindowsHookEx(priv->keyboard_hook);
614 priv->keyboard_hook = NULL;
615 }
616 #endif
617
618 priv->in_keyboard_grab = FALSE;
619 if (!quiet)
620 g_signal_emit(obj, signals[VNC_KEYBOARD_UNGRAB], 0);
621 }
622
do_pointer_hide(VncDisplay * obj)623 static void do_pointer_hide(VncDisplay *obj)
624 {
625 VncDisplayPrivate *priv = obj->priv;
626 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(obj));
627
628 if (window == NULL)
629 return;
630
631 gdk_window_set_cursor(window,
632 priv->remote_cursor ? priv->remote_cursor : priv->null_cursor);
633 }
634
do_pointer_show(VncDisplay * obj)635 static void do_pointer_show(VncDisplay *obj)
636 {
637 VncDisplayPrivate *priv = obj->priv;
638 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(obj));
639
640 if (window == NULL)
641 return;
642
643 gdk_window_set_cursor(window, priv->remote_cursor);
644 }
645
do_pointer_grab(VncDisplay * obj,gboolean quiet)646 static void do_pointer_grab(VncDisplay *obj, gboolean quiet)
647 {
648 VncDisplayPrivate *priv = obj->priv;
649
650 /* If we're not already grabbing keyboard, grab it now */
651 if (!priv->grab_keyboard)
652 do_keyboard_grab(obj, quiet);
653
654 /*
655 * For relative mouse to work correctly when grabbed we need to
656 * allow the pointer to move anywhere on the local desktop, so
657 * use NULL for the 'confine_to' argument. Furthermore we need
658 * the coords to be reported to our VNC window, regardless of
659 * what window the pointer is actally over, so use 'FALSE' for
660 * 'owner_events' parameter
661 */
662 do_pointer_grab_all(gtk_widget_get_window(GTK_WIDGET(obj)),
663 priv->remote_cursor ? priv->remote_cursor : priv->null_cursor);
664 priv->in_pointer_grab = TRUE;
665 if (!quiet)
666 g_signal_emit(obj, signals[VNC_POINTER_GRAB], 0);
667 }
668
do_pointer_ungrab(VncDisplay * obj,gboolean quiet)669 static void do_pointer_ungrab(VncDisplay *obj, gboolean quiet)
670 {
671 VncDisplayPrivate *priv = obj->priv;
672
673 /* If we grabbed keyboard upon pointer grab, then ungrab it now */
674 if (!priv->grab_keyboard)
675 do_keyboard_ungrab(obj, quiet);
676
677 do_pointer_ungrab_all(gtk_widget_get_window(GTK_WIDGET(obj)));
678 priv->in_pointer_grab = FALSE;
679
680 if (priv->absolute)
681 do_pointer_hide(obj);
682
683 if (!quiet)
684 g_signal_emit(obj, signals[VNC_POINTER_UNGRAB], 0);
685 }
686
687 /**
688 * vnc_display_force_grab:
689 * @obj: (transfer none): the VNC display widget
690 * @enable: TRUE to force pointer grabbing, FALSE otherwise
691 *
692 * If @enable is TRUE, immediately grab the pointer.
693 * If @enable is FALSE, immediately ungrab the pointer.
694 * This overrides any automatic grabs that may have
695 * been done.
696 */
vnc_display_force_grab(VncDisplay * obj,gboolean enable)697 void vnc_display_force_grab(VncDisplay *obj, gboolean enable)
698 {
699 if (enable)
700 do_pointer_grab(obj, FALSE);
701 else
702 do_pointer_ungrab(obj, FALSE);
703 }
704
button_event(GtkWidget * widget,GdkEventButton * button)705 static gboolean button_event(GtkWidget *widget, GdkEventButton *button)
706 {
707 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
708 int n;
709
710 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
711 return FALSE;
712
713 if (priv->read_only)
714 return FALSE;
715
716 gtk_widget_grab_focus (widget);
717
718 if (priv->grab_pointer && !priv->absolute && !priv->in_pointer_grab &&
719 button->button == 1 && button->type == GDK_BUTTON_PRESS)
720 do_pointer_grab(VNC_DISPLAY(widget), FALSE);
721
722 n = 1 << (button->button - 1);
723 if (button->type == GDK_BUTTON_PRESS)
724 priv->button_mask |= n;
725 else if (button->type == GDK_BUTTON_RELEASE)
726 priv->button_mask &= ~n;
727
728 if (priv->absolute) {
729 vnc_connection_pointer_event(priv->conn, priv->button_mask,
730 priv->last_x, priv->last_y);
731 } else {
732 vnc_connection_pointer_event(priv->conn, priv->button_mask,
733 0x7FFF, 0x7FFF);
734 }
735
736 return TRUE;
737 }
738
scroll_event(GtkWidget * widget,GdkEventScroll * scroll)739 static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *scroll)
740 {
741 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
742 int mask;
743
744 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
745 return FALSE;
746
747 if (priv->read_only)
748 return FALSE;
749
750 if (scroll->direction == GDK_SCROLL_UP)
751 mask = (1 << 3);
752 else if (scroll->direction == GDK_SCROLL_DOWN)
753 mask = (1 << 4);
754 else if (scroll->direction == GDK_SCROLL_LEFT)
755 mask = (1 << 5);
756 else if (scroll->direction == GDK_SCROLL_RIGHT)
757 mask = (1 << 6);
758 else
759 return FALSE;
760
761 if (priv->absolute) {
762 vnc_connection_pointer_event(priv->conn, priv->button_mask | mask,
763 priv->last_x, priv->last_y);
764 vnc_connection_pointer_event(priv->conn, priv->button_mask,
765 priv->last_x, priv->last_y);
766 } else {
767 vnc_connection_pointer_event(priv->conn, priv->button_mask | mask,
768 0x7FFF, 0x7FFF);
769 vnc_connection_pointer_event(priv->conn, priv->button_mask,
770 0x7FFF, 0x7FFF);
771 }
772
773 return TRUE;
774 }
775
776
777 /*
778 * There are several scenarios to considier when handling client
779 * mouse motion events:
780 *
781 * - Mouse in relative mode + centered rendering of desktop
782 * - Mouse in relative mode + scaled rendering of desktop
783 * - Mouse in absolute mode + centered rendering of desktop
784 * - Mouse in absolute mode + scaled rendering of desktop
785 *
786 * Once scaled / offset, absolute mode is easy.
787 *
788 * Relative mode has a couple of special complications
789 *
790 * - Need to turn client absolute events into a delta
791 * - Need to warp local pointer to avoid hitting a wall
792 */
motion_event(GtkWidget * widget,GdkEventMotion * motion)793 static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
794 {
795 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
796 int ww, wh;
797 int fbw, fbh;
798
799 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
800 return FALSE;
801
802 if (!priv->fb)
803 return FALSE;
804
805 fbw = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
806 fbh = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
807
808 /* In relative mode, only move the server mouse pointer
809 * if the client grab is active */
810 if (!priv->absolute && !priv->in_pointer_grab)
811 return FALSE;
812
813 if (priv->read_only)
814 return FALSE;
815
816 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
817
818 /* First apply adjustments to the coords in the motion event */
819 if (priv->allow_scaling) {
820 double sx, sy;
821 sx = (double)fbw / (double)ww;
822 sy = (double)fbh / (double)wh;
823
824 /* Scaling the desktop, so scale the mouse coords
825 * by same ratio */
826 motion->x *= sx;
827 motion->y *= sy;
828 } else {
829 int mw = 0, mh = 0;
830
831 if (ww > fbw)
832 mw = (ww - fbw) / 2;
833 if (wh > fbh)
834 mh = (wh - fbh) / 2;
835
836 /* Not scaling, drawing the desktop centered
837 * in the larger window, so offset the mouse
838 * coords to match centering */
839 motion->x -= mw;
840 motion->y -= mh;
841 }
842
843 /* Next adjust the real client pointer */
844 if (!priv->absolute) {
845 GdkScreen *screen = gtk_widget_get_screen(widget);
846 int x = (int)motion->x_root;
847 int y = (int)motion->y_root;
848
849 /* In relative mode check to see if client pointer hit
850 * one of the screen edges, and if so move it back by
851 * 200 pixels. This is important because the pointer
852 * in the server doesn't correspond 1-for-1, and so
853 * may still be only half way across the screen. Without
854 * this warp, the server pointer would thus appear to hit
855 * an invisible wall */
856 if (x <= 0) x += 200;
857 if (y <= 0) y += 200;
858 if (x >= (gdk_screen_get_width(screen) - 1)) x -= 200;
859 if (y >= (gdk_screen_get_height(screen) - 1)) y -= 200;
860
861 if (x != (int)motion->x_root || y != (int)motion->y_root) {
862 #if GTK_CHECK_VERSION(3, 0, 0)
863 GdkDevice *dev = gdk_event_get_device((GdkEvent*)motion);
864 gdk_device_warp(dev, screen, x, y);
865 #else
866 GdkDisplay *display = gtk_widget_get_display(widget);
867 gdk_display_warp_pointer(display, screen, x, y);
868 #endif
869 priv->last_x = -1;
870 priv->last_y = -1;
871 return FALSE;
872 }
873 }
874
875 /* Finally send the event to server */
876 if (priv->last_x != -1) {
877 int dx, dy;
878 if (priv->absolute) {
879 dx = (int)motion->x;
880 dy = (int)motion->y;
881
882 /* If the co-ords are out of bounds we want to clamp
883 * them to the boundaries. We don't want to actually
884 * drop the events though, because even if the X coord
885 * is out of bounds we want the server to see Y coord
886 * changes, and vica-verca. */
887 if (dx < 0)
888 dx = 0;
889 if (dy < 0)
890 dy = 0;
891 if (dx >= fbw)
892 dx = fbw - 1;
893 if (dy >= fbh)
894 dy = fbh - 1;
895 } else {
896 /* Just send the delta since last motion event */
897 dx = (int)motion->x + 0x7FFF - priv->last_x;
898 dy = (int)motion->y + 0x7FFF - priv->last_y;
899 }
900
901 vnc_connection_pointer_event(priv->conn, priv->button_mask, dx, dy);
902 }
903
904 priv->last_x = (int)motion->x;
905 priv->last_y = (int)motion->y;
906
907 return TRUE;
908 }
909
910
911 /*
912 * Lets say the grab sequence of Ctrl_L + Alt_L
913 *
914 * We first need to detect when both Ctrl_L and Alt_L are pressed.
915 * When this happens we are "primed" to tigger.
916 *
917 * If any further key is pressed though, we unprime ourselves
918 *
919 * If any key is released while we are primed, then we
920 * trigger.
921 */
check_for_grab_key(GtkWidget * widget,int type,int keyval)922 static gboolean check_for_grab_key(GtkWidget *widget, int type, int keyval)
923 {
924 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
925 int i;
926
927 if (!priv->vncgrabseq->nkeysyms)
928 return FALSE;
929
930 if (type == GDK_KEY_RELEASE) {
931 gboolean active = priv->vncgrabpending;
932 /* Any key release resets the whole grab sequence */
933 memset(priv->vncactiveseq, 0,
934 sizeof(gboolean)*priv->vncgrabseq->nkeysyms);
935 priv->vncgrabpending = FALSE;
936 return active;
937 } else {
938 gboolean setone = FALSE;
939
940 /* Record the new key press */
941 for (i = 0 ; i < priv->vncgrabseq->nkeysyms ; i++) {
942 if (priv->vncgrabseq->keysyms[i] == keyval) {
943 priv->vncactiveseq[i] = TRUE;
944 setone = TRUE;
945 }
946 }
947
948 if (setone) {
949 /* Return if any key is not pressed */
950 for (i = 0 ; i < priv->vncgrabseq->nkeysyms ; i++)
951 if (priv->vncactiveseq[i] == FALSE)
952 return FALSE;
953
954 /* All keys in grab seq are pressed, so prime
955 * to trigger on release
956 */
957 priv->vncgrabpending = TRUE;
958 } else {
959 /* Key not in grab seq, so must reset any pending
960 * grab keys we have */
961 memset(priv->vncactiveseq, 0,
962 sizeof(gboolean)*priv->vncgrabseq->nkeysyms);
963 priv->vncgrabpending = FALSE;
964 }
965
966 return FALSE;
967 }
968 }
969
970
971 /* Compatability code to allow build on Gtk2 and Gtk3 */
972 #ifndef GDK_Tab
973 #define GDK_Tab GDK_KEY_Tab
974 #endif
975 #ifndef GDK_ISO_Left_Tab
976 #define GDK_ISO_Left_Tab GDK_KEY_ISO_Left_Tab
977 #endif
978
key_event(GtkWidget * widget,GdkEventKey * key)979 static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
980 {
981 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
982 int i;
983 int keyval = key->keyval;
984
985 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
986 return FALSE;
987
988 if (priv->read_only)
989 return FALSE;
990
991 VNC_DEBUG("%s keycode: %d state: %u group %d, keyval: %d",
992 key->type == GDK_KEY_PRESS ? "press" : "release",
993 key->hardware_keycode, key->state, key->group, keyval);
994
995 #ifdef G_OS_WIN32
996 /* on windows, we ought to ignore the reserved key event? */
997 if (key->hardware_keycode == 0xff)
998 return FALSE;
999
1000 if (!priv->in_keyboard_grab) {
1001 if (key->hardware_keycode == VK_LWIN ||
1002 key->hardware_keycode == VK_RWIN ||
1003 key->hardware_keycode == VK_APPS)
1004 return FALSE;
1005 }
1006 #endif
1007
1008 /* VNC servers don't want to get an ISO_Left_Tab.
1009 * They need the Tab key and will apply the
1010 * Shift modifier themselves
1011 */
1012 if (keyval == GDK_ISO_Left_Tab) {
1013 keyval = GDK_Tab;
1014 }
1015
1016 /*
1017 * Some VNC suckiness with key state & modifiers in particular
1018 *
1019 * Because VNC has no concept of modifiers, we have to track what keys are
1020 * pressed and when the widget looses focus send fake key up events for all
1021 * keys current held down. This is because upon gaining focus any keys held
1022 * down are no longer likely to be down. This would thus result in keys
1023 * being 'stuck on' in the remote server. eg upon Alt-Tab to switch window
1024 * focus you'd never see key up for the Alt or Tab keys without this :-(
1025 *
1026 * This is mostly a problem with modifier keys, but its best to just track
1027 * all key presses regardless. There's a limit to how many keys a user can
1028 * press at once due to a max of 10 fingers (normally :-), so down_key_vals
1029 * is only storing upto 16 for now. Should be plenty...
1030 *
1031 * Arggggh.
1032 */
1033
1034 /*
1035 * First the key release handling. This is *always* run, even for Key press
1036 * events, because GTK will often merge sequential press+release pairs of
1037 * the same key into a sequence of press+press+press+press+release. VNC
1038 * servers don't like this, so we have to see if we're already pressed
1039 * send release events. So, we run the release handling code all the time.
1040 */
1041 for (i = 0 ; i < (int)(sizeof(priv->down_keyval)/sizeof(priv->down_keyval[0])) ; i++) {
1042 /* We were pressed, and now we're released, so... */
1043 if (priv->down_scancode[i] == key->hardware_keycode) {
1044 guint16 scancode = vnc_display_keymap_gdk2rfb(priv->keycode_map,
1045 priv->keycode_maplen,
1046 key->hardware_keycode);
1047 #ifdef G_OS_WIN32
1048 /* MapVirtualKey doesn't return scancode with needed higher byte */
1049 scancode = MapVirtualKey(key->hardware_keycode, MAPVK_VK_TO_VSC) |
1050 (scancode & 0xff00);
1051 #endif
1052 /*
1053 * ..send the key release event we're dealing with
1054 *
1055 * NB, we use priv->down_keyval[i], and not our
1056 * current 'keyval', because we need to make sure
1057 * that the release keyval is identical to the
1058 * press keyval. In some layouts, this isn't always
1059 * true, with "Tab" generating Tab on press, and
1060 * ISO_Prev_Group on release.
1061 */
1062 vnc_connection_key_event(priv->conn, 0, priv->down_keyval[i], scancode);
1063 priv->down_keyval[i] = 0;
1064 priv->down_scancode[i] = 0;
1065 break;
1066 }
1067 }
1068
1069 if (key->type == GDK_KEY_PRESS) {
1070 for (i = 0 ; i < (int)(sizeof(priv->down_keyval)/sizeof(priv->down_keyval[0])) ; i++) {
1071 if (priv->down_scancode[i] == 0) {
1072 guint16 scancode = vnc_display_keymap_gdk2rfb(priv->keycode_map,
1073 priv->keycode_maplen,
1074 key->hardware_keycode);
1075 #ifdef G_OS_WIN32
1076 /* MapVirtualKey doesn't return scancode with needed higher byte */
1077 scancode = MapVirtualKey(key->hardware_keycode, MAPVK_VK_TO_VSC) |
1078 (scancode & 0xff00);
1079 #endif
1080 priv->down_keyval[i] = keyval;
1081 priv->down_scancode[i] = key->hardware_keycode;
1082 /* Send the actual key event we're dealing with */
1083 vnc_connection_key_event(priv->conn, 1, keyval, scancode);
1084 break;
1085 }
1086 }
1087 }
1088
1089 if (check_for_grab_key(widget, key->type, key->keyval)) {
1090 if (priv->in_pointer_grab)
1091 do_pointer_ungrab(VNC_DISPLAY(widget), FALSE);
1092 else if (!priv->grab_keyboard || !priv->absolute)
1093 do_pointer_grab(VNC_DISPLAY(widget), FALSE);
1094 }
1095
1096 return TRUE;
1097 }
1098
enter_event(GtkWidget * widget,GdkEventCrossing * crossing G_GNUC_UNUSED)1099 static gboolean enter_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC_UNUSED)
1100 {
1101 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
1102
1103 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
1104 return FALSE;
1105
1106 if (priv->grab_keyboard)
1107 do_keyboard_grab(VNC_DISPLAY(widget), FALSE);
1108
1109 if (priv->local_pointer)
1110 do_pointer_show(VNC_DISPLAY(widget));
1111
1112 #ifdef G_OS_WIN32
1113 win32_window = gdk_win32_window_get_impl_hwnd(gtk_widget_get_window(widget));
1114 #endif
1115
1116 return TRUE;
1117 }
1118
leave_event(GtkWidget * widget,GdkEventCrossing * crossing G_GNUC_UNUSED)1119 static gboolean leave_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC_UNUSED)
1120 {
1121 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
1122
1123 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
1124 return FALSE;
1125
1126 if (priv->grab_keyboard)
1127 do_keyboard_ungrab(VNC_DISPLAY(widget), FALSE);
1128
1129 if (priv->local_pointer)
1130 do_pointer_hide(VNC_DISPLAY(widget));
1131
1132 if (priv->grab_pointer && !priv->absolute)
1133 do_pointer_ungrab(VNC_DISPLAY(widget), FALSE);
1134
1135 return TRUE;
1136 }
1137
release_keys(VncDisplay * display)1138 static void release_keys(VncDisplay *display)
1139 {
1140 VncDisplayPrivate *priv = display->priv;
1141 int i;
1142
1143 for (i = 0 ; i < (int)(sizeof(priv->down_keyval)/sizeof(priv->down_keyval[0])) ; i++) {
1144 /* We are currently pressed so... */
1145 if (priv->down_scancode[i] != 0) {
1146 guint16 scancode = vnc_display_keymap_gdk2rfb(priv->keycode_map,
1147 priv->keycode_maplen,
1148 priv->down_scancode[i]);
1149 /* ..send the fake key release event to match */
1150 vnc_connection_key_event(priv->conn, 0,
1151 priv->down_keyval[i], scancode);
1152 priv->down_keyval[i] = 0;
1153 priv->down_scancode[i] = 0;
1154 }
1155 }
1156 }
1157
focus_in_event(GtkWidget * widget,GdkEventFocus * focus G_GNUC_UNUSED)1158 static gboolean focus_in_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSED)
1159 {
1160 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
1161
1162 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
1163 return FALSE;
1164
1165 if (!gtk_widget_get_realized(widget))
1166 return TRUE;
1167
1168 #ifdef G_OS_WIN32
1169 win32_window = gdk_win32_window_get_impl_hwnd(gtk_widget_get_window(widget));
1170 #endif
1171
1172 return TRUE;
1173 }
1174
1175
focus_out_event(GtkWidget * widget,GdkEventFocus * focus G_GNUC_UNUSED)1176 static gboolean focus_out_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSED)
1177 {
1178 VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
1179
1180 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
1181 return FALSE;
1182
1183 release_keys(VNC_DISPLAY(widget));
1184 #ifdef G_OS_WIN32
1185 win32_window = NULL;
1186 #endif
1187
1188 return TRUE;
1189 }
1190
1191
grab_notify(GtkWidget * widget,gboolean was_grabbed)1192 static void grab_notify(GtkWidget *widget, gboolean was_grabbed)
1193 {
1194 if (was_grabbed == FALSE)
1195 release_keys(VNC_DISPLAY(widget));
1196 }
1197
1198
realize_event(GtkWidget * widget)1199 static void realize_event(GtkWidget *widget)
1200 {
1201 VncDisplay *obj = VNC_DISPLAY(widget);
1202 VncDisplayPrivate *priv = obj->priv;
1203
1204 GTK_WIDGET_CLASS (vnc_display_parent_class)->realize (widget);
1205
1206 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(obj)),
1207 priv->remote_cursor ? priv->remote_cursor : priv->null_cursor);
1208 }
1209
1210
on_framebuffer_update(VncConnection * conn G_GNUC_UNUSED,int x,int y,int w,int h,gpointer opaque)1211 static void on_framebuffer_update(VncConnection *conn G_GNUC_UNUSED,
1212 int x, int y, int w, int h,
1213 gpointer opaque)
1214 {
1215 GtkWidget *widget = GTK_WIDGET(opaque);
1216 VncDisplay *obj = VNC_DISPLAY(widget);
1217 VncDisplayPrivate *priv = obj->priv;
1218 int ww, wh;
1219 int fbw, fbh;
1220
1221 fbw = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
1222 fbh = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
1223
1224 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
1225
1226 /* If we have a pixmap, update the region which changed.
1227 * If we don't have a pixmap, the entire thing will be
1228 * created & rendered during the drawing handler
1229 */
1230 if (priv->fbCache) {
1231 cairo_t *cr = cairo_create(priv->fbCache);
1232 cairo_surface_t *surface = vnc_cairo_framebuffer_get_surface(priv->fb);
1233
1234 cairo_rectangle(cr, x, y, w, h);
1235 cairo_clip(cr);
1236 cairo_set_source_surface(cr, surface, 0, 0);
1237 cairo_paint(cr);
1238
1239 cairo_destroy(cr);
1240 }
1241
1242 if (priv->allow_scaling) {
1243 double sx, sy;
1244
1245 /* Scale the VNC region to produce expose region */
1246
1247 sx = (double)ww / (double)fbw;
1248 sy = (double)wh / (double)fbh;
1249
1250 x *= sx;
1251 y *= sy;
1252 w *= sx;
1253 h *= sy;
1254
1255 /* Without this, we get horizontal & vertical line artifacts
1256 * when drawing. This "fix" is somewhat dubious though. The
1257 * true mistake & fix almost certainly lies elsewhere.
1258 */
1259 x -= 2;
1260 y -= 2;
1261 w += 4;
1262 h += 4;
1263 } else {
1264 int mw = 0, mh = 0;
1265
1266 /* Offset the VNC region to produce expose region */
1267
1268 if (ww > fbw)
1269 mw = (ww - fbw) / 2;
1270 if (wh > fbh)
1271 mh = (wh - fbh) / 2;
1272
1273 x += mw;
1274 y += mh;
1275 }
1276
1277 gtk_widget_queue_draw_area(widget, x, y, w, h);
1278
1279 vnc_connection_framebuffer_update_request(priv->conn, 1,
1280 0, 0,
1281 vnc_connection_get_width(priv->conn),
1282 vnc_connection_get_height(priv->conn));
1283 }
1284
1285
do_framebuffer_init(VncDisplay * obj,const VncPixelFormat * remoteFormat,int width,int height,gboolean quiet)1286 static void do_framebuffer_init(VncDisplay *obj,
1287 const VncPixelFormat *remoteFormat,
1288 int width, int height, gboolean quiet)
1289 {
1290 VncDisplayPrivate *priv = obj->priv;
1291
1292 if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
1293 return;
1294
1295 if (priv->fb) {
1296 g_object_unref(priv->fb);
1297 priv->fb = NULL;
1298 }
1299 if (priv->fbCache) {
1300 cairo_surface_destroy(priv->fbCache);
1301 priv->fbCache = NULL;
1302 }
1303
1304 if (priv->null_cursor == NULL) {
1305 priv->null_cursor = create_null_cursor();
1306 if (priv->local_pointer)
1307 do_pointer_show(obj);
1308 else if (priv->in_pointer_grab || priv->absolute)
1309 do_pointer_hide(obj);
1310 }
1311
1312 priv->fb = vnc_cairo_framebuffer_new(width, height, remoteFormat);
1313 vnc_connection_set_framebuffer(priv->conn, VNC_FRAMEBUFFER(priv->fb));
1314
1315 if (priv->force_size)
1316 gtk_widget_set_size_request(GTK_WIDGET(obj), width, height);
1317
1318 if (!quiet) {
1319 g_signal_emit(G_OBJECT(obj),
1320 signals[VNC_DESKTOP_RESIZE],
1321 0,
1322 width, height);
1323 }
1324 }
1325
on_desktop_resize(VncConnection * conn G_GNUC_UNUSED,int width,int height,gpointer opaque)1326 static void on_desktop_resize(VncConnection *conn G_GNUC_UNUSED,
1327 int width, int height,
1328 gpointer opaque)
1329 {
1330 VncDisplay *obj = VNC_DISPLAY(opaque);
1331 VncDisplayPrivate *priv = obj->priv;
1332 const VncPixelFormat *remoteFormat;
1333
1334 remoteFormat = vnc_connection_get_pixel_format(priv->conn);
1335
1336 do_framebuffer_init(opaque, remoteFormat, width, height, FALSE);
1337
1338 vnc_connection_framebuffer_update_request(priv->conn, 0, 0, 0, width, height);
1339 }
1340
on_pixel_format_changed(VncConnection * conn G_GNUC_UNUSED,VncPixelFormat * remoteFormat,gpointer opaque)1341 static void on_pixel_format_changed(VncConnection *conn G_GNUC_UNUSED,
1342 VncPixelFormat *remoteFormat,
1343 gpointer opaque)
1344 {
1345 VncDisplay *obj = VNC_DISPLAY(opaque);
1346 VncDisplayPrivate *priv = obj->priv;
1347 gint16 width = vnc_connection_get_width(priv->conn);
1348 gint16 height = vnc_connection_get_height(priv->conn);
1349
1350 do_framebuffer_init(opaque, remoteFormat, width, height, TRUE);
1351
1352 vnc_connection_framebuffer_update_request(priv->conn, 0, 0, 0, width, height);
1353 }
1354
vnc_display_set_preferred_pixel_format(VncDisplay * display)1355 static gboolean vnc_display_set_preferred_pixel_format(VncDisplay *display)
1356 {
1357 VncDisplayPrivate *priv = display->priv;
1358 GdkVisual *v = gtk_widget_get_visual(GTK_WIDGET(display));
1359 VncPixelFormat fmt;
1360 const VncPixelFormat *currentFormat;
1361
1362 memset(&fmt, 0, sizeof(fmt));
1363
1364 /* Get current pixel format for server */
1365 currentFormat = vnc_connection_get_pixel_format(priv->conn);
1366
1367 switch (priv->depth) {
1368 case VNC_DISPLAY_DEPTH_COLOR_DEFAULT:
1369 VNC_DEBUG ("Using default colour depth %d (%d bpp) (true color? %d)",
1370 currentFormat->depth, currentFormat->bits_per_pixel,
1371 currentFormat->true_color_flag);
1372 /* TigerVNC always sends back the encoding even if
1373 unchanged from what the server suggested. This is
1374 important with some VNC servers, since they won't
1375 otherwise send us the colour map entries */
1376 memcpy(&fmt, currentFormat, sizeof(fmt));
1377 break;
1378
1379 case VNC_DISPLAY_DEPTH_COLOR_FULL:
1380 fmt.depth = 24;
1381 fmt.bits_per_pixel = 32;
1382 fmt.red_max = 255;
1383 fmt.green_max = 255;
1384 fmt.blue_max = 255;
1385 fmt.red_shift = 16;
1386 fmt.green_shift = 8;
1387 fmt.blue_shift = 0;
1388 fmt.true_color_flag = 1;
1389 break;
1390
1391 case VNC_DISPLAY_DEPTH_COLOR_MEDIUM:
1392 fmt.depth = 15;
1393 fmt.bits_per_pixel = 16;
1394 fmt.red_max = 31;
1395 fmt.green_max = 31;
1396 fmt.blue_max = 31;
1397 fmt.red_shift = 11;
1398 fmt.green_shift = 6;
1399 fmt.blue_shift = 1;
1400 fmt.true_color_flag = 1;
1401 break;
1402
1403 case VNC_DISPLAY_DEPTH_COLOR_LOW:
1404 fmt.depth = 8;
1405 fmt.bits_per_pixel = 8;
1406 fmt.red_max = 7;
1407 fmt.green_max = 7;
1408 fmt.blue_max = 3;
1409 fmt.red_shift = 5;
1410 fmt.green_shift = 2;
1411 fmt.blue_shift = 0;
1412 fmt.true_color_flag = 1;
1413 break;
1414
1415 case VNC_DISPLAY_DEPTH_COLOR_ULTRA_LOW:
1416 fmt.depth = 3;
1417 fmt.bits_per_pixel = 8;
1418 fmt.red_max = 1;
1419 fmt.green_max = 1;
1420 fmt.blue_max = 1;
1421 fmt.red_shift = 7;
1422 fmt.green_shift = 6;
1423 fmt.blue_shift = 5;
1424 fmt.true_color_flag = 1;
1425 break;
1426
1427 default:
1428 g_assert_not_reached ();
1429 }
1430
1431 #if GTK_CHECK_VERSION (2, 21, 1)
1432 fmt.byte_order = gdk_visual_get_byte_order (v) == GDK_LSB_FIRST ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1433 #else
1434 fmt.byte_order = v->byte_order == GDK_LSB_FIRST ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1435 #endif
1436
1437 VNC_DEBUG ("Set depth color to %d (%d bpp)", fmt.depth, fmt.bits_per_pixel);
1438 if (!vnc_connection_set_pixel_format(priv->conn, &fmt))
1439 return FALSE;
1440
1441 return TRUE;
1442 }
1443
on_pointer_mode_changed(VncConnection * conn G_GNUC_UNUSED,gboolean absPointer,gpointer opaque)1444 static void on_pointer_mode_changed(VncConnection *conn G_GNUC_UNUSED,
1445 gboolean absPointer,
1446 gpointer opaque)
1447 {
1448 VncDisplay *obj = VNC_DISPLAY(opaque);
1449 VncDisplayPrivate *priv = obj->priv;
1450
1451 if (absPointer && priv->in_pointer_grab && priv->grab_pointer)
1452 do_pointer_ungrab(obj, FALSE);
1453
1454 priv->absolute = absPointer;
1455
1456 if (!priv->in_pointer_grab && !priv->absolute)
1457 do_pointer_show(obj);
1458 }
1459
on_auth_cred(VncConnection * conn G_GNUC_UNUSED,GValueArray * creds,gpointer opaque)1460 static void on_auth_cred(VncConnection *conn G_GNUC_UNUSED,
1461 GValueArray *creds,
1462 gpointer opaque)
1463 {
1464 VncDisplay *obj = VNC_DISPLAY(opaque);
1465 GValueArray *newCreds = g_value_array_new(0);
1466 gsize i;
1467
1468 for (i = 0 ; i < creds->n_values ; i++) {
1469 GValue *cred = g_value_array_get_nth(creds, i);
1470 GValue newCred;
1471 memset(&newCred, 0, sizeof(newCred));
1472 g_value_init(&newCred, VNC_TYPE_DISPLAY_CREDENTIAL);
1473 /* Take advantage that VncDisplayCredential &
1474 * VncConnectionCredential share same enum values
1475 */
1476 g_value_set_enum(&newCred, g_value_get_enum(cred));
1477 newCreds = g_value_array_append(newCreds, &newCred);
1478 }
1479
1480 g_signal_emit(G_OBJECT(obj), signals[VNC_AUTH_CREDENTIAL], 0, newCreds);
1481
1482 g_value_array_free(newCreds);
1483 }
1484
on_auth_choose_type(VncConnection * conn,GValueArray * types,gpointer opaque)1485 static void on_auth_choose_type(VncConnection *conn,
1486 GValueArray *types,
1487 gpointer opaque)
1488 {
1489 VncDisplay *obj = VNC_DISPLAY(opaque);
1490 VncDisplayPrivate *priv = obj->priv;
1491 GSList *l;
1492 guint i;
1493
1494 if (!types->n_values) {
1495 VNC_DEBUG("No auth types available to choose from");
1496 vnc_connection_shutdown(conn);
1497 return;
1498 }
1499
1500 for (l = priv->preferable_auths; l; l=l->next) {
1501 int pref = GPOINTER_TO_UINT (l->data);
1502
1503 for (i=0; i< types->n_values; i++) {
1504 GValue *type = g_value_array_get_nth(types, i);
1505 if (pref == g_value_get_enum(type)) {
1506 vnc_connection_set_auth_type(conn, pref);
1507 return;
1508 }
1509 }
1510 }
1511
1512 /* No sub-auth matching our supported auth so have to give up */
1513 VNC_DEBUG("No preferred auth type found");
1514 vnc_connection_shutdown(conn);
1515 }
1516
on_auth_choose_subtype(VncConnection * conn,unsigned int type,GValueArray * subtypes,gpointer opaque)1517 static void on_auth_choose_subtype(VncConnection *conn,
1518 unsigned int type,
1519 GValueArray *subtypes,
1520 gpointer opaque)
1521 {
1522 VncDisplay *obj = VNC_DISPLAY(opaque);
1523 VncDisplayPrivate *priv = obj->priv;
1524 GSList *l;
1525 guint i;
1526
1527 if (!subtypes->n_values) {
1528 VNC_DEBUG("No subtypes available to choose from");
1529 vnc_connection_shutdown(conn);
1530 return;
1531 }
1532
1533 if (type == VNC_CONNECTION_AUTH_TLS) {
1534 l = priv->preferable_auths;
1535 } else if (type == VNC_CONNECTION_AUTH_VENCRYPT) {
1536 l = priv->preferable_vencrypt_subauths;
1537 } else {
1538 VNC_DEBUG("Unexpected stackable auth type %u", type);
1539 vnc_connection_shutdown(conn);
1540 return;
1541 }
1542
1543 for (; l; l=l->next) {
1544 int pref = GPOINTER_TO_UINT (l->data);
1545
1546 /* Don't want to recursively do the same major auth */
1547 if (pref == type)
1548 continue;
1549
1550 for (i=0; i< subtypes->n_values; i++) {
1551 GValue *subtype = g_value_array_get_nth(subtypes, i);
1552 if (pref == g_value_get_enum(subtype)) {
1553 vnc_connection_set_auth_subtype(conn, pref);
1554 return;
1555 }
1556 }
1557 }
1558
1559 /* No sub-auth matching our supported auth so have to give up */
1560 VNC_DEBUG("No preferred auth subtype found");
1561 vnc_connection_shutdown(conn);
1562 }
1563
on_auth_failure(VncConnection * conn G_GNUC_UNUSED,const char * reason,gpointer opaque)1564 static void on_auth_failure(VncConnection *conn G_GNUC_UNUSED,
1565 const char *reason,
1566 gpointer opaque)
1567 {
1568 VncDisplay *obj = VNC_DISPLAY(opaque);
1569
1570 g_signal_emit(G_OBJECT(obj), signals[VNC_AUTH_FAILURE], 0, reason);
1571 }
1572
on_auth_unsupported(VncConnection * conn G_GNUC_UNUSED,unsigned int authType,gpointer opaque)1573 static void on_auth_unsupported(VncConnection *conn G_GNUC_UNUSED,
1574 unsigned int authType,
1575 gpointer opaque)
1576 {
1577 VncDisplay *obj = VNC_DISPLAY(opaque);
1578
1579 g_signal_emit(G_OBJECT(obj), signals[VNC_AUTH_UNSUPPORTED], 0, authType);
1580 }
1581
on_server_cut_text(VncConnection * conn G_GNUC_UNUSED,const gchar * text,gpointer opaque)1582 static void on_server_cut_text(VncConnection *conn G_GNUC_UNUSED,
1583 const gchar *text,
1584 gpointer opaque)
1585 {
1586 VncDisplay *obj = VNC_DISPLAY(opaque);
1587
1588 if (obj->priv->read_only)
1589 return;
1590
1591 g_signal_emit(G_OBJECT(obj), signals[VNC_SERVER_CUT_TEXT], 0, text);
1592 }
1593
on_bell(VncConnection * conn G_GNUC_UNUSED,gpointer opaque)1594 static void on_bell(VncConnection *conn G_GNUC_UNUSED,
1595 gpointer opaque)
1596 {
1597 VncDisplay *obj = VNC_DISPLAY(opaque);
1598
1599 g_signal_emit(G_OBJECT(obj), signals[VNC_BELL], 0);
1600 }
1601
on_cursor_changed(VncConnection * conn G_GNUC_UNUSED,VncCursor * cursor,gpointer opaque)1602 static void on_cursor_changed(VncConnection *conn G_GNUC_UNUSED,
1603 VncCursor *cursor,
1604 gpointer opaque)
1605 {
1606 VncDisplay *obj = VNC_DISPLAY(opaque);
1607 VncDisplayPrivate *priv = obj->priv;
1608
1609 VNC_DEBUG("Cursor changed %p x=%d y=%d w=%d h=%d",
1610 cursor,
1611 cursor ? vnc_cursor_get_hotx(cursor) : -1,
1612 cursor ? vnc_cursor_get_hoty(cursor) : -1,
1613 cursor ? vnc_cursor_get_width(cursor) : -1,
1614 cursor ? vnc_cursor_get_height(cursor) : -1);
1615
1616 if (priv->remote_cursor) {
1617 gdk_cursor_unref(priv->remote_cursor);
1618 priv->remote_cursor = NULL;
1619 }
1620
1621 if (cursor) {
1622 GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(obj));
1623 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(vnc_cursor_get_data(cursor),
1624 GDK_COLORSPACE_RGB,
1625 TRUE, 8,
1626 vnc_cursor_get_width(cursor),
1627 vnc_cursor_get_height(cursor),
1628 vnc_cursor_get_width(cursor) * 4,
1629 NULL, NULL);
1630 priv->remote_cursor = gdk_cursor_new_from_pixbuf(display,
1631 pixbuf,
1632 vnc_cursor_get_hotx(cursor),
1633 vnc_cursor_get_hoty(cursor));
1634 g_object_unref(pixbuf);
1635 }
1636
1637 if (priv->in_pointer_grab) {
1638 do_pointer_ungrab(obj, TRUE);
1639 do_pointer_grab(obj, TRUE);
1640 } else if (priv->absolute) {
1641 do_pointer_hide(obj);
1642 }
1643 }
1644
check_pixbuf_support(const char * name)1645 static gboolean check_pixbuf_support(const char *name)
1646 {
1647 GSList *list, *i;
1648
1649 list = gdk_pixbuf_get_formats();
1650
1651 for (i = list; i; i = i->next) {
1652 GdkPixbufFormat *fmt = i->data;
1653 gchar *fmt_name = gdk_pixbuf_format_get_name(fmt);
1654 int cmp;
1655
1656 cmp = strcmp(fmt_name, name);
1657 g_free (fmt_name);
1658
1659 if (!cmp)
1660 break;
1661 }
1662
1663 g_slist_free(list);
1664
1665 return !!(i);
1666 }
1667
on_connected(VncConnection * conn G_GNUC_UNUSED,gpointer opaque)1668 static void on_connected(VncConnection *conn G_GNUC_UNUSED,
1669 gpointer opaque)
1670 {
1671 VncDisplay *obj = VNC_DISPLAY(opaque);
1672
1673 g_signal_emit(G_OBJECT(obj), signals[VNC_CONNECTED], 0);
1674 VNC_DEBUG("Connected to VNC server");
1675 }
1676
1677
on_error(VncConnection * conn G_GNUC_UNUSED,const char * message,gpointer opaque)1678 static void on_error(VncConnection *conn G_GNUC_UNUSED,
1679 const char *message,
1680 gpointer opaque)
1681 {
1682 VncDisplay *obj = VNC_DISPLAY(opaque);
1683
1684 g_signal_emit(G_OBJECT(obj), signals[VNC_ERROR], 0, message);
1685 VNC_DEBUG("VNC server error");
1686 }
1687
1688
on_initialized(VncConnection * conn G_GNUC_UNUSED,gpointer opaque)1689 static void on_initialized(VncConnection *conn G_GNUC_UNUSED,
1690 gpointer opaque)
1691 {
1692 VncDisplay *obj = VNC_DISPLAY(opaque);
1693 VncDisplayPrivate *priv = obj->priv;
1694 int i;
1695
1696 /* The order determines which encodings the
1697 * server prefers when it has a choice to use */
1698 gint32 encodings[] = { VNC_CONNECTION_ENCODING_TIGHT_JPEG5,
1699 VNC_CONNECTION_ENCODING_TIGHT,
1700 VNC_CONNECTION_ENCODING_EXT_KEY_EVENT,
1701 VNC_CONNECTION_ENCODING_LED_STATE,
1702 VNC_CONNECTION_ENCODING_DESKTOP_RESIZE,
1703 VNC_CONNECTION_ENCODING_WMVi,
1704 VNC_CONNECTION_ENCODING_AUDIO,
1705 VNC_CONNECTION_ENCODING_RICH_CURSOR,
1706 VNC_CONNECTION_ENCODING_XCURSOR,
1707 VNC_CONNECTION_ENCODING_POINTER_CHANGE,
1708 VNC_CONNECTION_ENCODING_ZRLE,
1709 VNC_CONNECTION_ENCODING_HEXTILE,
1710 VNC_CONNECTION_ENCODING_RRE,
1711 VNC_CONNECTION_ENCODING_COPY_RECT,
1712 VNC_CONNECTION_ENCODING_RAW };
1713 int n_encodings = G_N_ELEMENTS(encodings);
1714
1715 #define REMOVE_ENCODING(e) \
1716 for (i = 0 ; i < n_encodings ; i++) { \
1717 if (encodings[i] == e) { \
1718 if (i < (n_encodings - 1)) \
1719 memmove(encodings + i, \
1720 encodings + (i + 1), \
1721 sizeof(gint32) * \
1722 (n_encodings - (i + 1))); \
1723 n_encodings--; \
1724 VNC_DEBUG("Removed encoding %d", e); \
1725 break; \
1726 } \
1727 }
1728
1729 if (!vnc_display_set_preferred_pixel_format(obj))
1730 goto error;
1731
1732 do_framebuffer_init(obj,
1733 vnc_connection_get_pixel_format(priv->conn),
1734 vnc_connection_get_width(priv->conn),
1735 vnc_connection_get_height(priv->conn),
1736 FALSE);
1737
1738 if (check_pixbuf_support("jpeg")) {
1739 if (!priv->allow_lossy)
1740 REMOVE_ENCODING(VNC_CONNECTION_ENCODING_TIGHT_JPEG5);
1741 } else {
1742 REMOVE_ENCODING(VNC_CONNECTION_ENCODING_TIGHT_JPEG5);
1743 REMOVE_ENCODING(VNC_CONNECTION_ENCODING_TIGHT);
1744 }
1745
1746 if (priv->keycode_map == NULL)
1747 REMOVE_ENCODING(VNC_CONNECTION_ENCODING_EXT_KEY_EVENT);
1748
1749 VNC_DEBUG("Sending %d encodings", n_encodings);
1750 if (!vnc_connection_set_encodings(priv->conn, n_encodings, encodings))
1751 goto error;
1752
1753 VNC_DEBUG("Requesting first framebuffer update");
1754 if (!vnc_connection_framebuffer_update_request(priv->conn, 0, 0, 0,
1755 vnc_connection_get_width(priv->conn),
1756 vnc_connection_get_height(priv->conn)))
1757 goto error;
1758
1759 g_signal_emit(G_OBJECT(obj), signals[VNC_INITIALIZED], 0);
1760
1761 VNC_DEBUG("Initialized VNC server");
1762 return;
1763
1764 error:
1765 vnc_connection_shutdown(priv->conn);
1766 }
1767
1768
on_disconnected(VncConnection * conn G_GNUC_UNUSED,gpointer opaque)1769 static void on_disconnected(VncConnection *conn G_GNUC_UNUSED,
1770 gpointer opaque)
1771 {
1772 VncDisplay *obj = VNC_DISPLAY(opaque);
1773 VNC_DEBUG("Disconnected from VNC server");
1774
1775 g_signal_emit(G_OBJECT(obj), signals[VNC_DISCONNECTED], 0);
1776 g_object_unref(G_OBJECT(obj));
1777 }
1778
1779
1780 /**
1781 * vnc_display_open_fd:
1782 * @obj: (transfer none): the VNC display widget
1783 * @fd: file descriptor to use for the connection
1784 *
1785 * Open a connection using @fd as the transport. If @fd
1786 * refers to a TCP connection, it is recommended to use
1787 * vnc_display_open_fd_with_hostname instead, to
1788 * provide the remote hostname. This allows use of
1789 * x509 based authentication which requires a hostname
1790 * to be available.
1791 *
1792 * Returns: TRUE if a connection was opened, FALSE if already open
1793 */
vnc_display_open_fd(VncDisplay * obj,int fd)1794 gboolean vnc_display_open_fd(VncDisplay *obj, int fd)
1795 {
1796 VncDisplayPrivate *priv;
1797
1798 g_return_val_if_fail(VNC_IS_DISPLAY(obj), FALSE);
1799
1800 priv = obj->priv;
1801 if (vnc_connection_is_open(priv->conn))
1802 return FALSE;
1803
1804 if (!vnc_connection_set_shared(priv->conn, priv->shared_flag))
1805 return FALSE;
1806
1807 if (!vnc_connection_open_fd(priv->conn, fd))
1808 return FALSE;
1809
1810 g_object_ref(G_OBJECT(obj));
1811
1812 return TRUE;
1813 }
1814
1815
1816 /**
1817 * vnc_display_open_fd_with_hostname:
1818 * @obj: (transfer none): the VNC display widget
1819 * @fd: file descriptor to use for the connection
1820 * @hostname: (transfer none)(nullable): the host associated with the connection
1821 *
1822 * Open a connection using @fd as the transport. The
1823 * @hostname provided should reflect the name of the
1824 * host that the @fd provides a connection to. This
1825 * will be used by some authentication schemes, for
1826 * example x509 certificate validation against @hostname.
1827 *
1828 * Returns: TRUE if a connection was opened, FALSE if already open
1829 */
vnc_display_open_fd_with_hostname(VncDisplay * obj,int fd,const char * hostname)1830 gboolean vnc_display_open_fd_with_hostname(VncDisplay *obj, int fd, const char *hostname)
1831 {
1832 VncDisplayPrivate *priv;
1833
1834 g_return_val_if_fail(VNC_IS_DISPLAY(obj), FALSE);
1835
1836 priv = obj->priv;
1837 if (vnc_connection_is_open(priv->conn))
1838 return FALSE;
1839
1840 if (!vnc_connection_set_shared(priv->conn, priv->shared_flag))
1841 return FALSE;
1842
1843 if (!vnc_connection_open_fd_with_hostname(priv->conn, fd, hostname))
1844 return FALSE;
1845
1846 g_object_ref(G_OBJECT(obj));
1847
1848 return TRUE;
1849 }
1850
1851
1852 /**
1853 * vnc_display_open_addr:
1854 * @obj: (transfer none): the VNC display widget
1855 * @addr: (transfer none): the socket address
1856 * @hostname: (transfer none)(nullable): the hostname
1857 *
1858 * Open a socket connection to server identified by @addr.
1859 * @addr may refer to either a TCP address (IPv4/6) or
1860 * a UNIX socket address. The @hostname provided should
1861 * reflect the name of the host that the @addr provides a
1862 * connection to, if it is not already available in @addr.
1863 * For example, if @addr points to a proxy server, then
1864 * @hostname can be used to provide the name of the final
1865 * endpoint. This will be used by some authentication
1866 * schemes, for example x509 certificate validation
1867 * against @hostname.
1868 *
1869 * Returns: TRUE if a connection was opened, FALSE if already open
1870 */
vnc_display_open_addr(VncDisplay * obj,GSocketAddress * addr,const char * hostname)1871 gboolean vnc_display_open_addr(VncDisplay *obj, GSocketAddress *addr, const char *hostname)
1872 {
1873 VncDisplayPrivate *priv;
1874
1875 g_return_val_if_fail(VNC_IS_DISPLAY(obj), FALSE);
1876 g_return_val_if_fail(addr != NULL, FALSE);
1877
1878 priv = obj->priv;
1879 if (vnc_connection_is_open(priv->conn))
1880 return FALSE;
1881
1882 if (!vnc_connection_set_shared(priv->conn, priv->shared_flag))
1883 return FALSE;
1884
1885 if (!vnc_connection_open_addr(priv->conn, addr, hostname))
1886 return FALSE;
1887
1888 g_object_ref(G_OBJECT(obj));
1889
1890 return TRUE;
1891 }
1892
1893
1894 /**
1895 * vnc_display_open_host:
1896 * @obj: (transfer none): the VNC display widget
1897 * @host: (transfer none): the host name or IP address
1898 * @port: (transfer none): the service name or port number
1899 *
1900 * Open a TCP connection to the remote desktop at @host
1901 * listening on @port.
1902 *
1903 * Returns: TRUE if a connection was opened, FALSE if already open
1904 */
vnc_display_open_host(VncDisplay * obj,const char * host,const char * port)1905 gboolean vnc_display_open_host(VncDisplay *obj, const char *host, const char *port)
1906 {
1907 VncDisplayPrivate *priv;
1908
1909 g_return_val_if_fail(VNC_IS_DISPLAY(obj), FALSE);
1910 g_return_val_if_fail(host != NULL, FALSE);
1911 g_return_val_if_fail(port != NULL, FALSE);
1912
1913 priv = obj->priv;
1914 if (vnc_connection_is_open(priv->conn))
1915 return FALSE;
1916
1917 if (!vnc_connection_set_shared(priv->conn, priv->shared_flag))
1918 return FALSE;
1919
1920 if (!vnc_connection_open_host(priv->conn, host, port))
1921 return FALSE;
1922
1923 g_object_ref(G_OBJECT(obj));
1924
1925 return TRUE;
1926 }
1927
1928
1929 /**
1930 * vnc_display_is_open:
1931 * @obj: (transfer none): the VNC display widget
1932 *
1933 * Check if the connection for the display is currently open
1934 *
1935 * Returns: TRUE if open, FALSE if closing/closed
1936 */
vnc_display_is_open(VncDisplay * obj)1937 gboolean vnc_display_is_open(VncDisplay *obj)
1938 {
1939 g_return_val_if_fail(VNC_IS_DISPLAY(obj), FALSE);
1940
1941 return vnc_connection_is_open(obj->priv->conn);
1942 }
1943
1944
1945 /**
1946 * vnc_display_close:
1947 * @obj: (transfer none): the VNC display widget
1948 *
1949 * Request that the connection to the remote display
1950 * is closed. The actual close will complete asynchronously
1951 * and the "vnc-disconnected" signal will be emitted once
1952 * complete.
1953 */
vnc_display_close(VncDisplay * obj)1954 void vnc_display_close(VncDisplay *obj)
1955 {
1956 VncDisplayPrivate *priv;
1957 GtkWidget *widget = GTK_WIDGET(obj);
1958
1959 g_return_if_fail(VNC_IS_DISPLAY(obj));
1960
1961 priv = obj->priv;
1962 if (vnc_connection_is_open(priv->conn)) {
1963 VNC_DEBUG("Requesting graceful shutdown of connection");
1964 vnc_connection_shutdown(priv->conn);
1965 }
1966
1967 if (gtk_widget_get_window(widget)) {
1968 gint width, height;
1969
1970 gdk_drawable_get_size(gtk_widget_get_window(widget), &width, &height);
1971 gtk_widget_queue_draw_area(widget, 0, 0, width, height);
1972 }
1973 }
1974
1975
1976 /**
1977 * vnc_display_get_connection:
1978 * @obj: (transfer none): the VNC display widget
1979 *
1980 * Get the VNC connection object associated with the
1981 * display
1982 *
1983 * Returns: (transfer none): the connection object
1984 */
vnc_display_get_connection(VncDisplay * obj)1985 VncConnection * vnc_display_get_connection(VncDisplay *obj)
1986 {
1987 g_return_val_if_fail(VNC_IS_DISPLAY(obj), NULL);
1988
1989 return obj->priv->conn;
1990 }
1991
1992
1993 /**
1994 * vnc_display_send_keys:
1995 * @obj: (transfer none): the VNC display widget
1996 * @keyvals: (array length=nkeyvals): Keyval array
1997 * @nkeyvals: Length of keyvals
1998 *
1999 * Send keyval click events to the display. Al the
2000 * key press events will be sent first and then all
2001 * the key release events.
2002 *
2003 * @keyvals should contain the X11 key value constants
2004 */
vnc_display_send_keys(VncDisplay * obj,const guint * keyvals,int nkeyvals)2005 void vnc_display_send_keys(VncDisplay *obj, const guint *keyvals, int nkeyvals)
2006 {
2007 g_return_if_fail(VNC_IS_DISPLAY(obj));
2008
2009 vnc_display_send_keys_ex(obj, keyvals,
2010 nkeyvals, VNC_DISPLAY_KEY_EVENT_CLICK);
2011 }
2012
get_scancode_from_keyval(VncDisplay * obj,guint keyval)2013 static guint get_scancode_from_keyval(VncDisplay *obj, guint keyval)
2014 {
2015 VncDisplayPrivate *priv = obj->priv;
2016 guint keycode = 0;
2017 GdkKeymapKey *keys = NULL;
2018 gint n_keys = 0;
2019
2020 if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(),
2021 keyval, &keys, &n_keys)) {
2022 /* FIXME what about levels? */
2023 keycode = keys[0].keycode;
2024 g_free(keys);
2025 }
2026
2027 return vnc_display_keymap_gdk2rfb(priv->keycode_map, priv->keycode_maplen, keycode);
2028 }
2029
2030
2031 /**
2032 * vnc_display_send_keys_ex:
2033 * @obj: (transfer none): the VNC display widget
2034 * @keyvals: (array length=nkeyvals): Keyval array
2035 * @nkeyvals: Length of keyvals
2036 * @kind: the type of event to send
2037 *
2038 * Sends key events to the remote server. @keyvals
2039 * should contain X11 key code values. These will
2040 * be automatically converted to XT scancodes if
2041 * needed
2042 *
2043 * If @kind is VNC_DISPLAY_KEY_EVENT_CLICK then all
2044 * the key press events will be sent first, followed
2045 * by all the key release events.
2046 */
vnc_display_send_keys_ex(VncDisplay * obj,const guint * keyvals,int nkeyvals,VncDisplayKeyEvent kind)2047 void vnc_display_send_keys_ex(VncDisplay *obj, const guint *keyvals,
2048 int nkeyvals, VncDisplayKeyEvent kind)
2049 {
2050 int i;
2051
2052 g_return_if_fail(VNC_IS_DISPLAY(obj));
2053
2054 if (obj->priv->conn == NULL || !vnc_connection_is_open(obj->priv->conn) || obj->priv->read_only)
2055 return;
2056
2057 if (kind & VNC_DISPLAY_KEY_EVENT_PRESS) {
2058 for (i = 0 ; i < nkeyvals ; i++)
2059 vnc_connection_key_event(obj->priv->conn, 1, keyvals[i],
2060 get_scancode_from_keyval(obj, keyvals[i]));
2061 }
2062
2063 if (kind & VNC_DISPLAY_KEY_EVENT_RELEASE) {
2064 for (i = (nkeyvals-1) ; i >= 0 ; i--)
2065 vnc_connection_key_event(obj->priv->conn, 0, keyvals[i],
2066 get_scancode_from_keyval(obj, keyvals[i]));
2067 }
2068 }
2069
2070
2071 /**
2072 * vnc_display_send_pointer:
2073 * @obj: (transfer none): the VNC display widget
2074 * @x: the desired horizontal position
2075 * @y: the desired vertical position
2076 * @button_mask: the state of the buttons
2077 *
2078 * Move the remote pointer to position (@x, @y) and set the
2079 * button state to @button_mask. This method will only
2080 * work if the desktop is using absolute pointer mode. It
2081 * will be a no-op if in relative pointer mode.
2082 */
vnc_display_send_pointer(VncDisplay * obj,gint x,gint y,int button_mask)2083 void vnc_display_send_pointer(VncDisplay *obj, gint x, gint y, int button_mask)
2084 {
2085 VncDisplayPrivate *priv = obj->priv;
2086
2087 g_return_if_fail(VNC_IS_DISPLAY(obj));
2088
2089 if (priv->conn == NULL || !vnc_connection_is_open(obj->priv->conn))
2090 return;
2091
2092 if (priv->absolute) {
2093 priv->button_mask = button_mask;
2094 priv->last_x = x;
2095 priv->last_y = y;
2096 vnc_connection_pointer_event(priv->conn, priv->button_mask, x, y);
2097 }
2098 }
2099
vnc_display_destroy(GtkObject * obj)2100 static void vnc_display_destroy (GtkObject *obj)
2101 {
2102 VncDisplay *display = VNC_DISPLAY (obj);
2103 VNC_DEBUG("Display destroy, requesting that VNC connection close");
2104 vnc_display_close(display);
2105 GTK_OBJECT_CLASS (vnc_display_parent_class)->destroy (obj);
2106 }
2107
2108
vnc_display_finalize(GObject * obj)2109 static void vnc_display_finalize (GObject *obj)
2110 {
2111 VncDisplay *display = VNC_DISPLAY (obj);
2112 VncDisplayPrivate *priv = display->priv;
2113
2114 VNC_DEBUG("Releasing VNC widget");
2115 if (vnc_connection_is_open(priv->conn)) {
2116 g_warning("VNC widget finalized before the connection finished shutting down\n");
2117 }
2118 g_object_unref(G_OBJECT(priv->conn));
2119 display->priv->conn = NULL;
2120
2121 if (priv->fb) {
2122 g_object_unref(priv->fb);
2123 priv->fb = NULL;
2124 }
2125 if (priv->fbCache) {
2126 cairo_surface_destroy(priv->fbCache);
2127 priv->fbCache = NULL;
2128 }
2129
2130 if (priv->null_cursor) {
2131 gdk_cursor_unref (priv->null_cursor);
2132 priv->null_cursor = NULL;
2133 }
2134
2135 if (priv->remote_cursor) {
2136 gdk_cursor_unref(priv->remote_cursor);
2137 priv->remote_cursor = NULL;
2138 }
2139
2140 if (priv->vncgrabseq) {
2141 vnc_grab_sequence_free(priv->vncgrabseq);
2142 priv->vncgrabseq = NULL;
2143 }
2144
2145 if (priv->vncactiveseq) {
2146 g_free(priv->vncactiveseq);
2147 priv->vncactiveseq = NULL;
2148 }
2149
2150 g_slist_free (priv->preferable_auths);
2151 g_slist_free (priv->preferable_vencrypt_subauths);
2152
2153 G_OBJECT_CLASS (vnc_display_parent_class)->finalize (obj);
2154 }
2155
vnc_display_class_init(VncDisplayClass * klass)2156 static void vnc_display_class_init(VncDisplayClass *klass)
2157 {
2158 GObjectClass *object_class = G_OBJECT_CLASS (klass);
2159 GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass);
2160 GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
2161
2162 #if GTK_CHECK_VERSION (2, 91, 0)
2163 gtkwidget_class->draw = draw_event;
2164 #else
2165 gtkwidget_class->expose_event = expose_event;
2166 #endif
2167 gtkwidget_class->motion_notify_event = motion_event;
2168 gtkwidget_class->button_press_event = button_event;
2169 gtkwidget_class->button_release_event = button_event;
2170 gtkwidget_class->scroll_event = scroll_event;
2171 gtkwidget_class->key_press_event = key_event;
2172 gtkwidget_class->key_release_event = key_event;
2173 gtkwidget_class->enter_notify_event = enter_event;
2174 gtkwidget_class->leave_notify_event = leave_event;
2175 gtkwidget_class->focus_in_event = focus_in_event;
2176 gtkwidget_class->focus_out_event = focus_out_event;
2177 gtkwidget_class->grab_notify = grab_notify;
2178 gtkwidget_class->realize = realize_event;
2179
2180 object_class->finalize = vnc_display_finalize;
2181 object_class->get_property = vnc_display_get_property;
2182 object_class->set_property = vnc_display_set_property;
2183
2184 gtkobject_class->destroy = vnc_display_destroy;
2185
2186 g_object_class_install_property (object_class,
2187 PROP_POINTER_LOCAL,
2188 g_param_spec_boolean ( "local-pointer",
2189 "Local Pointer",
2190 "Whether we should use the local pointer",
2191 FALSE,
2192 G_PARAM_READWRITE |
2193 G_PARAM_CONSTRUCT |
2194 G_PARAM_STATIC_NAME |
2195 G_PARAM_STATIC_NICK |
2196 G_PARAM_STATIC_BLURB));
2197 g_object_class_install_property (object_class,
2198 PROP_POINTER_GRAB,
2199 g_param_spec_boolean ( "grab-pointer",
2200 "Grab Pointer",
2201 "Whether we should grab the pointer",
2202 FALSE,
2203 G_PARAM_READWRITE |
2204 G_PARAM_CONSTRUCT |
2205 G_PARAM_STATIC_NAME |
2206 G_PARAM_STATIC_NICK |
2207 G_PARAM_STATIC_BLURB));
2208 g_object_class_install_property (object_class,
2209 PROP_KEYBOARD_GRAB,
2210 g_param_spec_boolean ( "grab-keyboard",
2211 "Grab Keyboard",
2212 "Whether we should grab the keyboard",
2213 FALSE,
2214 G_PARAM_READWRITE |
2215 G_PARAM_CONSTRUCT |
2216 G_PARAM_STATIC_NAME |
2217 G_PARAM_STATIC_NICK |
2218 G_PARAM_STATIC_BLURB));
2219 g_object_class_install_property (object_class,
2220 PROP_READ_ONLY,
2221 g_param_spec_boolean ( "read-only",
2222 "Read Only",
2223 "Whether this connection is read-only mode",
2224 FALSE,
2225 G_PARAM_READWRITE |
2226 G_PARAM_CONSTRUCT |
2227 G_PARAM_STATIC_NAME |
2228 G_PARAM_STATIC_NICK |
2229 G_PARAM_STATIC_BLURB));
2230 g_object_class_install_property (object_class,
2231 PROP_WIDTH,
2232 g_param_spec_int ( "width",
2233 "Width",
2234 "The width of the remote screen",
2235 0,
2236 G_MAXINT,
2237 0,
2238 G_PARAM_READABLE |
2239 G_PARAM_STATIC_NAME |
2240 G_PARAM_STATIC_NICK |
2241 G_PARAM_STATIC_BLURB));
2242 g_object_class_install_property (object_class,
2243 PROP_HEIGHT,
2244 g_param_spec_int ( "height",
2245 "Height",
2246 "The height of the remote screen",
2247 0,
2248 G_MAXINT,
2249 0,
2250 G_PARAM_READABLE |
2251 G_PARAM_STATIC_NAME |
2252 G_PARAM_STATIC_NICK |
2253 G_PARAM_STATIC_BLURB));
2254 g_object_class_install_property (object_class,
2255 PROP_NAME,
2256 g_param_spec_string ( "name",
2257 "Name",
2258 "The screen name of the remote connection",
2259 NULL,
2260 G_PARAM_READABLE |
2261 G_PARAM_STATIC_NAME |
2262 G_PARAM_STATIC_NICK |
2263 G_PARAM_STATIC_BLURB));
2264 g_object_class_install_property (object_class,
2265 PROP_LOSSY_ENCODING,
2266 g_param_spec_boolean ( "lossy-encoding",
2267 "Lossy Encoding",
2268 "Whether we should use a lossy encoding",
2269 FALSE,
2270 G_PARAM_READWRITE |
2271 G_PARAM_CONSTRUCT |
2272 G_PARAM_STATIC_NAME |
2273 G_PARAM_STATIC_NICK |
2274 G_PARAM_STATIC_BLURB));
2275 g_object_class_install_property (object_class,
2276 PROP_SCALING,
2277 g_param_spec_boolean ( "scaling",
2278 "Scaling",
2279 "Whether we should use scaling",
2280 FALSE,
2281 G_PARAM_READWRITE |
2282 G_PARAM_CONSTRUCT |
2283 G_PARAM_STATIC_NAME |
2284 G_PARAM_STATIC_NICK |
2285 G_PARAM_STATIC_BLURB));
2286 g_object_class_install_property (object_class,
2287 PROP_SHARED_FLAG,
2288 g_param_spec_boolean ( "shared-flag",
2289 "Shared Flag",
2290 "Whether we should leave other clients connected to the server",
2291 FALSE,
2292 G_PARAM_READWRITE |
2293 G_PARAM_CONSTRUCT |
2294 G_PARAM_STATIC_NAME |
2295 G_PARAM_STATIC_NICK |
2296 G_PARAM_STATIC_BLURB));
2297 g_object_class_install_property (object_class,
2298 PROP_FORCE_SIZE,
2299 g_param_spec_boolean ( "force-size",
2300 "Force widget size",
2301 "Whether we should define the widget size",
2302 TRUE,
2303 G_PARAM_READWRITE |
2304 G_PARAM_CONSTRUCT |
2305 G_PARAM_STATIC_NAME |
2306 G_PARAM_STATIC_NICK |
2307 G_PARAM_STATIC_BLURB));
2308
2309 g_object_class_install_property (object_class,
2310 PROP_SMOOTHING,
2311 g_param_spec_boolean ( "smoothing",
2312 "Smooth scaling",
2313 "Whether we should smoothly interpolate when scaling",
2314 TRUE,
2315 G_PARAM_READWRITE |
2316 G_PARAM_CONSTRUCT |
2317 G_PARAM_STATIC_NAME |
2318 G_PARAM_STATIC_NICK |
2319 G_PARAM_STATIC_BLURB));
2320
2321 g_object_class_install_property (object_class,
2322 PROP_DEPTH,
2323 g_param_spec_enum ( "depth",
2324 "Depth",
2325 "The color depth",
2326 VNC_TYPE_DISPLAY_DEPTH_COLOR,
2327 VNC_DISPLAY_DEPTH_COLOR_DEFAULT,
2328 G_PARAM_READWRITE |
2329 G_PARAM_CONSTRUCT |
2330 G_PARAM_STATIC_NAME |
2331 G_PARAM_STATIC_NICK |
2332 G_PARAM_STATIC_BLURB));
2333 g_object_class_install_property (object_class,
2334 PROP_GRAB_KEYS,
2335 g_param_spec_boxed( "grab-keys",
2336 "Grab keys",
2337 "The key grab sequence",
2338 VNC_TYPE_GRAB_SEQUENCE,
2339 G_PARAM_READWRITE |
2340 G_PARAM_CONSTRUCT |
2341 G_PARAM_STATIC_NAME |
2342 G_PARAM_STATIC_NICK |
2343 G_PARAM_STATIC_BLURB));
2344 g_object_class_install_property (object_class,
2345 PROP_CONNECTION,
2346 g_param_spec_object("connection",
2347 "Connection",
2348 "The VNC connection",
2349 VNC_TYPE_CONNECTION,
2350 G_PARAM_READABLE |
2351 G_PARAM_STATIC_NAME |
2352 G_PARAM_STATIC_NICK |
2353 G_PARAM_STATIC_BLURB));
2354
2355 signals[VNC_CONNECTED] =
2356 g_signal_new ("vnc-connected",
2357 G_OBJECT_CLASS_TYPE (object_class),
2358 G_SIGNAL_RUN_FIRST,
2359 G_STRUCT_OFFSET (VncDisplayClass, vnc_connected),
2360 NULL, NULL,
2361 g_cclosure_marshal_VOID__VOID,
2362 G_TYPE_NONE,
2363 0);
2364
2365 signals[VNC_INITIALIZED] =
2366 g_signal_new ("vnc-initialized",
2367 G_OBJECT_CLASS_TYPE (object_class),
2368 G_SIGNAL_RUN_FIRST,
2369 G_STRUCT_OFFSET (VncDisplayClass, vnc_initialized),
2370 NULL, NULL,
2371 g_cclosure_marshal_VOID__VOID,
2372 G_TYPE_NONE,
2373 0);
2374
2375 signals[VNC_DISCONNECTED] =
2376 g_signal_new ("vnc-disconnected",
2377 G_OBJECT_CLASS_TYPE (object_class),
2378 G_SIGNAL_RUN_FIRST,
2379 G_STRUCT_OFFSET (VncDisplayClass, vnc_disconnected),
2380 NULL, NULL,
2381 g_cclosure_marshal_VOID__VOID,
2382 G_TYPE_NONE,
2383 0);
2384
2385 signals[VNC_ERROR] =
2386 g_signal_new ("vnc-error",
2387 G_OBJECT_CLASS_TYPE (object_class),
2388 G_SIGNAL_RUN_FIRST,
2389 0,
2390 NULL, NULL,
2391 g_cclosure_marshal_VOID__STRING,
2392 G_TYPE_NONE,
2393 1,
2394 G_TYPE_STRING);
2395
2396 signals[VNC_AUTH_CREDENTIAL] =
2397 g_signal_new ("vnc-auth-credential",
2398 G_OBJECT_CLASS_TYPE (object_class),
2399 G_SIGNAL_RUN_FIRST,
2400 G_STRUCT_OFFSET (VncDisplayClass, vnc_auth_credential),
2401 NULL, NULL,
2402 g_cclosure_marshal_VOID__BOXED,
2403 G_TYPE_NONE,
2404 1,
2405 G_TYPE_VALUE_ARRAY);
2406
2407
2408 signals[VNC_POINTER_GRAB] =
2409 g_signal_new("vnc-pointer-grab",
2410 G_TYPE_FROM_CLASS(klass),
2411 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2412 0,
2413 NULL,
2414 NULL,
2415 g_cclosure_marshal_VOID__VOID,
2416 G_TYPE_NONE,
2417 0);
2418
2419 signals[VNC_POINTER_UNGRAB] =
2420 g_signal_new("vnc-pointer-ungrab",
2421 G_TYPE_FROM_CLASS(klass),
2422 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2423 0,
2424 NULL,
2425 NULL,
2426 g_cclosure_marshal_VOID__VOID,
2427 G_TYPE_NONE,
2428 0);
2429
2430 signals[VNC_KEYBOARD_GRAB] =
2431 g_signal_new("vnc-keyboard-grab",
2432 G_TYPE_FROM_CLASS(klass),
2433 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2434 0,
2435 NULL,
2436 NULL,
2437 g_cclosure_marshal_VOID__VOID,
2438 G_TYPE_NONE,
2439 0);
2440
2441 signals[VNC_KEYBOARD_UNGRAB] =
2442 g_signal_new("vnc-keyboard-ungrab",
2443 G_TYPE_FROM_CLASS(klass),
2444 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2445 0,
2446 NULL,
2447 NULL,
2448 g_cclosure_marshal_VOID__VOID,
2449 G_TYPE_NONE,
2450 0);
2451
2452
2453 signals[VNC_DESKTOP_RESIZE] =
2454 g_signal_new("vnc-desktop-resize",
2455 G_TYPE_FROM_CLASS(klass),
2456 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2457 0,
2458 NULL,
2459 NULL,
2460 g_cclosure_user_marshal_VOID__INT_INT,
2461 G_TYPE_NONE,
2462 2,
2463 G_TYPE_INT, G_TYPE_INT);
2464
2465 signals[VNC_AUTH_FAILURE] =
2466 g_signal_new("vnc-auth-failure",
2467 G_TYPE_FROM_CLASS(klass),
2468 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2469 0,
2470 NULL,
2471 NULL,
2472 g_cclosure_marshal_VOID__STRING,
2473 G_TYPE_NONE,
2474 1,
2475 G_TYPE_STRING);
2476
2477 signals[VNC_AUTH_UNSUPPORTED] =
2478 g_signal_new("vnc-auth-unsupported",
2479 G_TYPE_FROM_CLASS(klass),
2480 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2481 0,
2482 NULL,
2483 NULL,
2484 g_cclosure_marshal_VOID__UINT,
2485 G_TYPE_NONE,
2486 1,
2487 G_TYPE_UINT);
2488
2489 signals[VNC_SERVER_CUT_TEXT] =
2490 g_signal_new("vnc-server-cut-text",
2491 G_TYPE_FROM_CLASS(klass),
2492 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2493 0,
2494 NULL,
2495 NULL,
2496 g_cclosure_marshal_VOID__STRING,
2497 G_TYPE_NONE,
2498 1,
2499 G_TYPE_STRING);
2500
2501 signals[VNC_BELL] =
2502 g_signal_new("vnc-bell",
2503 G_TYPE_FROM_CLASS(klass),
2504 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
2505 0,
2506 NULL,
2507 NULL,
2508 g_cclosure_marshal_VOID__VOID,
2509 G_TYPE_NONE,
2510 0);
2511
2512 g_type_class_add_private(klass, sizeof(VncDisplayPrivate));
2513 }
2514
vnc_display_init(VncDisplay * display)2515 static void vnc_display_init(VncDisplay *display)
2516 {
2517 GtkWidget *widget = GTK_WIDGET(display);
2518 VncDisplayPrivate *priv;
2519
2520 gtk_widget_set_can_focus (widget, TRUE);
2521
2522 gtk_widget_add_events(widget,
2523 GDK_POINTER_MOTION_MASK |
2524 GDK_BUTTON_PRESS_MASK |
2525 GDK_BUTTON_RELEASE_MASK |
2526 GDK_BUTTON_MOTION_MASK |
2527 GDK_ENTER_NOTIFY_MASK |
2528 GDK_LEAVE_NOTIFY_MASK |
2529 GDK_SCROLL_MASK |
2530 GDK_KEY_PRESS_MASK);
2531 /* We already have off-screen buffers we render to
2532 * but with GTK-3 there are problems with overlaid
2533 * windows. We end up rendering over the top of the
2534 * child overlaid windows despite having a clip
2535 * mask set :-( We've turned on GTK3's built-in
2536 * double buffering to work around this until we
2537 * find a better idea.
2538 */
2539 #if GTK_CHECK_VERSION(3, 0, 0)
2540 gtk_widget_set_double_buffered(widget, TRUE);
2541 #else
2542 gtk_widget_set_double_buffered(widget, FALSE);
2543 #endif
2544
2545 priv = display->priv = VNC_DISPLAY_GET_PRIVATE(display);
2546 memset(priv, 0, sizeof(VncDisplayPrivate));
2547 priv->last_x = -1;
2548 priv->last_y = -1;
2549 priv->absolute = TRUE;
2550 priv->read_only = FALSE;
2551 priv->allow_lossy = FALSE;
2552 priv->allow_scaling = FALSE;
2553 priv->grab_pointer = FALSE;
2554 priv->grab_keyboard = FALSE;
2555 priv->local_pointer = FALSE;
2556 priv->shared_flag = FALSE;
2557 priv->force_size = TRUE;
2558 priv->smoothing = TRUE;
2559 priv->vncgrabseq = vnc_grab_sequence_new_from_string("Control_L+Alt_L");
2560 priv->vncactiveseq = g_new0(gboolean, priv->vncgrabseq->nkeysyms);
2561
2562 /*
2563 * Both these two provide TLS based auth, and can layer
2564 * all the other auth types on top. So these two must
2565 * be the first listed
2566 */
2567 priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_VENCRYPT));
2568 priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_TLS));
2569
2570 /*
2571 * Then stackable auth types in order of preference
2572 */
2573 priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_SASL));
2574 priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_MSLOGON));
2575 priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_ARD));
2576 priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_VNC));
2577
2578 /*
2579 * Or nothing at all
2580 */
2581 priv->preferable_auths = g_slist_append (priv->preferable_auths, GUINT_TO_POINTER (VNC_CONNECTION_AUTH_NONE));
2582
2583
2584 /* Prefered order for VeNCrypt subtypes */
2585 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2586 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_X509SASL));
2587 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2588 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_X509PLAIN));
2589 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2590 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_X509VNC));
2591 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2592 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_X509NONE));
2593 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2594 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_TLSSASL));
2595 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2596 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_TLSPLAIN));
2597 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2598 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_TLSVNC));
2599 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2600 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_TLSNONE));
2601 /*
2602 * Refuse fully cleartext passwords
2603 priv->preferable_vencrypt_subauths = g_slist_append(priv->preferable_vencrypt_subauths,
2604 GUINT_TO_POINTER(VNC_CONNECTION_AUTH_VENCRYPT_PLAIN));
2605 */
2606
2607 priv->conn = vnc_connection_new();
2608
2609 g_signal_connect(G_OBJECT(priv->conn), "vnc-cursor-changed",
2610 G_CALLBACK(on_cursor_changed), display);
2611 g_signal_connect(G_OBJECT(priv->conn), "vnc-pointer-mode-changed",
2612 G_CALLBACK(on_pointer_mode_changed), display);
2613 g_signal_connect(G_OBJECT(priv->conn), "vnc-bell",
2614 G_CALLBACK(on_bell), display);
2615 g_signal_connect(G_OBJECT(priv->conn), "vnc-server-cut-text",
2616 G_CALLBACK(on_server_cut_text), display);
2617 g_signal_connect(G_OBJECT(priv->conn), "vnc-framebuffer-update",
2618 G_CALLBACK(on_framebuffer_update), display);
2619 g_signal_connect(G_OBJECT(priv->conn), "vnc-desktop-resize",
2620 G_CALLBACK(on_desktop_resize), display);
2621 g_signal_connect(G_OBJECT(priv->conn), "vnc-pixel-format-changed",
2622 G_CALLBACK(on_pixel_format_changed), display);
2623 g_signal_connect(G_OBJECT(priv->conn), "vnc-auth-failure",
2624 G_CALLBACK(on_auth_failure), display);
2625 g_signal_connect(G_OBJECT(priv->conn), "vnc-auth-unsupported",
2626 G_CALLBACK(on_auth_unsupported), display);
2627 g_signal_connect(G_OBJECT(priv->conn), "vnc-auth-credential",
2628 G_CALLBACK(on_auth_cred), display);
2629 g_signal_connect(G_OBJECT(priv->conn), "vnc-auth-choose-type",
2630 G_CALLBACK(on_auth_choose_type), display);
2631 g_signal_connect(G_OBJECT(priv->conn), "vnc-auth-choose-subtype",
2632 G_CALLBACK(on_auth_choose_subtype), display);
2633 g_signal_connect(G_OBJECT(priv->conn), "vnc-connected",
2634 G_CALLBACK(on_connected), display);
2635 g_signal_connect(G_OBJECT(priv->conn), "vnc-initialized",
2636 G_CALLBACK(on_initialized), display);
2637 g_signal_connect(G_OBJECT(priv->conn), "vnc-disconnected",
2638 G_CALLBACK(on_disconnected), display);
2639 g_signal_connect(G_OBJECT(priv->conn), "vnc-error",
2640 G_CALLBACK(on_error), display);
2641
2642 priv->keycode_map = vnc_display_keymap_gdk2rfb_table(&priv->keycode_maplen);
2643 }
2644
2645
2646 /**
2647 * vnc_display_set_credential:
2648 * @obj: (transfer none): the VNC display widget
2649 * @type: the authentication credential type
2650 * @data: (transfer none): the value associated with the credential
2651 *
2652 * Sets the value of the authentication credential
2653 * @type to the string @data.
2654 *
2655 * @type is one of the VncConnectionCredential enum vlaues
2656 *
2657 * Returns: TRUE if an error occurs, FALSE otherwise
2658 */
vnc_display_set_credential(VncDisplay * obj,int type,const gchar * data)2659 gboolean vnc_display_set_credential(VncDisplay *obj, int type, const gchar *data)
2660 {
2661 return !vnc_connection_set_credential(obj->priv->conn, type, data);
2662 }
2663
2664
2665 /**
2666 * vnc_display_set_poiter_local:
2667 * @obj: (transfer none): the VNC display widget
2668 * @enable: TRUE to show a local cursor, FALSE otherwise
2669 *
2670 * If @enable is TRUE, then a local mouse cursor will be
2671 * made visible. If @enable is FALSE, the local mouse
2672 * cursor will be hidden.
2673 */
vnc_display_set_pointer_local(VncDisplay * obj,gboolean enable)2674 void vnc_display_set_pointer_local(VncDisplay *obj, gboolean enable)
2675 {
2676 if (obj->priv->null_cursor) {
2677 if (enable)
2678 do_pointer_show(obj);
2679 else if (obj->priv->in_pointer_grab || obj->priv->absolute)
2680 do_pointer_hide(obj);
2681 }
2682 obj->priv->local_pointer = enable;
2683 }
2684
2685
2686 /**
2687 * vnc_display_set_pointer_grab:
2688 * @obj: (transfer none): the VNC display widget
2689 * @enable: TRUE to enable automatic pointer grab, FALSE otherwise
2690 *
2691 * Set whether the widget will automatically grab the mouse
2692 * pointer upon a button click
2693 */
vnc_display_set_pointer_grab(VncDisplay * obj,gboolean enable)2694 void vnc_display_set_pointer_grab(VncDisplay *obj, gboolean enable)
2695 {
2696 VncDisplayPrivate *priv = obj->priv;
2697
2698 priv->grab_pointer = enable;
2699 if (!enable && priv->absolute && priv->in_pointer_grab)
2700 do_pointer_ungrab(obj, FALSE);
2701 }
2702
2703
2704 /**
2705 * vnc_display_set_grab_keys:
2706 * @obj: (transfer none): the VNC display widget
2707 * @seq: (transfer none): the new grab sequence
2708 *
2709 * Set the sequence of keys that must be pressed to
2710 * activate keyborad and pointer grab
2711 */
vnc_display_set_grab_keys(VncDisplay * obj,VncGrabSequence * seq)2712 void vnc_display_set_grab_keys(VncDisplay *obj, VncGrabSequence *seq)
2713 {
2714 obj->priv->vncgrabpending = FALSE;
2715 if (obj->priv->vncgrabseq) {
2716 vnc_grab_sequence_free(obj->priv->vncgrabseq);
2717 g_free(obj->priv->vncactiveseq);
2718 }
2719 if (seq)
2720 obj->priv->vncgrabseq = vnc_grab_sequence_copy(seq);
2721 else
2722 obj->priv->vncgrabseq = vnc_grab_sequence_new_from_string("Control_L+Alt_L");
2723 obj->priv->vncactiveseq = g_new0(gboolean, obj->priv->vncgrabseq->nkeysyms);
2724 if (G_UNLIKELY(vnc_util_get_debug())) {
2725 gchar *str = vnc_grab_sequence_as_string(obj->priv->vncgrabseq);
2726 VNC_DEBUG("Grab sequence is now %s", str);
2727 g_free(str);
2728 }
2729 }
2730
2731
2732 /**
2733 * vnc_display_get_grab_keys:
2734 * @obj: (transfer none): the VNC display widget
2735 *
2736 * Get the current grab key sequence
2737 *
2738 * Returns: (transfer none): the current grab keys
2739 */
vnc_display_get_grab_keys(VncDisplay * obj)2740 VncGrabSequence *vnc_display_get_grab_keys(VncDisplay *obj)
2741 {
2742 return obj->priv->vncgrabseq;
2743 }
2744
2745
2746 /**
2747 * vnc_display_set_keyboard_grab:
2748 * @obj: (transfer none): the VNC display widget
2749 * @enable: TRUE to enable keyboard grab, FALSE otherwise
2750 *
2751 * Set whether the widget will grab the keyboard when it
2752 * has focus. Grabbing the keyboard allows it to intercept
2753 * special key sequences, ensuring they get sent to the
2754 * remote desktop, rather than intepreted locally.
2755 */
vnc_display_set_keyboard_grab(VncDisplay * obj,gboolean enable)2756 void vnc_display_set_keyboard_grab(VncDisplay *obj, gboolean enable)
2757 {
2758 VncDisplayPrivate *priv = obj->priv;
2759
2760 priv->grab_keyboard = enable;
2761 if (!enable && priv->in_keyboard_grab && !priv->in_pointer_grab)
2762 do_keyboard_ungrab(obj, FALSE);
2763 }
2764
2765
2766 /**
2767 * vnc_display_set_read_only:
2768 * @obj: (transfer none): the VNC display widget
2769 * @enable: TRUE to enable read-only mode, FALSE otherwise
2770 *
2771 * Set whether the widget is running in read-only mode. In
2772 * read-only mode, keyboard and mouse events will not be
2773 * sent to the remote desktop server. The widget will merely
2774 * display activity from the server.
2775 */
vnc_display_set_read_only(VncDisplay * obj,gboolean enable)2776 void vnc_display_set_read_only(VncDisplay *obj, gboolean enable)
2777 {
2778 obj->priv->read_only = enable;
2779 }
2780
vnc_display_convert_data(GdkPixbuf * pixbuf,cairo_surface_t * surface,int width,int height)2781 static void vnc_display_convert_data(GdkPixbuf *pixbuf,
2782 cairo_surface_t *surface,
2783 int width,
2784 int height)
2785 {
2786 int x, y;
2787 guchar *dest_data = gdk_pixbuf_get_pixels(pixbuf);
2788 int dest_stride = gdk_pixbuf_get_rowstride(pixbuf);
2789 guchar *src_data = cairo_image_surface_get_data(surface);
2790 int src_stride = cairo_image_surface_get_stride(surface);
2791
2792 for (y = 0; y < height; y++) {
2793 guint32 *src = (guint32 *) src_data;
2794 for (x = 0; x < width; x++) {
2795 dest_data[x * 3 + 0] = src[x] >> 16;
2796 dest_data[x * 3 + 1] = src[x] >> 8;
2797 dest_data[x * 3 + 2] = src[x];
2798 }
2799
2800 src_data += src_stride;
2801 dest_data += dest_stride;
2802 }
2803 }
2804
2805 /**
2806 * vnc_display_get_pixbuf:
2807 * @obj: (transfer none): the VNC display widget
2808 *
2809 * Take a screenshot of the display.
2810 *
2811 * Returns: (transfer full): a #GdkPixbuf with the screenshot image buffer
2812 */
vnc_display_get_pixbuf(VncDisplay * obj)2813 GdkPixbuf *vnc_display_get_pixbuf(VncDisplay *obj)
2814 {
2815 VncDisplayPrivate *priv = obj->priv;
2816 VncFramebuffer *fb;
2817 cairo_content_t content;
2818 cairo_surface_t *surface;
2819 GdkPixbuf *pixbuf;
2820
2821 if (!priv->conn ||
2822 !vnc_connection_is_initialized(priv->conn))
2823 return NULL;
2824
2825 if (!priv->fb)
2826 return NULL;
2827
2828 fb = VNC_FRAMEBUFFER(priv->fb);
2829 surface = vnc_cairo_framebuffer_get_surface(priv->fb);
2830 content = cairo_surface_get_content(surface) | CAIRO_CONTENT_COLOR;
2831 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
2832 !!(content & CAIRO_CONTENT_ALPHA),
2833 8,
2834 vnc_framebuffer_get_width(fb),
2835 vnc_framebuffer_get_height(fb));
2836
2837 vnc_display_convert_data(pixbuf, surface,
2838 vnc_framebuffer_get_width(fb),
2839 vnc_framebuffer_get_height(fb));
2840
2841 return pixbuf;
2842 }
2843
2844
2845 /**
2846 * vnc_display_get_width:
2847 * @obj: (transfer none): the VNC display widget
2848 *
2849 * Get the width of the remote desktop. This is only
2850 * valid after the "vnc-initialized" signal has been
2851 * emitted
2852 *
2853 * Returns: the remote desktop width
2854 */
vnc_display_get_width(VncDisplay * obj)2855 int vnc_display_get_width(VncDisplay *obj)
2856 {
2857 g_return_val_if_fail (VNC_IS_DISPLAY (obj), -1);
2858
2859 return vnc_connection_get_width (obj->priv->conn);
2860 }
2861
2862
2863 /**
2864 * vnc_display_get_height:
2865 * @obj: (transfer none): the VNC display widget
2866 *
2867 * Get the height of the remote desktop. This is only
2868 * valid after the "vnc-initialized" signal has been
2869 * emitted
2870 *
2871 * Returns: the remote desktop height
2872 */
vnc_display_get_height(VncDisplay * obj)2873 int vnc_display_get_height(VncDisplay *obj)
2874 {
2875 g_return_val_if_fail (VNC_IS_DISPLAY (obj), -1);
2876
2877 return vnc_connection_get_height (obj->priv->conn);
2878 }
2879
2880
2881 /**
2882 * vnc_display_get_name:
2883 * @obj: (transfer none): the VNC display widget
2884 *
2885 * Get the name of the remote desktop. This is only
2886 * valid after the "vnc-initialized" signal has been
2887 * emitted
2888 *
2889 * Returns: (transfer none): the remote desktop name
2890 */
vnc_display_get_name(VncDisplay * obj)2891 const char * vnc_display_get_name(VncDisplay *obj)
2892 {
2893 g_return_val_if_fail (VNC_IS_DISPLAY (obj), NULL);
2894
2895 return vnc_connection_get_name (obj->priv->conn);
2896 }
2897
2898
2899 /**
2900 * vnc_display_cut_text:
2901 * @obj: (transfer none): the VNC display widget
2902 * @text: (transfer none): the clipboard text
2903 *
2904 * Send a text string to the remote desktop clipboard. The
2905 * encoding for @text is undefined, but it is recommended
2906 * to use UTF-8.
2907 */
vnc_display_client_cut_text(VncDisplay * obj,const gchar * text)2908 void vnc_display_client_cut_text(VncDisplay *obj, const gchar *text)
2909 {
2910 g_return_if_fail (VNC_IS_DISPLAY (obj));
2911
2912 if (!obj->priv->read_only)
2913 vnc_connection_client_cut_text(obj->priv->conn, text, strlen (text));
2914 }
2915
2916
2917 /**
2918 * vnc_display_set_lossy_encoding:
2919 * @obj: (transfer none): the VNC display widget
2920 * @enable: TRUE to permit lossy encodings, FALSE otherwise
2921 *
2922 * Set whether the client is willing to accept lossy
2923 * framebuffer update encodings. Lossy encodings can
2924 * improve performance by lowering network bandwidth
2925 * requirements, with a cost that the display received
2926 * by the client will not be pixel perfect
2927 */
vnc_display_set_lossy_encoding(VncDisplay * obj,gboolean enable)2928 void vnc_display_set_lossy_encoding(VncDisplay *obj, gboolean enable)
2929 {
2930 g_return_if_fail (VNC_IS_DISPLAY (obj));
2931 obj->priv->allow_lossy = enable;
2932 }
2933
2934
2935 /**
2936 * vnc_display_set_shared_flag:
2937 * @obj: (transfer none): the VNC display widget
2938 * @shared: the new sharing state
2939 *
2940 * Set the shared state for the connection. A TRUE value
2941 * allow allow this client to co-exist with other existing
2942 * clients. A FALSE value will cause other clients to be
2943 * dropped
2944 */
vnc_display_set_shared_flag(VncDisplay * obj,gboolean shared)2945 void vnc_display_set_shared_flag(VncDisplay *obj, gboolean shared)
2946 {
2947 g_return_if_fail (VNC_IS_DISPLAY (obj));
2948 obj->priv->shared_flag = shared;
2949 }
2950
2951
2952 /**
2953 * vnc_display_set_scaling:
2954 * @obj: (transfer none): the VNC display widget
2955 * @enable: TRUE to allow scaling the desktop to fit, FALSE otherwise
2956 *
2957 * Set whether the remote desktop contents is automatically
2958 * scaled to fit the available widget size, or whether it
2959 * will be rendered at 1:1 size
2960 *
2961 * Returns: TRUE always
2962 */
vnc_display_set_scaling(VncDisplay * obj,gboolean enable)2963 gboolean vnc_display_set_scaling(VncDisplay *obj,
2964 gboolean enable)
2965 {
2966 int ww, wh;
2967
2968 obj->priv->allow_scaling = enable;
2969
2970 if (obj->priv->fb != NULL) {
2971 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(obj));
2972
2973 if (window != NULL) {
2974 gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(obj)),
2975 &ww, &wh);
2976 gtk_widget_queue_draw_area(GTK_WIDGET(obj), 0, 0, ww, wh);
2977 }
2978 }
2979
2980 return TRUE;
2981 }
2982
2983
2984 /**
2985 * vnc_display_force_size:
2986 * @obj: (transfer none): the VNC display widget
2987 * @enabled: TRUE to force the widget size, FALSE otherwise
2988 *
2989 * Set whether the widget size will be forced to match the
2990 * remote desktop size. If the widget size does not match
2991 * the remote desktop size, and scaling is disabled, some
2992 * of the remote desktop may be hidden, or black borders
2993 * may be drawn.
2994 */
vnc_display_set_force_size(VncDisplay * obj,gboolean enabled)2995 void vnc_display_set_force_size(VncDisplay *obj, gboolean enabled)
2996 {
2997 g_return_if_fail (VNC_IS_DISPLAY (obj));
2998 obj->priv->force_size = enabled;
2999 }
3000
3001
3002 /**
3003 * vnc_display_smoothing:
3004 * @obj: (transfer none): the VNC display widget
3005 * @enabled: TRUE to enable smooth scaling, FALSE otherwise
3006 *
3007 * Set whether pixels are smoothly interpolated when scaling,
3008 * to avoid aliasing.
3009 */
vnc_display_set_smoothing(VncDisplay * obj,gboolean enabled)3010 void vnc_display_set_smoothing(VncDisplay *obj, gboolean enabled)
3011 {
3012 int ww, wh;
3013
3014 g_return_if_fail (VNC_IS_DISPLAY (obj));
3015 obj->priv->smoothing = enabled;
3016
3017 if (obj->priv->fb != NULL) {
3018 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(obj));
3019
3020 if (window != NULL) {
3021 gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(obj)),
3022 &ww, &wh);
3023 gtk_widget_queue_draw_area(GTK_WIDGET(obj), 0, 0, ww, wh);
3024 }
3025 }
3026 }
3027
3028
3029 /**
3030 * vnc_display_set_depth:
3031 * @obj: (transfer none): the VNC display widget
3032 * @depth: the desired colour depth
3033 *
3034 * Set the desired colour depth. Higher quality colour
3035 * depths will require greater network bandwidth. The
3036 * colour depth must be set prior to connecting to the
3037 * remote server
3038 */
vnc_display_set_depth(VncDisplay * obj,VncDisplayDepthColor depth)3039 void vnc_display_set_depth(VncDisplay *obj, VncDisplayDepthColor depth)
3040 {
3041 g_return_if_fail (VNC_IS_DISPLAY (obj));
3042
3043 /* Ignore if we are already connected */
3044 if (obj->priv->conn && vnc_connection_is_initialized(obj->priv->conn))
3045 return;
3046
3047 if (obj->priv->depth == depth)
3048 return;
3049
3050 obj->priv->depth = depth;
3051 }
3052
3053
3054 /**
3055 * vnc_display_get_depth:
3056 * @obj: (transfer none): the VNC display widget
3057 *
3058 * Get the desired colour depth
3059 *
3060 * Returns: the color depth
3061 */
vnc_display_get_depth(VncDisplay * obj)3062 VncDisplayDepthColor vnc_display_get_depth(VncDisplay *obj)
3063 {
3064 g_return_val_if_fail (VNC_IS_DISPLAY (obj), 0);
3065
3066 return obj->priv->depth;
3067 }
3068
3069
3070 /**
3071 * vnc_display_get_force_size:
3072 * @obj: (transfer none): the VNC display widget
3073 *
3074 * Determine whether the widget size is being forced
3075 * to match the desktop size
3076 *
3077 * Returns: TRUE if force size is enabled, FALSE otherwise
3078 */
vnc_display_get_force_size(VncDisplay * obj)3079 gboolean vnc_display_get_force_size(VncDisplay *obj)
3080 {
3081 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3082
3083 return obj->priv->force_size;
3084 }
3085
3086
3087 /**
3088 * vnc_display_get_smoothing:
3089 * @obj: (transfer none): the VNC display widget
3090 *
3091 * Determine whether pixels are smoothly interpolated when
3092 * scaling.
3093 *
3094 * Returns: TRUE if smoothing is enabled, FALSE otherwise
3095 */
vnc_display_get_smoothing(VncDisplay * obj)3096 gboolean vnc_display_get_smoothing(VncDisplay *obj)
3097 {
3098 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3099
3100 return obj->priv->smoothing;
3101 }
3102
3103
3104 /**
3105 * vnc_display_get_scaling:
3106 * @obj: (transfer none): the VNC display widget
3107 *
3108 * Determine whether the widget is permitted to
3109 * scale the remote desktop to fit the current
3110 * widget size.
3111 *
3112 * Returns: TRUE if scaling is permitted, FALSE otherwise
3113 */
vnc_display_get_scaling(VncDisplay * obj)3114 gboolean vnc_display_get_scaling(VncDisplay *obj)
3115 {
3116 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3117
3118 return obj->priv->allow_scaling;
3119 }
3120
3121
3122 /**
3123 * vnc_display_get_lossy_encoding:
3124 * @obj: (transfer none): the VNC display widget
3125 *
3126 * Determine whether lossy framebuffer update encodings
3127 * are permitted
3128 *
3129 * Returns: TRUE if lossy encodings are permitted, FALSE otherwie
3130 */
vnc_display_get_lossy_encoding(VncDisplay * obj)3131 gboolean vnc_display_get_lossy_encoding(VncDisplay *obj)
3132 {
3133 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3134
3135 return obj->priv->allow_lossy;
3136 }
3137
3138
3139 /**
3140 * vnc_display_get_shared_flag:
3141 * @obj: (transfer none): the VNC display widget
3142 *
3143 * Determine if other clients are permitted to
3144 * share the VNC connection
3145 *
3146 * Returns: TRUE if sharing is permittted, FALSE otherwise
3147 */
vnc_display_get_shared_flag(VncDisplay * obj)3148 gboolean vnc_display_get_shared_flag(VncDisplay *obj)
3149 {
3150 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3151
3152 return obj->priv->shared_flag;
3153 }
3154
3155
3156 /**
3157 * vnc_display_get_pointer_local:
3158 * @obj: (transfer none): the VNC display widget
3159 *
3160 * Determine if a local pointer will be shown
3161 *
3162 * Returns: TRUE if a local pointer is shown, FALSE otherwise
3163 */
vnc_display_get_pointer_local(VncDisplay * obj)3164 gboolean vnc_display_get_pointer_local(VncDisplay *obj)
3165 {
3166 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3167
3168 return obj->priv->local_pointer;
3169 }
3170
3171
3172 /**
3173 * vnc_display_get_pointer_grab:
3174 * @obj: (transfer none): the VNC display widget
3175 *
3176 * Determine if the mouse pointer will be grabbed
3177 * on first click
3178 *
3179 * Returns: TRUE if the pointer will be grabbed, FALSE otherwise
3180 */
vnc_display_get_pointer_grab(VncDisplay * obj)3181 gboolean vnc_display_get_pointer_grab(VncDisplay *obj)
3182 {
3183 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3184
3185 return obj->priv->grab_pointer;
3186 }
3187
3188
3189 /**
3190 * vnc_display_get_keyboard_grab:
3191 * @obj: (transfer none): the VNC display widget
3192 *
3193 * Determine if the keyboard will be grabbed when the
3194 * widget has input focus.
3195 *
3196 * Returns: TRUE if the keyboard will be grabbed, FALSE otherwise
3197 */
vnc_display_get_keyboard_grab(VncDisplay * obj)3198 gboolean vnc_display_get_keyboard_grab(VncDisplay *obj)
3199 {
3200 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3201
3202 return obj->priv->grab_keyboard;
3203 }
3204
3205
3206 /**
3207 * vnc_display_get_read_only:
3208 * @obj: (transfer none): the VNC display widget
3209 *
3210 * Determine if the widget will operate in read-only
3211 * mode, denying keyboard/mouse inputs
3212 *
3213 * Returns: TRUE if in read-only mode, FALSE otherwise
3214 */
vnc_display_get_read_only(VncDisplay * obj)3215 gboolean vnc_display_get_read_only(VncDisplay *obj)
3216 {
3217 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3218
3219 return obj->priv->read_only;
3220 }
3221
3222
3223 /**
3224 * vnc_display_is_pointer_absolute:
3225 * @obj: (transfer none): the VNC display widget
3226 *
3227 * Determine if the pointer is operating in absolute
3228 * mode. This is only valid after the "vnc-initialized"
3229 * signal has been emitted
3230 *
3231 * Returns: TRUE if in absolute mode, FALSE for relative mode
3232 */
vnc_display_is_pointer_absolute(VncDisplay * obj)3233 gboolean vnc_display_is_pointer_absolute(VncDisplay *obj)
3234 {
3235 return obj->priv->absolute;
3236 }
3237
3238
3239 /**
3240 * vnc_display_get_option_group:
3241 *
3242 * Get a command line option group containing VNC specific
3243 * options.
3244 *
3245 * Returns: (transfer full): the option group
3246 */
3247 GOptionGroup *
vnc_display_get_option_group(void)3248 vnc_display_get_option_group (void)
3249 {
3250 GOptionGroup *group;
3251
3252 group = g_option_group_new ("gtk-vnc", N_("GTK-VNC Options:"), N_("Show GTK-VNC Options"), NULL, NULL);
3253 g_option_group_set_translation_domain (group, GETTEXT_PACKAGE);
3254
3255 g_option_group_add_entries (group, gtk_vnc_args);
3256
3257 return group;
3258 }
3259
3260
3261 /**
3262 * vnc_display_get_option_entries:
3263 *
3264 * Get the array of command line option entries containing
3265 * VNC specific otions
3266 *
3267 * Returns: (array zero-terminated=1): the option entries
3268 */
3269 const GOptionEntry *
vnc_display_get_option_entries(void)3270 vnc_display_get_option_entries (void)
3271 {
3272 return gtk_vnc_args;
3273 }
3274
3275
3276 /**
3277 * vnc_display_:
3278 * @obj: (transfer none): the VNC display widget
3279 */
3280 gboolean
vnc_display_request_update(VncDisplay * obj)3281 vnc_display_request_update(VncDisplay *obj)
3282 {
3283 g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
3284
3285 if (!obj->priv->conn || !vnc_connection_is_initialized(obj->priv->conn))
3286 return FALSE;
3287
3288 VNC_DEBUG ("Requesting a full update");
3289 return vnc_connection_framebuffer_update_request(obj->priv->conn,
3290 0,
3291 0,
3292 0,
3293 vnc_connection_get_width(obj->priv->conn),
3294 vnc_connection_get_width(obj->priv->conn));
3295 }
3296
3297 /*
3298 * Local variables:
3299 * c-indent-level: 4
3300 * c-basic-offset: 4
3301 * indent-tabs-mode: nil
3302 * End:
3303 */
3304