gtk.c 65.8 KB
Newer Older
A
Anthony Liguori 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * GTK UI
 *
 * Copyright IBM, Corp. 2012
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 *
 * Portions from gtk-vnc:
 *
 * GTK VNC Widget
 *
 * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
 * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 */

34 35 36
#define GETTEXT_PACKAGE "qemu"
#define LOCALEDIR "po"

K
Kevin Wolf 已提交
37 38
#include "qemu-common.h"

G
Gerd Hoffmann 已提交
39 40
#include "ui/console.h"
#include "ui/gtk.h"
K
Kevin Wolf 已提交
41

42
#include <glib/gi18n.h>
43
#include <locale.h>
S
Stefan Weil 已提交
44
#if defined(CONFIG_VTE)
A
Anthony Liguori 已提交
45
#include <vte/vte.h>
S
Stefan Weil 已提交
46
#endif
A
Anthony Liguori 已提交
47 48
#include <math.h>

49
#include "trace.h"
50
#include "ui/input.h"
A
Anthony Liguori 已提交
51 52 53 54
#include "sysemu/sysemu.h"
#include "qmp-commands.h"
#include "x_keymap.h"
#include "keymaps.h"
55
#include "sysemu/char.h"
G
Gerd Hoffmann 已提交
56
#include "qom/object.h"
A
Anthony Liguori 已提交
57

58
#define MAX_VCS 10
G
Gerd Hoffmann 已提交
59 60 61 62 63 64
#define VC_WINDOW_X_MIN  320
#define VC_WINDOW_Y_MIN  240
#define VC_TERM_X_MIN     80
#define VC_TERM_Y_MIN     25
#define VC_SCALE_MIN    0.25
#define VC_SCALE_STEP   0.25
65

S
Stefan Weil 已提交
66 67 68
#if !defined(CONFIG_VTE)
# define VTE_CHECK_VERSION(a, b, c) 0
#endif
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83
#if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
/*
 * The gtk2 vte terminal widget seriously messes up the window resize
 * for some reason.  You basically can't make the qemu window smaller
 * any more because the toplevel window geoemtry hints are overridden.
 *
 * Workaround that by hiding all vte widgets, except the one in the
 * current tab.
 *
 * Luckily everything works smooth in gtk3.
 */
# define VTE_RESIZE_HACK 1
#endif

84 85 86 87
#if !GTK_CHECK_VERSION(2, 20, 0)
#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
#endif

88 89 90 91 92 93 94
#ifndef GDK_IS_X11_DISPLAY
#define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
#endif
#ifndef GDK_IS_WIN32_DISPLAY
#define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
#endif

95 96 97 98 99 100
#ifndef GDK_KEY_0
#define GDK_KEY_0 GDK_0
#define GDK_KEY_1 GDK_1
#define GDK_KEY_2 GDK_2
#define GDK_KEY_f GDK_f
#define GDK_KEY_g GDK_g
101
#define GDK_KEY_q GDK_q
102 103
#define GDK_KEY_plus GDK_plus
#define GDK_KEY_minus GDK_minus
G
Gerd Hoffmann 已提交
104
#define GDK_KEY_Pause GDK_Pause
105
#endif
106

107 108 109 110 111 112 113 114 115
/* Some older mingw versions lack this constant or have
 * it conditionally defined */
#ifdef _WIN32
# ifndef MAPVK_VK_TO_VSC
#  define MAPVK_VK_TO_VSC 0
# endif
#endif


J
Jan Kiszka 已提交
116 117
#define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)

118 119 120 121 122
static const int modifier_keycode[] = {
    /* shift, control, alt keys, meta keys, both left & right */
    0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
};

123
struct GtkDisplayState {
A
Anthony Liguori 已提交
124 125 126 127
    GtkWidget *window;

    GtkWidget *menu_bar;

128 129
    GtkAccelGroup *accel_group;

130 131 132 133 134
    GtkWidget *machine_menu_item;
    GtkWidget *machine_menu;
    GtkWidget *pause_item;
    GtkWidget *reset_item;
    GtkWidget *powerdown_item;
A
Anthony Liguori 已提交
135 136 137 138
    GtkWidget *quit_item;

    GtkWidget *view_menu_item;
    GtkWidget *view_menu;
139 140 141 142 143
    GtkWidget *full_screen_item;
    GtkWidget *zoom_in_item;
    GtkWidget *zoom_out_item;
    GtkWidget *zoom_fixed_item;
    GtkWidget *zoom_fit_item;
144 145
    GtkWidget *grab_item;
    GtkWidget *grab_on_hover_item;
A
Anthony Liguori 已提交
146

147 148 149
    int nb_vcs;
    VirtualConsole vc[MAX_VCS];

A
Anthony Liguori 已提交
150
    GtkWidget *show_tabs_item;
151
    GtkWidget *untabify_item;
A
Anthony Liguori 已提交
152 153 154 155

    GtkWidget *vbox;
    GtkWidget *notebook;
    int button_mask;
156
    gboolean last_set;
A
Anthony Liguori 已提交
157 158
    int last_x;
    int last_y;
159 160
    int grab_x_root;
    int grab_y_root;
G
Gerd Hoffmann 已提交
161 162
    VirtualConsole *kbd_owner;
    VirtualConsole *ptr_owner;
A
Anthony Liguori 已提交
163

164
    gboolean full_screen;
A
Anthony Liguori 已提交
165 166 167

    GdkCursor *null_cursor;
    Notifier mouse_mode_notifier;
168
    gboolean free_scale;
169 170

    bool external_pause_update;
171 172

    bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
173
    bool has_evdev;
174
    bool ignore_keys;
175
};
A
Anthony Liguori 已提交
176

G
Gerd Hoffmann 已提交
177
static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
G
Gerd Hoffmann 已提交
178
static void gd_ungrab_pointer(GtkDisplayState *s);
G
Gerd Hoffmann 已提交
179
static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
180
static void gd_ungrab_keyboard(GtkDisplayState *s);
G
Gerd Hoffmann 已提交
181

A
Anthony Liguori 已提交
182 183
/** Utility Functions **/

G
Gerd Hoffmann 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
{
    VirtualConsole *vc;
    gint i;

    for (i = 0; i < s->nb_vcs; i++) {
        vc = &s->vc[i];
        if (gtk_check_menu_item_get_active
            (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
            return vc;
        }
    }
    return NULL;
}

static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
{
    VirtualConsole *vc;
    gint i, p;

    for (i = 0; i < s->nb_vcs; i++) {
        vc = &s->vc[i];
        p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
        if (p == page) {
            return vc;
        }
    }
    return NULL;
}

214 215 216 217 218 219 220 221
static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
{
    gint page;

    page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
    return gd_vc_find_by_page(s, page);
}

222 223 224 225 226 227 228 229 230 231
static bool gd_is_grab_active(GtkDisplayState *s)
{
    return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
}

static bool gd_grab_on_hover(GtkDisplayState *s)
{
    return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
}

232
static void gd_update_cursor(VirtualConsole *vc)
A
Anthony Liguori 已提交
233
{
234
    GtkDisplayState *s = vc->s;
A
Anthony Liguori 已提交
235 236
    GdkWindow *window;

237 238
    if (vc->type != GD_VC_GFX ||
        !qemu_console_is_graphic(vc->gfx.dcl.con)) {
239 240
        return;
    }
A
Anthony Liguori 已提交
241

242 243 244 245
    if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
        return;
    }

246
    window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
G
Gerd Hoffmann 已提交
247
    if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
A
Anthony Liguori 已提交
248 249 250 251 252 253 254 255 256
        gdk_window_set_cursor(window, s->null_cursor);
    } else {
        gdk_window_set_cursor(window, NULL);
    }
}

static void gd_update_caption(GtkDisplayState *s)
{
    const char *status = "";
G
Gerd Hoffmann 已提交
257
    gchar *prefix;
A
Anthony Liguori 已提交
258
    gchar *title;
259
    const char *grab = "";
260
    bool is_paused = !runstate_is_running();
G
Gerd Hoffmann 已提交
261 262 263 264 265 266 267
    int i;

    if (qemu_name) {
        prefix = g_strdup_printf("QEMU (%s)", qemu_name);
    } else {
        prefix = g_strdup_printf("QEMU");
    }
268

G
Gerd Hoffmann 已提交
269 270
    if (s->ptr_owner != NULL &&
        s->ptr_owner->window == NULL) {
271
        grab = _(" - Press Ctrl+Alt+G to release grab");
272
    }
A
Anthony Liguori 已提交
273

274
    if (is_paused) {
275
        status = _(" [Paused]");
A
Anthony Liguori 已提交
276
    }
277 278 279 280
    s->external_pause_update = true;
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
                                   is_paused);
    s->external_pause_update = false;
A
Anthony Liguori 已提交
281

G
Gerd Hoffmann 已提交
282
    title = g_strdup_printf("%s%s%s", prefix, status, grab);
A
Anthony Liguori 已提交
283 284
    gtk_window_set_title(GTK_WINDOW(s->window), title);
    g_free(title);
G
Gerd Hoffmann 已提交
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299

    for (i = 0; i < s->nb_vcs; i++) {
        VirtualConsole *vc = &s->vc[i];

        if (!vc->window) {
            continue;
        }
        title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
                                vc == s->kbd_owner ? " +kbd" : "",
                                vc == s->ptr_owner ? " +ptr" : "");
        gtk_window_set_title(GTK_WINDOW(vc->window), title);
        g_free(title);
    }

    g_free(prefix);
A
Anthony Liguori 已提交
300 301
}

