[virt-tools-list] [PATCH 3/7] Port to GtkApplication API's

Jonathon Jongsma jjongsma at redhat.com
Mon Dec 14 22:46:24 UTC 2015


On Fri, 2015-12-11 at 14:40 -0200, Eduardo Lima (Etrunko) wrote:
> Most of this patch consists in code being shuffled around to fit the
> expected flow while using the new APIs. I tried my best to make this
> patch the less intrusive as possible. Main changes are:
> 
> - Update glib requirements to 2.40.0 and adds gio as build dependency.
> 
> - VirtViewerApp is now a subclass of GtkApplication.
>   Some mainloop calls were replaced:
>    * gtk_main() -> g_application_run()
>    * gtk_quit() -> g_application_quit()
> 
> - Unified command line option handling.
>   The logic has moved from the main functions and split in three, the
>   common options, and specific ones for each application. With this, the
>   main functions were highly simplified, and now basically responsible
>   for instantiating the App object and running the main loop.
> 
> - All Window objects must be associated with the Application.
>   With this, there is no need to emit our own 'window-added'/'window-
>   removed' signals, as those will be emited by GtkApplication whenever
>   gtk_application_add_window() and gtk_application_remove_window() are
>   called. Also, 'window-removed' was not being used anywhere.
> 
> Signed-off-by: Eduardo Lima (Etrunko) <etrunko at redhat.com>
> ---
>  configure.ac             |   4 +-
>  src/remote-viewer-main.c | 122 +-------------------------
>  src/remote-viewer.c      | 152 +++++++++++++++++++++++++-------
>  src/remote-viewer.h      |   3 +-
>  src/virt-viewer-app.c    | 223 +++++++++++++++++++++++++++-------------------
> -
>  src/virt-viewer-app.h    |  10 +--
>  src/virt-viewer-main.c   | 102 +---------------------
>  src/virt-viewer.c        | 117 ++++++++++++++++++++-----
>  src/virt-viewer.h        |   8 +-
>  src/virt-viewer.xml      |   2 +-
>  10 files changed, 355 insertions(+), 388 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index 250a7fe..bd9524e 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -12,7 +12,7 @@ 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.40.0"
>  LIBXML2_REQUIRED="2.6.0"
>  LIBVIRT_REQUIRED="0.10.0"
>  GTK3_REQUIRED="3.0"
> @@ -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 6ac2523..0329d16 100644
> --- a/src/remote-viewer-main.c
> +++ b/src/remote-viewer-main.c
> @@ -30,32 +30,11 @@
>  #include <io.h>
>  #endif
>  
> -#ifdef HAVE_GTK_VNC
> -#include <vncdisplay.h>
> -#endif
> -#ifdef HAVE_SPICE_GTK
> -#include <spice-client.h>
> -#endif
> -#ifdef HAVE_OVIRT
> -#include <govirt/ovirt-options.h>
> -#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;
> @@ -87,118 +66,23 @@ static void connected(VirtViewerSession *session,
>  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;
>  
>      virt_viewer_util_init(_("Remote Viewer"));
>  
> -    /* 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
> +    viewer = remote_viewer_new();
>      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;
> +    ret = g_application_run(G_APPLICATION(viewer), argc, argv);
>  
>   cleanup:
> -    g_free(uri);
> -    if (viewer)
> -        g_object_unref(viewer);
> -    g_strfreev(args);
> -    g_clear_error(&error);
> +    g_object_unref(viewer);
>  
>      return ret;
>  }
> diff --git a/src/remote-viewer.c b/src/remote-viewer.c
> index e712d61..036ee06 100644
> --- a/src/remote-viewer.c
> +++ b/src/remote-viewer.c
> @@ -82,12 +82,44 @@ static OvirtVm * choose_vm(GtkWindow *main_window,
>  #endif
>  
>  static gboolean remote_viewer_start(VirtViewerApp *self, GError **error);
> +
> +/* VirtViewerApp overrides */
> +static gint remote_viewer_handle_local_options(GApplication *app,
> GVariantDict *options);
> +
>  #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 *win);
>  static void spice_foreign_menu_updated(RemoteViewer *self);
>  #endif
>  
> +static const char OPT_VERSION[] = "version";
> +static const char OPT_TITLE[] = "title";
> +static const char OPT_CONTROLLER[] = "spice-controller";
> +
> +static void
> +remote_viewer_constructed(GObject *object)
> +{
> +    GApplication *app = G_APPLICATION(object);
> +    const GOptionEntry options[] = {
> +        { OPT_TITLE, 't', 0, G_OPTION_ARG_STRING, NULL,
> +          N_("Set window title"), NULL },
> +#ifdef HAVE_SPICE_GTK
> +        { OPT_CONTROLLER, '\0', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Open connection using Spice controller communication"), NULL },
> +#endif
> +        { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, NULL,
> +          NULL, "URI|VV-FILE" },
> +        { NULL },
> +    };
> +
> +   G_OBJECT_CLASS(remote_viewer_parent_class)->constructed(object);
> +   g_application_add_main_option_entries(app, options);
> +
> +#ifdef HAVE_OVIRT
> +    g_application_add_option_group(app, ovirt_get_option_group ());
> +#endif
> +}
> +
>  static void
>  remote_viewer_dispose (GObject *object)
>  {
> @@ -188,18 +220,24 @@ remote_viewer_class_init (RemoteViewerClass *klass)
>  {
>      GObjectClass *object_class = G_OBJECT_CLASS (klass);
>      VirtViewerAppClass *app_class = VIRT_VIEWER_APP_CLASS (klass);
> +    GApplicationClass *g_app_class = G_APPLICATION_CLASS(klass);
> +    GtkApplicationClass *gtk_app_class = GTK_APPLICATION_CLASS(klass);
>  
>      g_type_class_add_private (klass, sizeof (RemoteViewerPrivate));
>  
>      object_class->get_property = remote_viewer_get_property;
>      object_class->set_property = remote_viewer_set_property;
> +    object_class->constructed = remote_viewer_constructed;
>      object_class->dispose = remote_viewer_dispose;
>  
>      app_class->start = remote_viewer_start;
>      app_class->deactivated = remote_viewer_deactivated;
> +
> +    g_app_class->handle_local_options = remote_viewer_handle_local_options;
> +
>  #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 +246,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 +254,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 +265,6 @@ remote_viewer_class_init (RemoteViewerClass *klass)
>                                                           "Open recent
> dialog",
>                                                           FALSE,
>                                                           G_PARAM_READWRITE |
> -                                                        
>  G_PARAM_CONSTRUCT_ONLY |
>                                                          
>  G_PARAM_STATIC_STRINGS));
>  }
>  
> @@ -238,11 +275,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.fedorahosted.remote-viewer",

Still using org.fedorahosted. Does anybody else have an opinion on this? I 


> +                        "flags", G_APPLICATION_NON_UNIQUE,
>                          NULL);
>  }
>  
> @@ -265,26 +302,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,11 +651,12 @@ remote_viewer_activate(VirtViewerApp *app, GError
> **error)
>  }
>  
>  static void
> -remote_viewer_window_added(VirtViewerApp *app,
> -                           VirtViewerWindow *win)
> +remote_viewer_window_added(GtkApplication *app,
> +                           G_GNUC_UNUSED GtkWindow *win)
>  {
> -    spice_menu_update(REMOTE_VIEWER(app), win);
> -    spice_foreign_menu_update(REMOTE_VIEWER(app), win);
> +    VirtViewerWindow *window =
> virt_viewer_app_get_main_window(VIRT_VIEWER_APP(app));
> +    spice_menu_update(REMOTE_VIEWER(app), window);
> +    spice_foreign_menu_update(REMOTE_VIEWER(app), window);


This seems wrong to me. When we add a new window to the appliation, I think we
should be updating the menu, etc for the new window, rather than the main
window. 


>  }
>  #endif
>  
> @@ -1185,6 +1203,76 @@ cleanup:
>      return ret;
>  }
>  
> +static gint
> +remote_viewer_handle_local_options(GApplication *gapp, GVariantDict *options)
> +{
> +    VirtViewerApp *app = VIRT_VIEWER_APP(gapp);
> +    gint ret = -1;
> +    gchar *title = NULL;
> +    gchar **args = NULL;
> +    gboolean controller = FALSE;
> +
> +    if (g_variant_dict_contains(options, 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");
> +        return 0;
> +    }
> +
> +    if (g_variant_dict_lookup(options, G_OPTION_REMAINING, "^as", &args)) {
> +        if (args && (g_strv_length(args) != 1)) {
> +            g_printerr(_("Error: can't handle multiple URIs\n"));
> +            ret = 1;
> +            goto end;
> +        }
> +
> +        g_object_set(app, "guri", args[0], NULL);
> +    }
> +
> +    g_variant_dict_lookup(options, OPT_TITLE, "s", &title);

This didn't occur to me during the last review, but: why did you decide to pass
NULL for arg_data in the GOptionEntry array instead of continuing to set
arg_data to a pointer to e.g. a string variable? Setting arg_data to NULL there
means that the argument value will be stuffed into a GVariantDict which you can
inspect in this vfunc. But that just seems like more work to me. If you had left
it the old way, you could just use the (already-populated) variable here instead
of needing to extract the value from the variant dict here. It also means that
you wouldn't need to create string variables for each of the option names
(OPT_TITLE, etc) since you wouldn't need to query the variant dict by name here.

> +
> +#ifdef HAVE_SPICE_GTK
> +    if (g_variant_dict_lookup(options, OPT_CONTROLLER, "b", &controller)) {
> +        if (args) {
> +            g_printerr(_("Error: extra arguments given while using Spice
> controller\n\n"));
> +            ret = 1;
> +            goto end;
> +        } else {
> +            RemoteViewer *self = REMOTE_VIEWER(app);
> +            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 (title && !controller)
> +        g_object_set(app, "title", title, NULL);
> +
> +    ret = G_APPLICATION_CLASS(remote_viewer_parent_class)
> ->handle_local_options(gapp, options);
> +
> +end:
> +    if (ret == 1)

I know you only use -1, 0, and 1 above, but technically any positive number is
an error condition, so I think this test should be >0 instead of ==1

> +        g_printerr(_("Run '%s --help' to see a full list of available command
> line options\n\n"),
> +                   g_get_prgname());
> +
> +    g_strfreev(args);
> +    return ret;
> +}
> +
>  /*
>   * Local variables:
>   *  c-indent-level: 4
> diff --git a/src/remote-viewer.h b/src/remote-viewer.h
> index 6d445ca..e200fc1 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 653b30c..67cac77 100644
> --- a/src/virt-viewer-app.c
> +++ b/src/virt-viewer-app.c
> @@ -32,6 +32,7 @@
>  #include <string.h>
>  #include <unistd.h>
>  #include <locale.h>
> +#include <gio/gio.h>
>  #include <glib/gprintf.h>
>  #include <glib/gi18n.h>
>  
> @@ -63,6 +64,7 @@
>  #endif
>  
>  gboolean doDebug = FALSE;
> +static int opt_zoom = NORMAL_ZOOM_LEVEL;
>  
>  /* Signal handlers for about dialog */
>  void virt_viewer_app_about_close(GtkWidget *dialog, VirtViewerApp *self);
> @@ -104,6 +106,12 @@ static void virt_viewer_app_set_fullscreen(VirtViewerApp
> *self, gboolean fullscr
>  static void virt_viewer_app_update_menu_displays(VirtViewerApp *self);
>  static void virt_viewer_update_smartcard_accels(VirtViewerApp *self);
>  
> +/* From GApplication */
> +static gint virt_viewer_app_handle_local_options(GApplication *app,
> +                                                 GVariantDict *options);
> +static void virt_viewer_app_startup_cb(GApplication *app, gpointer data);
> +static void virt_viewer_app_activate_cb(GApplication *app, gpointer data);
> +
>  
>  struct _VirtViewerAppPrivate {
>      VirtViewerWindow *main_window;
> @@ -155,7 +163,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))
>  
> @@ -174,14 +182,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)
>  {
> @@ -298,7 +298,7 @@ virt_viewer_app_quit(VirtViewerApp *self)
>          }
>      }
>  
> -    gtk_main_quit();
> +    g_application_quit(G_APPLICATION(self));
>  }
>  
>  static gint
> @@ -926,12 +926,11 @@ virt_viewer_app_window_new(VirtViewerApp *self, gint
> nth)
>                      virt_viewer_session_get_has_usbredir(self->priv
> ->session));
>      }
>  
> -    g_signal_emit(self, signals[SIGNAL_WINDOW_ADDED], 0, window);
> -
>      if (self->priv->fullscreen)
>          app_window_try_fullscreen(self, window, nth);
>  
>      w = virt_viewer_window_get_window(window);
> +    gtk_application_add_window(GTK_APPLICATION(self), w);

Copying comments from my first review:

This is a slight change in behavior. It will now emit the window-added signal
after the call to app_window_try_fullscreen() rather than before. Not sure
whether this is important, just noting it. Perhaps move this where the original
signal was emitted?


>      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);
> @@ -1046,8 +1045,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);
>  }
>  
> @@ -1401,7 +1398,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
> @@ -1479,7 +1476,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));



