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