G
Gerd Hoffmann 已提交
302
static void gd_update_geometry_hints(VirtualConsole *vc)
G
Gerd Hoffmann 已提交
303
{
304
    GtkDisplayState *s = vc->s;
G
Gerd Hoffmann 已提交
305 306 307 308
    GdkWindowHints mask = 0;
    GdkGeometry geo = {};
    GtkWidget *geo_widget = NULL;
    GtkWindow *geo_window;
309

G
Gerd Hoffmann 已提交
310
    if (vc->type == GD_VC_GFX) {
311 312 313
        if (!vc->gfx.ds) {
            return;
        }
G
Gerd Hoffmann 已提交
314 315 316 317 318 319 320 321 322 323 324
        if (s->free_scale) {
            geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
            geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
            mask |= GDK_HINT_MIN_SIZE;
        } else {
            geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
            geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
            mask |= GDK_HINT_MIN_SIZE;
        }
        geo_widget = vc->gfx.drawing_area;
        gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
G
Gerd Hoffmann 已提交
325

G
Gerd Hoffmann 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
#if defined(CONFIG_VTE)
    } else if (vc->type == GD_VC_VTE) {
        VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
        GtkBorder *ib;

        geo.width_inc  = vte_terminal_get_char_width(term);
        geo.height_inc = vte_terminal_get_char_height(term);
        mask |= GDK_HINT_RESIZE_INC;
        geo.base_width  = geo.width_inc;
        geo.base_height = geo.height_inc;
        mask |= GDK_HINT_BASE_SIZE;
        geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
        geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
        mask |= GDK_HINT_MIN_SIZE;
        gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
        geo.base_width  += ib->left + ib->right;
        geo.base_height += ib->top + ib->bottom;
        geo.min_width   += ib->left + ib->right;
        geo.min_height  += ib->top + ib->bottom;
        geo_widget = vc->vte.terminal;
#endif
G
Gerd Hoffmann 已提交
347
    }
G
Gerd Hoffmann 已提交
348 349 350 351 352

    geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
    gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
}

353
void gd_update_windowsize(VirtualConsole *vc)
G
Gerd Hoffmann 已提交
354 355 356 357 358 359 360 361
{
    GtkDisplayState *s = vc->s;

    gd_update_geometry_hints(vc);

    if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
        gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
                          VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
G
Gerd Hoffmann 已提交
362
    }
G
Gerd Hoffmann 已提交
363 364
}

365
static void gd_update_full_redraw(VirtualConsole *vc)
G
Gerd Hoffmann 已提交
366
{
367
    GtkWidget *area = vc->gfx.drawing_area;
G
Gerd Hoffmann 已提交
368
    int ww, wh;
369
    gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
370 371 372 373 374 375
#if defined(CONFIG_GTK_GL)
    if (vc->gfx.gls) {
        gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
        return;
    }
#endif
376
    gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
G
Gerd Hoffmann 已提交
377 378
}

379 380
static void gtk_release_modifiers(GtkDisplayState *s)
{
381
    VirtualConsole *vc = gd_vc_find_current(s);
382 383
    int i, keycode;

384 385
    if (vc->type != GD_VC_GFX ||
        !qemu_console_is_graphic(vc->gfx.dcl.con)) {
386 387 388 389 390 391 392
        return;
    }
    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
        keycode = modifier_keycode[i];
        if (!s->modifier_pressed[i]) {
            continue;
        }
393
        qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
394 395 396 397
        s->modifier_pressed[i] = false;
    }
}

398 399 400 401 402 403 404 405 406
static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
                               GtkWidget *widget)
{
    g_object_ref(G_OBJECT(widget));
    gtk_container_remove(GTK_CONTAINER(from), widget);
    gtk_container_add(GTK_CONTAINER(to), widget);
    g_object_unref(G_OBJECT(widget));
}

A
Anthony Liguori 已提交
407 408
/** DisplayState Callbacks **/

409
static void gd_update(DisplayChangeListener *dcl,
410
                      int x, int y, int w, int h)
A
Anthony Liguori 已提交
411
{
412
    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
413
    GdkWindow *win;
A
Anthony Liguori 已提交
414
    int x1, x2, y1, y2;
415 416 417
    int mx, my;
    int fbw, fbh;
    int ww, wh;
A
Anthony Liguori 已提交
418

G
Gerd Hoffmann 已提交
419
    trace_gd_update(vc->label, x, y, w, h);
A
Anthony Liguori 已提交
420

421 422 423 424
    if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
        return;
    }

425 426 427
    if (vc->gfx.convert) {
        pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
                               NULL, vc->gfx.convert,
428 429 430
                               x, y, 0, 0, x, y, w, h);
    }

431 432
    x1 = floor(x * vc->gfx.scale_x);
    y1 = floor(y * vc->gfx.scale_y);
A
Anthony Liguori 已提交
433

434 435
    x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
    y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
A
Anthony Liguori 已提交
436

437 438
    fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
    fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
439

440 441 442 443 444
    win = gtk_widget_get_window(vc->gfx.drawing_area);
    if (!win) {
        return;
    }
    gdk_drawable_get_size(win, &ww, &wh);
445 446 447 448 449 450 451 452 453

    mx = my = 0;
    if (ww > fbw) {
        mx = (ww - fbw) / 2;
    }
    if (wh > fbh) {
        my = (wh - fbh) / 2;
    }

454 455
    gtk_widget_queue_draw_area(vc->gfx.drawing_area,
                               mx + x1, my + y1, (x2 - x1), (y2 - y1));
A
Anthony Liguori 已提交
456 457
}

458
static void gd_refresh(DisplayChangeListener *dcl)
A
Anthony Liguori 已提交
459
{
460
    graphic_hw_update(dcl->con);
A
Anthony Liguori 已提交
461 462
}

463 464 465 466
#if GTK_CHECK_VERSION(3, 0, 0)
static void gd_mouse_set(DisplayChangeListener *dcl,
                         int x, int y, int visible)
{
467
    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
468 469 470 471
    GdkDisplay *dpy;
    GdkDeviceManager *mgr;
    gint x_root, y_root;

C
Cole Robinson 已提交
472 473 474 475
    if (qemu_input_is_absolute()) {
        return;
    }

476
    dpy = gtk_widget_get_display(vc->gfx.drawing_area);
477
    mgr = gdk_display_get_device_manager(dpy);
478
    gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
479 480
                               x, y, &x_root, &y_root);
    gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
481
                    gtk_widget_get_screen(vc->gfx.drawing_area),
C
Cole Robinson 已提交
482
                    x_root, y_root);
483 484
    vc->s->last_x = x;
    vc->s->last_y = y;
485 486
}
#else
G
Gerd Hoffmann 已提交
487 488 489
static void gd_mouse_set(DisplayChangeListener *dcl,
                         int x, int y, int visible)
{
490
    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
G
Gerd Hoffmann 已提交
491 492
    gint x_root, y_root;

C
Cole Robinson 已提交
493 494 495 496
    if (qemu_input_is_absolute()) {
        return;
    }

497
    gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
G
Gerd Hoffmann 已提交
498
                               x, y, &x_root, &y_root);
499 500
    gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
                             gtk_widget_get_screen(vc->gfx.drawing_area),
G
Gerd Hoffmann 已提交
501 502
                             x_root, y_root);
}
503
#endif
G
Gerd Hoffmann 已提交
504 505 506 507

static void gd_cursor_define(DisplayChangeListener *dcl,
                             QEMUCursor *c)
{
508
    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
G
Gerd Hoffmann 已提交
509 510 511
    GdkPixbuf *pixbuf;
    GdkCursor *cursor;

512 513 514 515
    if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
        return;
    }

G
Gerd Hoffmann 已提交
516 517 518 519
    pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
                                      GDK_COLORSPACE_RGB, true, 8,
                                      c->width, c->height, c->width * 4,
                                      NULL, NULL);
520 521 522 523
    cursor = gdk_cursor_new_from_pixbuf
        (gtk_widget_get_display(vc->gfx.drawing_area),
         pixbuf, c->hot_x, c->hot_y);
    gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
G
Gerd Hoffmann 已提交
524
    g_object_unref(pixbuf);
525
#if !GTK_CHECK_VERSION(3, 0, 0)
526
    gdk_cursor_unref(cursor);
527 528 529
#else
    g_object_unref(cursor);
#endif
G
Gerd Hoffmann 已提交
530 531
}

532 533
static void gd_switch(DisplayChangeListener *dcl,
                      DisplaySurface *surface)
