1 /*
2  * Virt Viewer: A virtual machine console viewer
3  *
4  * Copyright (C) 2007-2012 Red Hat, Inc.
5  * Copyright (C) 2009-2012 Daniel P. Berrange
6  * Copyright (C) 2010 Marc-André Lureau
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * Author: Daniel P. Berrange <berrange@redhat.com>
23  */
24 
25 #include <config.h>
26 
27 #include <locale.h>
28 #include <math.h>
29 
30 #include "virt-viewer-session.h"
31 #include "virt-viewer-display.h"
32 #include "virt-viewer-util.h"
33 
34 typedef struct _VirtViewerDisplayPrivate VirtViewerDisplayPrivate;
35 struct _VirtViewerDisplayPrivate
36 {
37     guint desktopWidth;
38     guint desktopHeight;
39     guint zoom_level;
40     gint nth_display; /* Monitor number inside the guest */
41     gint monitor;     /* Monitor number on the client */
42     guint show_hint;
43     VirtViewerSession *session;
44     gboolean fullscreen;
45     gboolean auto_resize;
46     gboolean force_aspect;
47 };
48 
49 static void virt_viewer_display_get_preferred_width(GtkWidget *widget,
50                                                     int *minwidth,
51                                                     int *defwidth);
52 static void virt_viewer_display_get_preferred_height(GtkWidget *widget,
53                                                      int *minheight,
54                                                      int *defheight);
55 static void virt_viewer_display_size_allocate(GtkWidget *widget,
56                                               GtkAllocation *allocation);
57 static void virt_viewer_display_set_property(GObject *object,
58                                              guint prop_id,
59                                              const GValue *value,
60                                              GParamSpec *pspec);
61 static void virt_viewer_display_get_property(GObject *object,
62                                              guint prop_id,
63                                              GValue *value,
64                                              GParamSpec *pspec);
65 static void virt_viewer_display_grab_focus(GtkWidget *widget);
66 
67 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(VirtViewerDisplay, virt_viewer_display, GTK_TYPE_BIN)
68 
69 enum {
70     PROP_0,
71 
72     PROP_DESKTOP_WIDTH,
73     PROP_DESKTOP_HEIGHT,
74     PROP_FULLSCREEN,
75     PROP_NTH_DISPLAY,
76     PROP_ZOOM_LEVEL,
77     PROP_SHOW_HINT,
78     PROP_SESSION,
79     PROP_SELECTABLE,
80     PROP_MONITOR,
81     PROP_AUTO_RESIZE,
82     PROP_FORCE_ASPECT,
83 };
84 
85 static void
virt_viewer_display_class_init(VirtViewerDisplayClass * class)86 virt_viewer_display_class_init(VirtViewerDisplayClass *class)
87 {
88     GObjectClass *object_class = G_OBJECT_CLASS(class);
89     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
90 
91     object_class->set_property = virt_viewer_display_set_property;
92     object_class->get_property = virt_viewer_display_get_property;
93 
94     widget_class->get_preferred_width = virt_viewer_display_get_preferred_width;
95     widget_class->get_preferred_height = virt_viewer_display_get_preferred_height;
96     widget_class->size_allocate = virt_viewer_display_size_allocate;
97     widget_class->grab_focus = virt_viewer_display_grab_focus;
98 
99     g_object_class_install_property(object_class,
100                                     PROP_DESKTOP_WIDTH,
101                                     g_param_spec_int("desktop-width",
102                                                      "Width",
103                                                      "Desktop width",
104                                                      1,
105                                                      G_MAXINT32,
106                                                      MIN_DISPLAY_WIDTH,
107                                                      G_PARAM_READWRITE));
108 
109     g_object_class_install_property(object_class,
110                                     PROP_DESKTOP_HEIGHT,
111                                     g_param_spec_int("desktop-height",
112                                                      "Height",
113                                                      "Desktop height",
114                                                      1,
115                                                      G_MAXINT32,
116                                                      MIN_DISPLAY_HEIGHT,
117                                                      G_PARAM_READWRITE));
118 
119     g_object_class_install_property(object_class,
120                                     PROP_ZOOM_LEVEL,
121                                     g_param_spec_int("zoom-level",
122                                                      "Zoom",
123                                                      "Zoom level",
124                                                      MIN_ZOOM_LEVEL,
125                                                      MAX_ZOOM_LEVEL,
126                                                      NORMAL_ZOOM_LEVEL,
127                                                      G_PARAM_READWRITE));
128 
129     g_object_class_install_property(object_class,
130                                     PROP_NTH_DISPLAY,
131                                     g_param_spec_int("nth-display",
132                                                      "Nth display",
133                                                      "Nth display",
134                                                      -1,
135                                                      G_MAXINT32,
136                                                      0,
137                                                      G_PARAM_READWRITE |
138                                                      G_PARAM_CONSTRUCT_ONLY));
139 
140     g_object_class_install_property(object_class,
141                                     PROP_SHOW_HINT,
142                                     g_param_spec_flags("show-hint",
143                                                        "Show hint",
144                                                        "Show state hint",
145                                                        VIRT_VIEWER_TYPE_DISPLAY_SHOW_HINT_FLAGS,
146                                                        0,
147                                                        G_PARAM_READABLE));
148 
149     g_object_class_install_property(object_class,
150                                     PROP_SESSION,
151                                     g_param_spec_object("session",
152                                                         "Session",
153                                                         "VirtSession",
154                                                         VIRT_VIEWER_TYPE_SESSION,
155                                                         G_PARAM_READWRITE |
156                                                         G_PARAM_CONSTRUCT_ONLY));
157 
158     g_object_class_install_property(object_class,
159                                     PROP_SELECTABLE,
160                                     g_param_spec_boolean("selectable",
161                                                          "Selectable",
162                                                          "Selectable",
163                                                          FALSE,
164                                                          G_PARAM_READABLE));
165 
166     g_object_class_install_property(object_class,
167                                     PROP_MONITOR,
168                                     g_param_spec_int("monitor",
169                                                      "Monitor",
170                                                      "Display Monitor",
171                                                      -1,
172                                                      G_MAXINT32,
173                                                      -1,
174                                                      G_PARAM_READWRITE |
175                                                      G_PARAM_CONSTRUCT));
176 
177     g_object_class_install_property(object_class,
178                                     PROP_FULLSCREEN,
179                                     g_param_spec_boolean("fullscreen",
180                                                          "Fullscreen",
181                                                          "Fullscreen",
182                                                          FALSE,
183                                                          G_PARAM_READABLE));
184 
185     g_object_class_install_property(object_class,
186                                     PROP_AUTO_RESIZE,
187                                     g_param_spec_boolean("auto-resize",
188                                                          "Auto-resize",
189                                                          "Auto-resize",
190                                                          TRUE,
191                                                          G_PARAM_READABLE |
192                                                          G_PARAM_WRITABLE));
193 
194     g_object_class_install_property(object_class,
195                                     PROP_FORCE_ASPECT,
196                                     g_param_spec_boolean("force-aspect",
197                                                          "Force aspect",
198                                                          "Force aspect ratio for widget",
199                                                          TRUE,
200                                                          G_PARAM_READABLE |
201                                                          G_PARAM_WRITABLE));
202 
203     g_signal_new("display-pointer-grab",
204                  G_OBJECT_CLASS_TYPE(object_class),
205                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
206                  0,
207                  NULL,
208                  NULL,
209                  g_cclosure_marshal_VOID__VOID,
210                  G_TYPE_NONE,
211                  0);
212 
213     g_signal_new("display-pointer-ungrab",
214                  G_OBJECT_CLASS_TYPE(object_class),
215                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
216                  0,
217                  NULL,
218                  NULL,
219                  g_cclosure_marshal_VOID__VOID,
220                  G_TYPE_NONE,
221                  0);
222 
223     g_signal_new("display-keyboard-grab",
224                  G_OBJECT_CLASS_TYPE(object_class),
225                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
226                  0,
227                  NULL,
228                  NULL,
229                  g_cclosure_marshal_VOID__VOID,
230                  G_TYPE_NONE,
231                  0);
232 
233     g_signal_new("display-keyboard-ungrab",
234                  G_OBJECT_CLASS_TYPE(object_class),
235                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
236                  0,
237                  NULL,
238                  NULL,
239                  g_cclosure_marshal_VOID__VOID,
240                  G_TYPE_NONE,
241                  0);
242 
243     g_signal_new("display-desktop-resize",
244                  G_OBJECT_CLASS_TYPE(object_class),
245                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
246                  0,
247                  NULL,
248                  NULL,
249                  g_cclosure_marshal_VOID__VOID,
250                  G_TYPE_NONE,
251                  0);
252 
253     g_signal_new("monitor-geometry-changed",
254                  G_OBJECT_CLASS_TYPE(object_class),
255                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
256                  0,
257                  NULL,
258                  NULL,
259                  g_cclosure_marshal_VOID__VOID,
260                  G_TYPE_NONE,
261                  0);
262 }
263 
264 static void
virt_viewer_display_init(VirtViewerDisplay * display)265 virt_viewer_display_init(VirtViewerDisplay *display)
266 {
267     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
268     gtk_widget_set_has_window(GTK_WIDGET(display), FALSE);
269     gtk_widget_set_redraw_on_allocate(GTK_WIDGET(display), FALSE);
270 
271     priv->desktopWidth = MIN_DISPLAY_WIDTH;
272     priv->desktopHeight = MIN_DISPLAY_HEIGHT;
273     priv->zoom_level = NORMAL_ZOOM_LEVEL;
274     priv->force_aspect = TRUE;
275 }
276 
277 GtkWidget*
virt_viewer_display_new(void)278 virt_viewer_display_new(void)
279 {
280     return g_object_new(VIRT_VIEWER_TYPE_DISPLAY, NULL);
281 }
282 
283 static void
virt_viewer_display_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)284 virt_viewer_display_set_property(GObject *object,
285                                  guint prop_id,
286                                  const GValue *value,
287                                  GParamSpec *pspec)
288 {
289     VirtViewerDisplay *display = VIRT_VIEWER_DISPLAY(object);
290     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
291 
292     switch (prop_id) {
293     case PROP_DESKTOP_WIDTH:
294         priv->desktopWidth = g_value_get_int(value);
295         break;
296     case PROP_DESKTOP_HEIGHT:
297         priv->desktopHeight = g_value_get_int(value);
298         break;
299     case PROP_NTH_DISPLAY:
300         priv->nth_display = g_value_get_int(value);
301         break;
302     case PROP_SESSION:
303         g_warn_if_fail(priv->session == NULL);
304         priv->session = g_value_get_object(value);
305         break;
306     case PROP_MONITOR:
307         priv->monitor = g_value_get_int(value);
308         break;
309     case PROP_AUTO_RESIZE:
310         priv->auto_resize = g_value_get_boolean(value);
311         break;
312     case PROP_FORCE_ASPECT:
313         priv->force_aspect = g_value_get_boolean(value);
314         break;
315 
316     default:
317         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
318         break;
319     }
320 }
321 
322 static void
virt_viewer_display_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)323 virt_viewer_display_get_property(GObject *object,
324                                  guint prop_id,
325                                  GValue *value,
326                                  GParamSpec *pspec)
327 {
328     VirtViewerDisplay *display = VIRT_VIEWER_DISPLAY(object);
329     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
330 
331     switch (prop_id) {
332     case PROP_DESKTOP_WIDTH:
333         g_value_set_int(value, priv->desktopWidth);
334         break;
335     case PROP_DESKTOP_HEIGHT:
336         g_value_set_int(value, priv->desktopHeight);
337         break;
338     case PROP_NTH_DISPLAY:
339         g_value_set_int(value, priv->nth_display);
340         break;
341     case PROP_SHOW_HINT:
342         g_value_set_flags(value, priv->show_hint);
343         break;
344     case PROP_SESSION:
345         g_value_set_object(value, virt_viewer_display_get_session(display));
346         break;
347     case PROP_SELECTABLE:
348         g_value_set_boolean(value, virt_viewer_display_get_selectable(display));
349         break;
350     case PROP_MONITOR:
351         g_value_set_int(value, priv->monitor);
352         break;
353     case PROP_FULLSCREEN:
354         g_value_set_boolean(value, virt_viewer_display_get_fullscreen(display));
355         break;
356     case PROP_AUTO_RESIZE:
357         g_value_set_boolean(value, virt_viewer_display_get_auto_resize(display));
358         break;
359     case PROP_FORCE_ASPECT:
360         g_value_set_boolean(value, priv->force_aspect);
361         break;
362 
363     default:
364         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
365         break;
366     }
367 }
368 
369 
370 static void
virt_viewer_display_grab_focus(GtkWidget * widget)371 virt_viewer_display_grab_focus(GtkWidget *widget)
372 {
373     GtkBin *bin = GTK_BIN(widget);
374 
375     gtk_widget_grab_focus(gtk_bin_get_child(bin));
376 }
377 
virt_viewer_display_get_preferred_dimension_from_desktop(VirtViewerDisplay * display,const int minimal_size,const int desktop_dim,int * minimal_dim,int * preferred_dim)378 static void virt_viewer_display_get_preferred_dimension_from_desktop(VirtViewerDisplay *display,
379                                                                      const int minimal_size,
380                                                                      const int desktop_dim,
381                                                                      int *minimal_dim,
382                                                                      int *preferred_dim)
383 {
384     int border_width = gtk_container_get_border_width(GTK_CONTAINER(display));
385 
386     if (virt_viewer_display_get_zoom(display)) {
387         guint zoom_level = virt_viewer_display_get_zoom_level(display);
388         *preferred_dim = round(desktop_dim * zoom_level / (double) NORMAL_ZOOM_LEVEL);
389         *minimal_dim = round(minimal_size * zoom_level / (double) NORMAL_ZOOM_LEVEL);
390     } else {
391         *preferred_dim = desktop_dim;
392         *minimal_dim = minimal_size;
393     }
394     *preferred_dim += 2 * border_width;
395     *minimal_dim += 2 * border_width;
396 }
397 
398 
virt_viewer_display_get_preferred_width(GtkWidget * widget,int * minwidth,int * defwidth)399 static void virt_viewer_display_get_preferred_width(GtkWidget *widget,
400                                                     int *minwidth,
401                                                     int *defwidth)
402 {
403     VirtViewerDisplay *display = VIRT_VIEWER_DISPLAY(widget);
404     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
405 
406     virt_viewer_display_get_preferred_dimension_from_desktop(display,
407                                                              MIN_DISPLAY_WIDTH,
408                                                              priv->desktopWidth,
409                                                              minwidth,
410                                                              defwidth);
411 }
412 
413 
virt_viewer_display_get_preferred_height(GtkWidget * widget,int * minheight,int * defheight)414 static void virt_viewer_display_get_preferred_height(GtkWidget *widget,
415                                                      int *minheight,
416                                                      int *defheight)
417 {
418     VirtViewerDisplay *display = VIRT_VIEWER_DISPLAY(widget);
419     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
420 
421     virt_viewer_display_get_preferred_dimension_from_desktop(display,
422                                                              MIN_DISPLAY_HEIGHT,
423                                                              priv->desktopHeight,
424                                                              minheight,
425                                                              defheight);
426 }
427 
428 
429 static void
virt_viewer_display_size_allocate(GtkWidget * widget,GtkAllocation * allocation)430 virt_viewer_display_size_allocate(GtkWidget *widget,
431                                   GtkAllocation *allocation)
432 {
433     GtkBin *bin = GTK_BIN(widget);
434     VirtViewerDisplay *display = VIRT_VIEWER_DISPLAY(widget);
435     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
436     GtkAllocation child_allocation;
437     gint width, height;
438     gint border_width;
439     GtkWidget *child = gtk_bin_get_child(bin);
440 
441     g_debug("Allocated %dx%d", allocation->width, allocation->height);
442     gtk_widget_set_allocation(widget, allocation);
443 
444     if (priv->desktopWidth == 0 || priv->desktopHeight == 0 ||
445         child == NULL || !gtk_widget_get_visible(child))
446         return;
447 
448     border_width = gtk_container_get_border_width(GTK_CONTAINER(display));
449 
450     width  = MAX(MIN_DISPLAY_WIDTH, allocation->width - 2 * border_width);
451     height = MAX(MIN_DISPLAY_HEIGHT, allocation->height - 2 * border_width);
452 
453     if (priv->force_aspect) {
454         double desktopAspect = (double) priv->desktopWidth / (double) priv->desktopHeight;
455         double actualAspect = (double) width / (double) height;
456 
457         if (actualAspect > desktopAspect) {
458             child_allocation.width = round(height * desktopAspect);
459             child_allocation.height = height;
460         } else {
461             child_allocation.width = width;
462             child_allocation.height = round(width / desktopAspect);
463         }
464     } else {
465         child_allocation.width = width;
466         child_allocation.height = height;
467     }
468 
469     child_allocation.x = 0.5 * (width - child_allocation.width) + allocation->x + border_width;
470     child_allocation.y = 0.5 * (height - child_allocation.height) + allocation->y + border_width;
471 
472     g_debug("Child allocate %dx%d", child_allocation.width, child_allocation.height);
473     gtk_widget_size_allocate(child, &child_allocation);
474 }
475 
476 
virt_viewer_display_set_desktop_size(VirtViewerDisplay * display,guint width,guint height)477 void virt_viewer_display_set_desktop_size(VirtViewerDisplay *display,
478                                           guint width,
479                                           guint height)
480 {
481     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
482 
483     if (width == priv->desktopWidth && height == priv->desktopHeight)
484         return;
485 
486     priv->desktopWidth = width;
487     priv->desktopHeight = height;
488 
489     virt_viewer_display_queue_resize(display);
490 
491     g_signal_emit_by_name(display, "display-desktop-resize");
492 }
493 
494 
virt_viewer_display_get_desktop_size(VirtViewerDisplay * display,guint * width,guint * height)495 void virt_viewer_display_get_desktop_size(VirtViewerDisplay *display,
496                                           guint *width,
497                                           guint *height)
498 {
499     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
500 
501     *width = priv->desktopWidth;
502     *height = priv->desktopHeight;
503 }
504 
505 
virt_viewer_display_queue_resize(VirtViewerDisplay * display)506 void virt_viewer_display_queue_resize(VirtViewerDisplay *display)
507 {
508     GtkWidget *child = gtk_bin_get_child(GTK_BIN(display));
509 
510     if (child && gtk_widget_get_visible(child)) {
511         gtk_widget_queue_resize(GTK_WIDGET(display));
512     }
513 }
514 
virt_viewer_display_set_zoom_level(VirtViewerDisplay * display,guint zoom)515 void virt_viewer_display_set_zoom_level(VirtViewerDisplay *display,
516                                         guint zoom)
517 {
518     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
519 
520     if (zoom < MIN_ZOOM_LEVEL)
521         zoom = MIN_ZOOM_LEVEL;
522     if (zoom > MAX_ZOOM_LEVEL)
523         zoom = MAX_ZOOM_LEVEL;
524 
525     if (priv->zoom_level == zoom)
526         return;
527 
528     priv->zoom_level = zoom;
529 
530     virt_viewer_display_queue_resize(display);
531     g_object_notify(G_OBJECT(display), "zoom-level");
532 }
533 
534 
virt_viewer_display_get_zoom_level(VirtViewerDisplay * display)535 guint virt_viewer_display_get_zoom_level(VirtViewerDisplay *display)
536 {
537     VirtViewerDisplayPrivate *priv = virt_viewer_display_get_instance_private(display);
538     return priv->zoom_level;
539 }
540 
virt_viewer_display_get_zoom(VirtViewerDisplay * display)541 gboolean virt_viewer_display_get_zoom(VirtViewerDisplay *display)
542 {
543     return virt_viewer_display_get_zoom_level(display) != NORMAL_ZOOM_LEVEL;
544 }
545 
546 
virt_viewer_display_send_keys(VirtViewerDisplay * display,const guint * keyvals,int nkeyvals)547 void virt_viewer_display_send_keys(VirtViewerDisplay *display,
548                                    const guint *keyvals, int nkeyvals)
549 {
550     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(display));
551 
552     VIRT_VIEWER_DISPLAY_GET_CLASS(display)->send_keys(display, keyvals, nkeyvals);
553 }
554 
virt_viewer_display_get_pixbuf(VirtViewerDisplay * display)555 GdkPixbuf* virt_viewer_display_get_pixbuf(VirtViewerDisplay *display)
556 {
557     g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(display), NULL);
558 
559     return VIRT_VIEWER_DISPLAY_GET_CLASS(display)->get_pixbuf(display);
560 }
561 
virt_viewer_display_get_show_hint(VirtViewerDisplay * self)562 guint virt_viewer_display_get_show_hint(VirtViewerDisplay *self)
563 {
564     VirtViewerDisplayPrivate *priv;
565     g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), 0);
566 
567     priv = virt_viewer_display_get_instance_private(self);
568     return priv->show_hint;
569 }
570 
virt_viewer_display_set_show_hint(VirtViewerDisplay * self,guint mask,gboolean enable)571 void virt_viewer_display_set_show_hint(VirtViewerDisplay *self, guint mask, gboolean enable)
572 {
573     VirtViewerDisplayPrivate *priv;
574     guint hint;
575     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
576 
577     priv = virt_viewer_display_get_instance_private(self);
578     hint = priv->show_hint;
579 
580     if (enable)
581         hint |= mask;
582     else
583         hint &= ~mask;
584 
585     if (priv->show_hint == hint)
586         return;
587 
588     priv->show_hint = hint;
589     g_object_notify(G_OBJECT(self), "show-hint");
590 }
591 
592 /* This function attempts to enable the display if supported by the backend */
virt_viewer_display_enable(VirtViewerDisplay * self)593 void virt_viewer_display_enable(VirtViewerDisplay *self)
594 {
595     VirtViewerDisplayClass *klass;
596 
597     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
598 
599     klass = VIRT_VIEWER_DISPLAY_GET_CLASS(self);
600     if (!klass->enable)
601         return;
602 
603     klass->enable(self);
604 }
605 
606 /* This function attempts to disable the display if supported by the backend */
virt_viewer_display_disable(VirtViewerDisplay * self)607 void virt_viewer_display_disable(VirtViewerDisplay *self)
608 {
609     VirtViewerDisplayClass *klass;
610 
611     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
612 
613     klass = VIRT_VIEWER_DISPLAY_GET_CLASS(self);
614     if (!klass->disable)
615         return;
616 
617     klass->disable(self);
618 }
619 
620 /* this function simply informs the display that it is enabled. see
621  * virt_viewer_display_enable()/disable() if you want to attempt to change the
622  * state of the display */
virt_viewer_display_set_enabled(VirtViewerDisplay * self,gboolean enabled)623 void virt_viewer_display_set_enabled(VirtViewerDisplay *self, gboolean enabled)
624 {
625     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
626 
627     g_object_freeze_notify(G_OBJECT(self));
628 
629     virt_viewer_display_set_show_hint(self, VIRT_VIEWER_DISPLAY_SHOW_HINT_SET, TRUE);
630     virt_viewer_display_set_show_hint(self, VIRT_VIEWER_DISPLAY_SHOW_HINT_DISABLED, !enabled);
631 
632     g_object_thaw_notify(G_OBJECT(self));
633 }
634 
virt_viewer_display_get_enabled(VirtViewerDisplay * self)635 gboolean virt_viewer_display_get_enabled(VirtViewerDisplay *self)
636 {
637     VirtViewerDisplayPrivate *priv;
638     priv = virt_viewer_display_get_instance_private(self);
639     return ((priv->show_hint & VIRT_VIEWER_DISPLAY_SHOW_HINT_SET) &&
640         !(priv->show_hint & VIRT_VIEWER_DISPLAY_SHOW_HINT_DISABLED));
641 }
642 
virt_viewer_display_get_session(VirtViewerDisplay * self)643 VirtViewerSession* virt_viewer_display_get_session(VirtViewerDisplay *self)
644 {
645     VirtViewerDisplayPrivate *priv;
646     g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), NULL);
647 
648     priv = virt_viewer_display_get_instance_private(self);
649     return priv->session;
650 }
651 
virt_viewer_display_set_monitor(VirtViewerDisplay * self,gint monitor)652 void virt_viewer_display_set_monitor(VirtViewerDisplay *self, gint monitor)
653 {
654     VirtViewerDisplayPrivate *priv;
655     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
656 
657     priv = virt_viewer_display_get_instance_private(self);
658     priv->monitor = monitor;
659     g_object_notify(G_OBJECT(self), "monitor");
660 }
661 
virt_viewer_display_get_monitor(VirtViewerDisplay * self)662 gint virt_viewer_display_get_monitor(VirtViewerDisplay *self)
663 {
664     VirtViewerDisplayPrivate *priv;
665     g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), -1);
666 
667     priv = virt_viewer_display_get_instance_private(self);
668     return priv->monitor;
669 }
670 
virt_viewer_display_release_cursor(VirtViewerDisplay * self)671 void virt_viewer_display_release_cursor(VirtViewerDisplay *self)
672 {
673     VirtViewerDisplayClass *klass;
674 
675     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
676 
677     klass = VIRT_VIEWER_DISPLAY_GET_CLASS(self);
678     g_return_if_fail(klass->release_cursor != NULL);
679 
680     klass->release_cursor(self);
681 }
682 
virt_viewer_display_get_selectable(VirtViewerDisplay * self)683 gboolean virt_viewer_display_get_selectable(VirtViewerDisplay *self)
684 {
685     VirtViewerDisplayClass *klass;
686 
687     g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE);
688 
689     klass = VIRT_VIEWER_DISPLAY_GET_CLASS(self);
690     if (klass->selectable)
691         return klass->selectable(self);
692 
693     return TRUE;
694 }
695 
virt_viewer_display_close(VirtViewerDisplay * self)696 void virt_viewer_display_close(VirtViewerDisplay *self)
697 {
698     VirtViewerDisplayClass *klass;
699 
700     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
701 
702     klass = VIRT_VIEWER_DISPLAY_GET_CLASS(self);
703     if (klass->close)
704         klass->close(self);
705 }
706 
virt_viewer_display_set_fullscreen(VirtViewerDisplay * self,gboolean fullscreen)707 void virt_viewer_display_set_fullscreen(VirtViewerDisplay *self, gboolean fullscreen)
708 {
709     VirtViewerDisplayPrivate *priv;
710     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
711 
712     priv = virt_viewer_display_get_instance_private(self);
713     if (priv->fullscreen == fullscreen)
714         return;
715 
716     priv->fullscreen = fullscreen;
717     g_object_notify(G_OBJECT(self), "fullscreen");
718 }
719 
virt_viewer_display_get_fullscreen(VirtViewerDisplay * self)720 gboolean virt_viewer_display_get_fullscreen(VirtViewerDisplay *self)
721 {
722     VirtViewerDisplayPrivate *priv;
723     g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE);
724 
725     priv = virt_viewer_display_get_instance_private(self);
726     return priv->fullscreen;
727 }
728 
virt_viewer_display_get_preferred_monitor_geometry(VirtViewerDisplay * self,GdkRectangle * preferred)729 void virt_viewer_display_get_preferred_monitor_geometry(VirtViewerDisplay* self,
730                                                         GdkRectangle* preferred)
731 {
732     GtkWidget *top = NULL;
733     gint topx = 0, topy = 0;
734 
735     g_return_if_fail(preferred != NULL);
736 
737     top = gtk_widget_get_toplevel(GTK_WIDGET(self));
738     if (!virt_viewer_display_get_enabled(self) ||
739         !GTK_IS_WINDOW(top)) {
740         preferred->width = 0;
741         preferred->height = 0;
742         preferred->x = 0;
743         preferred->y = 0;
744         return;
745     }
746 
747     gtk_window_get_position(GTK_WINDOW(top), &topx, &topy);
748     topx = MAX(topx, 0);
749     topy = MAX(topy, 0);
750 
751     if (virt_viewer_display_get_fullscreen(self)) {
752         GdkRectangle physical_monitor;
753         GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(self));
754         int n = virt_viewer_display_get_monitor(self);
755         if (n == -1)
756             n = gdk_screen_get_monitor_at_window(screen,
757                                                  gtk_widget_get_window(GTK_WIDGET(self)));
758         gdk_screen_get_monitor_geometry(screen, n, &physical_monitor);
759         preferred->x = physical_monitor.x;
760         preferred->y = physical_monitor.y;
761         preferred->width = physical_monitor.width;
762         preferred->height = physical_monitor.height;
763     } else {
764         gtk_widget_get_allocation(GTK_WIDGET(self), preferred);
765         preferred->x = topx;
766         preferred->y = topy;
767     }
768 
769     if (virt_viewer_display_get_zoom(self)) {
770         guint zoom = virt_viewer_display_get_zoom_level(self);
771 
772         preferred->width = round(preferred->width * NORMAL_ZOOM_LEVEL / (double) zoom);
773         preferred->height = round(preferred->height * NORMAL_ZOOM_LEVEL / (double) zoom);
774     }
775 
776     /* calculate device pixel size on HiDPI screens */
777     gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
778 
779     preferred->x *= scale_factor;
780     preferred->y *= scale_factor;
781     preferred->width *= scale_factor;
782     preferred->height *= scale_factor;
783 }
784 
785 gint
virt_viewer_display_get_nth(VirtViewerDisplay * self)786 virt_viewer_display_get_nth(VirtViewerDisplay *self)
787 {
788     VirtViewerDisplayPrivate *priv;
789     priv = virt_viewer_display_get_instance_private(self);
790     return priv->nth_display;
791 }
792 
virt_viewer_display_set_auto_resize(VirtViewerDisplay * self,gboolean enabled)793 void virt_viewer_display_set_auto_resize(VirtViewerDisplay *self, gboolean enabled)
794 {
795     VirtViewerDisplayPrivate *priv;
796     g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
797 
798     priv = virt_viewer_display_get_instance_private(self);
799     priv->auto_resize = enabled;
800     g_object_notify(G_OBJECT(self), "auto-resize");
801 }
802 
virt_viewer_display_get_auto_resize(VirtViewerDisplay * self)803 gboolean virt_viewer_display_get_auto_resize(VirtViewerDisplay *self)
804 {
805     VirtViewerDisplayPrivate *priv;
806     g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE);
807 
808     priv = virt_viewer_display_get_instance_private(self);
809     return priv->auto_resize;
810 }
811