diff --git a/configure.ac b/configure.ac index 2b979f4..5503d46 100644 --- a/configure.ac +++ b/configure.ac @@ -12,10 +12,10 @@ AC_CANONICAL_HOST m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])]) AM_SILENT_RULES([yes]) -GLIB2_REQUIRED=2.22.0 +GLIB2_REQUIRED="2.38.0" LIBXML2_REQUIRED="2.6.0" LIBVIRT_REQUIRED="0.10.0" -GTK_REQUIRED="3.0" +GTK_REQUIRED="3.10" GTK_VNC_REQUIRED="0.4.0" SPICE_GTK_REQUIRED="0.30" SPICE_PROTOCOL_REQUIRED="0.12.7" @@ -93,7 +93,7 @@ PKG_PROG_PKG_CONFIG GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` AC_SUBST(GLIB_MKENUMS) -PKG_CHECK_MODULES(GLIB2, glib-2.0 >= $GLIB2_REQUIRED gthread-2.0 gmodule-export-2.0) +PKG_CHECK_MODULES(GLIB2, glib-2.0 >= $GLIB2_REQUIRED gio-2.0 gthread-2.0 gmodule-export-2.0) PKG_CHECK_MODULES(LIBXML2, libxml-2.0 >= $LIBXML2_REQUIRED) AC_ARG_WITH([libvirt], diff --git a/src/remote-viewer-main.c b/src/remote-viewer-main.c index 81cf736..4ed74fb 100644 --- a/src/remote-viewer-main.c +++ b/src/remote-viewer-main.c @@ -22,184 +22,24 @@ #include #include +#include #include #include #include -#ifdef G_OS_WIN32 -#include -#include -#endif - -#ifdef HAVE_GTK_VNC -#include -#endif -#ifdef HAVE_SPICE_GTK -#include -#endif -#ifdef HAVE_OVIRT -#include -#endif #include "remote-viewer.h" -#include "virt-viewer-app.h" -#include "virt-viewer-session.h" - -static void -remote_viewer_version(void) -{ - g_print(_("remote-viewer version %s"), VERSION BUILDID); -#ifdef REMOTE_VIEWER_OS_ID - g_print(" (OS ID: %s)", REMOTE_VIEWER_OS_ID); -#endif - g_print("\n"); - exit(EXIT_SUCCESS); -} - -static void -recent_add(gchar *uri, const gchar *mime_type) -{ - GtkRecentManager *recent; - GtkRecentData meta = { - .app_name = (char*)"remote-viewer", - .app_exec = (char*)"remote-viewer %u", - .mime_type = (char*)mime_type, - }; - - if (uri == NULL) - return; - - recent = gtk_recent_manager_get_default(); - meta.display_name = uri; - if (!gtk_recent_manager_add_full(recent, uri, &meta)) - g_warning("Recent item couldn't be added"); -} - -static void connected(VirtViewerSession *session, - VirtViewerApp *self G_GNUC_UNUSED) -{ - gchar *uri = virt_viewer_session_get_uri(session); - const gchar *mime = virt_viewer_session_mime_type(session); - - recent_add(uri, mime); - g_free(uri); -} int main(int argc, char **argv) { - GOptionContext *context; - GError *error = NULL; int ret = 1; - gchar **args = NULL; - gchar *uri = NULL; - char *title = NULL; - RemoteViewer *viewer = NULL; -#ifdef HAVE_SPICE_GTK - gboolean controller = FALSE; -#endif - VirtViewerApp *app; - const GOptionEntry options [] = { - { "version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - remote_viewer_version, N_("Display version information"), NULL }, - { "title", 't', 0, G_OPTION_ARG_STRING, &title, - N_("Set window title"), NULL }, -#ifdef HAVE_SPICE_GTK - { "spice-controller", '\0', 0, G_OPTION_ARG_NONE, &controller, - N_("Open connection using Spice controller communication"), NULL }, -#endif - { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &args, - NULL, "URI|VV-FILE" }, - { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } - }; - GOptionGroup *app_options = NULL; + GApplication *app = NULL; virt_viewer_util_init(_("Remote Viewer")); + app = G_APPLICATION(remote_viewer_new()); - /* Setup command line options */ - context = g_option_context_new (NULL); - g_option_context_set_summary(context, _("Remote viewer client")); - app_options = virt_viewer_app_get_option_group(); - g_option_group_add_entries (app_options, options); - g_option_context_set_main_group (context, app_options); - g_option_context_add_group (context, gtk_get_option_group (TRUE)); -#ifdef HAVE_GTK_VNC - g_option_context_add_group (context, vnc_display_get_option_group ()); -#endif -#ifdef HAVE_SPICE_GTK - g_option_context_add_group (context, spice_get_option_group ()); -#endif -#ifdef HAVE_OVIRT - g_option_context_add_group (context, ovirt_get_option_group ()); -#endif - g_option_context_parse (context, &argc, &argv, &error); - if (error) { - char *base_name; - base_name = g_path_get_basename(argv[0]); - g_printerr(_("%s\nRun '%s --help' to see a full list of available command line options\n"), - error->message, base_name); - g_free(base_name); - goto cleanup; - } - - g_option_context_free(context); - -#ifdef HAVE_SPICE_GTK - if (controller) { - if (args) { - g_printerr(_("Error: extra arguments given while using Spice controller\n")); - goto cleanup; - } - } else -#endif - if (args) { - if (g_strv_length(args) > 1) { - g_printerr(_("Error: can't handle multiple URIs\n")); - goto cleanup; - } else if (g_strv_length(args) == 1) { - uri = g_strdup(args[0]); - } - } - -#ifdef HAVE_SPICE_GTK - if (controller) { - viewer = remote_viewer_new_with_controller(); - g_object_set(viewer, "guest-name", "defined by Spice controller", NULL); - } else { -#endif - viewer = remote_viewer_new(uri); - if (title) - g_object_set(viewer, "title", title, NULL); -#ifdef HAVE_SPICE_GTK - } -#endif - if (viewer == NULL) - goto cleanup; - - app = VIRT_VIEWER_APP(viewer); - - if (!virt_viewer_app_start(app, &error)) { - if (g_error_matches(error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_CANCELLED)) - ret = 0; - else if (error) { - virt_viewer_app_simple_message_dialog(app, error->message); - } - goto cleanup; - } - - g_signal_connect(virt_viewer_app_get_session(app), "session-connected", - G_CALLBACK(connected), app); - - gtk_main(); - - ret = 0; - - cleanup: - g_free(uri); - if (viewer) - g_object_unref(viewer); - g_strfreev(args); - g_clear_error(&error); - + ret = g_application_run(app, argc, argv); + g_object_unref(app); return ret; } diff --git a/src/remote-viewer.c b/src/remote-viewer.c index e712d61..5f8be6b 100644 --- a/src/remote-viewer.c +++ b/src/remote-viewer.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include @@ -84,8 +85,9 @@ static OvirtVm * choose_vm(GtkWindow *main_window, static gboolean remote_viewer_start(VirtViewerApp *self, GError **error); #ifdef HAVE_SPICE_GTK static gboolean remote_viewer_activate(VirtViewerApp *self, GError **error); -static void remote_viewer_window_added(VirtViewerApp *self, VirtViewerWindow *win); +static void remote_viewer_window_added(GtkApplication *app, GtkWindow *w); static void spice_foreign_menu_updated(RemoteViewer *self); +static void foreign_menu_title_changed(SpiceCtrlForeignMenu *menu, GParamSpec *pspec, RemoteViewer *self); #endif static void @@ -183,11 +185,104 @@ remote_viewer_deactivated(VirtViewerApp *app, gboolean connect_error) VIRT_VIEWER_APP_CLASS(remote_viewer_parent_class)->deactivated(app, connect_error); } +static gchar **opt_args = NULL; +static char *opt_title = NULL; +static gboolean opt_controller = FALSE; + +static void +remote_viewer_add_option_entries(VirtViewerApp *self, GOptionContext *context, GOptionGroup *group) +{ + static const GOptionEntry options[] = { + { "title", 't', 0, G_OPTION_ARG_STRING, &opt_title, + N_("Set window title"), NULL }, +#ifdef HAVE_SPICE_GTK + { "spice-controller", '\0', 0, G_OPTION_ARG_NONE, &opt_controller, + N_("Open connection using Spice controller communication"), NULL }, +#endif + { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &opt_args, + NULL, "URI|VV-FILE" }, + { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } + }; + + VIRT_VIEWER_APP_CLASS(remote_viewer_parent_class)->add_option_entries(self, context, group); + g_option_context_set_summary(context, _("Remote viewer client")); + g_option_group_add_entries(group, options); + +#ifdef HAVE_OVIRT + g_option_context_add_group (context, ovirt_get_option_group ()); +#endif +} + +static gboolean +remote_viewer_local_command_line (GApplication *gapp, + gchar ***args, + int *status) +{ + gboolean ret = FALSE; + VirtViewerApp *app = VIRT_VIEWER_APP(gapp); + RemoteViewer *self = REMOTE_VIEWER(app); + + ret = G_APPLICATION_CLASS(remote_viewer_parent_class)->local_command_line(gapp, args, status); + if (ret) + goto end; + + if (!opt_args) { + self->priv->open_recent_dialog = TRUE; + } else { + if (g_strv_length(opt_args) > 1) { + g_printerr(_("\nError: can't handle multiple URIs\n\n")); + ret = TRUE; + *status = 1; + goto end; + } + + g_object_set(app, "guri", opt_args[0], NULL); + } + +#ifdef HAVE_SPICE_GTK + if (opt_controller) { + if (opt_args) { + g_printerr(_("\nError: extra arguments given while using Spice controller\n\n")); + ret = TRUE; + *status = 1; + goto end; + } + + SpiceCtrlController *ctrl = spice_ctrl_controller_new(); + SpiceCtrlForeignMenu *menu = spice_ctrl_foreign_menu_new(); + + g_object_set(self, "guest-name", "defined by Spice controller", + "controller", ctrl, + "foreign-menu", menu, + NULL); + + g_signal_connect(menu, "notify::title", + G_CALLBACK(foreign_menu_title_changed), + self); + + g_object_unref(ctrl); + g_object_unref(menu); + } +#endif + + if (opt_title && !opt_controller) + g_object_set(app, "title", opt_title, NULL); + +end: + if (ret && *status) + g_printerr(_("Run '%s --help' to see a full list of available command line options\n"), g_get_prgname()); + + g_strfreev(opt_args); + return ret; +} + static void remote_viewer_class_init (RemoteViewerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkApplicationClass *gtk_app_class = GTK_APPLICATION_CLASS(klass); VirtViewerAppClass *app_class = VIRT_VIEWER_APP_CLASS (klass); + GApplicationClass *g_app_class = G_APPLICATION_CLASS(klass); g_type_class_add_private (klass, sizeof (RemoteViewerPrivate)); @@ -195,11 +290,15 @@ remote_viewer_class_init (RemoteViewerClass *klass) object_class->set_property = remote_viewer_set_property; object_class->dispose = remote_viewer_dispose; + g_app_class->local_command_line = remote_viewer_local_command_line; + app_class->start = remote_viewer_start; app_class->deactivated = remote_viewer_deactivated; + app_class->add_option_entries = remote_viewer_add_option_entries; #ifdef HAVE_SPICE_GTK app_class->activate = remote_viewer_activate; - app_class->window_added = remote_viewer_window_added; + + gtk_app_class->window_added = remote_viewer_window_added; g_object_class_install_property(object_class, PROP_CONTROLLER, @@ -208,7 +307,6 @@ remote_viewer_class_init (RemoteViewerClass *klass) "Spice controller", SPICE_CTRL_TYPE_CONTROLLER, G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(object_class, PROP_CTRL_FOREIGN_MENU, @@ -217,8 +315,9 @@ remote_viewer_class_init (RemoteViewerClass *klass) "Spice foreign menu", SPICE_CTRL_TYPE_FOREIGN_MENU, G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +#else + (void) gtk_app_class; #endif g_object_class_install_property(object_class, PROP_OPEN_RECENT_DIALOG, @@ -227,7 +326,6 @@ remote_viewer_class_init (RemoteViewerClass *klass) "Open recent dialog", FALSE, G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } @@ -238,11 +336,11 @@ remote_viewer_init(RemoteViewer *self) } RemoteViewer * -remote_viewer_new(const gchar *uri) +remote_viewer_new(void) { return g_object_new(REMOTE_VIEWER_TYPE, - "guri", uri, - "open-recent-dialog", uri == NULL, + "application-id", "org.virt-manager.remote-viewer", + "flags", G_APPLICATION_NON_UNIQUE, NULL); } @@ -265,26 +363,6 @@ foreign_menu_title_changed(SpiceCtrlForeignMenu *menu G_GNUC_UNUSED, spice_foreign_menu_updated(self); } -RemoteViewer * -remote_viewer_new_with_controller(void) -{ - RemoteViewer *self; - SpiceCtrlController *ctrl = spice_ctrl_controller_new(); - SpiceCtrlForeignMenu *menu = spice_ctrl_foreign_menu_new(); - - self = g_object_new(REMOTE_VIEWER_TYPE, - "controller", ctrl, - "foreign-menu", menu, - NULL); - g_signal_connect(menu, "notify::title", - G_CALLBACK(foreign_menu_title_changed), - self); - g_object_unref(ctrl); - g_object_unref(menu); - - return self; -} - static void spice_ctrl_do_connect(SpiceCtrlController *ctrl G_GNUC_UNUSED, VirtViewerApp *self) @@ -634,9 +712,11 @@ remote_viewer_activate(VirtViewerApp *app, GError **error) } static void -remote_viewer_window_added(VirtViewerApp *app, - VirtViewerWindow *win) +remote_viewer_window_added(GtkApplication *app, + GtkWindow *w) { + VirtViewerWindow *win = VIRT_VIEWER_WINDOW( + g_object_get_data(G_OBJECT(w), "virt-viewer-window")); spice_menu_update(REMOTE_VIEWER(app), win); spice_foreign_menu_update(REMOTE_VIEWER(app), win); } @@ -742,8 +822,10 @@ authenticate_cb(RestProxy *proxy, G_GNUC_UNUSED RestProxyAuth *auth, } static void -ovirt_foreign_menu_update(RemoteViewer *app, VirtViewerWindow *win) +ovirt_foreign_menu_update(GtkApplication *gtkapp, GtkWindow *gtkwin, G_GNUC_UNUSED gpointer data) { + RemoteViewer *app = REMOTE_VIEWER(gtkapp); + VirtViewerWindow *win = g_object_get_data(G_OBJECT(gtkwin), "virt-viewer-window"); GtkWidget *menu = g_object_get_data(G_OBJECT(win), "foreign-menu"); GtkWidget *submenu; GtkMenuShell *shell = GTK_MENU_SHELL(gtk_builder_get_object(virt_viewer_window_get_builder(win), "top-menu")); @@ -776,8 +858,9 @@ static void ovirt_foreign_menu_update_each(gpointer value, gpointer user_data) { - ovirt_foreign_menu_update(REMOTE_VIEWER(user_data), - VIRT_VIEWER_WINDOW(value)); + ovirt_foreign_menu_update(GTK_APPLICATION(user_data), + virt_viewer_window_get_window(VIRT_VIEWER_WINDOW(value)), + NULL); } static void @@ -1059,6 +1142,36 @@ choose_vm(GtkWindow *main_window, } #endif +static void +remote_viewer_recent_add(gchar *uri, const gchar *mime_type) +{ + GtkRecentManager *recent; + GtkRecentData meta = { + .app_name = (char*)"remote-viewer", + .app_exec = (char*)"remote-viewer %u", + .mime_type = (char*)mime_type, + }; + + if (uri == NULL) + return; + + recent = gtk_recent_manager_get_default(); + meta.display_name = uri; + if (!gtk_recent_manager_add_full(recent, uri, &meta)) + g_warning("Recent item couldn't be added"); +} + +static void +remote_viewer_session_connected(VirtViewerSession *session, + VirtViewerApp *self G_GNUC_UNUSED) +{ + gchar *uri = virt_viewer_session_get_uri(session); + const gchar *mime = virt_viewer_session_mime_type(session); + + remote_viewer_recent_add(uri, mime); + g_free(uri); +} + static gboolean remote_viewer_start(VirtViewerApp *app, GError **err) { @@ -1142,6 +1255,9 @@ retry_dialog: goto cleanup; } + g_signal_connect(virt_viewer_app_get_session(app), "session-connected", + G_CALLBACK(remote_viewer_session_connected), app); + virt_viewer_session_set_file(virt_viewer_app_get_session(app), vvfile); #ifdef HAVE_OVIRT if (vvfile != NULL) { diff --git a/src/remote-viewer.h b/src/remote-viewer.h index 6d445ca..53566fc 100644 --- a/src/remote-viewer.h +++ b/src/remote-viewer.h @@ -48,8 +48,7 @@ typedef struct { GType remote_viewer_get_type (void); -RemoteViewer* remote_viewer_new(const gchar *uri); -RemoteViewer* remote_viewer_new_with_controller(void); +RemoteViewer *remote_viewer_new (void); G_END_DECLS diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c index 7f7fed3..aa5e905 100644 --- a/src/virt-viewer-app.c +++ b/src/virt-viewer-app.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,7 @@ static void virt_viewer_app_update_pretty_address(VirtViewerApp *self); static void virt_viewer_app_set_fullscreen(VirtViewerApp *self, gboolean fullscreen); static void virt_viewer_app_update_menu_displays(VirtViewerApp *self); static void virt_viewer_update_smartcard_accels(VirtViewerApp *self); +static void virt_viewer_app_add_option_entries(VirtViewerApp *self, GOptionContext *context, GOptionGroup *group); struct _VirtViewerAppPrivate { @@ -154,7 +156,7 @@ struct _VirtViewerAppPrivate { }; -G_DEFINE_ABSTRACT_TYPE(VirtViewerApp, virt_viewer_app, G_TYPE_OBJECT) +G_DEFINE_ABSTRACT_TYPE(VirtViewerApp, virt_viewer_app, GTK_TYPE_APPLICATION) #define GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), VIRT_VIEWER_TYPE_APP, VirtViewerAppPrivate)) @@ -173,14 +175,6 @@ enum { PROP_UUID, }; -enum { - SIGNAL_WINDOW_ADDED, - SIGNAL_WINDOW_REMOVED, - SIGNAL_LAST, -}; - -static guint signals[SIGNAL_LAST]; - void virt_viewer_app_set_debug(gboolean debug) { @@ -297,7 +291,7 @@ virt_viewer_app_quit(VirtViewerApp *self) } } - gtk_main_quit(); + g_application_quit(G_APPLICATION(self)); } static gint @@ -948,12 +942,13 @@ virt_viewer_app_window_new(VirtViewerApp *self, gint nth) virt_viewer_app_update_menu_displays(self); virt_viewer_window_set_usb_options_sensitive(window, virt_viewer_app_has_usbredir(self)); - g_signal_emit(self, signals[SIGNAL_WINDOW_ADDED], 0, window); + w = virt_viewer_window_get_window(window); + g_object_set_data(G_OBJECT(w), "virt-viewer-window", window); + gtk_application_add_window(GTK_APPLICATION(self), w); if (self->priv->fullscreen) app_window_try_fullscreen(self, window, nth); - w = virt_viewer_window_get_window(window); g_signal_connect(w, "hide", G_CALLBACK(viewer_window_visible_cb), self); g_signal_connect(w, "show", G_CALLBACK(viewer_window_visible_cb), self); g_signal_connect(w, "focus-in-event", G_CALLBACK(viewer_window_focus_in_cb), self); @@ -1068,8 +1063,6 @@ static void virt_viewer_app_remove_nth_window(VirtViewerApp *self, g_debug("Remove window %d %p", nth, win); self->priv->windows = g_list_remove(self->priv->windows, win); - g_signal_emit(self, signals[SIGNAL_WINDOW_REMOVED], 0, win); - g_object_unref(win); } @@ -1423,7 +1416,7 @@ virt_viewer_app_default_deactivated(VirtViewerApp *self, gboolean connect_error) } if (self->priv->quit_on_disconnect) - gtk_main_quit(); + g_application_quit(G_APPLICATION(self)); } static void @@ -1501,7 +1494,7 @@ virt_viewer_app_disconnected(VirtViewerSession *session G_GNUC_UNUSED, const gch virt_viewer_app_hide_all_windows(self); if (priv->quitting) - gtk_main_quit(); + g_application_quit(G_APPLICATION(self)); if (connect_error) { GtkWidget *dialog = virt_viewer_app_make_message_dialog(self, @@ -1768,6 +1761,7 @@ gboolean virt_viewer_app_start(VirtViewerApp *self, GError **error) static int opt_zoom = NORMAL_ZOOM_LEVEL; static gchar *opt_hotkeys = NULL; +static gboolean opt_version = FALSE; static gboolean opt_verbose = FALSE; static gboolean opt_debug = FALSE; static gboolean opt_fullscreen = FALSE; @@ -1787,8 +1781,6 @@ virt_viewer_app_init(VirtViewerApp *self) self->priv = GET_PRIVATE(self); gtk_window_set_default_icon_name("virt-viewer"); - virt_viewer_app_set_debug(opt_debug); - virt_viewer_app_set_fullscreen(self, opt_fullscreen); self->priv->displays = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref); self->priv->config = g_key_file_new(); @@ -1804,14 +1796,7 @@ virt_viewer_app_init(VirtViewerApp *self) g_clear_error(&error); - if (opt_zoom < MIN_ZOOM_LEVEL || opt_zoom > MAX_ZOOM_LEVEL) { - g_printerr(_("Zoom level must be within %d-%d\n"), MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL); - opt_zoom = NORMAL_ZOOM_LEVEL; - } - self->priv->initial_display_map = virt_viewer_app_get_monitor_mapping_for_section(self, "fallback"); - self->priv->verbose = opt_verbose; - self->priv->quit_on_disconnect = opt_kiosk ? opt_kiosk_quit : TRUE; g_signal_connect(self, "notify::guest-name", G_CALLBACK(title_maybe_changed), NULL); g_signal_connect(self, "notify::title", G_CALLBACK(title_maybe_changed), NULL); g_signal_connect(self, "notify::guri", G_CALLBACK(title_maybe_changed), NULL); @@ -1870,9 +1855,18 @@ virt_viewer_update_smartcard_accels(VirtViewerApp *self) } static void -virt_viewer_app_constructed(GObject *object) +virt_viewer_app_on_application_startup(GApplication *app) { - VirtViewerApp *self = VIRT_VIEWER_APP(object); + VirtViewerApp *self = VIRT_VIEWER_APP(app); + GError *error = NULL; + + G_APPLICATION_CLASS(virt_viewer_app_parent_class)->startup(app); + + virt_viewer_app_set_debug(opt_debug); + virt_viewer_app_set_fullscreen(self, opt_fullscreen); + + self->priv->verbose = opt_verbose; + self->priv->quit_on_disconnect = opt_kiosk ? opt_kiosk_quit : TRUE; self->priv->main_window = virt_viewer_app_window_new(self, virt_viewer_app_get_first_monitor(self)); @@ -1880,6 +1874,12 @@ virt_viewer_app_constructed(GObject *object) virt_viewer_app_set_kiosk(self, opt_kiosk); virt_viewer_app_set_hotkeys(self, opt_hotkeys); + + if (opt_zoom < MIN_ZOOM_LEVEL || opt_zoom > MAX_ZOOM_LEVEL) { + g_printerr(_("Zoom level must be within %d-%d\n"), MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL); + opt_zoom = NORMAL_ZOOM_LEVEL; + } + virt_viewer_window_set_zoom_level(self->priv->main_window, opt_zoom); virt_viewer_set_insert_smartcard_accel(self, GDK_KEY_F8, GDK_SHIFT_MASK); @@ -1890,25 +1890,92 @@ virt_viewer_app_constructed(GObject *object) gtk_accel_map_add_entry("/view/zoom-out", GDK_KEY_minus, GDK_CONTROL_MASK); gtk_accel_map_add_entry("/view/zoom-in", GDK_KEY_plus, GDK_CONTROL_MASK); gtk_accel_map_add_entry("/send/secure-attention", GDK_KEY_End, GDK_CONTROL_MASK | GDK_MOD1_MASK); + + if (!virt_viewer_app_start(self, &error)) { + if (error && !g_error_matches(error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_CANCELLED)) + virt_viewer_app_simple_message_dialog(self, error->message); + + g_clear_error(&error); + g_application_quit(app); + return; + } + + g_application_hold(app); +} + +static gboolean +virt_viewer_app_local_command_line (GApplication *gapp, + gchar ***args, + int *status) +{ + VirtViewerApp *self = VIRT_VIEWER_APP(gapp); + gboolean ret = FALSE; + gint argc = g_strv_length(*args); + GError *error = NULL; + GOptionContext *context = g_option_context_new(NULL); + GOptionGroup *group = g_option_group_new("virt-viewer", NULL, NULL, gapp, NULL); + + *status = 0; + g_option_context_set_main_group(context, group); + VIRT_VIEWER_APP_GET_CLASS(self)->add_option_entries(self, context, group); + + g_option_context_add_group(context, gtk_get_option_group(TRUE)); + +#ifdef HAVE_GTK_VNC + g_option_context_add_group(context, vnc_display_get_option_group()); +#endif + +#ifdef HAVE_SPICE_GTK + g_option_context_add_group(context, spice_get_option_group()); +#endif + + if (!g_option_context_parse(context, &argc, args, &error)) { + if (error != NULL) { + g_printerr(_("%s\n"), error->message); + g_error_free(error); + } + + *status = 1; + ret = TRUE; + goto end; + } + + if (opt_version) { + g_print(_("%s version %s"), g_get_prgname(), VERSION BUILDID); +#ifdef REMOTE_VIEWER_OS_ID + g_print(" (OS ID: %s)", REMOTE_VIEWER_OS_ID); +#endif + g_print("\n"); + ret = TRUE; + } + +end: + g_option_context_free(context); + return ret; } static void virt_viewer_app_class_init (VirtViewerAppClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GApplicationClass *g_app_class = G_APPLICATION_CLASS(klass); g_type_class_add_private (klass, sizeof (VirtViewerAppPrivate)); - object_class->constructed = virt_viewer_app_constructed; object_class->get_property = virt_viewer_app_get_property; object_class->set_property = virt_viewer_app_set_property; object_class->dispose = virt_viewer_app_dispose; + g_app_class->local_command_line = virt_viewer_app_local_command_line; + g_app_class->startup = virt_viewer_app_on_application_startup; + g_app_class->command_line = NULL; /* inhibit GApplication default handler */ + klass->start = virt_viewer_app_default_start; klass->initial_connect = virt_viewer_app_default_initial_connect; klass->activate = virt_viewer_app_default_activate; klass->deactivated = virt_viewer_app_default_deactivated; klass->open_connection = virt_viewer_app_default_open_connection; + klass->add_option_entries = virt_viewer_app_add_option_entries; g_object_class_install_property(object_class, PROP_VERBOSE, @@ -2014,28 +2081,6 @@ virt_viewer_app_class_init (VirtViewerAppClass *klass) G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - - signals[SIGNAL_WINDOW_ADDED] = - g_signal_new("window-added", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(VirtViewerAppClass, window_added), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, - G_TYPE_OBJECT); - - signals[SIGNAL_WINDOW_REMOVED] = - g_signal_new("window-removed", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(VirtViewerAppClass, window_removed), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, - G_TYPE_OBJECT); } void @@ -2575,10 +2620,14 @@ option_kiosk_quit(G_GNUC_UNUSED const gchar *option_name, return FALSE; } -GOptionGroup* -virt_viewer_app_get_option_group(void) +static void +virt_viewer_app_add_option_entries(G_GNUC_UNUSED VirtViewerApp *self, + G_GNUC_UNUSED GOptionContext *context, + GOptionGroup *group) { static const GOptionEntry options [] = { + { "version", 'V', 0, G_OPTION_ARG_NONE, &opt_version, + N_("Display version information"), NULL }, { "zoom", 'z', 0, G_OPTION_ARG_INT, &opt_zoom, N_("Zoom level of window, in percentage"), "ZOOM" }, { "full-screen", 'f', 0, G_OPTION_ARG_NONE, &opt_fullscreen, @@ -2595,11 +2644,8 @@ virt_viewer_app_get_option_group(void) N_("Display debugging information"), NULL }, { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } }; - GOptionGroup *group; - group = g_option_group_new("virt-viewer", NULL, NULL, NULL, NULL); - g_option_group_add_entries(group, options); - return group; + g_option_group_add_entries(group, options); } gboolean virt_viewer_app_get_session_cancelled(VirtViewerApp *self) diff --git a/src/virt-viewer-app.h b/src/virt-viewer-app.h index bbbc9b4..7f6c401 100644 --- a/src/virt-viewer-app.h +++ b/src/virt-viewer-app.h @@ -24,6 +24,7 @@ #define VIRT_VIEWER_APP_H #include +#include #include "virt-viewer-util.h" #include "virt-viewer-window.h" @@ -39,16 +40,12 @@ G_BEGIN_DECLS typedef struct _VirtViewerAppPrivate VirtViewerAppPrivate; typedef struct { - GObject parent; + GtkApplication parent; VirtViewerAppPrivate *priv; } VirtViewerApp; typedef struct { - GObjectClass parent_class; - - /* signals */ - void (*window_added) (VirtViewerApp *self, VirtViewerWindow *window); - void (*window_removed) (VirtViewerApp *self, VirtViewerWindow *window); + GtkApplicationClass parent_class; /*< private >*/ gboolean (*start) (VirtViewerApp *self, GError **error); @@ -56,6 +53,7 @@ typedef struct { gboolean (*activate) (VirtViewerApp *self, GError **error); void (*deactivated) (VirtViewerApp *self, gboolean connect_error); gboolean (*open_connection)(VirtViewerApp *self, int *fd); + void (*add_option_entries)(VirtViewerApp *self, GOptionContext *context, GOptionGroup *group); } VirtViewerAppClass; GType virt_viewer_app_get_type (void); @@ -95,7 +93,6 @@ GList* virt_viewer_app_get_windows(VirtViewerApp *self); gboolean virt_viewer_app_get_enable_accel(VirtViewerApp *self); VirtViewerSession* virt_viewer_app_get_session(VirtViewerApp *self); gboolean virt_viewer_app_get_fullscreen(VirtViewerApp *app); -GOptionGroup* virt_viewer_app_get_option_group(void); void virt_viewer_app_clear_hotkeys(VirtViewerApp *app); GList* virt_viewer_app_get_initial_displays(VirtViewerApp* self); gint virt_viewer_app_get_initial_monitor_for_display(VirtViewerApp* self, gint display); diff --git a/src/virt-viewer-main.c b/src/virt-viewer-main.c index 505b472..56521f2 100644 --- a/src/virt-viewer-main.c +++ b/src/virt-viewer-main.c @@ -22,121 +22,23 @@ #include #include +#include #include #include #include -#ifdef HAVE_GTK_VNC -#include -#endif -#ifdef HAVE_SPICE_GTK -#include -#endif + #include "virt-viewer.h" -static void virt_viewer_version(void) -{ - g_print(_("%s version %s\n"), PACKAGE, VERSION BUILDID); - - exit(EXIT_SUCCESS); -} - - int main(int argc, char **argv) { - GOptionContext *context; - GError *error = NULL; int ret = 1; - char *uri = NULL; - gchar **args = NULL; - gboolean direct = FALSE; - gboolean attach = FALSE; - gboolean waitvm = FALSE; - gboolean reconnect = FALSE; - VirtViewer *viewer = NULL; - char *base_name; - char *help_msg = NULL; - const GOptionEntry options [] = { - { "version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, - virt_viewer_version, N_("Display version information"), NULL }, - { "direct", 'd', 0, G_OPTION_ARG_NONE, &direct, - N_("Direct connection with no automatic tunnels"), NULL }, - { "attach", 'a', 0, G_OPTION_ARG_NONE, &attach, - N_("Attach to the local display using libvirt"), NULL }, - { "connect", 'c', 0, G_OPTION_ARG_STRING, &uri, - N_("Connect to hypervisor"), "URI"}, - { "wait", 'w', 0, G_OPTION_ARG_NONE, &waitvm, - N_("Wait for domain to start"), NULL }, - { "reconnect", 'r', 0, G_OPTION_ARG_NONE, &reconnect, - N_("Reconnect to domain upon restart"), NULL }, - { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &args, - NULL, "-- DOMAIN-NAME|ID|UUID" }, - { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } - }; - GOptionGroup* app_options = NULL; + GApplication *app= NULL; virt_viewer_util_init(_("Virt Viewer")); + app = G_APPLICATION(virt_viewer_new()); - base_name = g_path_get_basename(argv[0]); - help_msg = g_strdup_printf(_("Run '%s --help' to see a full list of available command line options"), - base_name); - g_free(base_name); - - /* Setup command line options */ - context = g_option_context_new (NULL); - g_option_context_set_summary (context, _("Virtual machine graphical console")); - app_options = virt_viewer_app_get_option_group(); - g_option_group_add_entries (app_options, options); - g_option_context_set_main_group (context, app_options); - g_option_context_add_group (context, gtk_get_option_group (TRUE)); -#ifdef HAVE_GTK_VNC - g_option_context_add_group (context, vnc_display_get_option_group ()); -#endif -#ifdef HAVE_SPICE_GTK - g_option_context_add_group (context, spice_get_option_group ()); -#endif - g_option_context_parse (context, &argc, &argv, &error); - if (error) { - g_printerr("%s\n%s\n", - error->message, help_msg); - goto cleanup; - } - - g_option_context_free(context); - - if (args && (g_strv_length(args) != 1)) { - g_printerr(_("\nUsage: %s [OPTIONS] [DOMAIN-NAME|ID|UUID]\n\n%s\n\n"), argv[0], help_msg); - goto cleanup; - } - - if (args == NULL && waitvm) { - g_printerr(_("\nNo DOMAIN-NAME|ID|UUID was specified for '--wait'\n\n")); - goto cleanup; - } - - viewer = virt_viewer_new(uri, (args) ? args[0] : NULL, direct, attach, waitvm, reconnect); - if (viewer == NULL) - goto cleanup; - - if (!virt_viewer_app_start(VIRT_VIEWER_APP(viewer), &error)) { - if (g_error_matches(error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_CANCELLED)) - ret = 0; - else if (error) { - virt_viewer_app_simple_message_dialog(VIRT_VIEWER_APP(viewer), error->message); - } - goto cleanup; - } - - gtk_main(); - - ret = 0; - - cleanup: - if (viewer) - g_object_unref(viewer); - g_free(uri); - g_strfreev(args); - g_free(help_msg); - g_clear_error(&error); + ret = g_application_run(app, argc, argv); + g_object_unref(app); return ret; } diff --git a/src/virt-viewer-util.h b/src/virt-viewer-util.h index f1cb08b..0a7dd97 100644 --- a/src/virt-viewer-util.h +++ b/src/virt-viewer-util.h @@ -30,7 +30,7 @@ extern gboolean doDebug; enum { VIRT_VIEWER_ERROR_FAILED, - VIRT_VIEWER_ERROR_CANCELLED, + VIRT_VIEWER_ERROR_CANCELLED }; #define VIRT_VIEWER_ERROR virt_viewer_error_quark () diff --git a/src/virt-viewer.c b/src/virt-viewer.c index 10f624d..647c76a 100644 --- a/src/virt-viewer.c +++ b/src/virt-viewer.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -73,11 +74,93 @@ static gboolean virt_viewer_start(VirtViewerApp *self, GError **error); static void virt_viewer_dispose (GObject *object); static int virt_viewer_connect(VirtViewerApp *app, GError **error); +static gchar **opt_args = NULL; +static gchar *opt_uri = NULL; +static gboolean opt_direct = FALSE; +static gboolean opt_attach = FALSE; +static gboolean opt_waitvm = FALSE; +static gboolean opt_reconnect = FALSE; + +static void +virt_viewer_add_option_entries(VirtViewerApp *self, GOptionContext *context, GOptionGroup *group) +{ + static const GOptionEntry options[] = { + { "direct", 'd', 0, G_OPTION_ARG_NONE, &opt_direct, + N_("Direct connection with no automatic tunnels"), NULL }, + { "attach", 'a', 0, G_OPTION_ARG_NONE, &opt_attach, + N_("Attach to the local display using libvirt"), NULL }, + { "connect", 'c', 0, G_OPTION_ARG_STRING, &opt_uri, + N_("Connect to hypervisor"), "URI"}, + { "wait", 'w', 0, G_OPTION_ARG_NONE, &opt_waitvm, + N_("Wait for domain to start"), NULL }, + { "reconnect", 'r', 0, G_OPTION_ARG_NONE, &opt_reconnect, + N_("Reconnect to domain upon restart"), NULL }, + { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &opt_args, + NULL, "-- DOMAIN-NAME|ID|UUID" }, + { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } + }; + + VIRT_VIEWER_APP_CLASS(virt_viewer_parent_class)->add_option_entries(self, context, group); + g_option_context_set_summary(context, _("Virtual machine graphical console")); + g_option_group_add_entries(group, options); +} + +static gboolean +virt_viewer_local_command_line (GApplication *gapp, + gchar ***args, + int *status) +{ + gboolean ret = FALSE; + VirtViewer *self = VIRT_VIEWER(gapp); + VirtViewerApp *app = VIRT_VIEWER_APP(gapp); + + ret = G_APPLICATION_CLASS(virt_viewer_parent_class)->local_command_line(gapp, args, status); + if (ret) + goto end; + + if (opt_args) { + if (g_strv_length(opt_args) != 1) { + g_printerr(_("\nUsage: %s [OPTIONS] [DOMAIN-NAME|ID|UUID]\n\n"), PACKAGE); + ret = TRUE; + *status = 1; + goto end; + } + + self->priv->domkey = g_strdup(opt_args[0]); + } + + + if (opt_waitvm) { + if (!self->priv->domkey) { + g_printerr(_("\nNo DOMAIN-NAME|ID|UUID was specified for '--wait'\n\n")); + ret = TRUE; + *status = 1; + goto end; + } + + self->priv->waitvm = TRUE; + } + + virt_viewer_app_set_direct(app, opt_direct); + virt_viewer_app_set_attach(app, opt_attach); + self->priv->reconnect = opt_reconnect; + self->priv->uri = g_strdup(opt_uri); + +end: + if (ret && *status) + g_printerr(_("Run '%s --help' to see a full list of available command line options\n"), g_get_prgname()); + + g_strfreev(opt_args); + g_free(opt_uri); + return ret; +} + static void virt_viewer_class_init (VirtViewerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); VirtViewerAppClass *app_class = VIRT_VIEWER_APP_CLASS (klass); + GApplicationClass *g_app_class = G_APPLICATION_CLASS(klass); g_type_class_add_private (klass, sizeof (VirtViewerPrivate)); @@ -87,6 +170,9 @@ virt_viewer_class_init (VirtViewerClass *klass) app_class->deactivated = virt_viewer_deactivated; app_class->open_connection = virt_viewer_open_connection; app_class->start = virt_viewer_start; + app_class->add_option_entries = virt_viewer_add_option_entries; + + g_app_class->local_command_line = virt_viewer_local_command_line; } static void @@ -106,7 +192,7 @@ virt_viewer_connect_timer(void *opaque) if (!virt_viewer_app_is_active(app) && !virt_viewer_app_initial_connect(app, NULL)) - gtk_main_quit(); + g_application_quit(G_APPLICATION(app)); if (virt_viewer_app_is_active(app)) { self->priv->reconnect_poll = 0; @@ -976,33 +1062,12 @@ virt_viewer_start(VirtViewerApp *app, GError **error) } VirtViewer * -virt_viewer_new(const char *uri, - const char *name, - gboolean direct, - gboolean attach, - gboolean waitvm, - gboolean reconnect) +virt_viewer_new(void) { - VirtViewer *self; - VirtViewerApp *app; - VirtViewerPrivate *priv; - - self = g_object_new(VIRT_VIEWER_TYPE, - "guest-name", name, + return g_object_new(VIRT_VIEWER_TYPE, + "application-id", "org.virt-manager.virt-viewer", + "flags", G_APPLICATION_NON_UNIQUE, NULL); - app = VIRT_VIEWER_APP(self); - priv = self->priv; - - virt_viewer_app_set_direct(app, direct); - virt_viewer_app_set_attach(app, attach); - - /* should probably be properties instead */ - priv->uri = g_strdup(uri); - priv->domkey = g_strdup(name); - priv->waitvm = waitvm; - priv->reconnect = reconnect; - - return self; } /* diff --git a/src/virt-viewer.h b/src/virt-viewer.h index c962615..5aeacb0 100644 --- a/src/virt-viewer.h +++ b/src/virt-viewer.h @@ -48,13 +48,7 @@ typedef struct { GType virt_viewer_get_type (void); -VirtViewer * -virt_viewer_new(const char *uri, - const char *name, - gboolean direct, - gboolean attach, - gboolean waitvm, - gboolean reconnect); +VirtViewer *virt_viewer_new (void); G_END_DECLS diff --git a/src/virt-viewer.xml b/src/virt-viewer.xml index 07948bd..03f2f84 100644 --- a/src/virt-viewer.xml +++ b/src/virt-viewer.xml @@ -2,7 +2,7 @@ - + False 1024 768