A
Anthony Liguori 已提交
534
{
535
    VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
G
Gerd Hoffmann 已提交
536
    bool resized = true;
A
Anthony Liguori 已提交
537

538 539 540
    trace_gd_switch(vc->label,
                    surface ? surface_width(surface)  : 0,
                    surface ? surface_height(surface) : 0);
A
Anthony Liguori 已提交
541

542 543
    if (vc->gfx.surface) {
        cairo_surface_destroy(vc->gfx.surface);
544 545 546 547 548
        vc->gfx.surface = NULL;
    }
    if (vc->gfx.convert) {
        pixman_image_unref(vc->gfx.convert);
        vc->gfx.convert = NULL;
A
Anthony Liguori 已提交
549 550
    }

551
    if (vc->gfx.ds && surface &&
552 553
        surface_width(vc->gfx.ds) == surface_width(surface) &&
        surface_height(vc->gfx.ds) == surface_height(surface)) {
G
Gerd Hoffmann 已提交
554 555
        resized = false;
    }
556
    vc->gfx.ds = surface;
A
Anthony Liguori 已提交
557

558 559
    if (!surface) {
        return;
560
    }
A
Anthony Liguori 已提交
561

562 563 564 565 566 567 568
    if (surface->format == PIXMAN_x8r8g8b8) {
        /*
         * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
         *
         * No need to convert, use surface directly.  Should be the
         * common case as this is qemu_default_pixelformat(32) too.
         */
569
        vc->gfx.surface = cairo_image_surface_create_for_data
570 571 572 573 574 575 576
            (surface_data(surface),
             CAIRO_FORMAT_RGB24,
             surface_width(surface),
             surface_height(surface),
             surface_stride(surface));
    } else {
        /* Must convert surface, use pixman to do it. */
577 578 579 580 581 582
        vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
                                                   surface_width(surface),
                                                   surface_height(surface),
                                                   NULL, 0);
        vc->gfx.surface = cairo_image_surface_create_for_data
            ((void *)pixman_image_get_data(vc->gfx.convert),
583
             CAIRO_FORMAT_RGB24,
584 585 586 587 588
             pixman_image_get_width(vc->gfx.convert),
             pixman_image_get_height(vc->gfx.convert),
             pixman_image_get_stride(vc->gfx.convert));
        pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
                               NULL, vc->gfx.convert,
589
                               0, 0, 0, 0, 0, 0,
590 591
                               pixman_image_get_width(vc->gfx.convert),
                               pixman_image_get_height(vc->gfx.convert));
592
    }
593

G
Gerd Hoffmann 已提交
594
    if (resized) {
595
        gd_update_windowsize(vc);
G
Gerd Hoffmann 已提交
596
    } else {
597
        gd_update_full_redraw(vc);
598
    }
A
Anthony Liguori 已提交
599 600
}

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
static const DisplayChangeListenerOps dcl_ops = {
    .dpy_name             = "gtk",
    .dpy_gfx_update       = gd_update,
    .dpy_gfx_switch       = gd_switch,
    .dpy_gfx_check_format = qemu_pixman_check_format,
    .dpy_refresh          = gd_refresh,
    .dpy_mouse_set        = gd_mouse_set,
    .dpy_cursor_define    = gd_cursor_define,
};


#if defined(CONFIG_OPENGL)

/** DisplayState Callbacks (opengl version) **/

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
#if defined(CONFIG_GTK_GL)

static const DisplayChangeListenerOps dcl_gl_area_ops = {
    .dpy_name             = "gtk-egl",
    .dpy_gfx_update       = gd_gl_area_update,
    .dpy_gfx_switch       = gd_gl_area_switch,
    .dpy_gfx_check_format = console_gl_check_format,
    .dpy_refresh          = gd_gl_area_refresh,
    .dpy_mouse_set        = gd_mouse_set,
    .dpy_cursor_define    = gd_cursor_define,

    .dpy_gl_ctx_create       = gd_gl_area_create_context,
    .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
    .dpy_gl_ctx_make_current = gd_gl_area_make_current,
    .dpy_gl_ctx_get_current  = gd_gl_area_get_current_context,
    .dpy_gl_scanout          = gd_gl_area_scanout,
    .dpy_gl_update           = gd_gl_area_scanout_flush,
};

#else

637 638 639 640 641 642 643 644
static const DisplayChangeListenerOps dcl_egl_ops = {
    .dpy_name             = "gtk-egl",
    .dpy_gfx_update       = gd_egl_update,
    .dpy_gfx_switch       = gd_egl_switch,
    .dpy_gfx_check_format = console_gl_check_format,
    .dpy_refresh          = gd_egl_refresh,
    .dpy_mouse_set        = gd_mouse_set,
    .dpy_cursor_define    = gd_cursor_define,
645 646 647 648 649 650 651

    .dpy_gl_ctx_create       = gd_egl_create_context,
    .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
    .dpy_gl_ctx_make_current = gd_egl_make_current,
    .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
    .dpy_gl_scanout          = gd_egl_scanout,
    .dpy_gl_update           = gd_egl_scanout_flush,
652 653
};

654 655
#endif /* CONFIG_GTK_GL */
#endif /* CONFIG_OPENGL */
656

A
Anthony Liguori 已提交
657 658 659 660 661 662 663 664 665 666 667
/** QEMU Events **/

static void gd_change_runstate(void *opaque, int running, RunState state)
{
    GtkDisplayState *s = opaque;

    gd_update_caption(s);
}

static void gd_mouse_mode_change(Notifier *notify, void *data)
{
668
    GtkDisplayState *s;
669
    int i;
670 671 672 673 674 675 676

    s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
    /* release the grab at switching to absolute mode */
    if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
                                       FALSE);
    }
677 678 679 680
    for (i = 0; i < s->nb_vcs; i++) {
        VirtualConsole *vc = &s->vc[i];
        gd_update_cursor(vc);
    }
A
Anthony Liguori 已提交
681 682 683 684 685 686 687 688
}

/** GTK Events **/

static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
                                void *opaque)
{
    GtkDisplayState *s = opaque;
689
    int i;
A
Anthony Liguori 已提交
690 691

    if (!no_quit) {
692 693 694 695 696 697
        for (i = 0; i < s->nb_vcs; i++) {
            if (s->vc[i].type != GD_VC_GFX) {
                continue;
            }
            unregister_displaychangelistener(&s->vc[i].gfx.dcl);
        }
A
Anthony Liguori 已提交
698 699 700 701 702 703 704
        qmp_quit(NULL);
        return FALSE;
    }

    return TRUE;
}

705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
{
    QemuUIInfo info;

    memset(&info, 0, sizeof(info));
    info.width = width;
    info.height = height;
    dpy_set_ui_info(vc->gfx.dcl.con, &info);
}

#if defined(CONFIG_GTK_GL)

static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
                                void *opaque)
{
    VirtualConsole *vc = opaque;

    if (vc->gfx.gls) {
        gd_gl_area_draw(vc);
    }
    return TRUE;
}

static void gd_resize_event(GtkGLArea *area,
                            gint width, gint height, gpointer *opaque)
{
    VirtualConsole *vc = (void *)opaque;

    gd_set_ui_info(vc, width, height);
}

#endif

A
Anthony Liguori 已提交
738 739
static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
{
740 741
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;
742
    int mx, my;
A
Anthony Liguori 已提交
743 744 745
    int ww, wh;
    int fbw, fbh;

746 747
#if defined(CONFIG_OPENGL)
    if (vc->gfx.gls) {
748 749 750 751
#if defined(CONFIG_GTK_GL)
        /* invoke render callback please */
        return FALSE;
#else
752 753
        gd_egl_draw(vc);
        return TRUE;
754
#endif
755 756 757
    }
#endif

758 759 760
    if (!gtk_widget_get_realized(widget)) {
        return FALSE;
    }
761 762 763
    if (!vc->gfx.ds) {
        return FALSE;
    }
764

765 766
    fbw = surface_width(vc->gfx.ds);
    fbh = surface_height(vc->gfx.ds);
A
Anthony Liguori 已提交
767 768 769

    gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);

770
    if (s->full_screen) {
771 772
        vc->gfx.scale_x = (double)ww / fbw;
        vc->gfx.scale_y = (double)wh / fbh;
773 774 775 776 777 778
    } else if (s->free_scale) {
        double sx, sy;

        sx = (double)ww / fbw;
        sy = (double)wh / fbh;

779
        vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
A
Anthony Liguori 已提交
780 781
    }

782 783
    fbw *= vc->gfx.scale_x;
    fbh *= vc->gfx.scale_y;
784

785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
    mx = my = 0;
    if (ww > fbw) {
        mx = (ww - fbw) / 2;
    }
    if (wh > fbh) {
        my = (wh - fbh) / 2;
    }

    cairo_rectangle(cr, 0, 0, ww, wh);

    /* Optionally cut out the inner area where the pixmap
       will be drawn. This avoids 'flashing' since we're
       not double-buffering. Note we're using the undocumented
       behaviour of drawing the rectangle from right to left
       to cut out the whole */
    cairo_rectangle(cr, mx + fbw, my,
                    -1 * fbw, fbh);
    cairo_fill(cr);

804 805 806
    cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
    cairo_set_source_surface(cr, vc->gfx.surface,
                             mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
A
Anthony Liguori 已提交
807 808 809 810 811
    cairo_paint(cr);

    return TRUE;
}

812
#if !GTK_CHECK_VERSION(3, 0, 0)
A
Anthony Liguori 已提交
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
                                void *opaque)
{
    cairo_t *cr;
    gboolean ret;

    cr = gdk_cairo_create(gtk_widget_get_window(widget));
    cairo_rectangle(cr,
                    expose->area.x,
                    expose->area.y,
                    expose->area.width,
                    expose->area.height);
    cairo_clip(cr);

    ret = gd_draw_event(widget, cr, opaque);

    cairo_destroy(cr);

    return ret;
}
833
#endif
A
Anthony Liguori 已提交
834 835 836 837

static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
                                void *opaque)
{
838 839
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;
A
Anthony Liguori 已提交
840
    int x, y;
841 842 843 844
    int mx, my;
    int fbh, fbw;
    int ww, wh;

845 846 847 848
    if (!vc->gfx.ds) {
        return TRUE;
    }

849 850
    fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
    fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
A
Anthony Liguori 已提交
851

852 853
    gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
                          &ww, &wh);
854 855 856 857 858 859 860 861 862

    mx = my = 0;
    if (ww > fbw) {
        mx = (ww - fbw) / 2;
    }
    if (wh > fbh) {
        my = (wh - fbh) / 2;
    }

863 864
    x = (motion->x - mx) / vc->gfx.scale_x;
    y = (motion->y - my) / vc->gfx.scale_y;
865

