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