xref: /openbmc/qemu/ui/gtk.c (revision 56384cf3adaeb15bab479be328605e301ae253f2)
1  /*
2   * GTK UI
3   *
4   * Copyright IBM, Corp. 2012
5   *
6   * Authors:
7   *  Anthony Liguori   <aliguori@us.ibm.com>
8   *
9   * This program is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as published by
11   * the Free Software Foundation; either version 2 of the License, or
12   * (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with this program; if not, see <http://www.gnu.org/licenses/>.
21   *
22   * Portions from gtk-vnc (originally licensed under the LGPL v2+):
23   *
24   * GTK VNC Widget
25   *
26   * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
27   * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
28   */
29  
30  #define GETTEXT_PACKAGE "qemu"
31  #define LOCALEDIR "po"
32  
33  #include "qemu/osdep.h"
34  #include "qapi/error.h"
35  #include "qapi/qapi-commands-control.h"
36  #include "qapi/qapi-commands-machine.h"
37  #include "qapi/qapi-commands-misc.h"
38  #include "qemu/cutils.h"
39  
40  #include "ui/console.h"
41  #include "ui/gtk.h"
42  #ifdef G_OS_WIN32
43  #include <gdk/gdkwin32.h>
44  #endif
45  #include "ui/win32-kbd-hook.h"
46  
47  #include <glib/gi18n.h>
48  #include <locale.h>
49  #if defined(CONFIG_VTE)
50  #include <vte/vte.h>
51  #endif
52  #include <math.h>
53  
54  #include "trace.h"
55  #include "qemu/cutils.h"
56  #include "ui/input.h"
57  #include "sysemu/runstate.h"
58  #include "sysemu/sysemu.h"
59  #include "keymaps.h"
60  #include "chardev/char.h"
61  #include "qom/object.h"
62  
63  #define MAX_VCS 10
64  #define VC_WINDOW_X_MIN  320
65  #define VC_WINDOW_Y_MIN  240
66  #define VC_TERM_X_MIN     80
67  #define VC_TERM_Y_MIN     25
68  #define VC_SCALE_MIN    0.25
69  #define VC_SCALE_STEP   0.25
70  
71  #ifdef GDK_WINDOWING_X11
72  #include "x_keymap.h"
73  
74  /* Gtk2 compat */
75  #ifndef GDK_IS_X11_DISPLAY
76  #define GDK_IS_X11_DISPLAY(dpy) (dpy != NULL)
77  #endif
78  #endif
79  
80  
81  #ifdef GDK_WINDOWING_WAYLAND
82  /* Gtk2 compat */
83  #ifndef GDK_IS_WAYLAND_DISPLAY
84  #define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy != NULL)
85  #endif
86  #endif
87  
88  
89  #ifdef GDK_WINDOWING_WIN32
90  /* Gtk2 compat */
91  #ifndef GDK_IS_WIN32_DISPLAY
92  #define GDK_IS_WIN32_DISPLAY(dpy) (dpy != NULL)
93  #endif
94  #endif
95  
96  
97  #ifdef GDK_WINDOWING_BROADWAY
98  /* Gtk2 compat */
99  #ifndef GDK_IS_BROADWAY_DISPLAY
100  #define GDK_IS_BROADWAY_DISPLAY(dpy) (dpy != NULL)
101  #endif
102  #endif
103  
104  
105  #ifdef GDK_WINDOWING_QUARTZ
106  /* Gtk2 compat */
107  #ifndef GDK_IS_QUARTZ_DISPLAY
108  #define GDK_IS_QUARTZ_DISPLAY(dpy) (dpy != NULL)
109  #endif
110  #endif
111  
112  
113  #if !defined(CONFIG_VTE)
114  # define VTE_CHECK_VERSION(a, b, c) 0
115  #endif
116  
117  #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
118  
119  static const guint16 *keycode_map;
120  static size_t keycode_maplen;
121  
122  struct GtkDisplayState {
123      GtkWidget *window;
124  
125      GtkWidget *menu_bar;
126  
127      GtkAccelGroup *accel_group;
128  
129      GtkWidget *machine_menu_item;
130      GtkWidget *machine_menu;
131      GtkWidget *pause_item;
132      GtkWidget *reset_item;
133      GtkWidget *powerdown_item;
134      GtkWidget *quit_item;
135  
136      GtkWidget *view_menu_item;
137      GtkWidget *view_menu;
138      GtkWidget *full_screen_item;
139      GtkWidget *copy_item;
140      GtkWidget *zoom_in_item;
141      GtkWidget *zoom_out_item;
142      GtkWidget *zoom_fixed_item;
143      GtkWidget *zoom_fit_item;
144      GtkWidget *grab_item;
145      GtkWidget *grab_on_hover_item;
146  
147      int nb_vcs;
148      VirtualConsole vc[MAX_VCS];
149  
150      GtkWidget *show_tabs_item;
151      GtkWidget *untabify_item;
152      GtkWidget *show_menubar_item;
153  
154      GtkWidget *vbox;
155      GtkWidget *notebook;
156      int button_mask;
157      gboolean last_set;
158      int last_x;
159      int last_y;
160      int grab_x_root;
161      int grab_y_root;
162      VirtualConsole *kbd_owner;
163      VirtualConsole *ptr_owner;
164  
165      gboolean full_screen;
166  
167      GdkCursor *null_cursor;
168      Notifier mouse_mode_notifier;
169      gboolean free_scale;
170  
171      bool external_pause_update;
172  
173      DisplayOptions *opts;
174  };
175  
176  struct VCChardev {
177      Chardev parent;
178      VirtualConsole *console;
179      bool echo;
180  };
181  typedef struct VCChardev VCChardev;
182  
183  #define TYPE_CHARDEV_VC "chardev-vc"
184  DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
185                           TYPE_CHARDEV_VC)
186  
187  bool gtk_use_gl_area;
188  
189  static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
190  static void gd_ungrab_pointer(GtkDisplayState *s);
191  static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
192  static void gd_ungrab_keyboard(GtkDisplayState *s);
193  
194  /** Utility Functions **/
195  
196  static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
197  {
198      VirtualConsole *vc;
199      gint i;
200  
201      for (i = 0; i < s->nb_vcs; i++) {
202          vc = &s->vc[i];
203          if (gtk_check_menu_item_get_active
204              (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
205              return vc;
206          }
207      }
208      return NULL;
209  }
210  
211  static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
212  {
213      VirtualConsole *vc;
214      gint i, p;
215  
216      for (i = 0; i < s->nb_vcs; i++) {
217          vc = &s->vc[i];
218          p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
219          if (p == page) {
220              return vc;
221          }
222      }
223      return NULL;
224  }
225  
226  static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
227  {
228      gint page;
229  
230      page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
231      return gd_vc_find_by_page(s, page);
232  }
233  
234  static bool gd_is_grab_active(GtkDisplayState *s)
235  {
236      return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
237  }
238  
239  static bool gd_grab_on_hover(GtkDisplayState *s)
240  {
241      return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
242  }
243  
244  static void gd_update_cursor(VirtualConsole *vc)
245  {
246      GtkDisplayState *s = vc->s;
247      GdkWindow *window;
248  
249      if (vc->type != GD_VC_GFX ||
250          !qemu_console_is_graphic(vc->gfx.dcl.con)) {
251          return;
252      }
253  
254      if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
255          return;
256      }
257  
258      window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
259      if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
260          gdk_window_set_cursor(window, s->null_cursor);
261      } else {
262          gdk_window_set_cursor(window, NULL);
263      }
264  }
265  
266  static void gd_update_caption(GtkDisplayState *s)
267  {
268      const char *status = "";
269      gchar *prefix;
270      gchar *title;
271      const char *grab = "";
272      bool is_paused = !runstate_is_running();
273      int i;
274  
275      if (qemu_name) {
276          prefix = g_strdup_printf("QEMU (%s)", qemu_name);
277      } else {
278          prefix = g_strdup_printf("QEMU");
279      }
280  
281      if (s->ptr_owner != NULL &&
282          s->ptr_owner->window == NULL) {
283          grab = _(" - Press Ctrl+Alt+G to release grab");
284      }
285  
286      if (is_paused) {
287          status = _(" [Paused]");
288      }
289      s->external_pause_update = true;
290      gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
291                                     is_paused);
292      s->external_pause_update = false;
293  
294      title = g_strdup_printf("%s%s%s", prefix, status, grab);
295      gtk_window_set_title(GTK_WINDOW(s->window), title);
296      g_free(title);
297  
298      for (i = 0; i < s->nb_vcs; i++) {
299          VirtualConsole *vc = &s->vc[i];
300  
301          if (!vc->window) {
302              continue;
303          }
304          title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
305                                  vc == s->kbd_owner ? " +kbd" : "",
306                                  vc == s->ptr_owner ? " +ptr" : "");
307          gtk_window_set_title(GTK_WINDOW(vc->window), title);
308          g_free(title);
309      }
310  
311      g_free(prefix);
312  }
313  
314  static void gd_update_geometry_hints(VirtualConsole *vc)
315  {
316      GtkDisplayState *s = vc->s;
317      GdkWindowHints mask = 0;
318      GdkGeometry geo = {};
319      GtkWidget *geo_widget = NULL;
320      GtkWindow *geo_window;
321  
322      if (vc->type == GD_VC_GFX) {
323          if (!vc->gfx.ds) {
324              return;
325          }
326          if (s->free_scale) {
327              geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
328              geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
329              mask |= GDK_HINT_MIN_SIZE;
330          } else {
331              geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
332              geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
333              mask |= GDK_HINT_MIN_SIZE;
334          }
335          geo_widget = vc->gfx.drawing_area;
336          gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
337  
338  #if defined(CONFIG_VTE)
339      } else if (vc->type == GD_VC_VTE) {
340          VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
341          GtkBorder padding = { 0 };
342  
343  #if VTE_CHECK_VERSION(0, 37, 0)
344          gtk_style_context_get_padding(
345                  gtk_widget_get_style_context(vc->vte.terminal),
346                  gtk_widget_get_state_flags(vc->vte.terminal),
347                  &padding);
348  #else
349          {
350              GtkBorder *ib = NULL;
351              gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
352              if (ib) {
353                  padding = *ib;
354                  gtk_border_free(ib);
355              }
356          }
357  #endif
358  
359          geo.width_inc  = vte_terminal_get_char_width(term);
360          geo.height_inc = vte_terminal_get_char_height(term);
361          mask |= GDK_HINT_RESIZE_INC;
362          geo.base_width  = geo.width_inc;
363          geo.base_height = geo.height_inc;
364          mask |= GDK_HINT_BASE_SIZE;
365          geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
366          geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
367          mask |= GDK_HINT_MIN_SIZE;
368  
369          geo.base_width  += padding.left + padding.right;
370          geo.base_height += padding.top + padding.bottom;
371          geo.min_width   += padding.left + padding.right;
372          geo.min_height  += padding.top + padding.bottom;
373          geo_widget = vc->vte.terminal;
374  #endif
375      }
376  
377      geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
378      gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
379  }
380  
381  void gd_update_windowsize(VirtualConsole *vc)
382  {
383      GtkDisplayState *s = vc->s;
384  
385      gd_update_geometry_hints(vc);
386  
387      if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
388          gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
389                            VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
390      }
391  }
392  
393  static void gd_update_full_redraw(VirtualConsole *vc)
394  {
395      GtkWidget *area = vc->gfx.drawing_area;
396      int ww, wh;
397      ww = gdk_window_get_width(gtk_widget_get_window(area));
398      wh = gdk_window_get_height(gtk_widget_get_window(area));
399  #if defined(CONFIG_OPENGL)
400      if (vc->gfx.gls && gtk_use_gl_area) {
401          gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
402          return;
403      }
404  #endif
405      gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
406  }
407  
408  static void gtk_release_modifiers(GtkDisplayState *s)
409  {
410      VirtualConsole *vc = gd_vc_find_current(s);
411  
412      if (vc->type != GD_VC_GFX ||
413          !qemu_console_is_graphic(vc->gfx.dcl.con)) {
414          return;
415      }
416      qkbd_state_lift_all_keys(vc->gfx.kbd);
417  }
418  
419  static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
420                                 GtkWidget *widget)
421  {
422      g_object_ref(G_OBJECT(widget));
423      gtk_container_remove(GTK_CONTAINER(from), widget);
424      gtk_container_add(GTK_CONTAINER(to), widget);
425      g_object_unref(G_OBJECT(widget));
426  }
427  
428  static void *gd_win32_get_hwnd(VirtualConsole *vc)
429  {
430  #ifdef G_OS_WIN32
431      return gdk_win32_window_get_impl_hwnd(
432          gtk_widget_get_window(vc->window ? vc->window : vc->s->window));
433  #else
434      return NULL;
435  #endif
436  }
437  
438  /** DisplayState Callbacks **/
439  
440  static void gd_update(DisplayChangeListener *dcl,
441                        int x, int y, int w, int h)
442  {
443      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
444      GdkWindow *win;
445      int x1, x2, y1, y2;
446      int mx, my;
447      int fbw, fbh;
448      int ww, wh;
449  
450      trace_gd_update(vc->label, x, y, w, h);
451  
452      if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
453          return;
454      }
455  
456      if (vc->gfx.convert) {
457          pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
458                                 NULL, vc->gfx.convert,
459                                 x, y, 0, 0, x, y, w, h);
460      }
461  
462      x1 = floor(x * vc->gfx.scale_x);
463      y1 = floor(y * vc->gfx.scale_y);
464  
465      x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
466      y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
467  
468      fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
469      fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
470  
471      win = gtk_widget_get_window(vc->gfx.drawing_area);
472      if (!win) {
473          return;
474      }
475      ww = gdk_window_get_width(win);
476      wh = gdk_window_get_height(win);
477  
478      mx = my = 0;
479      if (ww > fbw) {
480          mx = (ww - fbw) / 2;
481      }
482      if (wh > fbh) {
483          my = (wh - fbh) / 2;
484      }
485  
486      gtk_widget_queue_draw_area(vc->gfx.drawing_area,
487                                 mx + x1, my + y1, (x2 - x1), (y2 - y1));
488  }
489  
490  static void gd_refresh(DisplayChangeListener *dcl)
491  {
492      graphic_hw_update(dcl->con);
493  }
494  
495  static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
496  {
497      return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
498  }
499  
500  static void gd_mouse_set(DisplayChangeListener *dcl,
501                           int x, int y, int visible)
502  {
503      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
504      GdkDisplay *dpy;
505      gint x_root, y_root;
506  
507      if (qemu_input_is_absolute()) {
508          return;
509      }
510  
511      dpy = gtk_widget_get_display(vc->gfx.drawing_area);
512      gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
513                                 x, y, &x_root, &y_root);
514      gdk_device_warp(gd_get_pointer(dpy),
515                      gtk_widget_get_screen(vc->gfx.drawing_area),
516                      x_root, y_root);
517      vc->s->last_x = x;
518      vc->s->last_y = y;
519  }
520  
521  static void gd_cursor_define(DisplayChangeListener *dcl,
522                               QEMUCursor *c)
523  {
524      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
525      GdkPixbuf *pixbuf;
526      GdkCursor *cursor;
527  
528      if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
529          return;
530      }
531  
532      pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
533                                        GDK_COLORSPACE_RGB, true, 8,
534                                        c->width, c->height, c->width * 4,
535                                        NULL, NULL);
536      cursor = gdk_cursor_new_from_pixbuf
537          (gtk_widget_get_display(vc->gfx.drawing_area),
538           pixbuf, c->hot_x, c->hot_y);
539      gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
540      g_object_unref(pixbuf);
541      g_object_unref(cursor);
542  }
543  
544  static void gd_switch(DisplayChangeListener *dcl,
545                        DisplaySurface *surface)
546  {
547      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
548      bool resized = true;
549  
550      trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
551  
552      if (vc->gfx.surface) {
553          cairo_surface_destroy(vc->gfx.surface);
554          vc->gfx.surface = NULL;
555      }
556      if (vc->gfx.convert) {
557          pixman_image_unref(vc->gfx.convert);
558          vc->gfx.convert = NULL;
559      }
560  
561      if (vc->gfx.ds &&
562          surface_width(vc->gfx.ds) == surface_width(surface) &&
563          surface_height(vc->gfx.ds) == surface_height(surface)) {
564          resized = false;
565      }
566      vc->gfx.ds = surface;
567  
568      if (surface->format == PIXMAN_x8r8g8b8) {
569          /*
570           * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
571           *
572           * No need to convert, use surface directly.  Should be the
573           * common case as this is qemu_default_pixelformat(32) too.
574           */
575          vc->gfx.surface = cairo_image_surface_create_for_data
576              (surface_data(surface),
577               CAIRO_FORMAT_RGB24,
578               surface_width(surface),
579               surface_height(surface),
580               surface_stride(surface));
581      } else {
582          /* Must convert surface, use pixman to do it. */
583          vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
584                                                     surface_width(surface),
585                                                     surface_height(surface),
586                                                     NULL, 0);
587          vc->gfx.surface = cairo_image_surface_create_for_data
588              ((void *)pixman_image_get_data(vc->gfx.convert),
589               CAIRO_FORMAT_RGB24,
590               pixman_image_get_width(vc->gfx.convert),
591               pixman_image_get_height(vc->gfx.convert),
592               pixman_image_get_stride(vc->gfx.convert));
593          pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
594                                 NULL, vc->gfx.convert,
595                                 0, 0, 0, 0, 0, 0,
596                                 pixman_image_get_width(vc->gfx.convert),
597                                 pixman_image_get_height(vc->gfx.convert));
598      }
599  
600      if (resized) {
601          gd_update_windowsize(vc);
602      } else {
603          gd_update_full_redraw(vc);
604      }
605  }
606  
607  static const DisplayChangeListenerOps dcl_ops = {
608      .dpy_name             = "gtk",
609      .dpy_gfx_update       = gd_update,
610      .dpy_gfx_switch       = gd_switch,
611      .dpy_gfx_check_format = qemu_pixman_check_format,
612      .dpy_refresh          = gd_refresh,
613      .dpy_mouse_set        = gd_mouse_set,
614      .dpy_cursor_define    = gd_cursor_define,
615  };
616  
617  
618  #if defined(CONFIG_OPENGL)
619  
620  static bool gd_has_dmabuf(DisplayChangeListener *dcl)
621  {
622      VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
623  
624      if (gtk_use_gl_area && !gtk_widget_get_realized(vc->gfx.drawing_area)) {
625          /* FIXME: Assume it will work, actual check done after realize */
626          /* fixing this would require delaying listener registration */
627          return true;
628      }
629  
630      return vc->gfx.has_dmabuf;
631  }
632  
633  /** DisplayState Callbacks (opengl version) **/
634  
635  static const DisplayChangeListenerOps dcl_gl_area_ops = {
636      .dpy_name             = "gtk-egl",
637      .dpy_gfx_update       = gd_gl_area_update,
638      .dpy_gfx_switch       = gd_gl_area_switch,
639      .dpy_gfx_check_format = console_gl_check_format,
640      .dpy_refresh          = gd_gl_area_refresh,
641      .dpy_mouse_set        = gd_mouse_set,
642      .dpy_cursor_define    = gd_cursor_define,
643  
644      .dpy_gl_ctx_create       = gd_gl_area_create_context,
645      .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
646      .dpy_gl_ctx_make_current = gd_gl_area_make_current,
647      .dpy_gl_scanout_texture  = gd_gl_area_scanout_texture,
648      .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
649      .dpy_gl_update           = gd_gl_area_scanout_flush,
650      .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
651      .dpy_has_dmabuf          = gd_has_dmabuf,
652  };
653  
654  #ifdef CONFIG_X11
655  
656  static const DisplayChangeListenerOps dcl_egl_ops = {
657      .dpy_name             = "gtk-egl",
658      .dpy_gfx_update       = gd_egl_update,
659      .dpy_gfx_switch       = gd_egl_switch,
660      .dpy_gfx_check_format = console_gl_check_format,
661      .dpy_refresh          = gd_egl_refresh,
662      .dpy_mouse_set        = gd_mouse_set,
663      .dpy_cursor_define    = gd_cursor_define,
664  
665      .dpy_gl_ctx_create       = gd_egl_create_context,
666      .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
667      .dpy_gl_ctx_make_current = gd_egl_make_current,
668      .dpy_gl_scanout_disable  = gd_egl_scanout_disable,
669      .dpy_gl_scanout_texture  = gd_egl_scanout_texture,
670      .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
671      .dpy_gl_cursor_dmabuf    = gd_egl_cursor_dmabuf,
672      .dpy_gl_cursor_position  = gd_egl_cursor_position,
673      .dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
674      .dpy_gl_update           = gd_egl_scanout_flush,
675      .dpy_has_dmabuf          = gd_has_dmabuf,
676  };
677  
678  #endif
679  
680  #endif /* CONFIG_OPENGL */
681  
682  /** QEMU Events **/
683  
684  static void gd_change_runstate(void *opaque, bool running, RunState state)
685  {
686      GtkDisplayState *s = opaque;
687  
688      gd_update_caption(s);
689  }
690  
691  static void gd_mouse_mode_change(Notifier *notify, void *data)
692  {
693      GtkDisplayState *s;
694      int i;
695  
696      s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
697      /* release the grab at switching to absolute mode */
698      if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
699          gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
700                                         FALSE);
701      }
702      for (i = 0; i < s->nb_vcs; i++) {
703          VirtualConsole *vc = &s->vc[i];
704          gd_update_cursor(vc);
705      }
706  }
707  
708  /** GTK Events **/
709  
710  static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
711                                  void *opaque)
712  {
713      GtkDisplayState *s = opaque;
714      bool allow_close = true;
715  
716      if (s->opts->has_window_close && !s->opts->window_close) {
717          allow_close = false;
718      }
719  
720      if (allow_close) {
721          qmp_quit(NULL);
722      }
723  
724      return TRUE;
725  }
726  
727  static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
728  {
729      QemuUIInfo info;
730  
731      memset(&info, 0, sizeof(info));
732      info.width = width;
733      info.height = height;
734      dpy_set_ui_info(vc->gfx.dcl.con, &info);
735  }
736  
737  #if defined(CONFIG_OPENGL)
738  
739  static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
740                                  void *opaque)
741  {
742      VirtualConsole *vc = opaque;
743  
744      if (vc->gfx.gls) {
745          gd_gl_area_draw(vc);
746      }
747      return TRUE;
748  }
749  
750  static void gd_resize_event(GtkGLArea *area,
751                              gint width, gint height, gpointer *opaque)
752  {
753      VirtualConsole *vc = (void *)opaque;
754  
755      gd_set_ui_info(vc, width, height);
756  }
757  
758  #endif
759  
760  /*
761   * If available, return the update interval of the monitor in ms,
762   * else return 0 (the default update interval).
763   */
764  int gd_monitor_update_interval(GtkWidget *widget)
765  {
766  #ifdef GDK_VERSION_3_22
767      GdkWindow *win = gtk_widget_get_window(widget);
768  
769      if (win) {
770          GdkDisplay *dpy = gtk_widget_get_display(widget);
771          GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
772          int refresh_rate = gdk_monitor_get_refresh_rate(monitor); /* [mHz] */
773  
774          if (refresh_rate) {
775              /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
776              return MIN(1000 * 1000 / refresh_rate,
777                         GUI_REFRESH_INTERVAL_DEFAULT);
778          }
779      }
780  #endif
781      return 0;
782  }
783  
784  static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
785  {
786      VirtualConsole *vc = opaque;
787      GtkDisplayState *s = vc->s;
788      int mx, my;
789      int ww, wh;
790      int fbw, fbh;
791  
792  #if defined(CONFIG_OPENGL)
793      if (vc->gfx.gls) {
794          if (gtk_use_gl_area) {
795              /* invoke render callback please */
796              return FALSE;
797          } else {
798  #ifdef CONFIG_X11
799              gd_egl_draw(vc);
800              return TRUE;
801  #else
802              abort();
803  #endif
804          }
805      }
806  #endif
807  
808      if (!gtk_widget_get_realized(widget)) {
809          return FALSE;
810      }
811      if (!vc->gfx.ds) {
812          return FALSE;
813      }
814  
815      vc->gfx.dcl.update_interval =
816          gd_monitor_update_interval(vc->window ? vc->window : s->window);
817  
818      fbw = surface_width(vc->gfx.ds);
819      fbh = surface_height(vc->gfx.ds);
820  
821      ww = gdk_window_get_width(gtk_widget_get_window(widget));
822      wh = gdk_window_get_height(gtk_widget_get_window(widget));
823  
824      if (s->full_screen) {
825          vc->gfx.scale_x = (double)ww / fbw;
826          vc->gfx.scale_y = (double)wh / fbh;
827      } else if (s->free_scale) {
828          double sx, sy;
829  
830          sx = (double)ww / fbw;
831          sy = (double)wh / fbh;
832  
833          vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
834      }
835  
836      fbw *= vc->gfx.scale_x;
837      fbh *= vc->gfx.scale_y;
838  
839      mx = my = 0;
840      if (ww > fbw) {
841          mx = (ww - fbw) / 2;
842      }
843      if (wh > fbh) {
844          my = (wh - fbh) / 2;
845      }
846  
847      cairo_rectangle(cr, 0, 0, ww, wh);
848  
849      /* Optionally cut out the inner area where the pixmap
850         will be drawn. This avoids 'flashing' since we're
851         not double-buffering. Note we're using the undocumented
852         behaviour of drawing the rectangle from right to left
853         to cut out the whole */
854      cairo_rectangle(cr, mx + fbw, my,
855                      -1 * fbw, fbh);
856      cairo_fill(cr);
857  
858      cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
859      cairo_set_source_surface(cr, vc->gfx.surface,
860                               mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
861      cairo_paint(cr);
862  
863      return TRUE;
864  }
865  
866  static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
867                                  void *opaque)
868  {
869      VirtualConsole *vc = opaque;
870      GtkDisplayState *s = vc->s;
871      int x, y;
872      int mx, my;
873      int fbh, fbw;
874      int ww, wh;
875  
876      if (!vc->gfx.ds) {
877          return TRUE;
878      }
879  
880      fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
881      fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
882  
883      ww = gdk_window_get_width(gtk_widget_get_window(vc->gfx.drawing_area));
884      wh = gdk_window_get_height(gtk_widget_get_window(vc->gfx.drawing_area));
885  
886      mx = my = 0;
887      if (ww > fbw) {
888          mx = (ww - fbw) / 2;
889      }
890      if (wh > fbh) {
891          my = (wh - fbh) / 2;
892      }
893  
894      x = (motion->x - mx) / vc->gfx.scale_x;
895      y = (motion->y - my) / vc->gfx.scale_y;
896  
897      if (qemu_input_is_absolute()) {
898          if (x < 0 || y < 0 ||
899              x >= surface_width(vc->gfx.ds) ||
900              y >= surface_height(vc->gfx.ds)) {
901              return TRUE;
902          }
903          qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
904                               0, surface_width(vc->gfx.ds));
905          qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
906                               0, surface_height(vc->gfx.ds));
907          qemu_input_event_sync();
908      } else if (s->last_set && s->ptr_owner == vc) {
909          qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
910          qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
911          qemu_input_event_sync();
912      }
913      s->last_x = x;
914      s->last_y = y;
915      s->last_set = TRUE;
916  
917      if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
918          GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
919          GdkDisplay *dpy = gtk_widget_get_display(widget);
920          GdkWindow *win = gtk_widget_get_window(widget);
921          GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
922          GdkRectangle geometry;
923          int screen_width, screen_height;
924  
925          int x = (int)motion->x_root;
926          int y = (int)motion->y_root;
927  
928          gdk_monitor_get_geometry(monitor, &geometry);
929          screen_width = geometry.width;
930          screen_height = geometry.height;
931  
932          /* In relative mode check to see if client pointer hit
933           * one of the screen edges, and if so move it back by
934           * 200 pixels. This is important because the pointer
935           * in the server doesn't correspond 1-for-1, and so
936           * may still be only half way across the screen. Without
937           * this warp, the server pointer would thus appear to hit
938           * an invisible wall */
939          if (x == 0) {
940              x += 200;
941          }
942          if (y == 0) {
943              y += 200;
944          }
945          if (x == (screen_width - 1)) {
946              x -= 200;
947          }
948          if (y == (screen_height - 1)) {
949              y -= 200;
950          }
951  
952          if (x != (int)motion->x_root || y != (int)motion->y_root) {
953              GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
954              gdk_device_warp(dev, screen, x, y);
955              s->last_set = FALSE;
956              return FALSE;
957          }
958      }
959      return TRUE;
960  }
961  
962  static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
963                                  void *opaque)
964  {
965      VirtualConsole *vc = opaque;
966      GtkDisplayState *s = vc->s;
967      InputButton btn;
968  
969      /* implicitly grab the input at the first click in the relative mode */
970      if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
971          !qemu_input_is_absolute() && s->ptr_owner != vc) {
972          if (!vc->window) {
973              gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
974                                             TRUE);
975          } else {
976              gd_grab_pointer(vc, "relative-mode-click");
977          }
978          return TRUE;
979      }
980  
981      if (button->button == 1) {
982          btn = INPUT_BUTTON_LEFT;
983      } else if (button->button == 2) {
984          btn = INPUT_BUTTON_MIDDLE;
985      } else if (button->button == 3) {
986          btn = INPUT_BUTTON_RIGHT;
987      } else if (button->button == 8) {
988          btn = INPUT_BUTTON_SIDE;
989      } else if (button->button == 9) {
990          btn = INPUT_BUTTON_EXTRA;
991      } else {
992          return TRUE;
993      }
994  
995      qemu_input_queue_btn(vc->gfx.dcl.con, btn,
996                           button->type == GDK_BUTTON_PRESS);
997      qemu_input_event_sync();
998      return TRUE;
999  }
1000  
1001  static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
1002                                  void *opaque)
1003  {
1004      VirtualConsole *vc = opaque;
1005      InputButton btn;
1006  
1007      if (scroll->direction == GDK_SCROLL_UP) {
1008          btn = INPUT_BUTTON_WHEEL_UP;
1009      } else if (scroll->direction == GDK_SCROLL_DOWN) {
1010          btn = INPUT_BUTTON_WHEEL_DOWN;
1011      } else if (scroll->direction == GDK_SCROLL_SMOOTH) {
1012          gdouble delta_x, delta_y;
1013          if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
1014                                           &delta_x, &delta_y)) {
1015              return TRUE;
1016          }
1017          if (delta_y == 0) {
1018              return TRUE;
1019          } else if (delta_y > 0) {
1020              btn = INPUT_BUTTON_WHEEL_DOWN;
1021          } else {
1022              btn = INPUT_BUTTON_WHEEL_UP;
1023          }
1024      } else {
1025          return TRUE;
1026      }
1027  
1028      qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
1029      qemu_input_event_sync();
1030      qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
1031      qemu_input_event_sync();
1032      return TRUE;
1033  }
1034  
1035  
1036  static const guint16 *gd_get_keymap(size_t *maplen)
1037  {
1038      GdkDisplay *dpy = gdk_display_get_default();
1039  
1040  #ifdef GDK_WINDOWING_X11
1041      if (GDK_IS_X11_DISPLAY(dpy)) {
1042          trace_gd_keymap_windowing("x11");
1043          return qemu_xkeymap_mapping_table(
1044              gdk_x11_display_get_xdisplay(dpy), maplen);
1045      }
1046  #endif
1047  
1048  #ifdef GDK_WINDOWING_WAYLAND
1049      if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
1050          trace_gd_keymap_windowing("wayland");
1051          *maplen = qemu_input_map_xorgevdev_to_qcode_len;
1052          return qemu_input_map_xorgevdev_to_qcode;
1053      }
1054  #endif
1055  
1056  #ifdef GDK_WINDOWING_WIN32
1057      if (GDK_IS_WIN32_DISPLAY(dpy)) {
1058          trace_gd_keymap_windowing("win32");
1059          *maplen = qemu_input_map_atset1_to_qcode_len;
1060          return qemu_input_map_atset1_to_qcode;
1061      }
1062  #endif
1063  
1064  #ifdef GDK_WINDOWING_QUARTZ
1065      if (GDK_IS_QUARTZ_DISPLAY(dpy)) {
1066          trace_gd_keymap_windowing("quartz");
1067          *maplen = qemu_input_map_osx_to_qcode_len;
1068          return qemu_input_map_osx_to_qcode;
1069      }
1070  #endif
1071  
1072  #ifdef GDK_WINDOWING_BROADWAY
1073      if (GDK_IS_BROADWAY_DISPLAY(dpy)) {
1074          trace_gd_keymap_windowing("broadway");
1075          g_warning("experimental: using broadway, x11 virtual keysym\n"
1076                    "mapping - with very limited support. See also\n"
1077                    "https://bugzilla.gnome.org/show_bug.cgi?id=700105");
1078          *maplen = qemu_input_map_x11_to_qcode_len;
1079          return qemu_input_map_x11_to_qcode;
1080      }
1081  #endif
1082  
1083      g_warning("Unsupported GDK Windowing platform.\n"
1084                "Disabling extended keycode tables.\n"
1085                "Please report to qemu-devel@nongnu.org\n"
1086                "including the following information:\n"
1087                "\n"
1088                "  - Operating system\n"
1089                "  - GDK Windowing system build\n");
1090      return NULL;
1091  }
1092  
1093  
1094  static int gd_map_keycode(int scancode)
1095  {
1096      if (!keycode_map) {
1097          return 0;
1098      }
1099      if (scancode > keycode_maplen) {
1100          return 0;
1101      }
1102  
1103      return keycode_map[scancode];
1104  }
1105  
1106  static int gd_get_keycode(GdkEventKey *key)
1107  {
1108  #ifdef G_OS_WIN32
1109      int scancode = gdk_event_get_scancode((GdkEvent *)key);
1110  
1111      /* translate Windows native scancodes to atset1 keycodes */
1112      switch (scancode & (KF_EXTENDED | 0xff)) {
1113      case 0x145:     /* NUMLOCK */
1114          return scancode & 0xff;
1115      }
1116  
1117      return scancode & KF_EXTENDED ?
1118          0xe000 | (scancode & 0xff) : scancode & 0xff;
1119  
1120  #else
1121      return key->hardware_keycode;
1122  #endif
1123  }
1124  
1125  static gboolean gd_text_key_down(GtkWidget *widget,
1126                                   GdkEventKey *key, void *opaque)
1127  {
1128      VirtualConsole *vc = opaque;
1129      QemuConsole *con = vc->gfx.dcl.con;
1130  
1131      if (key->keyval == GDK_KEY_Delete) {
1132          kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
1133      } else if (key->length) {
1134          kbd_put_string_console(con, key->string, key->length);
1135      } else {
1136          int qcode = gd_map_keycode(gd_get_keycode(key));
1137          kbd_put_qcode_console(con, qcode, false);
1138      }
1139      return TRUE;
1140  }
1141  
1142  static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
1143  {
1144      VirtualConsole *vc = opaque;
1145      int keycode, qcode;
1146  
1147  #ifdef G_OS_WIN32
1148      /* on windows, we ought to ignore the reserved key event? */
1149      if (key->hardware_keycode == 0xff)
1150          return false;
1151  
1152      if (!vc->s->kbd_owner) {
1153          if (key->hardware_keycode == VK_LWIN ||
1154              key->hardware_keycode == VK_RWIN) {
1155              return FALSE;
1156          }
1157      }
1158  #endif
1159  
1160      if (key->keyval == GDK_KEY_Pause
1161  #ifdef G_OS_WIN32
1162          /* for some reason GDK does not fill keyval for VK_PAUSE
1163           * See https://bugzilla.gnome.org/show_bug.cgi?id=769214
1164           */
1165          || key->hardware_keycode == VK_PAUSE
1166  #endif
1167          ) {
1168          qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
1169                               key->type == GDK_KEY_PRESS);
1170          return TRUE;
1171      }
1172  
1173      keycode = gd_get_keycode(key);
1174      qcode = gd_map_keycode(keycode);
1175  
1176      trace_gd_key_event(vc->label, keycode, qcode,
1177                         (key->type == GDK_KEY_PRESS) ? "down" : "up");
1178  
1179      qkbd_state_key_event(vc->gfx.kbd, qcode,
1180                           key->type == GDK_KEY_PRESS);
1181  
1182      return TRUE;
1183  }
1184  
1185  static gboolean gd_grab_broken_event(GtkWidget *widget,
1186                                       GdkEventGrabBroken *event, void *opaque)
1187  {
1188  #ifdef CONFIG_WIN32
1189      /*
1190       * On Windows the Ctrl-Alt-Del key combination can't be grabbed. This
1191       * key combination leaves all three keys in a stuck condition. We use
1192       * the grab-broken-event to release all keys.
1193       */
1194      if (event->keyboard) {
1195          VirtualConsole *vc = opaque;
1196          GtkDisplayState *s = vc->s;
1197  
1198          gtk_release_modifiers(s);
1199      }
1200  #endif
1201      return TRUE;
1202  }
1203  
1204  static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
1205  {
1206      if (event->type == GDK_MOTION_NOTIFY) {
1207          return gd_motion_event(widget, &event->motion, opaque);
1208      }
1209      return FALSE;
1210  }
1211  
1212  /** Window Menu Actions **/
1213  
1214  static void gd_menu_pause(GtkMenuItem *item, void *opaque)
1215  {
1216      GtkDisplayState *s = opaque;
1217  
1218      if (s->external_pause_update) {
1219          return;
1220      }
1221      if (runstate_is_running()) {
1222          qmp_stop(NULL);
1223      } else {
1224          qmp_cont(NULL);
1225      }
1226  }
1227  
1228  static void gd_menu_reset(GtkMenuItem *item, void *opaque)
1229  {
1230      qmp_system_reset(NULL);
1231  }
1232  
1233  static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
1234  {
1235      qmp_system_powerdown(NULL);
1236  }
1237  
1238  static void gd_menu_quit(GtkMenuItem *item, void *opaque)
1239  {
1240      qmp_quit(NULL);
1241  }
1242  
1243  static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
1244  {
1245      GtkDisplayState *s = opaque;
1246      VirtualConsole *vc = gd_vc_find_by_menu(s);
1247      GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1248      gint page;
1249  
1250      gtk_release_modifiers(s);
1251      if (vc) {
1252          page = gtk_notebook_page_num(nb, vc->tab_item);
1253          gtk_notebook_set_current_page(nb, page);
1254          gtk_widget_grab_focus(vc->focus);
1255      }
1256  }
1257  
1258  static void gd_accel_switch_vc(void *opaque)
1259  {
1260      VirtualConsole *vc = opaque;
1261  
1262      gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1263  }
1264  
1265  static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1266  {
1267      GtkDisplayState *s = opaque;
1268      VirtualConsole *vc = gd_vc_find_current(s);
1269  
1270      if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1271          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1272      } else {
1273          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1274      }
1275      gd_update_windowsize(vc);
1276  }
1277  
1278  static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1279                                      void *opaque)
1280  {
1281      VirtualConsole *vc = opaque;
1282      GtkDisplayState *s = vc->s;
1283  
1284      gtk_widget_set_sensitive(vc->menu_item, true);
1285      gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1286      gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1287                                      vc->tab_item, vc->label);
1288      gtk_widget_destroy(vc->window);
1289      vc->window = NULL;
1290      return TRUE;
1291  }
1292  
1293  static gboolean gd_win_grab(void *opaque)
1294  {
1295      VirtualConsole *vc = opaque;
1296  
1297      fprintf(stderr, "%s: %s\n", __func__, vc->label);
1298      if (vc->s->ptr_owner) {
1299          gd_ungrab_pointer(vc->s);
1300      } else {
1301          gd_grab_pointer(vc, "user-request-detached-tab");
1302      }
1303      return TRUE;
1304  }
1305  
1306  static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1307  {
1308      GtkDisplayState *s = opaque;
1309      VirtualConsole *vc = gd_vc_find_current(s);
1310  
1311      if (vc->type == GD_VC_GFX &&
1312          qemu_console_is_graphic(vc->gfx.dcl.con)) {
1313          gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1314                                         FALSE);
1315      }
1316      if (!vc->window) {
1317          gtk_widget_set_sensitive(vc->menu_item, false);
1318          vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1319          gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1320  
1321          g_signal_connect(vc->window, "delete-event",
1322                           G_CALLBACK(gd_tab_window_close), vc);
1323          gtk_widget_show_all(vc->window);
1324  
1325          if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1326              GtkAccelGroup *ag = gtk_accel_group_new();
1327              gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
1328  
1329              GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1330                                                 vc, NULL);
1331              gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1332          }
1333  
1334          gd_update_geometry_hints(vc);
1335          gd_update_caption(s);
1336      }
1337  }
1338  
1339  static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque)
1340  {
1341      GtkDisplayState *s = opaque;
1342      VirtualConsole *vc = gd_vc_find_current(s);
1343  
1344      if (s->full_screen) {
1345          return;
1346      }
1347  
1348      if (gtk_check_menu_item_get_active(
1349                  GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
1350          gtk_widget_show(s->menu_bar);
1351      } else {
1352          gtk_widget_hide(s->menu_bar);
1353      }
1354      gd_update_windowsize(vc);
1355  }
1356  
1357  static void gd_accel_show_menubar(void *opaque)
1358  {
1359      GtkDisplayState *s = opaque;
1360      gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item));
1361  }
1362  
1363  static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1364  {
1365      GtkDisplayState *s = opaque;
1366      VirtualConsole *vc = gd_vc_find_current(s);
1367  
1368      if (!s->full_screen) {
1369          gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1370          gtk_widget_hide(s->menu_bar);
1371          if (vc->type == GD_VC_GFX) {
1372              gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1373          }
1374          gtk_window_fullscreen(GTK_WINDOW(s->window));
1375          s->full_screen = TRUE;
1376      } else {
1377          gtk_window_unfullscreen(GTK_WINDOW(s->window));
1378          gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1379          if (gtk_check_menu_item_get_active(
1380                      GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
1381              gtk_widget_show(s->menu_bar);
1382          }
1383          s->full_screen = FALSE;
1384          if (vc->type == GD_VC_GFX) {
1385              vc->gfx.scale_x = 1.0;
1386              vc->gfx.scale_y = 1.0;
1387              gd_update_windowsize(vc);
1388          }
1389      }
1390  
1391      gd_update_cursor(vc);
1392  }
1393  
1394  static void gd_accel_full_screen(void *opaque)
1395  {
1396      GtkDisplayState *s = opaque;
1397      gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
1398  }
1399  
1400  static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1401  {
1402      GtkDisplayState *s = opaque;
1403      VirtualConsole *vc = gd_vc_find_current(s);
1404  
1405      gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1406                                     FALSE);
1407  
1408      vc->gfx.scale_x += VC_SCALE_STEP;
1409      vc->gfx.scale_y += VC_SCALE_STEP;
1410  
1411      gd_update_windowsize(vc);
1412  }
1413  
1414  static void gd_accel_zoom_in(void *opaque)
1415  {
1416      GtkDisplayState *s = opaque;
1417      gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_in_item));
1418  }
1419  
1420  static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1421  {
1422      GtkDisplayState *s = opaque;
1423      VirtualConsole *vc = gd_vc_find_current(s);
1424  
1425      gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1426                                     FALSE);
1427  
1428      vc->gfx.scale_x -= VC_SCALE_STEP;
1429      vc->gfx.scale_y -= VC_SCALE_STEP;
1430  
1431      vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
1432      vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1433  
1434      gd_update_windowsize(vc);
1435  }
1436  
1437  static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1438  {
1439      GtkDisplayState *s = opaque;
1440      VirtualConsole *vc = gd_vc_find_current(s);
1441  
1442      vc->gfx.scale_x = 1.0;
1443      vc->gfx.scale_y = 1.0;
1444  
1445      gd_update_windowsize(vc);
1446  }
1447  
1448  static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1449  {
1450      GtkDisplayState *s = opaque;
1451      VirtualConsole *vc = gd_vc_find_current(s);
1452  
1453      if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1454          s->free_scale = TRUE;
1455      } else {
1456          s->free_scale = FALSE;
1457          vc->gfx.scale_x = 1.0;
1458          vc->gfx.scale_y = 1.0;
1459      }
1460  
1461      gd_update_windowsize(vc);
1462      gd_update_full_redraw(vc);
1463  }
1464  
1465  static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
1466  {
1467      GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1468      GdkSeat *seat = gdk_display_get_default_seat(display);
1469      GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area);
1470      GdkSeatCapabilities caps = 0;
1471      GdkCursor *cursor = NULL;
1472  
1473      if (kbd) {
1474          caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
1475      }
1476      if (ptr) {
1477          caps |= GDK_SEAT_CAPABILITY_ALL_POINTING;
1478          cursor = vc->s->null_cursor;
1479      }
1480  
1481      if (caps) {
1482          gdk_seat_grab(seat, window, caps, false, cursor,
1483                        NULL, NULL, NULL);
1484      } else {
1485          gdk_seat_ungrab(seat);
1486      }
1487  }
1488  
1489  static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
1490  {
1491      if (vc->s->kbd_owner) {
1492          if (vc->s->kbd_owner == vc) {
1493              return;
1494          } else {
1495              gd_ungrab_keyboard(vc->s);
1496          }
1497      }
1498  
1499      win32_kbd_set_grab(true);
1500      gd_grab_update(vc, true, vc->s->ptr_owner == vc);
1501      vc->s->kbd_owner = vc;
1502      gd_update_caption(vc->s);
1503      trace_gd_grab(vc->label, "kbd", reason);
1504  }
1505  
1506  static void gd_ungrab_keyboard(GtkDisplayState *s)
1507  {
1508      VirtualConsole *vc = s->kbd_owner;
1509  
1510      if (vc == NULL) {
1511          return;
1512      }
1513      s->kbd_owner = NULL;
1514  
1515      win32_kbd_set_grab(false);
1516      gd_grab_update(vc, false, vc->s->ptr_owner == vc);
1517      gd_update_caption(s);
1518      trace_gd_ungrab(vc->label, "kbd");
1519  }
1520  
1521  static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
1522  {
1523      GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1524  
1525      if (vc->s->ptr_owner) {
1526          if (vc->s->ptr_owner == vc) {
1527              return;
1528          } else {
1529              gd_ungrab_pointer(vc->s);
1530          }
1531      }
1532  
1533      gd_grab_update(vc, vc->s->kbd_owner == vc, true);
1534      gdk_device_get_position(gd_get_pointer(display),
1535                              NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
1536      vc->s->ptr_owner = vc;
1537      gd_update_caption(vc->s);
1538      trace_gd_grab(vc->label, "ptr", reason);
1539  }
1540  
1541  static void gd_ungrab_pointer(GtkDisplayState *s)
1542  {
1543      VirtualConsole *vc = s->ptr_owner;
1544      GdkDisplay *display;
1545  
1546      if (vc == NULL) {
1547          return;
1548      }
1549      s->ptr_owner = NULL;
1550  
1551      display = gtk_widget_get_display(vc->gfx.drawing_area);
1552      gd_grab_update(vc, vc->s->kbd_owner == vc, false);
1553      gdk_device_warp(gd_get_pointer(display),
1554                      gtk_widget_get_screen(vc->gfx.drawing_area),
1555                      vc->s->grab_x_root, vc->s->grab_y_root);
1556      gd_update_caption(s);
1557      trace_gd_ungrab(vc->label, "ptr");
1558  }
1559  
1560  static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
1561  {
1562      GtkDisplayState *s = opaque;
1563      VirtualConsole *vc = gd_vc_find_current(s);
1564  
1565      if (gd_is_grab_active(s)) {
1566          gd_grab_keyboard(vc, "user-request-main-window");
1567          gd_grab_pointer(vc, "user-request-main-window");
1568      } else {
1569          gd_ungrab_keyboard(s);
1570          gd_ungrab_pointer(s);
1571      }
1572  
1573      gd_update_cursor(vc);
1574  }
1575  
1576  static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1577                             gpointer data)
1578  {
1579      GtkDisplayState *s = data;
1580      VirtualConsole *vc;
1581      gboolean on_vga;
1582  
1583      if (!gtk_widget_get_realized(s->notebook)) {
1584          return;
1585      }
1586  
1587      vc = gd_vc_find_by_page(s, arg2);
1588      if (!vc) {
1589          return;
1590      }
1591      gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1592                                     TRUE);
1593      on_vga = (vc->type == GD_VC_GFX &&
1594                qemu_console_is_graphic(vc->gfx.dcl.con));
1595      if (!on_vga) {
1596          gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1597                                         FALSE);
1598      } else if (s->full_screen) {
1599          gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1600                                         TRUE);
1601      }
1602      gtk_widget_set_sensitive(s->grab_item, on_vga);
1603  #ifdef CONFIG_VTE
1604      gtk_widget_set_sensitive(s->copy_item, vc->type == GD_VC_VTE);
1605  #endif
1606  
1607      gd_update_windowsize(vc);
1608      gd_update_cursor(vc);
1609  }
1610  
1611  static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1612                                 gpointer opaque)
1613  {
1614      VirtualConsole *vc = opaque;
1615      GtkDisplayState *s = vc->s;
1616  
1617      if (gd_grab_on_hover(s)) {
1618          gd_grab_keyboard(vc, "grab-on-hover");
1619      }
1620      return TRUE;
1621  }
1622  
1623  static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1624                                 gpointer opaque)
1625  {
1626      VirtualConsole *vc = opaque;
1627      GtkDisplayState *s = vc->s;
1628  
1629      if (gd_grab_on_hover(s)) {
1630          gd_ungrab_keyboard(s);
1631      }
1632      return TRUE;
1633  }
1634  
1635  static gboolean gd_focus_in_event(GtkWidget *widget,
1636                                    GdkEventFocus *event, gpointer opaque)
1637  {
1638      VirtualConsole *vc = opaque;
1639  
1640      win32_kbd_set_window(gd_win32_get_hwnd(vc));
1641      return TRUE;
1642  }
1643  
1644  static gboolean gd_focus_out_event(GtkWidget *widget,
1645                                     GdkEventFocus *event, gpointer opaque)
1646  {
1647      VirtualConsole *vc = opaque;
1648      GtkDisplayState *s = vc->s;
1649  
1650      win32_kbd_set_window(NULL);
1651      gtk_release_modifiers(s);
1652      return TRUE;
1653  }
1654  
1655  static gboolean gd_configure(GtkWidget *widget,
1656                               GdkEventConfigure *cfg, gpointer opaque)
1657  {
1658      VirtualConsole *vc = opaque;
1659  
1660      gd_set_ui_info(vc, cfg->width, cfg->height);
1661      return FALSE;
1662  }
1663  
1664  /** Virtual Console Callbacks **/
1665  
1666  static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1667                                 int idx, GSList *group, GtkWidget *view_menu)
1668  {
1669      vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1670      gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1671              HOTKEY_MODIFIERS, 0,
1672              g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1673      gtk_accel_label_set_accel(
1674              GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1675              GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1676  
1677      g_signal_connect(vc->menu_item, "activate",
1678                       G_CALLBACK(gd_menu_switch_vc), s);
1679      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1680  
1681      return gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1682  }
1683  
1684  #if defined(CONFIG_VTE)
1685  static void gd_menu_copy(GtkMenuItem *item, void *opaque)
1686  {
1687      GtkDisplayState *s = opaque;
1688      VirtualConsole *vc = gd_vc_find_current(s);
1689  
1690  #if VTE_CHECK_VERSION(0, 50, 0)
1691      vte_terminal_copy_clipboard_format(VTE_TERMINAL(vc->vte.terminal),
1692                                         VTE_FORMAT_TEXT);
1693  #else
1694      vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal));
1695  #endif
1696  }
1697  
1698  static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
1699  {
1700      VirtualConsole *vc = opaque;
1701  
1702      if (gtk_adjustment_get_upper(adjustment) >
1703          gtk_adjustment_get_page_size(adjustment)) {
1704          gtk_widget_show(vc->vte.scrollbar);
1705      } else {
1706          gtk_widget_hide(vc->vte.scrollbar);
1707      }
1708  }
1709  
1710  static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1711  {
1712      VCChardev *vcd = VC_CHARDEV(chr);
1713      VirtualConsole *vc = vcd->console;
1714  
1715      vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1716      return len;
1717  }
1718  
1719  static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
1720  {
1721      VCChardev *vcd = VC_CHARDEV(chr);
1722      VirtualConsole *vc = vcd->console;
1723  
1724      if (vc) {
1725          vc->vte.echo = echo;
1726      } else {
1727          vcd->echo = echo;
1728      }
1729  }
1730  
1731  static int nb_vcs;
1732  static Chardev *vcs[MAX_VCS];
1733  static void gd_vc_open(Chardev *chr,
1734                         ChardevBackend *backend,
1735                         bool *be_opened,
1736                         Error **errp)
1737  {
1738      if (nb_vcs == MAX_VCS) {
1739          error_setg(errp, "Maximum number of consoles reached");
1740          return;
1741      }
1742  
1743      vcs[nb_vcs++] = chr;
1744  
1745      /* console/chardev init sometimes completes elsewhere in a 2nd
1746       * stage, so defer OPENED events until they are fully initialized
1747       */
1748      *be_opened = false;
1749  }
1750  
1751  static void char_gd_vc_class_init(ObjectClass *oc, void *data)
1752  {
1753      ChardevClass *cc = CHARDEV_CLASS(oc);
1754  
1755      cc->parse = qemu_chr_parse_vc;
1756      cc->open = gd_vc_open;
1757      cc->chr_write = gd_vc_chr_write;
1758      cc->chr_set_echo = gd_vc_chr_set_echo;
1759  }
1760  
1761  static const TypeInfo char_gd_vc_type_info = {
1762      .name = TYPE_CHARDEV_VC,
1763      .parent = TYPE_CHARDEV,
1764      .instance_size = sizeof(VCChardev),
1765      .class_init = char_gd_vc_class_init,
1766  };
1767  
1768  static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1769                           gpointer user_data)
1770  {
1771      VirtualConsole *vc = user_data;
1772  
1773      if (vc->vte.echo) {
1774          VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
1775          int i;
1776          for (i = 0; i < size; i++) {
1777              uint8_t c = text[i];
1778              if (c >= 128 || isprint(c)) {
1779                  /* 8-bit characters are considered printable.  */
1780                  vte_terminal_feed(term, &text[i], 1);
1781              } else if (c == '\r' || c == '\n') {
1782                  vte_terminal_feed(term, "\r\n", 2);
1783              } else {
1784                  char ctrl[2] = { '^', 0};
1785                  ctrl[1] = text[i] ^ 64;
1786                  vte_terminal_feed(term, ctrl, 2);
1787              }
1788          }
1789      }
1790  
1791      int remaining = size;
1792      uint8_t* p = (uint8_t *)text;
1793      while (remaining > 0) {
1794          int can_write = qemu_chr_be_can_write(vc->vte.chr);
1795          int written = MIN(remaining, can_write);
1796          qemu_chr_be_write(vc->vte.chr, p, written);
1797  
1798          remaining -= written;
1799          p += written;
1800      }
1801      return TRUE;
1802  }
1803  
1804  static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
1805                                Chardev *chr, int idx,
1806                                GSList *group, GtkWidget *view_menu)
1807  {
1808      char buffer[32];
1809      GtkWidget *box;
1810      GtkWidget *scrollbar;
1811      GtkAdjustment *vadjustment;
1812      VCChardev *vcd = VC_CHARDEV(chr);
1813  
1814      vc->s = s;
1815      vc->vte.echo = vcd->echo;
1816      vc->vte.chr = chr;
1817      vcd->console = vc;
1818  
1819      snprintf(buffer, sizeof(buffer), "vc%d", idx);
1820      vc->label = g_strdup_printf("%s", vc->vte.chr->label
1821                                  ? vc->vte.chr->label : buffer);
1822      group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1823  
1824      vc->vte.terminal = vte_terminal_new();
1825      g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1826  
1827      /* The documentation says that the default is UTF-8, but actually it is
1828       * 7-bit ASCII at least in VTE 0.38. The function is deprecated since
1829       * VTE 0.54 (only UTF-8 is supported now). */
1830  #if !VTE_CHECK_VERSION(0, 54, 0)
1831  #if VTE_CHECK_VERSION(0, 38, 0)
1832      vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
1833  #else
1834      vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
1835  #endif
1836  #endif
1837  
1838      vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
1839      vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
1840                            VC_TERM_X_MIN, VC_TERM_Y_MIN);
1841  
1842  #if VTE_CHECK_VERSION(0, 28, 0)
1843      vadjustment = gtk_scrollable_get_vadjustment
1844          (GTK_SCROLLABLE(vc->vte.terminal));
1845  #else
1846      vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
1847  #endif
1848  
1849      box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
1850      scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
1851  
1852      gtk_box_pack_end(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
1853      gtk_box_pack_end(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
1854  
1855      vc->vte.box = box;
1856      vc->vte.scrollbar = scrollbar;
1857  
1858      g_signal_connect(vadjustment, "changed",
1859                       G_CALLBACK(gd_vc_adjustment_changed), vc);
1860  
1861      vc->type = GD_VC_VTE;
1862      vc->tab_item = box;
1863      vc->focus = vc->vte.terminal;
1864      gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1865                               gtk_label_new(vc->label));
1866  
1867      qemu_chr_be_event(vc->vte.chr, CHR_EVENT_OPENED);
1868  
1869      return group;
1870  }
1871  
1872  static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1873                          GtkWidget *view_menu)
1874  {
1875      int i;
1876  
1877      for (i = 0; i < nb_vcs; i++) {
1878          VirtualConsole *vc = &s->vc[s->nb_vcs];
1879          group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1880          s->nb_vcs++;
1881      }
1882  }
1883  #endif /* CONFIG_VTE */
1884  
1885  /** Window Creation **/
1886  
1887  static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1888  {
1889      g_signal_connect(vc->gfx.drawing_area, "draw",
1890                       G_CALLBACK(gd_draw_event), vc);
1891  #if defined(CONFIG_OPENGL)
1892      if (gtk_use_gl_area) {
1893          /* wire up GtkGlArea events */
1894          g_signal_connect(vc->gfx.drawing_area, "render",
1895                           G_CALLBACK(gd_render_event), vc);
1896          g_signal_connect(vc->gfx.drawing_area, "resize",
1897                           G_CALLBACK(gd_resize_event), vc);
1898      }
1899  #endif
1900      if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1901          g_signal_connect(vc->gfx.drawing_area, "event",
1902                           G_CALLBACK(gd_event), vc);
1903          g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1904                           G_CALLBACK(gd_button_event), vc);
1905          g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1906                           G_CALLBACK(gd_button_event), vc);
1907          g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1908                           G_CALLBACK(gd_scroll_event), vc);
1909          g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1910                           G_CALLBACK(gd_key_event), vc);
1911          g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1912                           G_CALLBACK(gd_key_event), vc);
1913  
1914          g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1915                           G_CALLBACK(gd_enter_event), vc);
1916          g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1917                           G_CALLBACK(gd_leave_event), vc);
1918          g_signal_connect(vc->gfx.drawing_area, "focus-in-event",
1919                           G_CALLBACK(gd_focus_in_event), vc);
1920          g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1921                           G_CALLBACK(gd_focus_out_event), vc);
1922          g_signal_connect(vc->gfx.drawing_area, "configure-event",
1923                           G_CALLBACK(gd_configure), vc);
1924          g_signal_connect(vc->gfx.drawing_area, "grab-broken-event",
1925                           G_CALLBACK(gd_grab_broken_event), vc);
1926      } else {
1927          g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1928                           G_CALLBACK(gd_text_key_down), vc);
1929      }
1930  }
1931  
1932  static void gd_connect_signals(GtkDisplayState *s)
1933  {
1934      g_signal_connect(s->show_tabs_item, "activate",
1935                       G_CALLBACK(gd_menu_show_tabs), s);
1936      g_signal_connect(s->untabify_item, "activate",
1937                       G_CALLBACK(gd_menu_untabify), s);
1938      g_signal_connect(s->show_menubar_item, "activate",
1939                       G_CALLBACK(gd_menu_show_menubar), s);
1940  
1941      g_signal_connect(s->window, "delete-event",
1942                       G_CALLBACK(gd_window_close), s);
1943  
1944      g_signal_connect(s->pause_item, "activate",
1945                       G_CALLBACK(gd_menu_pause), s);
1946      g_signal_connect(s->reset_item, "activate",
1947                       G_CALLBACK(gd_menu_reset), s);
1948      g_signal_connect(s->powerdown_item, "activate",
1949                       G_CALLBACK(gd_menu_powerdown), s);
1950      g_signal_connect(s->quit_item, "activate",
1951                       G_CALLBACK(gd_menu_quit), s);
1952  #if defined(CONFIG_VTE)
1953      g_signal_connect(s->copy_item, "activate",
1954                       G_CALLBACK(gd_menu_copy), s);
1955  #endif
1956      g_signal_connect(s->full_screen_item, "activate",
1957                       G_CALLBACK(gd_menu_full_screen), s);
1958      g_signal_connect(s->zoom_in_item, "activate",
1959                       G_CALLBACK(gd_menu_zoom_in), s);
1960      g_signal_connect(s->zoom_out_item, "activate",
1961                       G_CALLBACK(gd_menu_zoom_out), s);
1962      g_signal_connect(s->zoom_fixed_item, "activate",
1963                       G_CALLBACK(gd_menu_zoom_fixed), s);
1964      g_signal_connect(s->zoom_fit_item, "activate",
1965                       G_CALLBACK(gd_menu_zoom_fit), s);
1966      g_signal_connect(s->grab_item, "activate",
1967                       G_CALLBACK(gd_menu_grab_input), s);
1968      g_signal_connect(s->notebook, "switch-page",
1969                       G_CALLBACK(gd_change_page), s);
1970  }
1971  
1972  static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1973  {
1974      GtkWidget *machine_menu;
1975      GtkWidget *separator;
1976  
1977      machine_menu = gtk_menu_new();
1978      gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
1979  
1980      s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1981      gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
1982  
1983      separator = gtk_separator_menu_item_new();
1984      gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1985  
1986      s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1987      gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
1988  
1989      s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1990      gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
1991  
1992      separator = gtk_separator_menu_item_new();
1993      gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1994  
1995      s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1996      gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
1997                                   "<QEMU>/Machine/Quit");
1998      gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1999                              GDK_KEY_q, HOTKEY_MODIFIERS);
2000      gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
2001  
2002      return machine_menu;
2003  }
2004  
2005  #if defined(CONFIG_OPENGL)
2006  static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
2007  {
2008      gtk_gl_area_make_current(area);
2009      qemu_egl_display = eglGetCurrentDisplay();
2010      vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
2011      if (!vc->gfx.has_dmabuf) {
2012          error_report("GtkGLArea console lacks DMABUF support.");
2013      }
2014  }
2015  #endif
2016  
2017  static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
2018                                QemuConsole *con, int idx,
2019                                GSList *group, GtkWidget *view_menu)
2020  {
2021      bool zoom_to_fit = false;
2022  
2023      vc->label = qemu_console_get_label(con);
2024      vc->s = s;
2025      vc->gfx.scale_x = 1.0;
2026      vc->gfx.scale_y = 1.0;
2027  
2028  #if defined(CONFIG_OPENGL)
2029      if (display_opengl) {
2030          if (gtk_use_gl_area) {
2031              vc->gfx.drawing_area = gtk_gl_area_new();
2032              g_signal_connect(vc->gfx.drawing_area, "realize",
2033                               G_CALLBACK(gl_area_realize), vc);
2034              vc->gfx.dcl.ops = &dcl_gl_area_ops;
2035          } else {
2036  #ifdef CONFIG_X11
2037              vc->gfx.drawing_area = gtk_drawing_area_new();
2038              /*
2039               * gtk_widget_set_double_buffered() was deprecated in 3.14.
2040               * It is required for opengl rendering on X11 though.  A
2041               * proper replacement (native opengl support) is only
2042               * available in 3.16+.  Silence the warning if possible.
2043               */
2044  #pragma GCC diagnostic push
2045  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2046              gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
2047  #pragma GCC diagnostic pop
2048              vc->gfx.dcl.ops = &dcl_egl_ops;
2049              vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
2050  #else
2051              abort();
2052  #endif
2053          }
2054      } else
2055  #endif
2056      {
2057          vc->gfx.drawing_area = gtk_drawing_area_new();
2058          vc->gfx.dcl.ops = &dcl_ops;
2059      }
2060  
2061  
2062      gtk_widget_add_events(vc->gfx.drawing_area,
2063                            GDK_POINTER_MOTION_MASK |
2064                            GDK_BUTTON_PRESS_MASK |
2065                            GDK_BUTTON_RELEASE_MASK |
2066                            GDK_BUTTON_MOTION_MASK |
2067                            GDK_ENTER_NOTIFY_MASK |
2068                            GDK_LEAVE_NOTIFY_MASK |
2069                            GDK_SCROLL_MASK |
2070                            GDK_SMOOTH_SCROLL_MASK |
2071                            GDK_KEY_PRESS_MASK);
2072      gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
2073  
2074      vc->type = GD_VC_GFX;
2075      vc->tab_item = vc->gfx.drawing_area;
2076      vc->focus = vc->gfx.drawing_area;
2077      gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
2078                               vc->tab_item, gtk_label_new(vc->label));
2079  
2080      vc->gfx.kbd = qkbd_state_init(con);
2081      vc->gfx.dcl.con = con;
2082  
2083      register_displaychangelistener(&vc->gfx.dcl);
2084  
2085      gd_connect_vc_gfx_signals(vc);
2086      group = gd_vc_menu_init(s, vc, idx, group, view_menu);
2087  
2088      if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
2089          zoom_to_fit = true;
2090      }
2091      if (s->opts->u.gtk.has_zoom_to_fit) {
2092          zoom_to_fit = s->opts->u.gtk.zoom_to_fit;
2093      }
2094      if (zoom_to_fit) {
2095          gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
2096          s->free_scale = true;
2097      }
2098  
2099      return group;
2100  }
2101  
2102  static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
2103  {
2104      GSList *group = NULL;
2105      GtkWidget *view_menu;
2106      GtkWidget *separator;
2107      QemuConsole *con;
2108      int vc;
2109  
2110      view_menu = gtk_menu_new();
2111      gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
2112  
2113      s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
2114  
2115  #if defined(CONFIG_VTE)
2116      s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
2117      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item);
2118  #endif
2119  
2120      gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
2121              g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
2122      gtk_accel_label_set_accel(
2123              GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
2124              GDK_KEY_f, HOTKEY_MODIFIERS);
2125      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
2126  
2127      separator = gtk_separator_menu_item_new();
2128      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2129  
2130      s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
2131      gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
2132                                   "<QEMU>/View/Zoom In");
2133      gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
2134                              HOTKEY_MODIFIERS);
2135      gtk_accel_group_connect(s->accel_group, GDK_KEY_equal, HOTKEY_MODIFIERS, 0,
2136              g_cclosure_new_swap(G_CALLBACK(gd_accel_zoom_in), s, NULL));
2137      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
2138  
2139      s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
2140      gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
2141                                   "<QEMU>/View/Zoom Out");
2142      gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
2143                              HOTKEY_MODIFIERS);
2144      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
2145  
2146      s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
2147      gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
2148                                   "<QEMU>/View/Zoom Fixed");
2149      gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
2150                              HOTKEY_MODIFIERS);
2151      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
2152  
2153      s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
2154      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
2155  
2156      separator = gtk_separator_menu_item_new();
2157      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2158  
2159      s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
2160      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
2161  
2162      s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
2163      gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
2164                                   "<QEMU>/View/Grab Input");
2165      gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
2166                              HOTKEY_MODIFIERS);
2167      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
2168  
2169      separator = gtk_separator_menu_item_new();
2170      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2171  
2172      /* gfx */
2173      for (vc = 0;; vc++) {
2174          con = qemu_console_lookup_by_index(vc);
2175          if (!con) {
2176              break;
2177          }
2178          group = gd_vc_gfx_init(s, &s->vc[vc], con,
2179                                 vc, group, view_menu);
2180          s->nb_vcs++;
2181      }
2182  
2183  #if defined(CONFIG_VTE)
2184      /* vte */
2185      gd_vcs_init(s, group, view_menu);
2186  #endif
2187  
2188      separator = gtk_separator_menu_item_new();
2189      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2190  
2191      s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
2192      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
2193  
2194      s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
2195      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
2196  
2197      s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
2198              _("Show Menubar"));
2199      gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
2200                                     TRUE);
2201      gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
2202              g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
2203      gtk_accel_label_set_accel(
2204              GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))),
2205              GDK_KEY_m, HOTKEY_MODIFIERS);
2206      gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item);
2207  
2208      return view_menu;
2209  }
2210  
2211  static void gd_create_menus(GtkDisplayState *s)
2212  {
2213      GtkSettings *settings;
2214  
2215      s->accel_group = gtk_accel_group_new();
2216      s->machine_menu = gd_create_menu_machine(s);
2217      s->view_menu = gd_create_menu_view(s);
2218  
2219      s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
2220      gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
2221                                s->machine_menu);
2222      gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
2223  
2224      s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
2225      gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
2226      gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
2227  
2228      g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
2229      gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
2230  
2231      /* Disable the default "F10" menu shortcut. */
2232      settings = gtk_widget_get_settings(s->window);
2233      g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL);
2234  }
2235  
2236  
2237  static gboolean gtkinit;
2238  
2239  static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
2240  {
2241      VirtualConsole *vc;
2242  
2243      GtkDisplayState *s = g_malloc0(sizeof(*s));
2244      GdkDisplay *window_display;
2245      GtkIconTheme *theme;
2246      char *dir;
2247  
2248      if (!gtkinit) {
2249          fprintf(stderr, "gtk initialization failed\n");
2250          exit(1);
2251      }
2252      assert(opts->type == DISPLAY_TYPE_GTK);
2253      s->opts = opts;
2254  
2255      theme = gtk_icon_theme_get_default();
2256      dir = get_relocated_path(CONFIG_QEMU_ICONDIR);
2257      gtk_icon_theme_prepend_search_path(theme, dir);
2258      g_free(dir);
2259      g_set_prgname("qemu");
2260  
2261      s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2262      s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2263      s->notebook = gtk_notebook_new();
2264      s->menu_bar = gtk_menu_bar_new();
2265  
2266      s->free_scale = FALSE;
2267  
2268      /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For
2269       * LC_CTYPE, we need to make sure that non-ASCII characters are considered
2270       * printable, but without changing any of the character classes to make
2271       * sure that we don't accidentally break implicit assumptions.  */
2272      setlocale(LC_MESSAGES, "");
2273      setlocale(LC_CTYPE, "C.UTF-8");
2274      dir = get_relocated_path(CONFIG_QEMU_LOCALEDIR);
2275      bindtextdomain("qemu", dir);
2276      g_free(dir);
2277      bind_textdomain_codeset("qemu", "UTF-8");
2278      textdomain("qemu");
2279  
2280      window_display = gtk_widget_get_display(s->window);
2281      if (s->opts->has_show_cursor && s->opts->show_cursor) {
2282          s->null_cursor = NULL; /* default pointer */
2283      } else {
2284          s->null_cursor = gdk_cursor_new_for_display(window_display,
2285                                                      GDK_BLANK_CURSOR);
2286      }
2287  
2288      s->mouse_mode_notifier.notify = gd_mouse_mode_change;
2289      qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
2290      qemu_add_vm_change_state_handler(gd_change_runstate, s);
2291  
2292      gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu");
2293  
2294      gd_create_menus(s);
2295  
2296      gd_connect_signals(s);
2297  
2298      gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
2299      gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
2300  
2301      gd_update_caption(s);
2302  
2303      gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
2304      gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
2305  
2306      gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
2307  
2308      gtk_widget_show_all(s->window);
2309  
2310      vc = gd_vc_find_current(s);
2311      gtk_widget_set_sensitive(s->view_menu, vc != NULL);
2312  #ifdef CONFIG_VTE
2313      gtk_widget_set_sensitive(s->copy_item,
2314                               vc && vc->type == GD_VC_VTE);
2315  #endif
2316  
2317      if (opts->has_full_screen &&
2318          opts->full_screen) {
2319          gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
2320      }
2321      if (opts->u.gtk.has_grab_on_hover &&
2322          opts->u.gtk.grab_on_hover) {
2323          gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
2324      }
2325  }
2326  
2327  static void early_gtk_display_init(DisplayOptions *opts)
2328  {
2329      /* The QEMU code relies on the assumption that it's always run in
2330       * the C locale. Therefore it is not prepared to deal with
2331       * operations that produce different results depending on the
2332       * locale, such as printf's formatting of decimal numbers, and
2333       * possibly others.
2334       *
2335       * Since GTK+ calls setlocale() by default -importing the locale
2336       * settings from the environment- we must prevent it from doing so
2337       * using gtk_disable_setlocale().
2338       *
2339       * QEMU's GTK+ UI, however, _does_ have translations for some of
2340       * the menu items. As a trade-off between a functionally correct
2341       * QEMU and a fully internationalized UI we support importing
2342       * LC_MESSAGES from the environment (see the setlocale() call
2343       * earlier in this file). This allows us to display translated
2344       * messages leaving everything else untouched.
2345       */
2346      gtk_disable_setlocale();
2347      gtkinit = gtk_init_check(NULL, NULL);
2348      if (!gtkinit) {
2349          /* don't exit yet, that'll break -help */
2350          return;
2351      }
2352  
2353      assert(opts->type == DISPLAY_TYPE_GTK);
2354      if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
2355  #if defined(CONFIG_OPENGL)
2356  #if defined(GDK_WINDOWING_WAYLAND)
2357          if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
2358              gtk_use_gl_area = true;
2359              gtk_gl_area_init();
2360          } else
2361  #endif
2362          {
2363  #ifdef CONFIG_X11
2364              DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
2365              gtk_egl_init(mode);
2366  #endif
2367          }
2368  #endif
2369      }
2370  
2371      keycode_map = gd_get_keymap(&keycode_maplen);
2372  
2373  #if defined(CONFIG_VTE)
2374      type_register(&char_gd_vc_type_info);
2375  #endif
2376  }
2377  
2378  static QemuDisplay qemu_display_gtk = {
2379      .type       = DISPLAY_TYPE_GTK,
2380      .early_init = early_gtk_display_init,
2381      .init       = gtk_display_init,
2382  };
2383  
2384  static void register_gtk(void)
2385  {
2386      qemu_display_register(&qemu_display_gtk);
2387  }
2388  
2389  type_init(register_gtk);
2390