866
    if (qemu_input_is_absolute()) {
867
        if (x < 0 || y < 0 ||
868 869
            x >= surface_width(vc->gfx.ds) ||
            y >= surface_height(vc->gfx.ds)) {
870 871
            return TRUE;
        }
872 873 874 875
        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
                             surface_width(vc->gfx.ds));
        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
                             surface_height(vc->gfx.ds));
876
        qemu_input_event_sync();
G
Gerd Hoffmann 已提交
877
    } else if (s->last_set && s->ptr_owner == vc) {
878 879
        qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
        qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
880
        qemu_input_event_sync();
A
Anthony Liguori 已提交
881 882 883
    }
    s->last_x = x;
    s->last_y = y;
884
    s->last_set = TRUE;
A
Anthony Liguori 已提交
885

G
Gerd Hoffmann 已提交
886
    if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
887
        GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
        int x = (int)motion->x_root;
        int y = (int)motion->y_root;

        /* In relative mode check to see if client pointer hit
         * one of the screen edges, and if so move it back by
         * 200 pixels. This is important because the pointer
         * in the server doesn't correspond 1-for-1, and so
         * may still be only half way across the screen. Without
         * this warp, the server pointer would thus appear to hit
         * an invisible wall */
        if (x == 0) {
            x += 200;
        }
        if (y == 0) {
            y += 200;
        }
        if (x == (gdk_screen_get_width(screen) - 1)) {
            x -= 200;
        }
        if (y == (gdk_screen_get_height(screen) - 1)) {
            y -= 200;
        }

        if (x != (int)motion->x_root || y != (int)motion->y_root) {
912 913 914 915 916
#if GTK_CHECK_VERSION(3, 0, 0)
            GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
            gdk_device_warp(dev, screen, x, y);
#else
            GdkDisplay *display = gtk_widget_get_display(widget);
917
            gdk_display_warp_pointer(display, screen, x, y);
918
#endif
919
            s->last_set = FALSE;
920 921 922
            return FALSE;
        }
    }
A
Anthony Liguori 已提交
923 924 925 926 927 928
    return TRUE;
}

static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
                                void *opaque)
{
929 930
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;
931
    InputButton btn;
A
Anthony Liguori 已提交
932

933 934
    /* implicitly grab the input at the first click in the relative mode */
    if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
G
Gerd Hoffmann 已提交
935 936 937 938 939
        !qemu_input_is_absolute() && s->ptr_owner != vc) {
        if (!vc->window) {
            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
                                           TRUE);
        } else {
G
Gerd Hoffmann 已提交
940
            gd_grab_pointer(vc, "relative-mode-click");
G
Gerd Hoffmann 已提交
941
        }
942 943 944
        return TRUE;
    }

A
Anthony Liguori 已提交
945
    if (button->button == 1) {
946
        btn = INPUT_BUTTON_LEFT;
A
Anthony Liguori 已提交
947
    } else if (button->button == 2) {
948
        btn = INPUT_BUTTON_MIDDLE;
A
Anthony Liguori 已提交
949
    } else if (button->button == 3) {
950
        btn = INPUT_BUTTON_RIGHT;
A
Anthony Liguori 已提交
951
    } else {
952
        return TRUE;
A
Anthony Liguori 已提交
953 954
    }

955 956
    qemu_input_queue_btn(vc->gfx.dcl.con, btn,
                         button->type == GDK_BUTTON_PRESS);
957
    qemu_input_event_sync();
A
Anthony Liguori 已提交
958 959 960
    return TRUE;
}

J
Jan Kiszka 已提交
961 962 963
static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
                                void *opaque)
{
964
    VirtualConsole *vc = opaque;
J
Jan Kiszka 已提交
965 966 967
    InputButton btn;

    if (scroll->direction == GDK_SCROLL_UP) {
968
        btn = INPUT_BUTTON_WHEELUP;
J
Jan Kiszka 已提交
969
    } else if (scroll->direction == GDK_SCROLL_DOWN) {
970
        btn = INPUT_BUTTON_WHEELDOWN;
J
Jan Kiszka 已提交
971 972 973 974
    } else {
        return TRUE;
    }

975
    qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
J
Jan Kiszka 已提交
976
    qemu_input_event_sync();
977
    qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
J
Jan Kiszka 已提交
978 979 980 981
    qemu_input_event_sync();
    return TRUE;
}

982
static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
A
Anthony Liguori 已提交
983
{
G
Gerd Hoffmann 已提交
984
    int qemu_keycode;
A
Anthony Liguori 已提交
985

986 987 988 989 990 991 992 993 994
#ifdef GDK_WINDOWING_WIN32
    if (GDK_IS_WIN32_DISPLAY(dpy)) {
        qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
        switch (qemu_keycode) {
        case 103:   /* alt gr */
            qemu_keycode = 56 | SCANCODE_GREY;
            break;
        }
        return qemu_keycode;
995
    }
996
#endif
A
Anthony Liguori 已提交
997 998 999 1000 1001

    if (gdk_keycode < 9) {
        qemu_keycode = 0;
    } else if (gdk_keycode < 97) {
        qemu_keycode = gdk_keycode - 8;
1002 1003
#ifdef GDK_WINDOWING_X11
    } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
1004 1005 1006 1007 1008
        if (s->has_evdev) {
            qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
        } else {
            qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
        }
1009
#endif
A
Anthony Liguori 已提交
1010 1011 1012 1013 1014 1015 1016 1017
    } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
        qemu_keycode = 0x70;
    } else if (gdk_keycode == 211) { /* backslash */
        qemu_keycode = 0x73;
    } else {
        qemu_keycode = 0;
    }

G
Gerd Hoffmann 已提交
1018 1019 1020
    return qemu_keycode;
}

1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
static gboolean gd_text_key_down(GtkWidget *widget,
                                 GdkEventKey *key, void *opaque)
{
    VirtualConsole *vc = opaque;
    QemuConsole *con = vc->gfx.dcl.con;

    if (key->length) {
        kbd_put_string_console(con, key->string, key->length);
    } else {
        int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget),
                                 key->hardware_keycode);
        int qcode = qemu_input_key_number_to_qcode(num);
        kbd_put_qcode_console(con, qcode);
    }
    return TRUE;
}

G
Gerd Hoffmann 已提交
1038 1039 1040 1041 1042 1043 1044 1045
static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
{
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;
    int gdk_keycode = key->hardware_keycode;
    int qemu_keycode;
    int i;

1046 1047 1048 1049 1050
    if (s->ignore_keys) {
        s->ignore_keys = (key->type == GDK_KEY_PRESS);
        return TRUE;
    }

M
Martin Decky 已提交
1051 1052 1053 1054 1055 1056
    if (key->keyval == GDK_KEY_Pause) {
        qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
                                        key->type == GDK_KEY_PRESS);
        return TRUE;
    }

1057 1058
    qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
                                  gdk_keycode);
G
Gerd Hoffmann 已提交
1059

G
Gerd Hoffmann 已提交
1060
    trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
1061
                       (key->type == GDK_KEY_PRESS) ? "down" : "up");
A
Anthony Liguori 已提交
1062

1063 1064 1065 1066 1067 1068
    for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
        if (qemu_keycode == modifier_keycode[i]) {
            s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
        }
    }

1069
    qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
1070
                                     key->type == GDK_KEY_PRESS);
A
Anthony Liguori 已提交
1071 1072 1073 1074

    return TRUE;
}

1075 1076 1077 1078 1079 1080 1081 1082
static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
{
    if (event->type == GDK_MOTION_NOTIFY) {
        return gd_motion_event(widget, &event->motion, opaque);
    }
    return FALSE;
}

A
Anthony Liguori 已提交
1083 1084
/** Window Menu Actions **/

1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
static void gd_menu_pause(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;

    if (s->external_pause_update) {
        return;
    }
    if (runstate_is_running()) {
        qmp_stop(NULL);
    } else {
        qmp_cont(NULL);
    }
}

static void gd_menu_reset(GtkMenuItem *item, void *opaque)
{
    qmp_system_reset(NULL);
}

static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
{
    qmp_system_powerdown(NULL);
}

A
Anthony Liguori 已提交
1109 1110 1111 1112 1113 1114 1115 1116
static void gd_menu_quit(GtkMenuItem *item, void *opaque)
{
    qmp_quit(NULL);
}

static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1117
    VirtualConsole *vc = gd_vc_find_by_menu(s);
1118
    GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1119
    gint page;
A
Anthony Liguori 已提交
1120

1121 1122
    gtk_release_modifiers(s);
    if (vc) {
1123 1124
        page = gtk_notebook_page_num(nb, vc->tab_item);
        gtk_notebook_set_current_page(nb, page);
J
Jan Kiszka 已提交
1125
        gtk_widget_grab_focus(vc->focus);
A
Anthony Liguori 已提交
1126
    }
1127
    s->ignore_keys = false;
A
Anthony Liguori 已提交
1128 1129
}

1130 1131 1132
static void gd_accel_switch_vc(void *opaque)
{
    VirtualConsole *vc = opaque;
1133

1134
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1135 1136 1137 1138
#if !GTK_CHECK_VERSION(3, 0, 0)
    /* GTK2 sends the accel key to the target console - ignore this until */
    vc->s->ignore_keys = true;
#endif
1139 1140
}

A
Anthony Liguori 已提交
1141 1142 1143
static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1144
    VirtualConsole *vc = gd_vc_find_current(s);
A
Anthony Liguori 已提交
1145 1146 1147 1148 1149 1150

    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
    } else {
        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
    }
1151
    gd_update_windowsize(vc);
A
Anthony Liguori 已提交
1152 1153
}

