From 2d8d923fd0abd3a9287c9d5c41a2a6f417ffd604 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Dec 21 2018 14:16:39 +0000 Subject: app: add "machine" UI Add a new "Machine" menu, which allows to Pause/Reset/Power Down a VM. The menu is only visible if "vm-ui" app property is set. When the application quits, it will also send a quit action to the VM. This is a similar behaviour/UI as qemu -display gtk. Signed-off-by: Marc-André Lureau Acked-by: Victor Toso --- diff --git a/src/resources/ui/virt-viewer.ui b/src/resources/ui/virt-viewer.ui index 9da1403..93471a4 100644 --- a/src/resources/ui/virt-viewer.ui +++ b/src/resources/ui/virt-viewer.ui @@ -114,6 +114,52 @@ + + False + _Machine + True + + + True + False + + + True + False + _Pause + True + + + + + + True + False + + + + + True + False + _Reset + True + + + + + + True + False + _Power down + True + + + + + + + + True False diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c index 415cf3a..3d55add 100644 --- a/src/virt-viewer-app.c +++ b/src/virt-viewer-app.c @@ -127,6 +127,7 @@ struct _VirtViewerAppPrivate { gboolean attach; gboolean quitting; gboolean kiosk; + gboolean vm_ui; VirtViewerSession *session; gboolean active; @@ -174,6 +175,7 @@ enum { PROP_KIOSK, PROP_QUIT_ON_DISCONNECT, PROP_UUID, + PROP_VM_UI, }; void @@ -286,6 +288,11 @@ virt_viewer_app_quit(VirtViewerApp *self) virt_viewer_app_save_config(self); + if (priv->vm_ui) { + virt_viewer_session_vm_action(VIRT_VIEWER_SESSION(priv->session), + VIRT_VIEWER_SESSION_VM_ACTION_QUIT); + } + if (priv->session) { virt_viewer_session_close(VIRT_VIEWER_SESSION(priv->session)); if (priv->connected) { @@ -1573,6 +1580,10 @@ virt_viewer_app_get_property (GObject *object, guint property_id, g_value_set_string(value, priv->uuid); break; + case PROP_VM_UI: + g_value_set_boolean(value, priv->vm_ui); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -1627,6 +1638,10 @@ virt_viewer_app_set_property (GObject *object, guint property_id, virt_viewer_app_set_uuid_string(self, g_value_get_string(value)); break; + case PROP_VM_UI: + priv->vm_ui = g_value_get_boolean(value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -2017,6 +2032,15 @@ virt_viewer_app_class_init (VirtViewerAppClass *klass) G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_VM_UI, + g_param_spec_boolean("vm-ui", + "VM UI", + "QEMU UI & behaviour", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } void diff --git a/src/virt-viewer-session.c b/src/virt-viewer-session.c index 7fd941e..596d1e3 100644 --- a/src/virt-viewer-session.c +++ b/src/virt-viewer-session.c @@ -686,6 +686,17 @@ gboolean virt_viewer_session_can_retry_auth(VirtViewerSession *self) return klass->can_retry_auth ? klass->can_retry_auth(self) : FALSE; } +void virt_viewer_session_vm_action(VirtViewerSession *self, gint action) +{ + VirtViewerSessionClass *klass; + + g_return_if_fail(VIRT_VIEWER_IS_SESSION(self)); + + klass = VIRT_VIEWER_SESSION_GET_CLASS(self); + + if (klass->vm_action) + klass->vm_action(self, action); +} /* * Local variables: * c-indent-level: 4 diff --git a/src/virt-viewer-session.h b/src/virt-viewer-session.h index 0aab8ca..ddb54c4 100644 --- a/src/virt-viewer-session.h +++ b/src/virt-viewer-session.h @@ -53,6 +53,14 @@ typedef struct _VirtViewerSessionPrivate VirtViewerSessionPrivate; typedef struct _VirtViewerSessionChannel VirtViewerSessionChannel; +enum { + VIRT_VIEWER_SESSION_VM_ACTION_QUIT, + VIRT_VIEWER_SESSION_VM_ACTION_RESET, + VIRT_VIEWER_SESSION_VM_ACTION_POWER_DOWN, + VIRT_VIEWER_SESSION_VM_ACTION_PAUSE, + VIRT_VIEWER_SESSION_VM_ACTION_CONTINUE, +}; + /* perhaps this become an interface, and be pushed in gtkvnc and spice? */ struct _VirtViewerSession { @@ -79,6 +87,7 @@ struct _VirtViewerSessionClass { void (*apply_monitor_geometry)(VirtViewerSession *session, GHashTable* monitors); gboolean (*can_share_folder)(VirtViewerSession *session); gboolean (*can_retry_auth)(VirtViewerSession *session); + void (*vm_action)(VirtViewerSession *session, gint action); }; GType virt_viewer_session_get_type(void); @@ -116,6 +125,8 @@ VirtViewerFile* virt_viewer_session_get_file(VirtViewerSession *self); gboolean virt_viewer_session_can_share_folder(VirtViewerSession *self); gboolean virt_viewer_session_can_retry_auth(VirtViewerSession *self); +void virt_viewer_session_vm_action(VirtViewerSession *self, gint action); + G_END_DECLS #endif /* _VIRT_VIEWER_SESSION_H */ diff --git a/src/virt-viewer-window.c b/src/virt-viewer-window.c index 1761194..849f94d 100644 --- a/src/virt-viewer-window.c +++ b/src/virt-viewer-window.c @@ -52,6 +52,9 @@ void virt_viewer_window_menu_view_zoom_out(GtkWidget *menu, VirtViewerWindow *self); void virt_viewer_window_menu_view_zoom_in(GtkWidget *menu, VirtViewerWindow *self); void virt_viewer_window_menu_view_zoom_reset(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_machine_reset(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_machine_powerdown(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_machine_pause(GtkWidget *menu, VirtViewerWindow *self); gboolean virt_viewer_window_delete(GtkWidget *src, void *dummy, VirtViewerWindow *self); void virt_viewer_window_menu_file_quit(GtkWidget *src, VirtViewerWindow *self); void virt_viewer_window_guest_details_response(GtkDialog *dialog, gint response_id, gpointer user_data); @@ -223,6 +226,18 @@ rebuild_combo_menu(GObject *gobject G_GNUC_UNUSED, } static void +vm_ui_changed(GObject *gobject G_GNUC_UNUSED, + GParamSpec *pspec G_GNUC_UNUSED, + gpointer user_data) +{ + VirtViewerWindow *self = user_data; + gboolean vm_ui; + + g_object_get(G_OBJECT(self->priv->app), "vm-ui", &vm_ui, NULL); + gtk_widget_set_visible(GTK_WIDGET(gtk_builder_get_object(self->priv->builder, "menu-machine")), vm_ui); +} + +static void virt_viewer_window_constructed(GObject *object) { VirtViewerWindowPrivate *priv = VIRT_VIEWER_WINDOW(object)->priv; @@ -232,6 +247,8 @@ virt_viewer_window_constructed(GObject *object) g_signal_connect(priv->app, "notify::enable-accel", G_CALLBACK(rebuild_combo_menu), object); + g_signal_connect(priv->app, "notify::vm-ui", + G_CALLBACK(vm_ui_changed), object); rebuild_combo_menu(NULL, NULL, object); } @@ -381,6 +398,36 @@ virt_viewer_window_get_real_zoom_level(VirtViewerWindow *self) } G_MODULE_EXPORT void +virt_viewer_window_menu_machine_reset(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + virt_viewer_session_vm_action(virt_viewer_app_get_session(self->priv->app), + VIRT_VIEWER_SESSION_VM_ACTION_RESET); +} + +G_MODULE_EXPORT void +virt_viewer_window_menu_machine_powerdown(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + virt_viewer_session_vm_action(virt_viewer_app_get_session(self->priv->app), + VIRT_VIEWER_SESSION_VM_ACTION_POWER_DOWN); +} + +G_MODULE_EXPORT void +virt_viewer_window_menu_machine_pause(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + gint action; + + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) + action = VIRT_VIEWER_SESSION_VM_ACTION_PAUSE; + else + action = VIRT_VIEWER_SESSION_VM_ACTION_CONTINUE; + + virt_viewer_session_vm_action(virt_viewer_app_get_session(self->priv->app), action); +} + +G_MODULE_EXPORT void virt_viewer_window_menu_view_zoom_out(GtkWidget *menu G_GNUC_UNUSED, VirtViewerWindow *self) { @@ -1368,6 +1415,9 @@ virt_viewer_window_set_menus_sensitive(VirtViewerWindow *self, gboolean sensitiv menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-view-zoom")); gtk_widget_set_sensitive(menu, sensitive); + menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-machine")); + gtk_widget_set_sensitive(menu, sensitive); + { gboolean can_send = sensitive && VIRT_VIEWER_DISPLAY_CAN_SEND_KEYS(self->priv->display);