[virt-tools-list] [PATCH 3/4] Port to GtkApplication API's
Eduardo Lima (Etrunko)
etrunko at redhat.com
Thu Dec 3 14:02:57 UTC 2015
On 11/30/2015 07:29 PM, Jonathon Jongsma wrote:
>> - All Window objects must be associated with the Application, and with
>> this, there is no need to emit our own 'window-added' signal, it will
>> be done by GtkApplication by the time gtk_application_add_window() is
>> called. The 'window-removed' signal has also been removed, as it was
>> not being used anyway.
>
> The 'window-removed' signal was not being used, but I suspect that the main
> reason it was removed was that it conflicts with a signal of the same name from
> GtkApplication. Perhaps useful to note that here.
Yes, I will add it to the message.
>
>> diff --git a/src/remote-viewer.c b/src/remote-viewer.c
>> index 3f530a3..7a1e870 100644
>> --- a/src/remote-viewer.c
>> +++ b/src/remote-viewer.c
>> @@ -36,11 +36,9 @@
>>
>> #ifdef HAVE_SPICE_GTK
>> #include <spice-controller.h>
>> -#endif
>> -
>> -#ifdef HAVE_SPICE_GTK
>> #include "virt-viewer-session-spice.h"
>> #endif
>> +
>
>
> This hunk is fine, but not strictly necessary for porting to GtkApplication.
> Consider moving it to a separate cleanup patch?
>
Noted, I can add it to a cleanup patch together with the next one.
>> @@ -195,10 +199,14 @@ remote_viewer_class_init (RemoteViewerClass *klass)
>>
>> object_class->get_property = remote_viewer_get_property;
>> object_class->set_property = remote_viewer_set_property;
>> + object_class->dispose = remote_viewer_dispose;
>>
>> app_class->start = remote_viewer_start;
>> app_class->deactivated = remote_viewer_deactivated;
>> - object_class->dispose = remote_viewer_dispose;
>
> This move could also possibly be moved to a cleanup patch, I suppose. Not sure
> it's worth it though...
I will add it together with previous hunk.
>> #ifdef HAVE_SPICE_GTK
>> app_class->activate = remote_viewer_activate;
>> app_class->window_added = remote_viewer_window_added;
>> @@ -210,7 +218,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,
>> @@ -219,7 +226,6 @@ 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));
>> #endif
>> g_object_class_install_property(object_class,
>> @@ -229,7 +235,6 @@ remote_viewer_class_init (RemoteViewerClass *klass)
>> "Open recent
>> dialog",
>> FALSE,
>> G_PARAM_READWRITE |
>> -
>> G_PARAM_CONSTRUCT_ONLY |
>>
>> G_PARAM_STATIC_STRINGS));
>> }
>
>
> I understand why these properties are no longer construct-only, however I wonder
> if we want to add a warning if we try to set these properties after starting the
> application. We can now technically change these properties after construction,
> but we only use these values within remote_viewer_start(). So any property
> changes that are made after calling remote_viewer_start() will not have any
> effect. Do we care?
I think it is fine the way it is, there is a restriction in set property
that only allows those properties being set for the first time.
>>
>> @@ -240,11 +245,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",
>
> should we use "org.spice-space.remote-viewer" instead? or "org.virt
> -manager.remote-viewer" (since virt-manager.org is where the releases are
> hosted)?
>
Ok, simple to change, I was wondering which one to use, and ended up
with this one because of where the repository is hosted. I tend to like
"org.virt-manager" prefix rather than "org.spice-space", but I'm okay
with any of those.
>>
>> +static const gchar*
>> +remote_viewer_version_string(void)
>> +{
>> +#ifdef REMOTE_VIEWER_OS_ID
>> + return " (OS ID: " REMOTE_VIEWER_OS_ID ")";
>> +#endif
>> + return "";
>> +}
>> +
>
> This function seems a little strange. It doesn't really match the name of the
> function. It just returns an OSID rather than a version string.
>
>
It was a way to add OSID to the version string, and prior to this patch,
it is shown only if we are running remote-viewer. I wanted to keep the
behavior as the same as before the patch. If I move the #ifdef block
back to virt-viewer-app, virt-viewer will also show the string.
>> +static const char OPT_TITLE[] = "title";
>> +static const char OPT_CONTROLLER[] = "spice-controller";
>
>
> I'd use
> static const char *VARNAME
>
> rather than
> static const char VARNAME[]
>
>
> However, see my comments about handle_local_options() below. Depending on what
> we do there, we may not need to define constant variables for these option
> names.
>
>
>> +
>> +static void
>> +remote_viewer_add_main_options(VirtViewerApp *self)
>> +{
>> + GApplication *app = G_APPLICATION (self);
>> + const GOptionEntry options [] = {
>
> I know this is just moved code, but perhaps it's worthwhile to fix the coding
> style to remove the spaces before the parentheses and brackets:
>
> G_APPLICATION(self)
> 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_application_add_main_option_entries(app, options);
>> +
>> +#ifdef HAVE_OVIRT
>> + g_application_add_option_group(app, ovirt_get_option_group ());
>> +#endif
>> +}
>> +
>> +static gint
>> +remote_viewer_handle_options(VirtViewerApp *app, GVariantDict *options)
>> +{
>> + gint ret = -1;
>> + gchar *title = NULL;
>> + gchar **args = NULL;
>> + gboolean controller = FALSE;
>> +
>> + 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 = 0;
>> + goto end;
>> + }
>> +
>> + g_object_set(app, "guri", args[0], NULL);
>> + }
>> +
>> + g_variant_dict_lookup(options, OPT_TITLE, "s", &title);
>> +
>> +#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 = 0;
>> + 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_printerr("***** setting title to '%s'\n", title);
>> + g_object_set(app, "title", title, NULL);
>> + }
>> +
>> +end:
>> + g_strfreev(args);
>> + g_free(title);
>> + return ret;
>> +}
>
>
> Right now you have a virtual function the base class (handle_local_options())
> that does some work and then calls a different virtual function
> (handle_options()) that is implemented in each subclass (but not in the parent
> class). I think it might be cleaner to just get rid of the separate
> handle_options() vfunc and implement handle_local_options() in each of the
> subclasses. Then it could chain up and call the parent vfunc to do the common
> work in the parent class.
Yes, that was intentional, I could not find a way to control the flow so
that the function on the parent class is called first and also can
access the return value of the one on the child class.
>
> But a bigger issue is that I think that handle_local_options() is not really the
> function we're generally expected to use here. handle_local_options() is passed
> a GVariantDict of options, which means that you have to inspect and handle them
> all manually rather than letting the GOptionContext stuff do the work for you.
> That means that if we want to add or remove options, we update the list of
> GOptionEntry objects and also update the handle_local_options function to handle
> that new option. This adds maintenance burden and makes it more likely they'll
> get out of sync. If we instead handled the command_line() vfunc, we could take
> advantage of GOptionContext parsing instead of manually inspecting the
> GVariantDict. See
> https://git.gnome.org/browse/glib/tree/gio/tests/gapplication-example-cmdline3.c
> for an example of using g_option_context_parse() in a command-line handler.
>
> I admit that GApplication is designed to enable a slightly more complicated
> approach than we want here (a single-instance unique application), and I don't
> have extensive experience with using this class, but I get the impression that
> handle-local-options is not intended to be a signal that you normally need to
> handle. Is there a reason you chose this signal/vfunc over 'command-line'?
>
The main reason for using it rather than command-line is because
'handle-local-options' is the one called earlier in the process. So if
we receive any invalid option or another one that would make the process
exit, the earlier, the better.
>> diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c
>> index 653b30c..adabb27 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>
>>
>> @@ -60,9 +61,11 @@
>> #endif
>> #ifdef HAVE_SPICE_GTK
>> #include "virt-viewer-session-spice.h"
>> +#include <spice-client.h>
>
> This change is probably not directly related to the GtkApplication stuff so
> could possibly be in a separate commit?
>
Ok.
>> @@ -926,12 +927,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);
>
> 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?
>
Good catch, it was not intentional change. I will check for results with
try_fullscreen after the signal emission.
>> @@ -1479,7 +1477,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));
>
>
> 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?
Another one I need to check. Thanks.
>
>
>> @@ -1851,13 +1837,17 @@ static void
>> virt_viewer_app_constructed(GObject *object)
>> {
>> VirtViewerApp *self = VIRT_VIEWER_APP(object);
>> + virt_viewer_app_add_main_options(self);
>> +}
>> +
>> +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 +1861,26 @@ virt_viewer_app_constructed(GObject *object)
>> }
>
> Do we really need a separate 'startup' handler? Or can we move stuff from here
> into either the "handle-local-options" or "activate" handlers? (or to "command
> -line" if we choose that route)
Yes it is necessary, handle-local-options happens too early in the
process. But one thing it is not completely clear for me it the
difference between providing a signal handler and overriding the vfunc.
In this specific case, when I override, the application crashes, while
with the signal handler it works fine.
>> @@ -2536,48 +2523,122 @@ virt_viewer_app_show_preferences(VirtViewerApp *self,
>> GtkWidget *parent)
>> }
>>
>> static gboolean
>> -option_kiosk_quit(G_GNUC_UNUSED const gchar *option_name,
>> - const gchar *value,
>> - G_GNUC_UNUSED gpointer data, GError **error)
>> +virt_viewer_app_option_kiosk_quit(VirtViewerApp *self, const gchar *value)
>> {
>> - if (g_str_equal(value, "never")) {
>> - opt_kiosk_quit = FALSE;
>> + if (!g_strcmp0(value, "never")) {
>
> I'd prefer an explicit ' == 0' here and in the following test
>
Ok.
>
>> + self->priv->quit_on_disconnect = FALSE;
>> return TRUE;
>> }
>> - if (g_str_equal(value, "on-disconnect")) {
>> - opt_kiosk_quit = TRUE;
>> + if (!g_strcmp0(value, "on-disconnect")) {
>> + self->priv->quit_on_disconnect = TRUE;
>> return TRUE;
>> }
>>
>> - g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, _("Invalid
>> kiosk-quit argument: %s"), value);
>> + g_printerr(_("Invalid kiosk-quit argument: %s\n\n"), value);
>> +
>> return FALSE;
>> }
>>
>> -GOptionGroup*
>> -virt_viewer_app_get_option_group(void)
>> +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";
>
> Again, I'd prefer string declarations (char*) rather than array declarations
> here if we need these variables.
>
The difference between those two is that using explicit arrays one will
use slight less memory than pointers. See:
http://c-faq.com/decl/strlitinit.html
http://c-faq.com/aryptr/aryptr2.html
>
>> +
>> +static void
>> +virt_viewer_app_add_main_options(VirtViewerApp *self)
>> {
>> - static const GOptionEntry options [] = {
>> - { "zoom", 'z', 0, G_OPTION_ARG_INT, &opt_zoom,
>> + GApplication *app = G_APPLICATION (self);
>> + VirtViewerAppClass *app_class = VIRT_VIEWER_APP_GET_CLASS(self);
>> +
>> + 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" },
>> - { "full-screen", 'f', 0, G_OPTION_ARG_NONE, &opt_fullscreen,
>> + { OPT_FULLSCREEN, 'f', 0, G_OPTION_ARG_NONE, NULL,
>> N_("Open in full screen mode (adjusts guest resolution to fit the
>> client)"), NULL },
>> - { "hotkeys", 'H', 0, G_OPTION_ARG_STRING, &opt_hotkeys,
>> + { OPT_HOTKEYS, 'H', 0, G_OPTION_ARG_STRING, NULL,
>> N_("Customise hotkeys"), NULL },
>> - { "kiosk", 'k', 0, G_OPTION_ARG_NONE, &opt_kiosk,
>> + { OPT_KIOSK, 'k', 0, G_OPTION_ARG_NONE, NULL,
>> N_("Enable kiosk mode"), NULL },
>> - { "kiosk-quit", '\0', 0, G_OPTION_ARG_CALLBACK, option_kiosk_quit,
>> + { OPT_KIOSK_QUIT, '\0', 0, G_OPTION_ARG_STRING, NULL,
>> N_("Quit on given condition in kiosk mode"), N_("<never|on
>> -disconnect>") },
>> - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose,
>> + { OPT_VERBOSE, 'v', 0, G_OPTION_ARG_NONE, NULL,
>> N_("Display verbose information"), NULL },
>> - { "debug", '\0', 0, G_OPTION_ARG_NONE, &opt_debug,
>> + { OPT_DEBUG, '\0', 0, G_OPTION_ARG_NONE, NULL,
>> N_("Display debugging information"), NULL },
>> - { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, 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_application_add_main_option_entries(app, virt_viewer_app_options);
>> + app_class->add_main_options(self);
>> +
>> +#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 gint
>> +virt_viewer_app_handle_local_options(GApplication *app,
>> + GVariantDict *options)
>> +{
>> + VirtViewerApp *self = VIRT_VIEWER_APP(app);
>> + VirtViewerAppClass *app_class = VIRT_VIEWER_APP_GET_CLASS(self);
>> + gchar *opt_hotkeys = NULL;
>> + gint ret = -1;
>> +
>> + /* Finish program execution if version is supplied. */
>> + if (g_variant_dict_contains(options, OPT_VERSION)) {
>> + gchar *version_str = g_strdup_printf(_("%s version %s%s\n"),
>> + g_get_prgname(),
>> + VERSION BUILDID,
>> + app_class->version_string ?
>> app_class->version_string() : "");
>> + g_print(version_str);
>> + g_free(version_str);
>> + return 0;
>> + }
>> +
>> + 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)) {
>> + ret = 0;
>> + goto end;
>> + }
>> + }
>> +
>> + 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;
>> + }
>> + }
>> +
>> + if (g_variant_dict_lookup(options, OPT_HOTKEYS, "s", &opt_hotkeys))
>> + virt_viewer_app_set_hotkeys(self, opt_hotkeys);
>> +
>> + ret = app_class->handle_options(self, options);
>> +
>> +end:
>> + if (!ret)
>> + g_printerr(_("Run with --help to see a full list of available command
>> line options\n\n"));
>> +
>> + return ret;
>> }
>
>
> Same comment here about handle-local-options vs. command-line
>
Answered above.
>
>>
>> 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..3e47d08 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,15 @@ G_BEGIN_DECLS
>> typedef struct _VirtViewerAppPrivate VirtViewerAppPrivate;
>>
>> typedef struct {
>> - GObject parent;
>> + GtkApplication parent;
>> VirtViewerAppPrivate *priv;
>> } VirtViewerApp;
>>
>> typedef struct {
>> - GObjectClass parent_class;
>> + GtkApplicationClass parent_class;
>>
>> /* signals */
>> void (*window_added) (VirtViewerApp *self, VirtViewerWindow *window);
>> - void (*window_removed) (VirtViewerApp *self, VirtViewerWindow *window);
>
> I think we need to remove the 'window_added' vfunc here as well, since we
> removed the signal that was associated with this vfunc.
This was a left over, thanks for noticing.
>> @@ -975,34 +982,84 @@ 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 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";
>
>
> string vs. array again.
Same as above.
>
> Reviewed-by: Jonathon Jongsma <jjongsma at redhat.com>
>
V2 on the way. Thank you for the review.
--
Eduardo de Barros Lima (Etrunko)
Software Engineer - RedHat
etrunko at redhat.com
More information about the virt-tools-list
mailing list