1154 1155 1156 1157 1158 1159 1160
static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
                                    void *opaque)
{
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;

    gtk_widget_set_sensitive(vc->menu_item, true);
1161
    gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1162 1163 1164 1165 1166 1167 1168
    gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
                                    vc->tab_item, vc->label);
    gtk_widget_destroy(vc->window);
    vc->window = NULL;
    return TRUE;
}

1169 1170 1171 1172 1173 1174 1175 1176
static gboolean gd_win_grab(void *opaque)
{
    VirtualConsole *vc = opaque;

    fprintf(stderr, "%s: %s\n", __func__, vc->label);
    if (vc->s->ptr_owner) {
        gd_ungrab_pointer(vc->s);
    } else {
G
Gerd Hoffmann 已提交
1177
        gd_grab_pointer(vc, "user-request-detached-tab");
1178 1179 1180 1181
    }
    return TRUE;
}

1182 1183 1184 1185 1186
static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
    VirtualConsole *vc = gd_vc_find_current(s);

1187 1188
    if (vc->type == GD_VC_GFX &&
        qemu_console_is_graphic(vc->gfx.dcl.con)) {
G
Gerd Hoffmann 已提交
1189 1190
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
                                       FALSE);
1191 1192 1193 1194
    }
    if (!vc->window) {
        gtk_widget_set_sensitive(vc->menu_item, false);
        vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1195
        gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1196 1197 1198 1199

        g_signal_connect(vc->window, "delete-event",
                         G_CALLBACK(gd_tab_window_close), vc);
        gtk_widget_show_all(vc->window);
G
Gerd Hoffmann 已提交
1200

1201 1202 1203
        if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
            GtkAccelGroup *ag = gtk_accel_group_new();
            gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
1204

1205 1206 1207 1208
            GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
                                               vc, NULL);
            gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
        }
1209

G
Gerd Hoffmann 已提交
1210
        gd_update_geometry_hints(vc);
G
Gerd Hoffmann 已提交
1211
        gd_update_caption(s);
1212 1213 1214
    }
}

1215 1216 1217
static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1218
    VirtualConsole *vc = gd_vc_find_current(s);
1219

1220
    if (!s->full_screen) {
1221
        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1222
        gtk_widget_hide(s->menu_bar);
1223 1224
        if (vc->type == GD_VC_GFX) {
            gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1225
        }
1226
        gtk_window_fullscreen(GTK_WINDOW(s->window));
1227 1228 1229 1230
        s->full_screen = TRUE;
    } else {
        gtk_window_unfullscreen(GTK_WINDOW(s->window));
        gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1231
        gtk_widget_show(s->menu_bar);
1232
        s->full_screen = FALSE;
1233 1234 1235
        if (vc->type == GD_VC_GFX) {
            vc->gfx.scale_x = 1.0;
            vc->gfx.scale_y = 1.0;
G
Gerd Hoffmann 已提交
1236
            gd_update_windowsize(vc);
1237
        }
1238 1239
    }

1240
    gd_update_cursor(vc);
1241 1242
}

1243 1244 1245 1246 1247 1248
static void gd_accel_full_screen(void *opaque)
{
    GtkDisplayState *s = opaque;
    gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
}

1249 1250 1251
static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1252
    VirtualConsole *vc = gd_vc_find_current(s);
1253 1254 1255 1256

    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
                                   FALSE);

G
Gerd Hoffmann 已提交
1257 1258
    vc->gfx.scale_x += VC_SCALE_STEP;
    vc->gfx.scale_y += VC_SCALE_STEP;
1259

1260
    gd_update_windowsize(vc);
1261 1262 1263 1264 1265
}

static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1266
    VirtualConsole *vc = gd_vc_find_current(s);
1267 1268 1269 1270

    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
                                   FALSE);

G
Gerd Hoffmann 已提交
1271 1272
    vc->gfx.scale_x -= VC_SCALE_STEP;
    vc->gfx.scale_y -= VC_SCALE_STEP;
1273

G
Gerd Hoffmann 已提交
1274 1275
    vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
    vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1276

1277
    gd_update_windowsize(vc);
1278 1279 1280 1281 1282
}

static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1283
    VirtualConsole *vc = gd_vc_find_current(s);
1284

1285 1286
    vc->gfx.scale_x = 1.0;
    vc->gfx.scale_y = 1.0;
1287

1288
    gd_update_windowsize(vc);
1289 1290 1291 1292 1293
}

static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1294
    VirtualConsole *vc = gd_vc_find_current(s);
1295 1296 1297 1298 1299

    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
        s->free_scale = TRUE;
    } else {
        s->free_scale = FALSE;
1300 1301
        vc->gfx.scale_x = 1.0;
        vc->gfx.scale_y = 1.0;
1302 1303
    }

G
Gerd Hoffmann 已提交
1304
    gd_update_windowsize(vc);
1305
    gd_update_full_redraw(vc);
1306 1307
}

1308
#if GTK_CHECK_VERSION(3, 0, 0)
1309 1310 1311 1312
static void gd_grab_devices(VirtualConsole *vc, bool grab,
                            GdkInputSource source, GdkEventMask mask,
                            GdkCursor *cursor)
{
1313
    GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1314
    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1315 1316 1317 1318
    GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
    GList *tmp = devs;

    for (tmp = devs; tmp; tmp = tmp->next) {
1319
        GdkDevice *dev = tmp->data;
1320 1321 1322 1323 1324 1325 1326 1327 1328
        if (gdk_device_get_source(dev) != source) {
            continue;
        }
        if (grab) {
            GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
            gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
                            mask, cursor, GDK_CURRENT_TIME);
        } else {
            gdk_device_ungrab(dev, GDK_CURRENT_TIME);
1329 1330
        }
    }
1331 1332 1333 1334
    g_list_free(devs);
}
#endif

G
Gerd Hoffmann 已提交
1335
static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
1336
{
1337 1338 1339 1340 1341 1342 1343 1344
    if (vc->s->kbd_owner) {
        if (vc->s->kbd_owner == vc) {
            return;
        } else {
            gd_ungrab_keyboard(vc->s);
        }
    }

1345 1346 1347 1348
#if GTK_CHECK_VERSION(3, 0, 0)
    gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
                   GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
                   NULL);
1349
#else
1350
    gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
1351 1352
                      FALSE,
                      GDK_CURRENT_TIME);
1353
#endif
G
Gerd Hoffmann 已提交
1354
    vc->s->kbd_owner = vc;
1355
    gd_update_caption(vc->s);
G
Gerd Hoffmann 已提交
1356
    trace_gd_grab(vc->label, "kbd", reason);
1357 1358
}

G
Gerd Hoffmann 已提交
1359
static void gd_ungrab_keyboard(GtkDisplayState *s)
1360
{
G
Gerd Hoffmann 已提交
1361 1362 1363 1364 1365 1366 1367
    VirtualConsole *vc = s->kbd_owner;

    if (vc == NULL) {
        return;
    }
    s->kbd_owner = NULL;

1368
#if GTK_CHECK_VERSION(3, 0, 0)
1369
    gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
1370
#else
1371
    gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1372
#endif
1373
    gd_update_caption(s);
G
Gerd Hoffmann 已提交
1374
    trace_gd_ungrab(vc->label, "kbd");
1375 1376
}

G
Gerd Hoffmann 已提交
1377
static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
1378
{
1379
    GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1380 1381 1382 1383 1384 1385 1386 1387 1388

    if (vc->s->ptr_owner) {
        if (vc->s->ptr_owner == vc) {
            return;
        } else {
            gd_ungrab_pointer(vc->s);
        }
    }

1389
#if GTK_CHECK_VERSION(3, 0, 0)
1390
    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1391 1392 1393 1394 1395 1396 1397
    gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
                    GDK_POINTER_MOTION_MASK |
                    GDK_BUTTON_PRESS_MASK |
                    GDK_BUTTON_RELEASE_MASK |
                    GDK_BUTTON_MOTION_MASK |
                    GDK_SCROLL_MASK,
                    vc->s->null_cursor);
1398
    gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
1399
                            NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
1400
#else
1401
    gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
1402 1403 1404 1405 1406 1407 1408
                     FALSE, /* All events to come to our window directly */
                     GDK_POINTER_MOTION_MASK |
                     GDK_BUTTON_PRESS_MASK |
                     GDK_BUTTON_RELEASE_MASK |
                     GDK_BUTTON_MOTION_MASK |
                     GDK_SCROLL_MASK,
                     NULL, /* Allow cursor to move over entire desktop */
1409
                     vc->s->null_cursor,
1410
                     GDK_CURRENT_TIME);
1411
    gdk_display_get_pointer(display, NULL,
1412
                            &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
1413
#endif
G
Gerd Hoffmann 已提交
1414
    vc->s->ptr_owner = vc;
1415
    gd_update_caption(vc->s);
G
Gerd Hoffmann 已提交
1416
    trace_gd_grab(vc->label, "ptr", reason);
1417 1418
}

G
Gerd Hoffmann 已提交
1419
static void gd_ungrab_pointer(GtkDisplayState *s)
1420
{
G
Gerd Hoffmann 已提交
1421 1422 1423 1424 1425 1426 1427
    VirtualConsole *vc = s->ptr_owner;

    if (vc == NULL) {
        return;
    }
    s->ptr_owner = NULL;

1428
    GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1429
#if GTK_CHECK_VERSION(3, 0, 0)
1430
    GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1431
    gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
1432
    gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
1433 1434
                    gtk_widget_get_screen(vc->gfx.drawing_area),
                    vc->s->grab_x_root, vc->s->grab_y_root);
