2013-02-20 14:43:20 +01:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2013-02-20 14:43:24 +01:00
|
|
|
#define GETTEXT_PACKAGE "qemu"
|
|
|
|
#define LOCALEDIR "po"
|
|
|
|
|
2013-02-22 21:08:51 +01:00
|
|
|
#include "qemu-common.h"
|
|
|
|
|
2015-03-04 15:37:27 +01:00
|
|
|
#include "ui/console.h"
|
|
|
|
#include "ui/gtk.h"
|
2013-02-22 21:08:51 +01:00
|
|
|
|
2013-02-20 14:43:24 +01:00
|
|
|
#include <glib/gi18n.h>
|
2013-02-22 07:28:01 +01:00
|
|
|
#include <locale.h>
|
2014-02-19 07:04:34 +01:00
|
|
|
#if defined(CONFIG_VTE)
|
2013-02-20 14:43:20 +01:00
|
|
|
#include <vte/vte.h>
|
2014-02-19 07:04:34 +01:00
|
|
|
#endif
|
2013-02-20 14:43:20 +01:00
|
|
|
#include <math.h>
|
|
|
|
|
2013-11-10 16:24:02 +01:00
|
|
|
#include "trace.h"
|
2013-11-28 11:40:27 +01:00
|
|
|
#include "ui/input.h"
|
2013-02-20 14:43:20 +01:00
|
|
|
#include "sysemu/sysemu.h"
|
|
|
|
#include "qmp-commands.h"
|
|
|
|
#include "x_keymap.h"
|
|
|
|
#include "keymaps.h"
|
2013-04-08 16:55:25 +02:00
|
|
|
#include "sysemu/char.h"
|
2014-05-05 14:36:56 +02:00
|
|
|
#include "qom/object.h"
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-02-20 14:43:21 +01:00
|
|
|
#define MAX_VCS 10
|
2014-05-16 12:26:12 +02:00
|
|
|
#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
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-02-19 07:04:34 +01:00
|
|
|
#if !defined(CONFIG_VTE)
|
|
|
|
# define VTE_CHECK_VERSION(a, b, c) 0
|
|
|
|
#endif
|
2013-02-25 16:20:34 +01:00
|
|
|
|
2014-05-16 15:34:31 +02:00
|
|
|
#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
|
|
|
|
|
2013-02-25 16:20:45 +01:00
|
|
|
#if !GTK_CHECK_VERSION(2, 20, 0)
|
|
|
|
#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
|
|
|
|
#endif
|
|
|
|
|
2014-05-28 22:33:06 +02:00
|
|
|
#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
|
|
|
|
|
2013-02-25 16:20:46 +01:00
|
|
|
#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
|
2014-03-17 21:06:27 +01:00
|
|
|
#define GDK_KEY_q GDK_q
|
2013-02-25 16:20:46 +01:00
|
|
|
#define GDK_KEY_plus GDK_plus
|
|
|
|
#define GDK_KEY_minus GDK_minus
|
2014-11-04 08:32:26 +01:00
|
|
|
#define GDK_KEY_Pause GDK_Pause
|
2013-02-25 16:20:46 +01:00
|
|
|
#endif
|
2013-02-25 16:20:45 +01:00
|
|
|
|
2013-07-22 09:04:32 +02:00
|
|
|
#define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK)
|
|
|
|
|
2013-03-24 19:10:02 +01:00
|
|
|
static const int modifier_keycode[] = {
|
|
|
|
/* shift, control, alt keys, meta keys, both left & right */
|
|
|
|
0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
|
|
|
|
};
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
struct GtkDisplayState {
|
2013-02-20 14:43:20 +01:00
|
|
|
GtkWidget *window;
|
|
|
|
|
|
|
|
GtkWidget *menu_bar;
|
|
|
|
|
2013-02-20 14:43:26 +01:00
|
|
|
GtkAccelGroup *accel_group;
|
|
|
|
|
2013-02-22 20:53:33 +01:00
|
|
|
GtkWidget *machine_menu_item;
|
|
|
|
GtkWidget *machine_menu;
|
|
|
|
GtkWidget *pause_item;
|
|
|
|
GtkWidget *reset_item;
|
|
|
|
GtkWidget *powerdown_item;
|
2013-02-20 14:43:20 +01:00
|
|
|
GtkWidget *quit_item;
|
|
|
|
|
|
|
|
GtkWidget *view_menu_item;
|
|
|
|
GtkWidget *view_menu;
|
2013-02-20 14:43:23 +01:00
|
|
|
GtkWidget *full_screen_item;
|
|
|
|
GtkWidget *zoom_in_item;
|
|
|
|
GtkWidget *zoom_out_item;
|
|
|
|
GtkWidget *zoom_fixed_item;
|
|
|
|
GtkWidget *zoom_fit_item;
|
2013-02-20 14:43:22 +01:00
|
|
|
GtkWidget *grab_item;
|
|
|
|
GtkWidget *grab_on_hover_item;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-02-20 14:43:21 +01:00
|
|
|
int nb_vcs;
|
|
|
|
VirtualConsole vc[MAX_VCS];
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
GtkWidget *show_tabs_item;
|
2014-05-05 16:23:33 +02:00
|
|
|
GtkWidget *untabify_item;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
GtkWidget *vbox;
|
|
|
|
GtkWidget *notebook;
|
|
|
|
int button_mask;
|
2014-04-04 12:41:22 +02:00
|
|
|
gboolean last_set;
|
2013-02-20 14:43:20 +01:00
|
|
|
int last_x;
|
|
|
|
int last_y;
|
2014-04-04 12:41:23 +02:00
|
|
|
int grab_x_root;
|
|
|
|
int grab_y_root;
|
2014-05-06 11:01:31 +02:00
|
|
|
VirtualConsole *kbd_owner;
|
|
|
|
VirtualConsole *ptr_owner;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-02-20 14:43:23 +01:00
|
|
|
gboolean full_screen;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
GdkCursor *null_cursor;
|
|
|
|
Notifier mouse_mode_notifier;
|
2013-02-20 14:43:23 +01:00
|
|
|
gboolean free_scale;
|
2013-02-22 20:53:33 +01:00
|
|
|
|
|
|
|
bool external_pause_update;
|
2013-03-24 19:10:02 +01:00
|
|
|
|
|
|
|
bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
|
2014-05-01 04:29:42 +02:00
|
|
|
bool has_evdev;
|
2015-04-26 21:04:21 +02:00
|
|
|
bool ignore_keys;
|
2014-04-30 16:30:07 +02:00
|
|
|
};
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2015-09-09 10:12:20 +02:00
|
|
|
static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
|
2014-05-06 11:45:30 +02:00
|
|
|
static void gd_ungrab_pointer(GtkDisplayState *s);
|
2015-09-09 10:12:20 +02:00
|
|
|
static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
|
2015-09-09 09:57:01 +02:00
|
|
|
static void gd_ungrab_keyboard(GtkDisplayState *s);
|
2014-05-06 11:45:30 +02:00
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
/** Utility Functions **/
|
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:22 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
static void gd_update_cursor(VirtualConsole *vc)
|
2013-02-20 14:43:20 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
GtkDisplayState *s = vc->s;
|
2013-02-20 14:43:20 +01:00
|
|
|
GdkWindow *window;
|
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
if (vc->type != GD_VC_GFX ||
|
|
|
|
!qemu_console_is_graphic(vc->gfx.dcl.con)) {
|
2014-04-30 16:30:07 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2015-03-24 20:08:48 +01:00
|
|
|
if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
|
2014-05-06 11:45:30 +02:00
|
|
|
if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
|
2013-02-20 14:43:20 +01:00
|
|
|
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 = "";
|
2014-05-06 11:20:17 +02:00
|
|
|
gchar *prefix;
|
2013-02-20 14:43:20 +01:00
|
|
|
gchar *title;
|
2013-02-20 14:43:22 +01:00
|
|
|
const char *grab = "";
|
2013-02-22 20:53:33 +01:00
|
|
|
bool is_paused = !runstate_is_running();
|
2014-05-06 11:20:17 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (qemu_name) {
|
|
|
|
prefix = g_strdup_printf("QEMU (%s)", qemu_name);
|
|
|
|
} else {
|
|
|
|
prefix = g_strdup_printf("QEMU");
|
|
|
|
}
|
2013-02-20 14:43:22 +01:00
|
|
|
|
2014-05-06 11:20:17 +02:00
|
|
|
if (s->ptr_owner != NULL &&
|
|
|
|
s->ptr_owner->window == NULL) {
|
2013-04-01 19:12:02 +02:00
|
|
|
grab = _(" - Press Ctrl+Alt+G to release grab");
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-02-22 20:53:33 +01:00
|
|
|
if (is_paused) {
|
2013-04-01 19:12:02 +02:00
|
|
|
status = _(" [Paused]");
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
2013-02-22 20:53:33 +01:00
|
|
|
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;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-05-06 11:20:17 +02:00
|
|
|
title = g_strdup_printf("%s%s%s", prefix, status, grab);
|
2013-02-20 14:43:20 +01:00
|
|
|
gtk_window_set_title(GTK_WINDOW(s->window), title);
|
|
|
|
g_free(title);
|
2014-05-06 11:20:17 +02:00
|
|
|
|
|
|
|
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);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
static void gd_update_geometry_hints(VirtualConsole *vc)
|
2013-02-28 16:10:02 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
GtkDisplayState *s = vc->s;
|
2014-05-16 12:26:12 +02:00
|
|
|
GdkWindowHints mask = 0;
|
|
|
|
GdkGeometry geo = {};
|
|
|
|
GtkWidget *geo_widget = NULL;
|
|
|
|
GtkWindow *geo_window;
|
2014-04-30 16:30:07 +02:00
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
if (vc->type == GD_VC_GFX) {
|
2015-02-27 14:36:09 +01:00
|
|
|
if (!vc->gfx.ds) {
|
|
|
|
return;
|
|
|
|
}
|
2014-05-16 12:26:12 +02:00
|
|
|
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);
|
2013-02-28 16:10:02 +01:00
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
#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
|
2013-02-28 16:10:02 +01:00
|
|
|
}
|
2014-05-16 12:26:12 +02:00
|
|
|
|
|
|
|
geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
|
|
|
|
gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
|
|
|
|
}
|
|
|
|
|
2015-01-20 12:43:28 +01:00
|
|
|
void gd_update_windowsize(VirtualConsole *vc)
|
2014-05-16 12:26:12 +02:00
|
|
|
{
|
|
|
|
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);
|
2014-05-06 10:20:53 +02:00
|
|
|
}
|
2013-02-28 16:10:02 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
static void gd_update_full_redraw(VirtualConsole *vc)
|
2013-02-28 16:10:02 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
GtkWidget *area = vc->gfx.drawing_area;
|
2013-02-28 16:10:02 +01:00
|
|
|
int ww, wh;
|
2014-04-30 16:30:07 +02:00
|
|
|
gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
|
|
|
|
gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
|
2013-02-28 16:10:02 +01:00
|
|
|
}
|
|
|
|
|
2013-03-24 19:10:02 +01:00
|
|
|
static void gtk_release_modifiers(GtkDisplayState *s)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-03-24 19:10:02 +01:00
|
|
|
int i, keycode;
|
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
if (vc->type != GD_VC_GFX ||
|
|
|
|
!qemu_console_is_graphic(vc->gfx.dcl.con)) {
|
2013-03-24 19:10:02 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
|
|
|
|
keycode = modifier_keycode[i];
|
|
|
|
if (!s->modifier_pressed[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-04-30 16:30:07 +02:00
|
|
|
qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
|
2013-03-24 19:10:02 +01:00
|
|
|
s->modifier_pressed[i] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-23 17:21:00 +02:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
/** DisplayState Callbacks **/
|
|
|
|
|
2012-11-13 14:51:41 +01:00
|
|
|
static void gd_update(DisplayChangeListener *dcl,
|
2013-03-01 13:03:04 +01:00
|
|
|
int x, int y, int w, int h)
|
2013-02-20 14:43:20 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
|
2014-05-22 11:08:54 +02:00
|
|
|
GdkWindow *win;
|
2013-02-20 14:43:20 +01:00
|
|
|
int x1, x2, y1, y2;
|
2013-02-20 14:43:23 +01:00
|
|
|
int mx, my;
|
|
|
|
int fbw, fbh;
|
|
|
|
int ww, wh;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-05-06 10:27:54 +02:00
|
|
|
trace_gd_update(vc->label, x, y, w, h);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2015-03-24 20:08:48 +01:00
|
|
|
if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
if (vc->gfx.convert) {
|
|
|
|
pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
|
|
|
|
NULL, vc->gfx.convert,
|
2013-06-25 10:48:54 +02:00
|
|
|
x, y, 0, 0, x, y, w, h);
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
x1 = floor(x * vc->gfx.scale_x);
|
|
|
|
y1 = floor(y * vc->gfx.scale_y);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
|
|
|
|
y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
|
|
|
|
fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
win = gtk_widget_get_window(vc->gfx.drawing_area);
|
|
|
|
if (!win) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gdk_drawable_get_size(win, &ww, &wh);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
|
|
|
mx = my = 0;
|
|
|
|
if (ww > fbw) {
|
|
|
|
mx = (ww - fbw) / 2;
|
|
|
|
}
|
|
|
|
if (wh > fbh) {
|
|
|
|
my = (wh - fbh) / 2;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_widget_queue_draw_area(vc->gfx.drawing_area,
|
|
|
|
mx + x1, my + y1, (x2 - x1), (y2 - y1));
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2013-03-01 13:03:04 +01:00
|
|
|
static void gd_refresh(DisplayChangeListener *dcl)
|
2013-02-20 14:43:20 +01:00
|
|
|
{
|
2013-03-15 15:45:54 +01:00
|
|
|
graphic_hw_update(dcl->con);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2013-05-10 16:59:45 +02:00
|
|
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
|
|
|
static void gd_mouse_set(DisplayChangeListener *dcl,
|
|
|
|
int x, int y, int visible)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
|
2013-05-10 16:59:45 +02:00
|
|
|
GdkDisplay *dpy;
|
|
|
|
GdkDeviceManager *mgr;
|
|
|
|
gint x_root, y_root;
|
|
|
|
|
2014-03-13 20:30:24 +01:00
|
|
|
if (qemu_input_is_absolute()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
dpy = gtk_widget_get_display(vc->gfx.drawing_area);
|
2013-05-10 16:59:45 +02:00
|
|
|
mgr = gdk_display_get_device_manager(dpy);
|
2014-04-30 16:30:07 +02:00
|
|
|
gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
|
2013-05-10 16:59:45 +02:00
|
|
|
x, y, &x_root, &y_root);
|
|
|
|
gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_widget_get_screen(vc->gfx.drawing_area),
|
2014-03-13 20:30:23 +01:00
|
|
|
x_root, y_root);
|
2014-07-01 19:12:45 +02:00
|
|
|
vc->s->last_x = x;
|
|
|
|
vc->s->last_y = y;
|
2013-05-10 16:59:45 +02:00
|
|
|
}
|
|
|
|
#else
|
2013-03-20 09:11:41 +01:00
|
|
|
static void gd_mouse_set(DisplayChangeListener *dcl,
|
|
|
|
int x, int y, int visible)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
|
2013-03-20 09:11:41 +01:00
|
|
|
gint x_root, y_root;
|
|
|
|
|
2014-03-13 20:30:24 +01:00
|
|
|
if (qemu_input_is_absolute()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
|
2013-03-20 09:11:41 +01:00
|
|
|
x, y, &x_root, &y_root);
|
2014-04-30 16:30:07 +02:00
|
|
|
gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
|
|
|
|
gtk_widget_get_screen(vc->gfx.drawing_area),
|
2013-03-20 09:11:41 +01:00
|
|
|
x_root, y_root);
|
|
|
|
}
|
2013-05-10 16:59:45 +02:00
|
|
|
#endif
|
2013-03-20 09:11:41 +01:00
|
|
|
|
|
|
|
static void gd_cursor_define(DisplayChangeListener *dcl,
|
|
|
|
QEMUCursor *c)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
|
2013-03-20 09:11:41 +01:00
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
GdkCursor *cursor;
|
|
|
|
|
2015-03-24 20:08:48 +01:00
|
|
|
if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-20 09:11:41 +01:00
|
|
|
pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
|
|
|
|
GDK_COLORSPACE_RGB, true, 8,
|
|
|
|
c->width, c->height, c->width * 4,
|
|
|
|
NULL, NULL);
|
2014-04-30 16:30:07 +02:00
|
|
|
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);
|
2013-03-20 09:11:41 +01:00
|
|
|
g_object_unref(pixbuf);
|
2013-06-16 16:13:07 +02:00
|
|
|
#if !GTK_CHECK_VERSION(3, 0, 0)
|
2013-06-03 22:37:23 +02:00
|
|
|
gdk_cursor_unref(cursor);
|
2013-06-16 16:13:07 +02:00
|
|
|
#else
|
|
|
|
g_object_unref(cursor);
|
|
|
|
#endif
|
2013-03-20 09:11:41 +01:00
|
|
|
}
|
|
|
|
|
2013-02-28 15:03:04 +01:00
|
|
|
static void gd_switch(DisplayChangeListener *dcl,
|
|
|
|
DisplaySurface *surface)
|
2013-02-20 14:43:20 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
|
2013-02-28 16:10:02 +01:00
|
|
|
bool resized = true;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2015-02-27 14:36:09 +01:00
|
|
|
trace_gd_switch(vc->label,
|
|
|
|
surface ? surface_width(surface) : 0,
|
|
|
|
surface ? surface_height(surface) : 0);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
if (vc->gfx.surface) {
|
|
|
|
cairo_surface_destroy(vc->gfx.surface);
|
2015-02-27 14:36:09 +01:00
|
|
|
vc->gfx.surface = NULL;
|
|
|
|
}
|
|
|
|
if (vc->gfx.convert) {
|
|
|
|
pixman_image_unref(vc->gfx.convert);
|
|
|
|
vc->gfx.convert = NULL;
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2015-02-27 14:36:09 +01:00
|
|
|
if (vc->gfx.ds && surface &&
|
2014-04-30 16:30:07 +02:00
|
|
|
surface_width(vc->gfx.ds) == surface_width(surface) &&
|
|
|
|
surface_height(vc->gfx.ds) == surface_height(surface)) {
|
2013-02-28 16:10:02 +01:00
|
|
|
resized = false;
|
|
|
|
}
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->gfx.ds = surface;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2015-02-27 14:36:09 +01:00
|
|
|
if (!surface) {
|
|
|
|
return;
|
2013-06-25 10:48:54 +02:00
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-06-25 10:48:54 +02:00
|
|
|
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.
|
|
|
|
*/
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->gfx.surface = cairo_image_surface_create_for_data
|
2013-06-25 10:48:54 +02:00
|
|
|
(surface_data(surface),
|
|
|
|
CAIRO_FORMAT_RGB24,
|
|
|
|
surface_width(surface),
|
|
|
|
surface_height(surface),
|
|
|
|
surface_stride(surface));
|
|
|
|
} else {
|
|
|
|
/* Must convert surface, use pixman to do it. */
|
2014-04-30 16:30:07 +02:00
|
|
|
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),
|
2013-06-25 10:48:54 +02:00
|
|
|
CAIRO_FORMAT_RGB24,
|
2014-04-30 16:30:07 +02:00
|
|
|
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,
|
2013-06-25 10:48:54 +02:00
|
|
|
0, 0, 0, 0, 0, 0,
|
2014-04-30 16:30:07 +02:00
|
|
|
pixman_image_get_width(vc->gfx.convert),
|
|
|
|
pixman_image_get_height(vc->gfx.convert));
|
2013-06-25 10:48:54 +02:00
|
|
|
}
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2013-02-28 16:10:02 +01:00
|
|
|
if (resized) {
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_windowsize(vc);
|
2013-02-28 16:10:02 +01:00
|
|
|
} else {
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_full_redraw(vc);
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2015-01-20 12:43:28 +01:00
|
|
|
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) **/
|
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
/** 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)
|
|
|
|
{
|
2014-04-08 11:26:45 +02:00
|
|
|
GtkDisplayState *s;
|
2014-05-06 11:48:28 +02:00
|
|
|
int i;
|
2014-04-08 11:26:45 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-05-06 11:48:28 +02:00
|
|
|
for (i = 0; i < s->nb_vcs; i++) {
|
|
|
|
VirtualConsole *vc = &s->vc[i];
|
|
|
|
gd_update_cursor(vc);
|
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** GTK Events **/
|
|
|
|
|
|
|
|
static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-04-30 16:30:07 +02:00
|
|
|
int i;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
if (!no_quit) {
|
2014-04-30 16:30:07 +02:00
|
|
|
for (i = 0; i < s->nb_vcs; i++) {
|
|
|
|
if (s->vc[i].type != GD_VC_GFX) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
unregister_displaychangelistener(&s->vc[i].gfx.dcl);
|
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
qmp_quit(NULL);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = opaque;
|
|
|
|
GtkDisplayState *s = vc->s;
|
2013-02-20 14:43:23 +01:00
|
|
|
int mx, my;
|
2013-02-20 14:43:20 +01:00
|
|
|
int ww, wh;
|
|
|
|
int fbw, fbh;
|
|
|
|
|
2015-01-20 12:43:28 +01:00
|
|
|
#if defined(CONFIG_OPENGL)
|
|
|
|
if (vc->gfx.gls) {
|
|
|
|
gd_egl_draw(vc);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-02-20 14:43:23 +01:00
|
|
|
if (!gtk_widget_get_realized(widget)) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
2015-02-27 14:36:09 +01:00
|
|
|
if (!vc->gfx.ds) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
fbw = surface_width(vc->gfx.ds);
|
|
|
|
fbh = surface_height(vc->gfx.ds);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
|
|
|
|
|
2013-02-20 14:43:23 +01:00
|
|
|
if (s->full_screen) {
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->gfx.scale_x = (double)ww / fbw;
|
|
|
|
vc->gfx.scale_y = (double)wh / fbh;
|
2013-02-20 14:43:23 +01:00
|
|
|
} else if (s->free_scale) {
|
|
|
|
double sx, sy;
|
|
|
|
|
|
|
|
sx = (double)ww / fbw;
|
|
|
|
sy = (double)wh / fbh;
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
fbw *= vc->gfx.scale_x;
|
|
|
|
fbh *= vc->gfx.scale_y;
|
2013-02-20 14:43:22 +01:00
|
|
|
|
2013-02-20 14:43:23 +01:00
|
|
|
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);
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
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);
|
2013-02-20 14:43:20 +01:00
|
|
|
cairo_paint(cr);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-02-25 16:20:42 +01:00
|
|
|
#if !GTK_CHECK_VERSION(3, 0, 0)
|
2013-02-20 14:43:20 +01:00
|
|
|
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;
|
|
|
|
}
|
2013-02-25 16:20:42 +01:00
|
|
|
#endif
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = opaque;
|
|
|
|
GtkDisplayState *s = vc->s;
|
2013-02-20 14:43:20 +01:00
|
|
|
int x, y;
|
2013-02-20 14:43:23 +01:00
|
|
|
int mx, my;
|
|
|
|
int fbh, fbw;
|
|
|
|
int ww, wh;
|
|
|
|
|
2015-02-27 14:36:09 +01:00
|
|
|
if (!vc->gfx.ds) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
|
|
|
|
fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
|
|
|
|
&ww, &wh);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
|
|
|
mx = my = 0;
|
|
|
|
if (ww > fbw) {
|
|
|
|
mx = (ww - fbw) / 2;
|
|
|
|
}
|
|
|
|
if (wh > fbh) {
|
|
|
|
my = (wh - fbh) / 2;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
x = (motion->x - mx) / vc->gfx.scale_x;
|
|
|
|
y = (motion->y - my) / vc->gfx.scale_y;
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2013-11-28 12:06:04 +01:00
|
|
|
if (qemu_input_is_absolute()) {
|
2014-04-04 12:41:22 +02:00
|
|
|
if (x < 0 || y < 0 ||
|
2014-04-30 16:30:07 +02:00
|
|
|
x >= surface_width(vc->gfx.ds) ||
|
|
|
|
y >= surface_height(vc->gfx.ds)) {
|
2014-04-04 12:41:22 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
2014-04-30 16:30:07 +02:00
|
|
|
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));
|
2013-11-28 12:06:04 +01:00
|
|
|
qemu_input_event_sync();
|
2014-05-06 11:45:30 +02:00
|
|
|
} else if (s->last_set && s->ptr_owner == vc) {
|
2014-04-30 16:30:07 +02:00
|
|
|
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);
|
2013-11-28 12:06:04 +01:00
|
|
|
qemu_input_event_sync();
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
s->last_x = x;
|
|
|
|
s->last_y = y;
|
2014-04-04 12:41:22 +02:00
|
|
|
s->last_set = TRUE;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-05-06 11:45:30 +02:00
|
|
|
if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
|
2014-04-30 16:30:07 +02:00
|
|
|
GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
|
2013-02-20 14:43:22 +01:00
|
|
|
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) {
|
2013-02-25 16:20:40 +01:00
|
|
|
#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);
|
2013-02-20 14:43:22 +01:00
|
|
|
gdk_display_warp_pointer(display, screen, x, y);
|
2013-02-25 16:20:40 +01:00
|
|
|
#endif
|
2014-04-04 12:41:22 +02:00
|
|
|
s->last_set = FALSE;
|
2013-02-20 14:43:22 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = opaque;
|
|
|
|
GtkDisplayState *s = vc->s;
|
2013-11-28 12:06:04 +01:00
|
|
|
InputButton btn;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-08 11:26:45 +02:00
|
|
|
/* implicitly grab the input at the first click in the relative mode */
|
|
|
|
if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
|
2014-05-06 11:45:30 +02:00
|
|
|
!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 {
|
2015-09-09 10:12:20 +02:00
|
|
|
gd_grab_pointer(vc, "relative-mode-click");
|
2014-05-06 11:45:30 +02:00
|
|
|
}
|
2014-04-08 11:26:45 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
if (button->button == 1) {
|
2013-11-28 12:06:04 +01:00
|
|
|
btn = INPUT_BUTTON_LEFT;
|
2013-02-20 14:43:20 +01:00
|
|
|
} else if (button->button == 2) {
|
2013-11-28 12:06:04 +01:00
|
|
|
btn = INPUT_BUTTON_MIDDLE;
|
2013-02-20 14:43:20 +01:00
|
|
|
} else if (button->button == 3) {
|
2013-11-28 12:06:04 +01:00
|
|
|
btn = INPUT_BUTTON_RIGHT;
|
2013-02-20 14:43:20 +01:00
|
|
|
} else {
|
2013-11-28 12:06:04 +01:00
|
|
|
return TRUE;
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
qemu_input_queue_btn(vc->gfx.dcl.con, btn,
|
|
|
|
button->type == GDK_BUTTON_PRESS);
|
2013-11-28 12:06:04 +01:00
|
|
|
qemu_input_event_sync();
|
2013-02-20 14:43:20 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-03-11 17:26:44 +01:00
|
|
|
static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = opaque;
|
2014-03-11 17:26:44 +01:00
|
|
|
InputButton btn;
|
|
|
|
|
|
|
|
if (scroll->direction == GDK_SCROLL_UP) {
|
|
|
|
btn = INPUT_BUTTON_WHEEL_UP;
|
|
|
|
} else if (scroll->direction == GDK_SCROLL_DOWN) {
|
|
|
|
btn = INPUT_BUTTON_WHEEL_DOWN;
|
|
|
|
} else {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
|
2014-03-11 17:26:44 +01:00
|
|
|
qemu_input_event_sync();
|
2014-04-30 16:30:07 +02:00
|
|
|
qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
|
2014-03-11 17:26:44 +01:00
|
|
|
qemu_input_event_sync();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-05-28 22:33:06 +02:00
|
|
|
static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
|
2013-02-20 14:43:20 +01:00
|
|
|
{
|
2014-06-03 09:18:23 +02:00
|
|
|
int qemu_keycode;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-05-28 22:33:06 +02:00
|
|
|
#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;
|
2013-12-07 16:25:17 +01:00
|
|
|
}
|
2014-05-28 22:33:06 +02:00
|
|
|
#endif
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
if (gdk_keycode < 9) {
|
|
|
|
qemu_keycode = 0;
|
|
|
|
} else if (gdk_keycode < 97) {
|
|
|
|
qemu_keycode = gdk_keycode - 8;
|
2014-05-28 22:33:06 +02:00
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
|
|
} else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
|
2014-05-01 04:29:42 +02:00
|
|
|
if (s->has_evdev) {
|
|
|
|
qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
|
|
|
|
} else {
|
|
|
|
qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
|
|
|
|
}
|
2014-05-28 22:33:06 +02:00
|
|
|
#endif
|
2013-02-20 14:43:20 +01:00
|
|
|
} else if (gdk_keycode == 208) { /* Hiragana_Katakana */
|
|
|
|
qemu_keycode = 0x70;
|
|
|
|
} else if (gdk_keycode == 211) { /* backslash */
|
|
|
|
qemu_keycode = 0x73;
|
|
|
|
} else {
|
|
|
|
qemu_keycode = 0;
|
|
|
|
}
|
|
|
|
|
2014-06-03 09:18:23 +02:00
|
|
|
return qemu_keycode;
|
|
|
|
}
|
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-06-03 09:18:23 +02:00
|
|
|
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;
|
|
|
|
|
2015-04-26 21:04:21 +02:00
|
|
|
if (s->ignore_keys) {
|
|
|
|
s->ignore_keys = (key->type == GDK_KEY_PRESS);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-09-16 16:04:40 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-05-28 22:33:06 +02:00
|
|
|
qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
|
|
|
|
gdk_keycode);
|
2014-06-03 09:18:23 +02:00
|
|
|
|
2014-05-06 10:27:54 +02:00
|
|
|
trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
|
2013-11-10 16:24:02 +01:00
|
|
|
(key->type == GDK_KEY_PRESS) ? "down" : "up");
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-03-24 19:10:02 +01:00
|
|
|
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
|
|
|
|
if (qemu_keycode == modifier_keycode[i]) {
|
|
|
|
s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
|
2013-11-28 11:40:27 +01:00
|
|
|
key->type == GDK_KEY_PRESS);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-04-04 12:41:21 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
/** Window Menu Actions **/
|
|
|
|
|
2013-02-22 20:53:33 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
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;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_by_menu(s);
|
2014-07-08 20:28:57 +02:00
|
|
|
GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
|
2014-04-30 12:56:53 +02:00
|
|
|
gint page;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_release_modifiers(s);
|
|
|
|
if (vc) {
|
2014-07-08 20:28:57 +02:00
|
|
|
page = gtk_notebook_page_num(nb, vc->tab_item);
|
|
|
|
gtk_notebook_set_current_page(nb, page);
|
2015-04-26 21:04:20 +02:00
|
|
|
gtk_widget_grab_focus(vc->focus);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
2015-04-26 21:04:21 +02:00
|
|
|
s->ignore_keys = false;
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-10-30 20:34:34 +01:00
|
|
|
static void gd_accel_switch_vc(void *opaque)
|
|
|
|
{
|
|
|
|
VirtualConsole *vc = opaque;
|
2015-04-26 21:04:21 +02:00
|
|
|
|
2014-10-30 20:34:34 +01:00
|
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
|
2015-04-26 21:04:21 +02:00
|
|
|
#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
|
2014-10-30 20:34:34 +01:00
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-05-22 08:19:48 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-05-22 08:19:48 +02:00
|
|
|
gd_update_windowsize(vc);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-05-05 16:23:33 +02:00
|
|
|
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);
|
2014-10-23 17:21:00 +02:00
|
|
|
gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
|
2014-05-05 16:23:33 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-05-06 12:51:00 +02:00
|
|
|
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 {
|
2015-09-09 10:12:20 +02:00
|
|
|
gd_grab_pointer(vc, "user-request-detached-tab");
|
2014-05-06 12:51:00 +02:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-05-05 16:23:33 +02:00
|
|
|
static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
if (vc->type == GD_VC_GFX &&
|
|
|
|
qemu_console_is_graphic(vc->gfx.dcl.con)) {
|
2014-05-06 10:20:53 +02:00
|
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
|
|
|
|
FALSE);
|
2014-05-05 16:23:33 +02:00
|
|
|
}
|
|
|
|
if (!vc->window) {
|
|
|
|
gtk_widget_set_sensitive(vc->menu_item, false);
|
|
|
|
vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
2014-10-23 17:21:00 +02:00
|
|
|
gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
|
2014-05-05 16:23:33 +02:00
|
|
|
|
|
|
|
g_signal_connect(vc->window, "delete-event",
|
|
|
|
G_CALLBACK(gd_tab_window_close), vc);
|
|
|
|
gtk_widget_show_all(vc->window);
|
2014-05-06 11:20:17 +02:00
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
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);
|
2014-05-06 12:51:00 +02:00
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
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);
|
|
|
|
}
|
2014-05-06 12:51:00 +02:00
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
gd_update_geometry_hints(vc);
|
2014-05-06 11:20:17 +02:00
|
|
|
gd_update_caption(s);
|
2014-05-05 16:23:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:23 +01:00
|
|
|
static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2013-02-22 20:33:34 +01:00
|
|
|
if (!s->full_screen) {
|
2013-02-20 14:43:23 +01:00
|
|
|
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
|
2014-10-30 20:34:35 +01:00
|
|
|
gtk_widget_hide(s->menu_bar);
|
2014-04-30 16:30:07 +02:00
|
|
|
if (vc->type == GD_VC_GFX) {
|
|
|
|
gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_window_fullscreen(GTK_WINDOW(s->window));
|
2013-02-20 14:43:23 +01:00
|
|
|
s->full_screen = TRUE;
|
|
|
|
} else {
|
|
|
|
gtk_window_unfullscreen(GTK_WINDOW(s->window));
|
|
|
|
gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
|
2014-10-30 20:34:35 +01:00
|
|
|
gtk_widget_show(s->menu_bar);
|
2013-02-20 14:43:23 +01:00
|
|
|
s->full_screen = FALSE;
|
2014-04-30 16:30:07 +02:00
|
|
|
if (vc->type == GD_VC_GFX) {
|
|
|
|
vc->gfx.scale_x = 1.0;
|
|
|
|
vc->gfx.scale_y = 1.0;
|
2014-05-16 12:26:12 +02:00
|
|
|
gd_update_windowsize(vc);
|
2014-04-30 16:30:07 +02:00
|
|
|
}
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_cursor(vc);
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
|
|
|
|
2014-10-30 20:34:33 +01:00
|
|
|
static void gd_accel_full_screen(void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
|
|
|
gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:23 +01:00
|
|
|
static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
|
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
|
|
|
|
FALSE);
|
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
vc->gfx.scale_x += VC_SCALE_STEP;
|
|
|
|
vc->gfx.scale_y += VC_SCALE_STEP;
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_windowsize(vc);
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
|
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
|
|
|
|
FALSE);
|
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
vc->gfx.scale_x -= VC_SCALE_STEP;
|
|
|
|
vc->gfx.scale_y -= VC_SCALE_STEP;
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
|
|
|
|
vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_windowsize(vc);
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->gfx.scale_x = 1.0;
|
|
|
|
vc->gfx.scale_y = 1.0;
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_windowsize(vc);
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
|
|
|
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
|
|
|
|
s->free_scale = TRUE;
|
|
|
|
} else {
|
|
|
|
s->free_scale = FALSE;
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->gfx.scale_x = 1.0;
|
|
|
|
vc->gfx.scale_y = 1.0;
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
gd_update_windowsize(vc);
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_full_redraw(vc);
|
2013-02-20 14:43:23 +01:00
|
|
|
}
|
|
|
|
|
2013-02-25 16:20:36 +01:00
|
|
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
2014-05-19 11:52:18 +02:00
|
|
|
static void gd_grab_devices(VirtualConsole *vc, bool grab,
|
|
|
|
GdkInputSource source, GdkEventMask mask,
|
|
|
|
GdkCursor *cursor)
|
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
|
2013-02-25 16:20:36 +01:00
|
|
|
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
|
2014-05-19 11:52:18 +02:00
|
|
|
GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
|
|
|
|
GList *tmp = devs;
|
|
|
|
|
|
|
|
for (tmp = devs; tmp; tmp = tmp->next) {
|
2013-02-25 16:20:36 +01:00
|
|
|
GdkDevice *dev = tmp->data;
|
2014-05-19 11:52:18 +02:00
|
|
|
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);
|
2013-02-25 16:20:36 +01:00
|
|
|
}
|
|
|
|
}
|
2014-05-19 11:52:18 +02:00
|
|
|
g_list_free(devs);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-09-09 10:12:20 +02:00
|
|
|
static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
|
2014-05-19 11:52:18 +02:00
|
|
|
{
|
2015-09-09 09:57:01 +02:00
|
|
|
if (vc->s->kbd_owner) {
|
|
|
|
if (vc->s->kbd_owner == vc) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
gd_ungrab_keyboard(vc->s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-19 11:52:18 +02:00
|
|
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
|
|
|
gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
|
|
|
|
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
|
|
|
|
NULL);
|
2013-02-25 16:20:36 +01:00
|
|
|
#else
|
2014-04-30 16:30:07 +02:00
|
|
|
gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
|
2013-02-20 14:43:22 +01:00
|
|
|
FALSE,
|
|
|
|
GDK_CURRENT_TIME);
|
2013-02-25 16:20:36 +01:00
|
|
|
#endif
|
2014-05-06 11:01:31 +02:00
|
|
|
vc->s->kbd_owner = vc;
|
2015-09-09 10:03:59 +02:00
|
|
|
gd_update_caption(vc->s);
|
2015-09-09 10:12:20 +02:00
|
|
|
trace_gd_grab(vc->label, "kbd", reason);
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
|
|
|
|
2014-05-06 11:01:31 +02:00
|
|
|
static void gd_ungrab_keyboard(GtkDisplayState *s)
|
2013-02-20 14:43:22 +01:00
|
|
|
{
|
2014-05-06 11:01:31 +02:00
|
|
|
VirtualConsole *vc = s->kbd_owner;
|
|
|
|
|
|
|
|
if (vc == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s->kbd_owner = NULL;
|
|
|
|
|
2013-02-25 16:20:36 +01:00
|
|
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
2014-05-19 11:52:18 +02:00
|
|
|
gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
|
2013-02-25 16:20:36 +01:00
|
|
|
#else
|
2013-02-20 14:43:22 +01:00
|
|
|
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
|
2013-02-25 16:20:36 +01:00
|
|
|
#endif
|
2015-09-09 10:03:59 +02:00
|
|
|
gd_update_caption(s);
|
2015-09-09 10:12:20 +02:00
|
|
|
trace_gd_ungrab(vc->label, "kbd");
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
|
|
|
|
2015-09-09 10:12:20 +02:00
|
|
|
static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
|
2013-02-25 16:20:37 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
|
2015-09-09 09:57:01 +02:00
|
|
|
|
|
|
|
if (vc->s->ptr_owner) {
|
|
|
|
if (vc->s->ptr_owner == vc) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
gd_ungrab_pointer(vc->s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-04 12:41:23 +02:00
|
|
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
2013-02-25 16:20:37 +01:00
|
|
|
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
|
2014-05-19 11:52:18 +02:00
|
|
|
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);
|
2014-04-04 12:41:23 +02:00
|
|
|
gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
|
2014-04-30 16:30:07 +02:00
|
|
|
NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
|
2013-02-25 16:20:37 +01:00
|
|
|
#else
|
2014-04-30 16:30:07 +02:00
|
|
|
gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
|
2013-02-25 16:20:37 +01:00
|
|
|
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 */
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->s->null_cursor,
|
2013-02-25 16:20:37 +01:00
|
|
|
GDK_CURRENT_TIME);
|
2014-04-04 12:41:23 +02:00
|
|
|
gdk_display_get_pointer(display, NULL,
|
2014-04-30 16:30:07 +02:00
|
|
|
&vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
|
2013-02-25 16:20:37 +01:00
|
|
|
#endif
|
2014-05-06 11:01:31 +02:00
|
|
|
vc->s->ptr_owner = vc;
|
2015-09-09 10:03:59 +02:00
|
|
|
gd_update_caption(vc->s);
|
2015-09-09 10:12:20 +02:00
|
|
|
trace_gd_grab(vc->label, "ptr", reason);
|
2013-02-25 16:20:37 +01:00
|
|
|
}
|
|
|
|
|
2014-05-06 11:01:31 +02:00
|
|
|
static void gd_ungrab_pointer(GtkDisplayState *s)
|
2013-02-25 16:20:37 +01:00
|
|
|
{
|
2014-05-06 11:01:31 +02:00
|
|
|
VirtualConsole *vc = s->ptr_owner;
|
|
|
|
|
|
|
|
if (vc == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s->ptr_owner = NULL;
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
|
2014-04-04 12:41:23 +02:00
|
|
|
#if GTK_CHECK_VERSION(3, 0, 0)
|
2013-02-25 16:20:37 +01:00
|
|
|
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
|
2014-05-19 11:52:18 +02:00
|
|
|
gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
|
2014-04-04 12:41:23 +02:00
|
|
|
gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_widget_get_screen(vc->gfx.drawing_area),
|
|
|
|
vc->s->grab_x_root, vc->s->grab_y_root);
|
2013-02-25 16:20:37 +01:00
|
|
|
#else
|
|
|
|
gdk_pointer_ungrab(GDK_CURRENT_TIME);
|
2014-04-04 12:41:23 +02:00
|
|
|
gdk_display_warp_pointer(display,
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_widget_get_screen(vc->gfx.drawing_area),
|
|
|
|
vc->s->grab_x_root, vc->s->grab_y_root);
|
2013-02-25 16:20:37 +01:00
|
|
|
#endif
|
2015-09-09 10:03:59 +02:00
|
|
|
gd_update_caption(s);
|
2015-09-09 10:12:20 +02:00
|
|
|
trace_gd_ungrab(vc->label, "ptr");
|
2013-02-25 16:20:37 +01:00
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:22 +01:00
|
|
|
static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = opaque;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = gd_vc_find_current(s);
|
2013-02-20 14:43:22 +01:00
|
|
|
|
|
|
|
if (gd_is_grab_active(s)) {
|
2015-09-09 10:12:20 +02:00
|
|
|
gd_grab_keyboard(vc, "user-request-main-window");
|
|
|
|
gd_grab_pointer(vc, "user-request-main-window");
|
2013-02-20 14:43:22 +01:00
|
|
|
} else {
|
2014-05-06 11:01:31 +02:00
|
|
|
gd_ungrab_keyboard(s);
|
|
|
|
gd_ungrab_pointer(s);
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_cursor(vc);
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
GtkDisplayState *s = data;
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc;
|
2013-02-20 14:43:22 +01:00
|
|
|
gboolean on_vga;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
if (!gtk_widget_get_realized(s->notebook)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-16 15:34:31 +02:00
|
|
|
#ifdef VTE_RESIZE_HACK
|
|
|
|
vc = gd_vc_find_current(s);
|
|
|
|
if (vc && vc->type == GD_VC_VTE) {
|
|
|
|
gtk_widget_hide(vc->vte.terminal);
|
|
|
|
}
|
|
|
|
#endif
|
2014-04-30 16:30:07 +02:00
|
|
|
vc = gd_vc_find_by_page(s, arg2);
|
|
|
|
if (!vc) {
|
|
|
|
return;
|
|
|
|
}
|
2014-05-16 15:34:31 +02:00
|
|
|
#ifdef VTE_RESIZE_HACK
|
|
|
|
if (vc->type == GD_VC_VTE) {
|
|
|
|
gtk_widget_show(vc->vte.terminal);
|
|
|
|
}
|
|
|
|
#endif
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
|
|
|
|
TRUE);
|
2014-05-22 11:08:54 +02:00
|
|
|
on_vga = (vc->type == GD_VC_GFX &&
|
|
|
|
qemu_console_is_graphic(vc->gfx.dcl.con));
|
2013-02-20 14:43:22 +01:00
|
|
|
if (!on_vga) {
|
|
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
|
|
|
|
FALSE);
|
2013-02-20 14:43:23 +01:00
|
|
|
} else if (s->full_screen) {
|
|
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
|
|
|
|
TRUE);
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
|
|
|
gtk_widget_set_sensitive(s->grab_item, on_vga);
|
|
|
|
|
2014-05-16 12:26:12 +02:00
|
|
|
gd_update_windowsize(vc);
|
2014-04-30 16:30:07 +02:00
|
|
|
gd_update_cursor(vc);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
|
|
|
|
gpointer opaque)
|
2013-02-20 14:43:22 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = opaque;
|
|
|
|
GtkDisplayState *s = vc->s;
|
2013-02-20 14:43:22 +01:00
|
|
|
|
2014-05-06 11:45:30 +02:00
|
|
|
if (gd_grab_on_hover(s)) {
|
2015-09-09 10:12:20 +02:00
|
|
|
gd_grab_keyboard(vc, "grab-on-hover");
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
|
|
|
|
gpointer opaque)
|
2013-02-20 14:43:22 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = opaque;
|
|
|
|
GtkDisplayState *s = vc->s;
|
2013-02-20 14:43:22 +01:00
|
|
|
|
2014-05-06 11:45:30 +02:00
|
|
|
if (gd_grab_on_hover(s)) {
|
2014-05-06 11:01:31 +02:00
|
|
|
gd_ungrab_keyboard(s);
|
2013-02-20 14:43:22 +01:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-03-24 19:10:02 +01:00
|
|
|
static gboolean gd_focus_out_event(GtkWidget *widget,
|
2014-04-30 16:30:07 +02:00
|
|
|
GdkEventCrossing *crossing, gpointer opaque)
|
2013-03-24 19:10:02 +01:00
|
|
|
{
|
2014-04-30 16:30:07 +02:00
|
|
|
VirtualConsole *vc = opaque;
|
|
|
|
GtkDisplayState *s = vc->s;
|
2013-03-24 19:10:02 +01:00
|
|
|
|
|
|
|
gtk_release_modifiers(s);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2015-03-13 12:47:00 +01:00
|
|
|
static gboolean gd_configure(GtkWidget *widget,
|
|
|
|
GdkEventConfigure *cfg, gpointer opaque)
|
|
|
|
{
|
|
|
|
VirtualConsole *vc = opaque;
|
|
|
|
QemuUIInfo info;
|
|
|
|
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.width = cfg->width;
|
|
|
|
info.height = cfg->height;
|
|
|
|
dpy_set_ui_info(vc->gfx.dcl.con, &info);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:21 +01:00
|
|
|
/** Virtual Console Callbacks **/
|
|
|
|
|
2014-04-30 16:39:18 +02:00
|
|
|
static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
|
2014-05-05 16:23:33 +02:00
|
|
|
int idx, GSList *group, GtkWidget *view_menu)
|
2014-04-30 16:39:18 +02:00
|
|
|
{
|
2014-05-05 16:23:33 +02:00
|
|
|
vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
|
2014-10-30 20:34:34 +01:00
|
|
|
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
|
2014-04-30 16:39:18 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-10-30 20:34:34 +01:00
|
|
|
group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
|
2014-04-30 16:39:18 +02:00
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
2014-04-29 15:39:37 +02:00
|
|
|
#if defined(CONFIG_VTE)
|
2014-04-29 21:24:32 +02:00
|
|
|
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)) {
|
2014-04-30 13:53:16 +02:00
|
|
|
gtk_widget_show(vc->vte.scrollbar);
|
2014-04-29 21:24:32 +02:00
|
|
|
} else {
|
2014-04-30 13:53:16 +02:00
|
|
|
gtk_widget_hide(vc->vte.scrollbar);
|
2014-04-29 21:24:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:21 +01:00
|
|
|
static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
|
|
|
|
{
|
|
|
|
VirtualConsole *vc = chr->opaque;
|
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
|
2014-04-24 19:35:54 +02:00
|
|
|
return len;
|
2013-02-20 14:43:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nb_vcs;
|
|
|
|
static CharDriverState *vcs[MAX_VCS];
|
|
|
|
|
2013-02-25 15:52:32 +01:00
|
|
|
static CharDriverState *gd_vc_handler(ChardevVC *unused)
|
2013-02-20 14:43:21 +01:00
|
|
|
{
|
|
|
|
CharDriverState *chr;
|
|
|
|
|
|
|
|
chr = g_malloc0(sizeof(*chr));
|
|
|
|
chr->chr_write = gd_vc_chr_write;
|
qemu-char: don't issue CHR_EVENT_OPEN in a BH
When CHR_EVENT_OPENED was initially added, it was CHR_EVENT_RESET,
and it was issued as a bottom-half:
86e94dea5b740dad65446c857f6959eae43e0ba6
Which we basically used to print out a greeting/prompt for the
monitor.
AFAICT the only reason this was ever done in a BH was because in
some cases we'd modify the chr_write handler for a new chardev
backend *after* the site where we issued the reset (see:
86e94d:qemu_chr_open_stdio())
At some point this event was renamed to CHR_EVENT_OPENED, and we've
maintained the use of this BH ever since.
However, due to 9f939df955a4152aad69a19a77e0898631bb2c18, we schedule
the BH via g_idle_add(), which is causing events to sometimes be
delivered after we've already begun processing data from backends,
leading to:
known bugs:
QMP:
session negotation resets with OPENED event, in some cases this
is causing new sessions to get sporadically reset
potential bugs:
hw/usb/redirect.c:
can_read handler checks for dev->parser != NULL, which may be
true if CLOSED BH has not been executed yet. In the past, OPENED
quiesced outstanding CLOSED events prior to us reading client
data. If it's delayed, our check may allow reads to occur even
though we haven't processed the OPENED event yet, and when we
do finally get the OPENED event, our state may get reset.
qtest.c:
can begin session before OPENED event is processed, leading to
a spurious reset of the system and irq_levels
gdbstub.c:
may start a gdb session prior to the machine being paused
To fix these, let's just drop the BH.
Since the initial reasoning for using it still applies to an extent,
work around that by deferring the delivery of CHR_EVENT_OPENED until
after the chardevs have been fully initialized, toward the end of
qmp_chardev_add() (or some cases, qemu_chr_new_from_opts()). This
defers delivery long enough that we can be assured a CharDriverState
is fully initialized before CHR_EVENT_OPENED is sent.
Also, rather than requiring each chardev to do an explicit open, do it
automatically, and allow the small few who don't desire such behavior to
suppress the OPENED-on-init behavior by setting a 'explicit_be_open'
flag.
We additionally add missing OPENED events for stdio backends on w32,
which were previously not being issued, causing us to not recieve the
banner and initial prompts for qmp/hmp.
Reported-by: Stefan Priebe <s.priebe@profihost.ag>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-id: 1370636393-21044-1-git-send-email-mdroth@linux.vnet.ibm.com
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-06-07 22:19:53 +02:00
|
|
|
/* defer OPENED events until our vc is fully initialized */
|
|
|
|
chr->explicit_be_open = true;
|
2013-02-20 14:43:21 +01:00
|
|
|
|
|
|
|
vcs[nb_vcs++] = chr;
|
|
|
|
|
|
|
|
return chr;
|
|
|
|
}
|
|
|
|
|
2014-04-24 19:35:54 +02:00
|
|
|
static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
|
|
|
|
gpointer user_data)
|
2013-02-20 14:43:21 +01:00
|
|
|
{
|
2014-04-24 19:35:54 +02:00
|
|
|
VirtualConsole *vc = user_data;
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size);
|
2013-02-20 14:43:21 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:39:18 +02:00
|
|
|
static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
|
|
|
|
CharDriverState *chr, int idx,
|
2014-04-30 16:30:07 +02:00
|
|
|
GSList *group, GtkWidget *view_menu)
|
2013-02-20 14:43:21 +01:00
|
|
|
{
|
|
|
|
char buffer[32];
|
2014-04-29 21:24:32 +02:00
|
|
|
GtkWidget *box;
|
|
|
|
GtkWidget *scrollbar;
|
|
|
|
GtkAdjustment *vadjustment;
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->s = s;
|
2014-04-30 16:39:18 +02:00
|
|
|
vc->vte.chr = chr;
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-30 16:39:18 +02:00
|
|
|
snprintf(buffer, sizeof(buffer), "vc%d", idx);
|
2014-05-05 16:23:33 +02:00
|
|
|
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);
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
vc->vte.terminal = vte_terminal_new();
|
|
|
|
g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
|
2014-05-16 12:26:12 +02:00
|
|
|
vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
|
|
|
|
VC_TERM_X_MIN, VC_TERM_Y_MIN);
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-29 21:24:32 +02:00
|
|
|
#if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
|
2014-04-30 13:53:16 +02:00
|
|
|
vadjustment = gtk_scrollable_get_vadjustment
|
|
|
|
(GTK_SCROLLABLE(vc->vte.terminal));
|
2014-04-29 21:24:32 +02:00
|
|
|
#else
|
2014-04-30 13:53:16 +02:00
|
|
|
vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
|
2014-04-29 21:24:32 +02:00
|
|
|
#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
|
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
|
2014-04-29 21:24:32 +02:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
|
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
vc->vte.chr->opaque = vc;
|
|
|
|
vc->vte.box = box;
|
|
|
|
vc->vte.scrollbar = scrollbar;
|
2014-04-29 21:24:32 +02:00
|
|
|
|
|
|
|
g_signal_connect(vadjustment, "changed",
|
|
|
|
G_CALLBACK(gd_vc_adjustment_changed), vc);
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->type = GD_VC_VTE;
|
2014-04-30 13:53:16 +02:00
|
|
|
vc->tab_item = box;
|
2015-04-26 21:04:20 +02:00
|
|
|
vc->focus = vc->vte.terminal;
|
2014-04-30 13:53:16 +02:00
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
|
2014-05-05 16:23:33 +02:00
|
|
|
gtk_label_new(vc->label));
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2014-04-30 13:53:16 +02:00
|
|
|
qemu_chr_be_generic_open(vc->vte.chr);
|
|
|
|
if (vc->vte.chr->init) {
|
|
|
|
vc->vte.chr->init(vc->vte.chr);
|
2013-02-20 14:43:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return group;
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:39:18 +02:00
|
|
|
static void gd_vcs_init(GtkDisplayState *s, GSList *group,
|
2014-04-29 15:39:37 +02:00
|
|
|
GtkWidget *view_menu)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < nb_vcs; i++) {
|
2014-04-30 16:39:18 +02:00
|
|
|
VirtualConsole *vc = &s->vc[s->nb_vcs];
|
|
|
|
group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
|
2014-04-29 15:39:37 +02:00
|
|
|
s->nb_vcs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_VTE */
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
/** Window Creation **/
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
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);
|
|
|
|
#else
|
|
|
|
g_signal_connect(vc->gfx.drawing_area, "expose-event",
|
|
|
|
G_CALLBACK(gd_expose_event), vc);
|
|
|
|
#endif
|
2014-05-22 11:08:54 +02:00
|
|
|
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);
|
2015-03-13 12:47:00 +01:00
|
|
|
g_signal_connect(vc->gfx.drawing_area, "configure-event",
|
|
|
|
G_CALLBACK(gd_configure), vc);
|
2014-05-22 11:08:54 +02:00
|
|
|
} else {
|
|
|
|
g_signal_connect(vc->gfx.drawing_area, "key-press-event",
|
|
|
|
G_CALLBACK(gd_text_key_down), vc);
|
|
|
|
}
|
2014-04-30 16:30:07 +02:00
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
static void gd_connect_signals(GtkDisplayState *s)
|
|
|
|
{
|
|
|
|
g_signal_connect(s->show_tabs_item, "activate",
|
|
|
|
G_CALLBACK(gd_menu_show_tabs), s);
|
2014-05-05 16:23:33 +02:00
|
|
|
g_signal_connect(s->untabify_item, "activate",
|
|
|
|
G_CALLBACK(gd_menu_untabify), s);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
g_signal_connect(s->window, "delete-event",
|
|
|
|
G_CALLBACK(gd_window_close), s);
|
|
|
|
|
2013-02-22 20:53:33 +01:00
|
|
|
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);
|
2013-02-20 14:43:20 +01:00
|
|
|
g_signal_connect(s->quit_item, "activate",
|
|
|
|
G_CALLBACK(gd_menu_quit), s);
|
2013-02-20 14:43:23 +01:00
|
|
|
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);
|
2013-02-20 14:43:22 +01:00
|
|
|
g_signal_connect(s->grab_item, "activate",
|
|
|
|
G_CALLBACK(gd_menu_grab_input), s);
|
2013-02-20 14:43:20 +01:00
|
|
|
g_signal_connect(s->notebook, "switch-page",
|
|
|
|
G_CALLBACK(gd_change_page), s);
|
|
|
|
}
|
|
|
|
|
2014-10-30 20:34:32 +01:00
|
|
|
static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
|
2013-02-20 14:43:20 +01:00
|
|
|
{
|
2013-04-26 15:48:46 +02:00
|
|
|
GtkWidget *machine_menu;
|
2013-02-20 14:43:20 +01:00
|
|
|
GtkWidget *separator;
|
|
|
|
|
2013-04-26 15:48:46 +02:00
|
|
|
machine_menu = gtk_menu_new();
|
2014-10-30 20:34:32 +01:00
|
|
|
gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
|
2013-02-22 20:53:33 +01:00
|
|
|
|
|
|
|
s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
|
2013-02-22 20:53:33 +01:00
|
|
|
|
|
|
|
separator = gtk_separator_menu_item_new();
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
|
2013-02-22 20:53:33 +01:00
|
|
|
|
2014-03-17 21:06:24 +01:00
|
|
|
s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
|
2013-02-22 20:53:33 +01:00
|
|
|
|
2014-03-17 21:06:24 +01:00
|
|
|
s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
|
2013-02-22 20:53:33 +01:00
|
|
|
|
|
|
|
separator = gtk_separator_menu_item_new();
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-03-17 21:06:26 +01:00
|
|
|
s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
|
2013-02-20 14:43:20 +01:00
|
|
|
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
|
2013-02-22 20:53:33 +01:00
|
|
|
"<QEMU>/Machine/Quit");
|
2014-03-17 21:06:26 +01:00
|
|
|
gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
|
2014-03-17 21:06:27 +01:00
|
|
|
GDK_KEY_q, HOTKEY_MODIFIERS);
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-04-26 15:48:46 +02:00
|
|
|
return machine_menu;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
|
2014-04-30 16:39:18 +02:00
|
|
|
QemuConsole *con, int idx,
|
2014-04-30 16:30:07 +02:00
|
|
|
GSList *group, GtkWidget *view_menu)
|
|
|
|
{
|
2015-02-17 10:41:08 +01:00
|
|
|
vc->label = qemu_console_get_label(con);
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->s = s;
|
|
|
|
vc->gfx.scale_x = 1.0;
|
|
|
|
vc->gfx.scale_y = 1.0;
|
|
|
|
|
|
|
|
vc->gfx.drawing_area = gtk_drawing_area_new();
|
|
|
|
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;
|
2015-04-26 21:04:20 +02:00
|
|
|
vc->focus = vc->gfx.drawing_area;
|
2014-04-30 16:30:07 +02:00
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
|
2014-05-05 16:23:33 +02:00
|
|
|
vc->tab_item, gtk_label_new(vc->label));
|
2014-04-30 16:30:07 +02:00
|
|
|
|
2015-01-20 12:43:28 +01:00
|
|
|
#if defined(CONFIG_OPENGL)
|
|
|
|
if (display_opengl) {
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
vc->gfx.dcl.ops = &dcl_ops;
|
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
vc->gfx.dcl.con = con;
|
|
|
|
register_displaychangelistener(&vc->gfx.dcl);
|
|
|
|
|
2014-05-22 11:08:54 +02:00
|
|
|
gd_connect_vc_gfx_signals(vc);
|
|
|
|
group = gd_vc_menu_init(s, vc, idx, group, view_menu);
|
|
|
|
|
2015-03-13 12:47:00 +01:00
|
|
|
if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
|
|
|
|
gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
|
2015-09-09 10:34:41 +02:00
|
|
|
s->free_scale = true;
|
2015-03-13 12:47:00 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
2014-10-30 20:34:32 +01:00
|
|
|
static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
|
2013-04-26 15:48:46 +02:00
|
|
|
{
|
|
|
|
GSList *group = NULL;
|
|
|
|
GtkWidget *view_menu;
|
|
|
|
GtkWidget *separator;
|
2014-04-30 16:39:18 +02:00
|
|
|
QemuConsole *con;
|
|
|
|
int vc;
|
2013-04-26 15:48:46 +02:00
|
|
|
|
|
|
|
view_menu = gtk_menu_new();
|
2014-10-30 20:34:32 +01:00
|
|
|
gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-03-17 21:06:26 +01:00
|
|
|
s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
|
2014-10-30 20:34:33 +01:00
|
|
|
|
|
|
|
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
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
|
|
|
separator = gtk_separator_menu_item_new();
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-03-17 21:06:26 +01:00
|
|
|
s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
|
2013-02-20 14:43:23 +01:00
|
|
|
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
|
|
|
|
"<QEMU>/View/Zoom In");
|
2013-07-22 09:04:32 +02:00
|
|
|
gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
|
|
|
|
HOTKEY_MODIFIERS);
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-03-17 21:06:26 +01:00
|
|
|
s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
|
2013-02-20 14:43:23 +01:00
|
|
|
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
|
|
|
|
"<QEMU>/View/Zoom Out");
|
2013-07-22 09:04:32 +02:00
|
|
|
gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
|
|
|
|
HOTKEY_MODIFIERS);
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2014-03-17 21:06:26 +01:00
|
|
|
s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
|
2013-02-20 14:43:23 +01:00
|
|
|
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
|
|
|
|
"<QEMU>/View/Zoom Fixed");
|
2013-07-22 09:04:32 +02:00
|
|
|
gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
|
|
|
|
HOTKEY_MODIFIERS);
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2013-02-20 14:43:24 +01:00
|
|
|
s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
|
|
|
separator = gtk_separator_menu_item_new();
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
|
2013-02-20 14:43:23 +01:00
|
|
|
|
2013-02-20 14:43:24 +01:00
|
|
|
s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
|
2013-02-20 14:43:22 +01:00
|
|
|
|
2013-02-20 14:43:24 +01:00
|
|
|
s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
|
2013-02-20 14:43:22 +01:00
|
|
|
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
|
|
|
|
"<QEMU>/View/Grab Input");
|
2013-07-22 09:04:32 +02:00
|
|
|
gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
|
|
|
|
HOTKEY_MODIFIERS);
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
|
2013-02-20 14:43:22 +01:00
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
separator = gtk_separator_menu_item_new();
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-30 16:30:07 +02:00
|
|
|
/* gfx */
|
2014-04-30 16:39:18 +02:00
|
|
|
for (vc = 0;; vc++) {
|
|
|
|
con = qemu_console_lookup_by_index(vc);
|
2014-05-22 11:08:54 +02:00
|
|
|
if (!con) {
|
2014-04-30 16:39:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
group = gd_vc_gfx_init(s, &s->vc[vc], con,
|
|
|
|
vc, group, view_menu);
|
|
|
|
s->nb_vcs++;
|
|
|
|
}
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-04-29 15:39:37 +02:00
|
|
|
#if defined(CONFIG_VTE)
|
2014-04-30 16:30:07 +02:00
|
|
|
/* vte */
|
2014-04-30 16:39:18 +02:00
|
|
|
gd_vcs_init(s, group, view_menu);
|
2014-04-29 15:39:37 +02:00
|
|
|
#endif
|
2013-02-20 14:43:21 +01:00
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
separator = gtk_separator_menu_item_new();
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-02-20 14:43:24 +01:00
|
|
|
s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
|
2013-04-26 15:48:46 +02:00
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2014-05-05 16:23:33 +02:00
|
|
|
s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
|
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
|
|
|
|
|
2013-04-26 15:48:46 +02:00
|
|
|
return view_menu;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gd_create_menus(GtkDisplayState *s)
|
|
|
|
{
|
2014-10-30 20:34:32 +01:00
|
|
|
s->accel_group = gtk_accel_group_new();
|
|
|
|
s->machine_menu = gd_create_menu_machine(s);
|
|
|
|
s->view_menu = gd_create_menu_view(s);
|
2013-04-26 15:48:46 +02:00
|
|
|
|
|
|
|
s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
|
2013-02-22 20:53:33 +01:00
|
|
|
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);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2013-04-26 15:48:46 +02:00
|
|
|
s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
|
2013-02-20 14:43:20 +01:00
|
|
|
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);
|
2013-04-26 15:48:46 +02:00
|
|
|
|
2014-10-30 20:34:32 +01:00
|
|
|
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);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
|
|
|
|
2014-05-01 04:29:42 +02:00
|
|
|
static void gd_set_keycode_type(GtkDisplayState *s)
|
|
|
|
{
|
2014-05-28 22:33:06 +02:00
|
|
|
#ifdef GDK_WINDOWING_X11
|
2014-05-01 04:29:42 +02:00
|
|
|
GdkDisplay *display = gtk_widget_get_display(s->window);
|
2014-05-28 22:33:06 +02:00
|
|
|
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);
|
|
|
|
}
|
2014-09-02 08:33:59 +02:00
|
|
|
|
|
|
|
if (desc) {
|
|
|
|
XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
|
|
|
|
}
|
|
|
|
if (keycodes) {
|
|
|
|
XFree(keycodes);
|
|
|
|
}
|
2014-05-01 04:29:42 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-06-05 13:07:58 +02:00
|
|
|
static gboolean gtkinit;
|
|
|
|
|
2014-03-12 08:33:50 +01:00
|
|
|
void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
|
2013-02-20 14:43:20 +01:00
|
|
|
{
|
|
|
|
GtkDisplayState *s = g_malloc0(sizeof(*s));
|
2013-03-30 15:21:40 +01:00
|
|
|
char *filename;
|
2015-05-20 15:35:31 +02:00
|
|
|
GdkDisplay *window_display;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2015-06-05 13:07:58 +02:00
|
|
|
if (!gtkinit) {
|
|
|
|
fprintf(stderr, "gtk initialization failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
2013-02-25 16:20:38 +01:00
|
|
|
#if GTK_CHECK_VERSION(3, 2, 0)
|
|
|
|
s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
#else
|
2013-02-20 14:43:20 +01:00
|
|
|
s->vbox = gtk_vbox_new(FALSE, 0);
|
2013-02-25 16:20:38 +01:00
|
|
|
#endif
|
2013-02-20 14:43:20 +01:00
|
|
|
s->notebook = gtk_notebook_new();
|
|
|
|
s->menu_bar = gtk_menu_bar_new();
|
|
|
|
|
2013-02-20 14:43:23 +01:00
|
|
|
s->free_scale = FALSE;
|
2013-02-20 14:43:20 +01:00
|
|
|
|
2015-09-10 17:19:32 +02:00
|
|
|
/* LC_MESSAGES only. See early_gtk_display_init() for details */
|
|
|
|
setlocale(LC_MESSAGES, "");
|
2013-02-20 14:43:24 +01:00
|
|
|
bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
|
|
|
|
textdomain("qemu");
|
|
|
|
|
2015-05-20 15:35:31 +02:00
|
|
|
window_display = gtk_widget_get_display(s->window);
|
|
|
|
s->null_cursor = gdk_cursor_new_for_display(window_display,
|
|
|
|
GDK_BLANK_CURSOR);
|
2013-02-20 14:43:20 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2013-06-07 20:24:17 +02:00
|
|
|
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
|
2013-03-30 15:21:40 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-20 14:43:20 +01:00
|
|
|
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);
|
|
|
|
|
2014-05-16 15:34:31 +02:00
|
|
|
#ifdef VTE_RESIZE_HACK
|
|
|
|
{
|
|
|
|
VirtualConsole *cur = gd_vc_find_current(s);
|
2014-11-21 02:59:09 +01:00
|
|
|
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);
|
|
|
|
}
|
2014-05-16 15:34:31 +02:00
|
|
|
}
|
2014-11-21 02:59:09 +01:00
|
|
|
gd_update_windowsize(cur);
|
2014-05-16 15:34:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-06-10 20:04:43 +02:00
|
|
|
if (full_screen) {
|
|
|
|
gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
|
|
|
|
}
|
2014-03-12 08:33:50 +01:00
|
|
|
if (grab_on_hover) {
|
|
|
|
gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
|
|
|
|
}
|
2013-06-10 20:04:43 +02:00
|
|
|
|
2014-05-01 04:29:42 +02:00
|
|
|
gd_set_keycode_type(s);
|
2013-02-20 14:43:20 +01:00
|
|
|
}
|
2014-04-29 15:39:37 +02:00
|
|
|
|
2015-01-20 12:43:28 +01:00
|
|
|
void early_gtk_display_init(int opengl)
|
2014-04-29 15:39:37 +02:00
|
|
|
{
|
2015-09-10 17:19:32 +02:00
|
|
|
/* 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();
|
2015-06-05 13:07:58 +02:00
|
|
|
gtkinit = gtk_init_check(NULL, NULL);
|
|
|
|
if (!gtkinit) {
|
|
|
|
/* don't exit yet, that'll break -help */
|
|
|
|
return;
|
|
|
|
}
|
2015-01-20 12:43:28 +01:00
|
|
|
|
|
|
|
switch (opengl) {
|
|
|
|
case -1: /* default */
|
|
|
|
case 0: /* off */
|
|
|
|
break;
|
|
|
|
case 1: /* on */
|
|
|
|
#if defined(CONFIG_OPENGL)
|
|
|
|
gtk_egl_init();
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-04-29 15:39:37 +02:00
|
|
|
#if defined(CONFIG_VTE)
|
|
|
|
register_vc_handler(gd_vc_handler);
|
|
|
|
#endif
|
|
|
|
}
|