Copying comments from my first review:

I'd need to test to make sure, but I think it's possible that the call to
virt_viewer_app_hide_all_windows() a few lines above might result in the
application quitting because the application no longer has any associated
windows. In other words, even if priv->quitting is false, the application might
quit here. I could be wrong though. Have you tested this path?

(Just want to make sure this has been tested)


>  
>      if (connect_error) {
>          GtkWidget *dialog = virt_viewer_app_make_message_dialog(self,
> @@ -1744,13 +1741,6 @@ gboolean virt_viewer_app_start(VirtViewerApp *self,
> GError **error)
>      return self->priv->started;
>  }
>  
> -static int opt_zoom = NORMAL_ZOOM_LEVEL;
> -static gchar *opt_hotkeys = NULL;
> -static gboolean opt_verbose = FALSE;
> -static gboolean opt_debug = FALSE;
> -static gboolean opt_fullscreen = FALSE;
> -static gboolean opt_kiosk = FALSE;
> -static gboolean opt_kiosk_quit = FALSE;
>  
>  static void
>  title_maybe_changed(VirtViewerApp *self, GParamSpec* pspec G_GNUC_UNUSED,
> gpointer user_data G_GNUC_UNUSED)
> @@ -1765,8 +1755,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();
> @@ -1782,17 +1770,14 @@ 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);
> +
> +    /* From GApplication */
> +    g_signal_connect(self, "startup", G_CALLBACK(virt_viewer_app_startup_cb),
> NULL);
> +    g_signal_connect(self, "activate",
> G_CALLBACK(virt_viewer_app_activate_cb), NULL);
>  }
>  
>  static void
> @@ -1847,17 +1832,75 @@ virt_viewer_update_smartcard_accels(VirtViewerApp
> *self)
>      }
>  }
>  
> +static gboolean
> +virt_viewer_app_option_kiosk_quit(VirtViewerApp *self, const gchar *value)
> +{
> +    if (g_strcmp0(value, "never") == 0) {
> +        self->priv->quit_on_disconnect = FALSE;
> +        return TRUE;
> +    }
> +    if (g_strcmp0(value, "on-disconnect") == 0) {
> +        self->priv->quit_on_disconnect = TRUE;
> +        return TRUE;
> +    }
> +
> +    g_printerr(_("Invalid kiosk-quit argument: %s\n\n"), value);
> +
> +    return FALSE;
> +}
> +
> +static const char OPT_VERSION[] = "version";
> +static const char OPT_ZOOM[] = "zoom";
> +static const char OPT_FULLSCREEN[] = "full-screen";
> +static const char OPT_HOTKEYS[] = "hotkeys";
> +static const char OPT_KIOSK[] = "kiosk";
> +static const char OPT_KIOSK_QUIT[] = "kiosk-quit";
> +static const char OPT_VERBOSE[] = "verbose";
> +static const char OPT_DEBUG[] = "debug";
> +
>  static void
>  virt_viewer_app_constructed(GObject *object)
>  {
> -    VirtViewerApp *self = VIRT_VIEWER_APP(object);
> +    GApplication *app = G_APPLICATION(object);
> +
> +    static const GOptionEntry virt_viewer_app_options[] = {
> +        { OPT_VERSION, 'V', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Display version information"), NULL },
> +        { OPT_ZOOM, 'z', 0, G_OPTION_ARG_INT, NULL,
> +          N_("Zoom level of window, in percentage"), "ZOOM" },
> +        { OPT_FULLSCREEN, 'f', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Open in full screen mode (adjusts guest resolution to fit the
> client)"), NULL },
> +        { OPT_HOTKEYS, 'H', 0, G_OPTION_ARG_STRING, NULL,
> +          N_("Customise hotkeys"), NULL },
> +        { OPT_KIOSK, 'k', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Enable kiosk mode"), NULL },
> +        { OPT_KIOSK_QUIT, '\0', 0, G_OPTION_ARG_STRING, NULL,
> +          N_("Quit on given condition in kiosk mode"), N_("<never|on
> -disconnect>") },
> +        { OPT_VERBOSE, 'v', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Display verbose information"), NULL },
> +        { OPT_DEBUG, '\0', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Display debugging information"), NULL },
> +        { NULL },
> +    };
> +
> +    g_application_add_main_option_entries(app, virt_viewer_app_options);
> +
> +#ifdef HAVE_GTK_VNC
> +    g_application_add_option_group(app, vnc_display_get_option_group());
> +#endif
> +#ifdef HAVE_SPICE_GTK
> +    g_application_add_option_group(app, spice_get_option_group());
> +#endif
> +}
> +
> +static void
> +virt_viewer_app_startup_cb(GApplication *app, G_GNUC_UNUSED gpointer data)
> +{
> +    VirtViewerApp *self = VIRT_VIEWER_APP(app);
>  
>      self->priv->main_window = virt_viewer_app_window_new(self,
>                                                          
>  virt_viewer_app_get_first_monitor(self));
>      self->priv->main_notebook =
> GTK_WIDGET(virt_viewer_window_get_notebook(self->priv->main_window));
> -
> -    virt_viewer_app_set_kiosk(self, opt_kiosk);
> -    virt_viewer_app_set_hotkeys(self, opt_hotkeys);
>      virt_viewer_window_set_zoom_level(self->priv->main_window, opt_zoom);
>  
>      virt_viewer_set_insert_smartcard_accel(self, GDK_F8, GDK_SHIFT_MASK);
> @@ -1871,9 +1914,26 @@ virt_viewer_app_constructed(GObject *object)
>  }
>  
>  static void
> +virt_viewer_app_activate_cb(GApplication *app, G_GNUC_UNUSED gpointer data)
> +{
> +    GError *error = NULL;
> +    VirtViewerApp *self = VIRT_VIEWER_APP(app);
> +
> +    if (!virt_viewer_app_start(VIRT_VIEWER_APP(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);
> +    }
> +}
> +
> +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));
>  
> @@ -1882,6 +1942,8 @@ virt_viewer_app_class_init (VirtViewerAppClass *klass)
>      object_class->set_property = virt_viewer_app_set_property;
>      object_class->dispose = virt_viewer_app_dispose;
>  
> +    g_app_class->handle_local_options = virt_viewer_app_handle_local_options;
> +
>      klass->start = virt_viewer_app_default_start;
>      klass->initial_connect = virt_viewer_app_default_initial_connect;
>      klass->activate = virt_viewer_app_default_activate;
> @@ -1992,28 +2054,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
> @@ -2535,49 +2575,40 @@ virt_viewer_app_show_preferences(VirtViewerApp *self,
> GtkWidget *parent)
>      gtk_window_present(GTK_WINDOW(preferences));
>  }
>  
> -static gboolean
> -option_kiosk_quit(G_GNUC_UNUSED const gchar *option_name,
> -                  const gchar *value,
> -                  G_GNUC_UNUSED gpointer data, GError **error)
> +static gint
> +virt_viewer_app_handle_local_options(GApplication *app,
> +                                     GVariantDict *options)
>  {
> -    if (g_str_equal(value, "never")) {
> -        opt_kiosk_quit = FALSE;
> -        return TRUE;
> -    }
> -    if (g_str_equal(value, "on-disconnect")) {
> -        opt_kiosk_quit = TRUE;
> -        return TRUE;
> +    VirtViewerApp *self = VIRT_VIEWER_APP(app);
> +    gchar *opt_hotkeys = NULL;
> +
> +    virt_viewer_app_set_fullscreen(self, g_variant_dict_contains(options,
> OPT_FULLSCREEN));
> +    virt_viewer_app_set_debug(g_variant_dict_contains(options, OPT_DEBUG));
> +    self->priv->verbose = g_variant_dict_contains(options, OPT_VERBOSE);
> +
> +    if (g_variant_dict_contains(options, OPT_KIOSK)) {
> +        gchar *opt_kiosk_quit = NULL;
> +
> +        virt_viewer_app_set_kiosk(self, TRUE);
> +        self->priv->quit_on_disconnect = TRUE;
> +
> +        if (g_variant_dict_lookup(options, OPT_KIOSK_QUIT, "s",
> &opt_kiosk_quit) &&
> +            !virt_viewer_app_option_kiosk_quit(self, opt_kiosk_quit)) {
> +            return 1;
> +        }
>      }
>  
> -    g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, _("Invalid
> kiosk-quit argument: %s"), value);
> -    return FALSE;
> -}
> +    if (g_variant_dict_lookup(options, OPT_ZOOM, "i", &opt_zoom)) {
> +        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;
> +        }
> +    }
>  
> -GOptionGroup*
> -virt_viewer_app_get_option_group(void)
> -{
> -    static const GOptionEntry options [] = {
> -        { "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,
> -          N_("Open in full screen mode (adjusts guest resolution to fit the
> client)"), NULL },
> -        { "hotkeys", 'H', 0, G_OPTION_ARG_STRING, &opt_hotkeys,
> -          N_("Customise hotkeys"), NULL },
> -        { "kiosk", 'k', 0, G_OPTION_ARG_NONE, &opt_kiosk,
> -          N_("Enable kiosk mode"), NULL },
> -        { "kiosk-quit", '\0', 0, G_OPTION_ARG_CALLBACK, option_kiosk_quit,
> -          N_("Quit on given condition in kiosk mode"), N_("<never|on
> -disconnect>") },
> -        { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose,
> -          N_("Display verbose information"), NULL },
> -        { "debug", '\0', 0, G_OPTION_ARG_NONE, &opt_debug,
> -          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);
> +    if (g_variant_dict_lookup(options, OPT_HOTKEYS, "s", &opt_hotkeys))
> +        virt_viewer_app_set_hotkeys(self, opt_hotkeys);
>  
> -    return group;
> +    return -1;
>  }
>  
>  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..7bb22fb 100644
> --- a/src/virt-viewer-app.h
> +++ b/src/virt-viewer-app.h
> @@ -24,6 +24,7 @@
>  #define VIRT_VIEWER_APP_H
>  
>  #include <glib-object.h>
> +#include <gtk/gtk.h>
>  #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);
> @@ -95,7 +92,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 9b9807a..dd51dcb 100644
> --- a/src/virt-viewer-main.c
> +++ b/src/virt-viewer-main.c
> @@ -25,118 +25,24 @@
>  #include <gtk/gtk.h>
>  #include <glib/gi18n.h>
>  #include <stdlib.h>
> -#ifdef HAVE_GTK_VNC
> -#include <vncdisplay.h>
> -#endif
> -#ifdef HAVE_SPICE_GTK
> -#include <spice-client.h>
> -#endif
> -#include "virt-viewer.h"
> -
> -static void virt_viewer_version(void)
> -{
> -    g_print(_("%s version %s\n"), PACKAGE, VERSION BUILDID);
> -
> -    exit(EXIT_SUCCESS);
> -}
>  
> +#include "virt-viewer.h"
>  
>  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;
>  
>      virt_viewer_util_init(_("Virt Viewer"));
>  
> -    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);
> +    viewer = virt_viewer_new();
>      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;
> +    ret = g_application_run(G_APPLICATION(viewer), argc, argv);
>  
>   cleanup:
> -    if (viewer)
> -        g_object_unref(viewer);
> -    g_free(uri);
> -    g_strfreev(args);
> -    g_free(help_msg);
> -    g_clear_error(&error);
> +    g_object_unref(viewer);
>  
>      return ret;
>  }
> diff --git a/src/virt-viewer.c b/src/virt-viewer.c
> index 10f624d..0f870de 100644
> --- a/src/virt-viewer.c
> +++ b/src/virt-viewer.c
> @@ -73,20 +73,58 @@ 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);
>  
> +/* VirtViewerApp overrides */
> +static gint virt_viewer_handle_local_options(GApplication *app, GVariantDict
> *options);
> +
> +static const char OPT_VERSION[] = "version";
> +static const char OPT_DIRECT[] = "direct";
> +static const char OPT_ATTACH[] = "attach";
> +static const char OPT_CONNECT[] = "connect";
> +static const char OPT_WAIT[] = "wait";
> +static const char OPT_RECONNECT[] = "reconnect";
> +
> +static void
> +virt_viewer_constructed(GObject *object)
> +{
> +    GApplication *app = G_APPLICATION(object);
> +    const GOptionEntry options[] = {
> +        { OPT_DIRECT, 'd', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Direct connection with no automatic tunnels"), NULL },
> +        { OPT_ATTACH, 'a', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Attach to the local display using libvirt"), NULL },
> +        { OPT_CONNECT, 'c', 0, G_OPTION_ARG_STRING, NULL,
> +          N_("Connect to hypervisor"), "URI"},
> +        { OPT_WAIT, 'w', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Wait for domain to start"), NULL },
> +        { OPT_RECONNECT, 'r', 0, G_OPTION_ARG_NONE, NULL,
> +          N_("Reconnect to domain upon restart"), NULL },
> +        { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, NULL,
> +          NULL, "-- DOMAIN-NAME|ID|UUID" },
> +        { NULL },
> +    };
> +
> +    G_OBJECT_CLASS(virt_viewer_parent_class)->constructed(object);
> +    g_application_add_main_option_entries(app, options);
> +}
> +
>  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));
>  
>      object_class->dispose = virt_viewer_dispose;
> +    object_class->constructed = virt_viewer_constructed;
>  
>      app_class->initial_connect = virt_viewer_initial_connect;
>      app_class->deactivated = virt_viewer_deactivated;
>      app_class->open_connection = virt_viewer_open_connection;
>      app_class->start = virt_viewer_start;
> +
> +    g_app_class->handle_local_options = virt_viewer_handle_local_options;
>  }
>  
>  static void
> @@ -106,7 +144,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(self));
>  
>      if (virt_viewer_app_is_active(app)) {
>          self->priv->reconnect_poll = 0;
> @@ -975,34 +1013,65 @@ virt_viewer_start(VirtViewerApp *app, GError **error)
>      return VIRT_VIEWER_APP_CLASS(virt_viewer_parent_class)->start(app,
> error);
>  }
>  
> -VirtViewer *
> -virt_viewer_new(const char *uri,
> -                const char *name,
> -                gboolean direct,
> -                gboolean attach,
> -                gboolean waitvm,
> -                gboolean reconnect)
> +static gint
> +virt_viewer_handle_local_options(GApplication *gapp, GVariantDict *options)
>  {
> -    VirtViewer *self;
> -    VirtViewerApp *app;
> -    VirtViewerPrivate *priv;
> +    VirtViewer *self = VIRT_VIEWER(gapp);
> +    VirtViewerApp *app = VIRT_VIEWER_APP(gapp);
> +    gint ret = -1;
> +    gchar *uri = NULL;
> +    gchar **args = NULL;
>  
> -    self = g_object_new(VIRT_VIEWER_TYPE,
> -                        "guest-name", name,
> -                        NULL);
> -    app = VIRT_VIEWER_APP(self);
> -    priv = self->priv;
> +    if (g_variant_dict_contains(options, OPT_VERSION)) {
> +        g_print(_("%s version %s\n"), g_get_prgname(), VERSION BUILDID);
> +        return 0;
> +    }
> +
> +    if (g_variant_dict_lookup(options, G_OPTION_REMAINING, "^as", &args)) {
> +        if (args && (g_strv_length(args) != 1)) {
> +            g_printerr(_("\nUsage: %s [OPTIONS] [DOMAIN-NAME|ID|UUID]\n\n"),
> PACKAGE);
> +            ret = 1;
> +            goto end;
> +        }
> +
> +        self->priv->domkey = g_strdup(args[0]);
> +    }
> +
> +    if (g_variant_dict_contains(options, OPT_WAIT)) {
> +        if (!self->priv->domkey) {
> +            g_printerr(_("\nNo DOMAIN-NAME|ID|UUID was specified for '-
> -wait'\n\n"));
> +            ret = 1;
> +            goto end;
> +        }
> +
> +        self->priv->waitvm = TRUE;
> +    }
> +
> +    virt_viewer_app_set_direct(app, g_variant_dict_contains(options,
> OPT_DIRECT));
> +    virt_viewer_app_set_attach(app, g_variant_dict_contains(options,
> OPT_ATTACH));
> +    self->priv->reconnect = g_variant_dict_contains(options, OPT_RECONNECT);
>  
> -    virt_viewer_app_set_direct(app, direct);
> -    virt_viewer_app_set_attach(app, attach);
> +    if (g_variant_dict_lookup(options, OPT_CONNECT, "s", &uri))
> +        self->priv->uri = g_strdup(uri);
>  
> -    /* should probably be properties instead */
> -    priv->uri = g_strdup(uri);
> -    priv->domkey = g_strdup(name);
> -    priv->waitvm = waitvm;
> -    priv->reconnect = reconnect;
> +    ret = G_APPLICATION_CLASS(virt_viewer_parent_class)
> ->handle_local_options(gapp, options);
>  
> -    return self;
> +end:
> +    if (ret == 1)
> +        g_printerr(_("Run '%s --help' to see a full list of available command
> line options\n\n"),
> +                   g_get_prgname());
> +
> +    g_strfreev(args);
> +    return ret;
> +}
> +
> +VirtViewer *
> +virt_viewer_new(void)
> +{
> +    return g_object_new(VIRT_VIEWER_TYPE,
> +                        "application-id", "org.fedorahosted.virt-viewer",
> +                        "flags", G_APPLICATION_NON_UNIQUE,
> +                        NULL);
>  }
>  
>  /*
> 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 @@
>  <interface>
>    <!-- interface-requires gtk+ 2.6 -->
>    <object class="GtkAccelGroup" id="accelgroup"/>
> -  <object class="GtkWindow" id="viewer">
> +  <object class="GtkApplicationWindow" id="viewer">
>      <property name="can_focus">False</property>
>      <property name="default_width">1024</property>
>      <property name="default_height">768</property>



Reviewed-by: Jonathon Jongsma <jjongsma at redhat.com>




More information about the virt-tools-list mailing list