1435 1436
#else
    gdk_pointer_ungrab(GDK_CURRENT_TIME);
1437
    gdk_display_warp_pointer(display,
1438 1439
                             gtk_widget_get_screen(vc->gfx.drawing_area),
                             vc->s->grab_x_root, vc->s->grab_y_root);
1440
#endif
1441
    gd_update_caption(s);
G
Gerd Hoffmann 已提交
1442
    trace_gd_ungrab(vc->label, "ptr");
1443 1444
}

1445 1446 1447
static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
{
    GtkDisplayState *s = opaque;
1448
    VirtualConsole *vc = gd_vc_find_current(s);
1449 1450

    if (gd_is_grab_active(s)) {
G
Gerd Hoffmann 已提交
1451 1452
        gd_grab_keyboard(vc, "user-request-main-window");
        gd_grab_pointer(vc, "user-request-main-window");
1453
    } else {
G
Gerd Hoffmann 已提交
1454 1455
        gd_ungrab_keyboard(s);
        gd_ungrab_pointer(s);
1456 1457
    }

1458
    gd_update_cursor(vc);
1459 1460
}

A
Anthony Liguori 已提交
1461 1462 1463 1464
static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
                           gpointer data)
{
    GtkDisplayState *s = data;
1465
    VirtualConsole *vc;
1466
    gboolean on_vga;
A
Anthony Liguori 已提交
1467 1468 1469 1470 1471

    if (!gtk_widget_get_realized(s->notebook)) {
        return;
    }

1472 1473 1474 1475 1476 1477
#ifdef VTE_RESIZE_HACK
    vc = gd_vc_find_current(s);
    if (vc && vc->type == GD_VC_VTE) {
        gtk_widget_hide(vc->vte.terminal);
    }
#endif
1478 1479 1480 1481
    vc = gd_vc_find_by_page(s, arg2);
    if (!vc) {
        return;
    }
1482 1483 1484 1485 1486
#ifdef VTE_RESIZE_HACK
    if (vc->type == GD_VC_VTE) {
        gtk_widget_show(vc->vte.terminal);
    }
#endif
1487 1488
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
                                   TRUE);
1489 1490
    on_vga = (vc->type == GD_VC_GFX &&
              qemu_console_is_graphic(vc->gfx.dcl.con));
1491 1492 1493
    if (!on_vga) {
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
                                       FALSE);
1494 1495 1496
    } else if (s->full_screen) {
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
                                       TRUE);
1497 1498 1499
    }
    gtk_widget_set_sensitive(s->grab_item, on_vga);

G
Gerd Hoffmann 已提交
1500
    gd_update_windowsize(vc);
1501
    gd_update_cursor(vc);
A
Anthony Liguori 已提交
1502 1503
}

1504 1505
static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
                               gpointer opaque)
1506
{
1507 1508
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;
1509

G
Gerd Hoffmann 已提交
1510
    if (gd_grab_on_hover(s)) {
G
Gerd Hoffmann 已提交
1511
        gd_grab_keyboard(vc, "grab-on-hover");
1512 1513 1514 1515
    }
    return TRUE;
}

1516 1517
static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
                               gpointer opaque)
1518
{
1519 1520
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;
1521

G
Gerd Hoffmann 已提交
1522
    if (gd_grab_on_hover(s)) {
G
Gerd Hoffmann 已提交
1523
        gd_ungrab_keyboard(s);
1524 1525 1526 1527
    }
    return TRUE;
}

1528
static gboolean gd_focus_out_event(GtkWidget *widget,
1529
                                   GdkEventCrossing *crossing, gpointer opaque)
1530
{
1531 1532
    VirtualConsole *vc = opaque;
    GtkDisplayState *s = vc->s;
1533 1534 1535 1536 1537

    gtk_release_modifiers(s);
    return TRUE;
}

G
Gerd Hoffmann 已提交
1538 1539 1540 1541 1542
static gboolean gd_configure(GtkWidget *widget,
                             GdkEventConfigure *cfg, gpointer opaque)
{
    VirtualConsole *vc = opaque;

1543
    gd_set_ui_info(vc, cfg->width, cfg->height);
G
Gerd Hoffmann 已提交
1544 1545 1546
    return FALSE;
}

1547 1548
/** Virtual Console Callbacks **/

1549
static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1550
                               int idx, GSList *group, GtkWidget *view_menu)
1551
{
1552
    vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1553 1554 1555 1556 1557 1558 1559 1560
    gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
            HOTKEY_MODIFIERS, 0,
            g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
#if GTK_CHECK_VERSION(3, 8, 0)
    gtk_accel_label_set_accel(
            GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
            GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
#endif
1561 1562 1563 1564 1565

    g_signal_connect(vc->menu_item, "activate",
                     G_CALLBACK(gd_menu_switch_vc), s);
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);

1566
    group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1567 1568 1569
    return group;
}

1570
#if defined(CONFIG_VTE)
1571 1572 1573 1574 1575 1576
static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
{
    VirtualConsole *vc = opaque;

    if (gtk_adjustment_get_upper(adjustment) >
        gtk_adjustment_get_page_size(adjustment)) {
G
Gerd Hoffmann 已提交
1577
        gtk_widget_show(vc->vte.scrollbar);
1578
    } else {
G
Gerd Hoffmann 已提交
1579
        gtk_widget_hide(vc->vte.scrollbar);
1580 1581 1582
    }
}

1583 1584 1585 1586
static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
    VirtualConsole *vc = chr->opaque;

G
Gerd Hoffmann 已提交
1587
    vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
C
Cole Robinson 已提交
1588
    return len;
1589 1590
}

P
Paolo Bonzini 已提交
1591 1592 1593 1594 1595 1596 1597
static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
{
    VirtualConsole *vc = chr->opaque;

    vc->vte.echo = echo;
}

1598 1599 1600
static int nb_vcs;
static CharDriverState *vcs[MAX_VCS];

1601
static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
1602
{
1603
    ChardevCommon *common = qapi_ChardevVC_base(vc);
1604 1605
    CharDriverState *chr;

1606 1607 1608 1609 1610
    chr = qemu_chr_alloc(common, errp);
    if (!chr) {
        return NULL;
    }

1611
    chr->chr_write = gd_vc_chr_write;
P
Paolo Bonzini 已提交
1612 1613 1614 1615 1616
    chr->chr_set_echo = gd_vc_chr_set_echo;

    /* Temporary, until gd_vc_vte_init runs.  */
    chr->opaque = g_new(VirtualConsole, 1);

1617 1618
    /* defer OPENED events until our vc is fully initialized */
    chr->explicit_be_open = true;
1619 1620 1621 1622 1623 1624

    vcs[nb_vcs++] = chr;

    return chr;
}

C
Cole Robinson 已提交
1625 1626
static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
                         gpointer user_data)
1627
{
C
Cole Robinson 已提交
1628
    VirtualConsole *vc = user_data;
1629

P
Paolo Bonzini 已提交
1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
    if (vc->vte.echo) {
        VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
        int i;
        for (i = 0; i < size; i++) {
            uint8_t c = text[i];
            if (c >= 128 || isprint(c)) {
                /* 8-bit characters are considered printable.  */
                vte_terminal_feed(term, &text[i], 1);
            } else if (c == '\r' || c == '\n') {
                vte_terminal_feed(term, "\r\n", 2);
            } else {
                char ctrl[2] = { '^', 0};
                ctrl[1] = text[i] ^ 64;
                vte_terminal_feed(term, ctrl, 2);
            }
        }
    }

G
Gerd Hoffmann 已提交
1648
    qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1649 1650 1651
    return TRUE;
}

1652 1653
static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
                              CharDriverState *chr, int idx,
1654
                              GSList *group, GtkWidget *view_menu)
1655 1656
{
    char buffer[32];
1657 1658 1659
    GtkWidget *box;
    GtkWidget *scrollbar;
    GtkAdjustment *vadjustment;
P
Paolo Bonzini 已提交
1660
    VirtualConsole *tmp_vc = chr->opaque;
1661

1662
    vc->s = s;
P
Paolo Bonzini 已提交
1663 1664
    vc->vte.echo = tmp_vc->vte.echo;

1665
    vc->vte.chr = chr;
P
Paolo Bonzini 已提交
1666 1667
    chr->opaque = vc;
    g_free(tmp_vc);
1668

1669
    snprintf(buffer, sizeof(buffer), "vc%d", idx);
1670 1671 1672
    vc->label = g_strdup_printf("%s", vc->vte.chr->label
                                ? vc->vte.chr->label : buffer);
    group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1673

G
Gerd Hoffmann 已提交
1674 1675
    vc->vte.terminal = vte_terminal_new();
    g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1676

P
Paolo Bonzini 已提交
1677 1678 1679 1680 1681 1682 1683 1684 1685
    /* The documentation says that the default is UTF-8, but actually it is
     * 7-bit ASCII at least in VTE 0.38.
     */
#if VTE_CHECK_VERSION(0, 40, 0)
    vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
#else
    vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
#endif

G
Gerd Hoffmann 已提交
1686
    vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
G
Gerd Hoffmann 已提交
1687 1688
    vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
                          VC_TERM_X_MIN, VC_TERM_Y_MIN);
1689

1690
#if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
G
Gerd Hoffmann 已提交
1691 1692
    vadjustment = gtk_scrollable_get_vadjustment
        (GTK_SCROLLABLE(vc->vte.terminal));
1693
#else
G
Gerd Hoffmann 已提交
1694
    vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
1695 1696 1697 1698 1699 1700 1701 1702 1703 1704
#endif

#if GTK_CHECK_VERSION(3, 0, 0)
    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
    scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
