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