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