#else
    box = gtk_hbox_new(false, 2);
    scrollbar = gtk_vscrollbar_new(vadjustment);
#endif

G
Gerd Hoffmann 已提交
1705
    gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
1706 1707
    gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);

G
Gerd Hoffmann 已提交
1708 1709
    vc->vte.box = box;
    vc->vte.scrollbar = scrollbar;
1710 1711 1712

    g_signal_connect(vadjustment, "changed",
                     G_CALLBACK(gd_vc_adjustment_changed), vc);
1713

1714
    vc->type = GD_VC_VTE;
G
Gerd Hoffmann 已提交
1715
    vc->tab_item = box;
J
Jan Kiszka 已提交
1716
    vc->focus = vc->vte.terminal;
G
Gerd Hoffmann 已提交
1717
    gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1718
                             gtk_label_new(vc->label));
1719

G
Gerd Hoffmann 已提交
1720 1721 1722
    qemu_chr_be_generic_open(vc->vte.chr);
    if (vc->vte.chr->init) {
        vc->vte.chr->init(vc->vte.chr);
1723 1724 1725
    }

    return group;
A
Anthony Liguori 已提交
1726 1727
}

1728
static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1729 1730 1731 1732 1733
                        GtkWidget *view_menu)
{
    int i;

    for (i = 0; i < nb_vcs; i++) {
1734 1735
        VirtualConsole *vc = &s->vc[s->nb_vcs];
        group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1736 1737 1738 1739 1740
        s->nb_vcs++;
    }
}
#endif /* CONFIG_VTE */

A
Anthony Liguori 已提交
1741 1742
/** Window Creation **/

1743 1744 1745 1746 1747
static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
{
#if GTK_CHECK_VERSION(3, 0, 0)
    g_signal_connect(vc->gfx.drawing_area, "draw",
                     G_CALLBACK(gd_draw_event), vc);
1748 1749 1750 1751 1752 1753 1754 1755 1756
#if defined(CONFIG_GTK_GL)
    if (display_opengl) {
        /* wire up GtkGlArea events */
        g_signal_connect(vc->gfx.drawing_area, "render",
                         G_CALLBACK(gd_render_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "resize",
                         G_CALLBACK(gd_resize_event), vc);
    }
#endif
1757 1758 1759 1760
#else
    g_signal_connect(vc->gfx.drawing_area, "expose-event",
                     G_CALLBACK(gd_expose_event), vc);
#endif
1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780
    if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
        g_signal_connect(vc->gfx.drawing_area, "event",
                         G_CALLBACK(gd_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "button-press-event",
                         G_CALLBACK(gd_button_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "button-release-event",
                         G_CALLBACK(gd_button_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "scroll-event",
                         G_CALLBACK(gd_scroll_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "key-press-event",
                         G_CALLBACK(gd_key_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "key-release-event",
                         G_CALLBACK(gd_key_event), vc);

        g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
                         G_CALLBACK(gd_enter_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
                         G_CALLBACK(gd_leave_event), vc);
        g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
                         G_CALLBACK(gd_focus_out_event), vc);
G
Gerd Hoffmann 已提交
1781 1782
        g_signal_connect(vc->gfx.drawing_area, "configure-event",
                         G_CALLBACK(gd_configure), vc);
1783 1784 1785 1786
    } else {
        g_signal_connect(vc->gfx.drawing_area, "key-press-event",
                         G_CALLBACK(gd_text_key_down), vc);
    }
1787 1788
}

A
Anthony Liguori 已提交
1789 1790 1791 1792
static void gd_connect_signals(GtkDisplayState *s)
{
    g_signal_connect(s->show_tabs_item, "activate",
                     G_CALLBACK(gd_menu_show_tabs), s);
1793 1794
    g_signal_connect(s->untabify_item, "activate",
                     G_CALLBACK(gd_menu_untabify), s);
A
Anthony Liguori 已提交
1795 1796 1797 1798

    g_signal_connect(s->window, "delete-event",
                     G_CALLBACK(gd_window_close), s);

1799 1800 1801 1802 1803 1804
    g_signal_connect(s->pause_item, "activate",
                     G_CALLBACK(gd_menu_pause), s);
    g_signal_connect(s->reset_item, "activate",
                     G_CALLBACK(gd_menu_reset), s);
    g_signal_connect(s->powerdown_item, "activate",
                     G_CALLBACK(gd_menu_powerdown), s);
A
Anthony Liguori 已提交
1805 1806
    g_signal_connect(s->quit_item, "activate",
                     G_CALLBACK(gd_menu_quit), s);
1807 1808 1809 1810 1811 1812 1813 1814 1815 1816
    g_signal_connect(s->full_screen_item, "activate",
                     G_CALLBACK(gd_menu_full_screen), s);
    g_signal_connect(s->zoom_in_item, "activate",
                     G_CALLBACK(gd_menu_zoom_in), s);
    g_signal_connect(s->zoom_out_item, "activate",
                     G_CALLBACK(gd_menu_zoom_out), s);
    g_signal_connect(s->zoom_fixed_item, "activate",
                     G_CALLBACK(gd_menu_zoom_fixed), s);
    g_signal_connect(s->zoom_fit_item, "activate",
                     G_CALLBACK(gd_menu_zoom_fit), s);
1817 1818
    g_signal_connect(s->grab_item, "activate",
                     G_CALLBACK(gd_menu_grab_input), s);
A
Anthony Liguori 已提交
1819 1820 1821 1822
    g_signal_connect(s->notebook, "switch-page",
                     G_CALLBACK(gd_change_page), s);
}

1823
static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
A
Anthony Liguori 已提交
1824
{
A
Anthony Liguori 已提交
1825
    GtkWidget *machine_menu;
A
Anthony Liguori 已提交
1826 1827
    GtkWidget *separator;

A
Anthony Liguori 已提交
1828
    machine_menu = gtk_menu_new();
1829
    gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
1830 1831

    s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
A
Anthony Liguori 已提交
1832
    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
1833 1834

    separator = gtk_separator_menu_item_new();
A
Anthony Liguori 已提交
1835
    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1836

1837
    s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
A
Anthony Liguori 已提交
1838
    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
1839

1840
    s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
A
Anthony Liguori 已提交
1841
    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
1842 1843

    separator = gtk_separator_menu_item_new();
A
Anthony Liguori 已提交
1844
    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
A
Anthony Liguori 已提交
1845

1846
    s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
A
Anthony Liguori 已提交
1847
    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
1848
                                 "<QEMU>/Machine/Quit");
1849
    gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1850
                            GDK_KEY_q, HOTKEY_MODIFIERS);
A
Anthony Liguori 已提交
1851
    gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
A
Anthony Liguori 已提交
1852

A
Anthony Liguori 已提交
1853 1854 1855
    return machine_menu;
}

1856
static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1857
                              QemuConsole *con, int idx,
1858 1859
                              GSList *group, GtkWidget *view_menu)
{
1860
    vc->label = qemu_console_get_label(con);
1861 1862 1863 1864
    vc->s = s;
    vc->gfx.scale_x = 1.0;
    vc->gfx.scale_y = 1.0;

1865 1866
#if defined(CONFIG_OPENGL)
    if (display_opengl) {
1867 1868 1869 1870 1871
#if defined(CONFIG_GTK_GL)
        vc->gfx.drawing_area = gtk_gl_area_new();
        vc->gfx.dcl.ops = &dcl_gl_area_ops;
#else
        vc->gfx.drawing_area = gtk_drawing_area_new();
1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886
        /*
         * gtk_widget_set_double_buffered() was deprecated in 3.14.
         * It is required for opengl rendering on X11 though.  A
         * proper replacement (native opengl support) is only
         * available in 3.16+.  Silence the warning if possible.
         */
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
        gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic pop
#endif
        vc->gfx.dcl.ops = &dcl_egl_ops;
1887
#endif /* CONFIG_GTK_GL */
1888 1889 1890
    } else
#endif
    {
1891
        vc->gfx.drawing_area = gtk_drawing_area_new();
1892 1893 1894
        vc->gfx.dcl.ops = &dcl_ops;
    }

1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912

    gtk_widget_add_events(vc->gfx.drawing_area,
                          GDK_POINTER_MOTION_MASK |
                          GDK_BUTTON_PRESS_MASK |
                          GDK_BUTTON_RELEASE_MASK |
                          GDK_BUTTON_MOTION_MASK |
                          GDK_ENTER_NOTIFY_MASK |
                          GDK_LEAVE_NOTIFY_MASK |
                          GDK_SCROLL_MASK |
                          GDK_KEY_PRESS_MASK);
    gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);

    vc->type = GD_VC_GFX;
    vc->tab_item = vc->gfx.drawing_area;
    vc->focus = vc->gfx.drawing_area;
    gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
                             vc->tab_item, gtk_label_new(vc->label));

1913 1914 1915
    vc->gfx.dcl.con = con;
    register_displaychangelistener(&vc->gfx.dcl);

1916 1917 1918
    gd_connect_vc_gfx_signals(vc);
    group = gd_vc_menu_init(s, vc, idx, group, view_menu);

G
Gerd Hoffmann 已提交
1919 1920
    if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
        gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
1921
        s->free_scale = true;
G
Gerd Hoffmann 已提交
1922 1923
    }

1924 1925 1926
    return group;
}

1927
static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
A
Anthony Liguori 已提交
1928 1929 1930 1931
{
    GSList *group = NULL;
    GtkWidget *view_menu;
    GtkWidget *separator;
1932 1933
    QemuConsole *con;
    int vc;
A
Anthony Liguori 已提交
1934 1935

    view_menu = gtk_menu_new();
1936
    gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
A
Anthony Liguori 已提交
1937

1938
    s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
1939 1940 1941 1942 1943 1944 1945 1946

    gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
            g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
#if GTK_CHECK_VERSION(3, 8, 0)
    gtk_accel_label_set_accel(
            GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
            GDK_KEY_f, HOTKEY_MODIFIERS);
#endif
A
Anthony Liguori 已提交
1947
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
1948 1949

    separator = gtk_separator_menu_item_new();
A
Anthony Liguori 已提交
1950
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1951

1952
    s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
1953 1954
    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
                                 "<QEMU>/View/Zoom In");
J
Jan Kiszka 已提交
1955 1956
    gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
                            HOTKEY_MODIFIERS);
A
Anthony Liguori 已提交
1957
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
1958

1959
    s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
1960 1961
    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
                                 "<QEMU>/View/Zoom Out");
J
Jan Kiszka 已提交
1962 1963
    gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
                            HOTKEY_MODIFIERS);
A
Anthony Liguori 已提交
1964
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
1965

1966
    s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
1967 1968
    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
                                 "<QEMU>/View/Zoom Fixed");
J
Jan Kiszka 已提交
1969 1970
    gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
                            HOTKEY_MODIFIERS);
A
Anthony Liguori 已提交
1971
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
1972

1973
    s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
A
Anthony Liguori 已提交
1974
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
1975 1976

    separator = gtk_separator_menu_item_new();
A
Anthony Liguori 已提交
1977
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1978

1979
    s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
A
Anthony Liguori 已提交
1980
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
1981

1982
    s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
1983 1984
    gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
                                 "<QEMU>/View/Grab Input");
J
Jan Kiszka 已提交
1985 1986
    gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
                            HOTKEY_MODIFIERS);
A
Anthony Liguori 已提交
1987
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
1988

A
Anthony Liguori 已提交
1989
    separator = gtk_separator_menu_item_new();
A
Anthony Liguori 已提交
1990
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
A
Anthony Liguori 已提交
1991

1992
    /* gfx */
1993 1994
    for (vc = 0;; vc++) {
        con = qemu_console_lookup_by_index(vc);
1995
        if (!con) {
1996 1997 1998 1999 2000 2001
            break;
        }
        group = gd_vc_gfx_init(s, &s->vc[vc], con,
                               vc, group, view_menu);
        s->nb_vcs++;
    }
A
Anthony Liguori 已提交
2002

2003
#if defined(CONFIG_VTE)
2004
    /* vte */
2005
    gd_vcs_init(s, group, view_menu);
2006
#endif
2007

A
Anthony Liguori 已提交
2008
    separator = gtk_separator_menu_item_new();
A
Anthony Liguori 已提交
2009
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
A
Anthony Liguori 已提交
2010

2011
    s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
A
Anthony Liguori 已提交
2012
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
A
Anthony Liguori 已提交
2013

2014 2015 2016
    s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);

A
Anthony Liguori 已提交
2017 2018 2019 2020 2021
    return view_menu;
}

static void gd_create_menus(GtkDisplayState *s)
{
2022 2023 2024
    s->accel_group = gtk_accel_group_new();
    s->machine_menu = gd_create_menu_machine(s);
    s->view_menu = gd_create_menu_view(s);
A
Anthony Liguori 已提交
2025 2026

    s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
2027 2028 2029
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
                              s->machine_menu);
    gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
A
Anthony Liguori 已提交
2030

A
Anthony Liguori 已提交
2031
    s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
A
Anthony Liguori 已提交
2032 2033
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
    gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
A
Anthony Liguori 已提交
2034

2035 2036
    g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
    gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
A
Anthony Liguori 已提交
2037 2038
}

2039 2040
static void gd_set_keycode_type(GtkDisplayState *s)
{
2041
#ifdef GDK_WINDOWING_X11
2042
    GdkDisplay *display = gtk_widget_get_display(s->window);
2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059
    if (GDK_IS_X11_DISPLAY(display)) {
        Display *x11_display = gdk_x11_display_get_xdisplay(display);
        XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
                                         XkbUseCoreKbd);
        char *keycodes = NULL;

        if (desc && desc->names) {
            keycodes = XGetAtomName(x11_display, desc->names->keycodes);
        }
        if (keycodes == NULL) {
            fprintf(stderr, "could not lookup keycode name\n");
        } else if (strstart(keycodes, "evdev", NULL)) {
            s->has_evdev = true;
        } else if (!strstart(keycodes, "xfree86", NULL)) {
            fprintf(stderr, "unknown keycodes `%s', please report to "
                    "qemu-devel@nongnu.org\n", keycodes);
        }
2060 2061 2062 2063 2064 2065 2066

        if (desc) {
            XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
        }
        if (keycodes) {
            XFree(keycodes);
        }
2067 2068 2069 2070
    }
#endif
}

2071 2072
static gboolean gtkinit;

2073
void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
A
Anthony Liguori 已提交
2074 2075
{
    GtkDisplayState *s = g_malloc0(sizeof(*s));
S
Stefan Weil 已提交
2076
    char *filename;
M
Max Reitz 已提交
2077
    GdkDisplay *window_display;
A
Anthony Liguori 已提交
2078

2079 2080 2081 2082 2083
    if (!gtkinit) {
        fprintf(stderr, "gtk initialization failed\n");
        exit(1);
    }

A
Anthony Liguori 已提交
2084
    s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2085 2086 2087
#if GTK_CHECK_VERSION(3, 2, 0)
    s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
#else
A
Anthony Liguori 已提交
2088
    s->vbox = gtk_vbox_new(FALSE, 0);
2089
#endif
A
Anthony Liguori 已提交
2090 2091 2092
    s->notebook = gtk_notebook_new();
    s->menu_bar = gtk_menu_bar_new();

2093
    s->free_scale = FALSE;
A
Anthony Liguori 已提交
2094

2095 2096
    /* LC_MESSAGES only. See early_gtk_display_init() for details */
    setlocale(LC_MESSAGES, "");
2097 2098 2099
    bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
    textdomain("qemu");

M
Max Reitz 已提交
2100 2101 2102
    window_display = gtk_widget_get_display(s->window);
    s->null_cursor = gdk_cursor_new_for_display(window_display,
                                                GDK_BLANK_CURSOR);
A
Anthony Liguori 已提交
2103 2104 2105 2106 2107

    s->mouse_mode_notifier.notify = gd_mouse_mode_change;
    qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
    qemu_add_vm_change_state_handler(gd_change_runstate, s);

A
Anthony Liguori 已提交
2108
    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
S
Stefan Weil 已提交
2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119
    if (filename) {
        GError *error = NULL;
        GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
        if (pixbuf) {
            gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
        } else {
            g_error_free(error);
        }
        g_free(filename);
    }

A
Anthony Liguori 已提交
2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135
    gd_create_menus(s);

    gd_connect_signals(s);

    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
    gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);

    gd_update_caption(s);

    gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);

    gtk_container_add(GTK_CONTAINER(s->window), s->vbox);

    gtk_widget_show_all(s->window);

2136 2137 2138
#ifdef VTE_RESIZE_HACK
    {
        VirtualConsole *cur = gd_vc_find_current(s);
F
Fam Zheng 已提交
2139 2140 2141 2142 2143 2144 2145 2146
        if (cur) {
            int i;

            for (i = 0; i < s->nb_vcs; i++) {
                VirtualConsole *vc = &s->vc[i];
                if (vc && vc->type == GD_VC_VTE && vc != cur) {
                    gtk_widget_hide(vc->vte.terminal);
                }
2147
            }
F
Fam Zheng 已提交
2148
            gd_update_windowsize(cur);
2149 2150 2151 2152
        }
    }
#endif

P
Peter Wu 已提交
2153 2154 2155
    if (full_screen) {
        gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
    }
2156 2157 2158
    if (grab_on_hover) {
        gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
    }
P
Peter Wu 已提交
2159

2160
    gd_set_keycode_type(s);
A
Anthony Liguori 已提交
2161
}
2162

2163
void early_gtk_display_init(int opengl)
2164
{
2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182
    /* The QEMU code relies on the assumption that it's always run in
     * the C locale. Therefore it is not prepared to deal with
     * operations that produce different results depending on the
     * locale, such as printf's formatting of decimal numbers, and
     * possibly others.
     *
     * Since GTK+ calls setlocale() by default -importing the locale
     * settings from the environment- we must prevent it from doing so
     * using gtk_disable_setlocale().
     *
     * QEMU's GTK+ UI, however, _does_ have translations for some of
     * the menu items. As a trade-off between a functionally correct
     * QEMU and a fully internationalized UI we support importing
     * LC_MESSAGES from the environment (see the setlocale() call
     * earlier in this file). This allows us to display translated
     * messages leaving everything else untouched.
     */
    gtk_disable_setlocale();
2183 2184 2185 2186 2187
    gtkinit = gtk_init_check(NULL, NULL);
    if (!gtkinit) {
        /* don't exit yet, that'll break -help */
        return;
    }
2188 2189 2190 2191 2192 2193 2194

    switch (opengl) {
    case -1: /* default */
    case 0:  /* off */
        break;
    case 1: /* on */
#if defined(CONFIG_OPENGL)
2195 2196 2197
#if defined(CONFIG_GTK_GL)
        gtk_gl_area_init();
#else
2198
        gtk_egl_init();
2199
#endif
2200 2201 2202 2203 2204 2205 2206
#endif
        break;
    default:
        g_assert_not_reached();
        break;
    }

2207 2208 2209 2210
#if defined(CONFIG_VTE)
    register_vc_handler(gd_vc_handler);
#endif
}