gcr: Fix dialog layout, and add dbus service
authorStef Walter <stefw@collabora.co.uk>
Tue, 1 Nov 2011 08:00:48 +0000 (09:00 +0100)
committerStef Walter <stefw@collabora.co.uk>
Tue, 13 Dec 2011 20:45:09 +0000 (21:45 +0100)
 * A bunch of bug fixes and other fixes to the default prompter tool
 * Add support for making the dialog transient

.gitignore
configure.ac
gcr/Makefile.am
gcr/gcr-dbus-constants.h
gcr/gcr-prompter-tool.c
gcr/gcr-system-prompt.c
gcr/gcr-system-prompter.c
gcr/org.gnome.keyring.Prompter.service.in [new file with mode: 0644]
gcr/tests/Makefile.am

index 3f5599f..eeffca7 100644 (file)
@@ -20,6 +20,7 @@ frob-*
 *.o
 *.plist
 *.pot
+*.service
 *.stamp
 *.tar.gz
 *.typelib
index eff8f86..c26d9fa 100644 (file)
@@ -170,6 +170,20 @@ AC_ARG_ENABLE(update-mime,
        [don't run update-mime-database utility (useful for packages) ]))
 AM_CONDITIONAL(WITH_UPDATE_MIME, test "$enable_update_mime" != "no")
 
+# ----------------------------------------------------------------------
+# DBus services
+
+AC_ARG_WITH(dbus-services,
+                 [AC_HELP_STRING([--with-dbus-services=<dir>],
+                 [where D-BUS session services directory is])])
+if ! test -z "$with_dbus_services" ; then
+       DBUS_SERVICES_DIR="$with_dbus_services"
+else
+       DBUS_SERVICES_DIR="$datadir/dbus-1/services"
+fi
+
+AC_SUBST(DBUS_SERVICES_DIR)
+
 # --------------------------------------------------------------------
 # Compilation and linking options
 #
index adeb3e6..631b62d 100644 (file)
@@ -305,6 +305,13 @@ desktop_in_files = $(desktop_in_in_files:.desktop.in.in=.desktop.in)
 desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
 @INTLTOOL_DESKTOP_RULE@
 
+service_in_files = org.gnome.keyring.Prompter.service.in
+servicedir       = $(DBUS_SERVICES_DIR)
+service_DATA     = $(service_in_files:.service.in=.service)
+
+$(service_DATA): $(service_in_files) Makefile
+       @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
+
 # ----------------------------------------------------------------
 # TOOLS
 
@@ -413,11 +420,13 @@ EXTRA_DIST = \
        $(desktop_in_in_files) \
        $(desktop_in_files) \
        $(desktop_DATA) \
+       $(service_in_files ) \
        $(mime_DATA)
 
 CLEANFILES = \
        $(BUILT_SOURCES) \
        $(pkgconfig_DATA) \
+       $(service_DATA) \
        gcr-actual.abi \
        gcr-actual-base.abi \
        gcr-expected.abi \
index d47c6f3..ac3787f 100644 (file)
@@ -38,6 +38,8 @@ G_BEGIN_DECLS
 #define GCR_DBUS_PROMPTER_METHOD_BEGIN               "BeginPrompting"
 #define GCR_DBUS_PROMPTER_METHOD_FINISH              "FinishPrompting"
 
+#define GCR_DBUS_PROMPTER_SIGNAL_READY               "PrompterReady"
+
 #define GCR_DBUS_PROMPT_INTERFACE                    "org.gnome.keyring.Prompter.Prompt"
 
 #define GCR_DBUS_PROMPT_ERROR_IN_PROGRESS            "org.gnome.keyring.Prompter.InProgress"
index aa5cebf..86ca293 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
+#include <gdk/gdkx.h>
 #include <pango/pango.h>
 
 #include <locale.h>
@@ -39,6 +40,7 @@
 
 #define LOG_ERRORS 1
 #define GRAB_KEYBOARD 1
+#define QUIT_TIMEOUT 10
 
 static GcrSystemPrompter *the_prompter = NULL;
 
@@ -70,6 +72,7 @@ typedef struct {
        PromptMode mode;
        GdkDevice *grabbed_device;
        gulong grab_broken_id;
+       guint quit_timeout;
 } GcrPrompterDialog;
 
 typedef struct {
@@ -78,20 +81,47 @@ typedef struct {
 
 G_DEFINE_TYPE (GcrPrompterDialog, gcr_prompter_dialog, GTK_TYPE_DIALOG);
 
+static gboolean
+on_timeout_quit ()
+{
+       gtk_main_quit ();
+       return FALSE; /* Don't run again */
+}
+
 static void
-on_show_prompt (GcrSystemPrompter *prompter,
+start_timeout (GcrPrompterDialog *self)
+{
+       if (g_getenv ("GCR_PERSIST") != NULL)
+               return;
+
+       if (!self->quit_timeout)
+               self->quit_timeout = g_timeout_add_seconds (QUIT_TIMEOUT, on_timeout_quit, NULL);
+}
+
+static void
+stop_timeout (GcrPrompterDialog *self)
+{
+       if (self->quit_timeout)
+               g_source_remove (self->quit_timeout);
+       self->quit_timeout = 0;
+}
+
+static void
+on_open_prompt (GcrSystemPrompter *prompter,
                 gpointer user_data)
 {
        GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
        gtk_widget_show (GTK_WIDGET (self));
+       stop_timeout (self);
 }
 
 static void
-on_hide_prompt (GcrSystemPrompter *prompter,
-                gpointer user_data)
+on_close_prompt (GcrSystemPrompter *prompter,
+                 gpointer user_data)
 {
        GcrPrompterDialog *self = GCR_PROMPTER_DIALOG (user_data);
        gtk_widget_hide (GTK_WIDGET (self));
+       start_timeout (self);
 }
 
 static gboolean
@@ -154,6 +184,7 @@ on_responded (GcrSystemPrompter *prompter,
        gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
        gtk_widget_hide (GTK_WIDGET (self->image));
        gtk_widget_show (GTK_WIDGET (self->spinner));
+       self->mode = PROMPT_NONE;
 }
 
 static const gchar *
@@ -370,6 +401,37 @@ handle_password_response (GcrPrompterDialog *self)
 }
 
 static void
+gcr_prompter_dialog_realize (GtkWidget *widget)
+{
+       gboolean modal = FALSE;
+       const gchar *value;
+       gulong caller_window_id;
+       GdkWindow *caller_window;
+       GdkWindow *self_window;
+       GdkDisplay *display;
+       gchar *end;
+
+       GTK_WIDGET_CLASS (gcr_prompter_dialog_parent_class)->realize (widget);
+
+       value= gcr_system_prompter_get_caller_window (the_prompter);
+       if (value) {
+               caller_window_id = strtoul (value, &end, 10);
+               if (caller_window_id && end && end[0] == '\0') {
+                       display = gtk_widget_get_display (widget);
+                       caller_window = gdk_x11_window_foreign_new_for_display (display, caller_window_id);
+                       if (caller_window) {
+                               self_window = gtk_widget_get_window (widget);
+                               gdk_window_set_transient_for (self_window, caller_window);
+                               g_object_unref (caller_window);
+                               modal = TRUE;
+                       }
+               }
+       }
+
+       gtk_window_set_modal (GTK_WINDOW (widget), modal);
+}
+
+static void
 gcr_prompter_dialog_response (GtkDialog *dialog,
                               gint response_id)
 {
@@ -395,54 +457,70 @@ gcr_prompter_dialog_init (GcrPrompterDialog *self)
        GtkDialog *dialog;
        GtkWidget *widget;
        GtkWidget *entry;
+       GtkWidget *content;
        GtkGrid *grid;
 
        g_assert (GCR_IS_SYSTEM_PROMPTER (the_prompter));
 
-       g_signal_connect (the_prompter, "show-prompt", G_CALLBACK (on_show_prompt), self);
+       g_signal_connect (the_prompter, "open", G_CALLBACK (on_open_prompt), self);
        g_signal_connect (the_prompter, "prompt-password", G_CALLBACK (on_prompt_password), self);
        g_signal_connect (the_prompter, "prompt-confirm", G_CALLBACK (on_prompt_confirm), self);
        g_signal_connect (the_prompter, "responded", G_CALLBACK (on_responded), self);
-       g_signal_connect (the_prompter, "hide-prompt", G_CALLBACK (on_hide_prompt), self);
+       g_signal_connect (the_prompter, "close", G_CALLBACK (on_close_prompt), self);
 
        dialog = GTK_DIALOG (self);
        gtk_dialog_add_buttons (dialog,
-                               _("Continue"), GTK_RESPONSE_OK,
                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                               _("Continue"), GTK_RESPONSE_OK,
                                NULL);
 
+       content = gtk_dialog_get_content_area (dialog);
+
        grid = GTK_GRID (gtk_grid_new ());
+       gtk_container_set_border_width (GTK_CONTAINER (grid), 6);
+       gtk_widget_set_hexpand (GTK_WIDGET (grid), TRUE);
+       gtk_grid_set_column_spacing (grid, 12);
+       gtk_grid_set_row_spacing (grid, 6);
 
        /* The prompt image */
        self->image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
-                                               GTK_ICON_SIZE_DIALOG),
+                                               GTK_ICON_SIZE_DIALOG);
+       gtk_widget_set_valign (self->image, GTK_ALIGN_START);
        gtk_grid_attach (grid, self->image, -1, 0, 1, 4);
        gtk_widget_show (self->image);
 
        /* The prompt spinner */
        self->spinner = gtk_spinner_new ();
+       gtk_widget_set_valign (self->image, GTK_ALIGN_START);
        gtk_grid_attach (grid, self->spinner, -2, -1, 1, 4);
        gtk_widget_show (self->spinner);
 
-       /* The title label */
+       /* The message label */
        widget = gtk_label_new ("");
        attrs = pango_attr_list_new ();
        pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
        pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_LARGE));
        gtk_label_set_attributes (GTK_LABEL (widget), attrs);
        pango_attr_list_unref (attrs);
-       g_object_bind_property (the_prompter, "title", widget, "label", G_BINDING_DEFAULT);
+       gtk_widget_set_halign (widget, GTK_ALIGN_START);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_widget_set_margin_bottom (widget, 8);
+       g_object_bind_property (the_prompter, "message", widget, "label", G_BINDING_DEFAULT);
        gtk_grid_attach (grid, widget, 0, 0, 2, 1);
        gtk_widget_show (widget);
 
        /* The description label */
        widget = gtk_label_new ("");
+       gtk_widget_set_halign (widget, GTK_ALIGN_START);
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_widget_set_margin_bottom (widget, 4);
        g_object_bind_property (the_prompter, "description", widget, "label", G_BINDING_DEFAULT);
        gtk_grid_attach (grid, widget, 0, 1, 2, 1);
        gtk_widget_show (widget);
 
        /* The password label */
        widget = gtk_label_new (_("Password:"));
+       gtk_widget_set_halign (widget, GTK_ALIGN_START);
        g_object_bind_property (self, "password-visible", widget, "visible", G_BINDING_DEFAULT);
        gtk_grid_attach (grid, widget, 0, 2, 1, 1);
 
@@ -450,23 +528,27 @@ gcr_prompter_dialog_init (GcrPrompterDialog *self)
        self->password_buffer = gcr_secure_entry_buffer_new ();
        entry = gtk_entry_new_with_buffer (self->password_buffer);
        gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
+       gtk_widget_set_hexpand (widget, TRUE);
        g_object_bind_property (self, "password-visible", entry, "visible", G_BINDING_DEFAULT);
        gtk_grid_attach (grid, entry, 1, 2, 1, 1);
 
        /* The confirm label */
        widget = gtk_label_new (_("Confirm:"));
+       gtk_widget_set_halign (widget, GTK_ALIGN_START);
        g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
        gtk_grid_attach (grid, widget, 0, 3, 1, 1);
 
        /* The confirm entry */
        self->confirm_buffer = gcr_secure_entry_buffer_new ();
        widget = gtk_entry_new_with_buffer (self->password_buffer);
+       gtk_widget_set_hexpand (widget, TRUE);
        gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
        g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
        gtk_grid_attach (grid, widget, 1, 3, 1, 1);
 
        /* The quality progress bar */
        widget = gtk_progress_bar_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
        g_object_bind_property (self, "confirm-visible", widget, "visible", G_BINDING_DEFAULT);
        gtk_grid_attach (grid, widget, 1, 4, 1, 1);
        g_signal_connect (entry, "changed", G_CALLBACK (on_password_changed), widget);
@@ -489,8 +571,7 @@ gcr_prompter_dialog_init (GcrPrompterDialog *self)
        g_object_bind_property (the_prompter, "choice-chosen", widget, "active", G_BINDING_BIDIRECTIONAL);
        gtk_grid_attach (grid, widget, 0, 6, 2, 1);
 
-       gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)),
-                          GTK_WIDGET (grid));
+       gtk_container_add (GTK_CONTAINER (content), GTK_WIDGET (grid));
        gtk_widget_show (GTK_WIDGET (grid));
 
        g_signal_connect (self, "map-event", G_CALLBACK (grab_keyboard), self);
@@ -541,6 +622,9 @@ gcr_prompter_dialog_class_init (GcrPrompterDialogClass *klass)
 {
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
        GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       widget_class->realize = gcr_prompter_dialog_realize;
 
        gobject_class->finalize = gcr_prompter_dialog_finalize;
        gobject_class->get_property = gcr_prompter_dialog_get_property;
@@ -577,7 +661,7 @@ on_name_acquired (GDBusConnection *connection,
                   const gchar *name,
                   gpointer user_data)
 {
-       g_printerr ("bus name acquired");
+
 }
 
 static void
@@ -585,7 +669,6 @@ on_name_lost (GDBusConnection *connection,
               const gchar *name,
               gpointer user_data)
 {
-       g_printerr ("bus name lost, quitting");
        gtk_main_quit ();
 }
 
index eeb615c..165f3e3 100644 (file)
@@ -202,6 +202,17 @@ gcr_system_prompt_get_property (GObject *obj,
 }
 
 static void
+gcr_system_prompt_constructed (GObject *obj)
+{
+       GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
+
+       G_OBJECT_CLASS (gcr_system_prompt_parent_class)->constructed (obj);
+
+       if (self->pv->prompter_bus_name == NULL)
+               self->pv->prompter_bus_name = g_strdup (GCR_DBUS_PROMPTER_BUS_NAME);
+}
+
+static void
 gcr_system_prompt_dispose (GObject *obj)
 {
        GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
@@ -253,6 +264,7 @@ gcr_system_prompt_class_init (GcrSystemPromptClass *klass)
 {
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
+       gobject_class->constructed = gcr_system_prompt_constructed;
        gobject_class->get_property = gcr_system_prompt_get_property;
        gobject_class->set_property = gcr_system_prompt_set_property;
        gobject_class->dispose = gcr_system_prompt_dispose;
@@ -589,7 +601,7 @@ gcr_system_prompt_real_init (GInitable *initable,
                                                   G_DBUS_CALL_FLAGS_NONE,
                                                   -1, cancellable, error);
                if (ret == NULL) {
-                       _gcr_debug ("failed to open prompt: %s",
+                       _gcr_debug ("failed to open prompt %s: %s", self->pv->prompter_bus_name,
                                    egg_error_result_message (error));
                        return FALSE;
                }
@@ -599,7 +611,7 @@ gcr_system_prompt_real_init (GInitable *initable,
                g_variant_get (ret, "(o)", &self->pv->prompt_path);
                g_variant_unref (ret);
 
-               _gcr_debug ("opened prompt: %s", self->pv->prompt_path);
+               _gcr_debug ("opened prompt %s: %s", self->pv->prompter_bus_name, self->pv->prompt_path);
        }
 
        /* 3. Create a dbus proxy */
@@ -689,13 +701,15 @@ on_prompter_begin_prompting (GObject *source,
                g_variant_get (ret, "(o)", &self->pv->prompt_path);
                g_variant_unref (ret);
 
-               _gcr_debug ("opened prompt: %s", self->pv->prompt_path);
+               _gcr_debug ("opened prompt %s: %s",
+                           self->pv->prompter_bus_name, self->pv->prompt_path);
 
                g_return_if_fail (self->pv->prompt_path != NULL);
                perform_init_async (self, res);
 
        } else {
-               _gcr_debug ("failed to open prompt: %s", egg_error_message (error));
+               _gcr_debug ("failed to open prompt %s: %s",
+                           self->pv->prompter_bus_name, egg_error_message (error));
 
                g_simple_async_result_take_error (res, error);
                g_simple_async_result_complete (res);
index 173d0ec..3f8546b 100644 (file)
@@ -665,6 +665,25 @@ gcr_system_prompter_class_init (GcrSystemPrompterClass *klass)
 }
 
 static void
+emit_prompter_ready (GcrSystemPrompter *self)
+{
+       GError *error = NULL;
+
+       /* Now let everyone else know, we're ready! */
+       g_dbus_connection_emit_signal (self->pv->connection, NULL,
+                                      GCR_DBUS_PROMPTER_OBJECT_PATH,
+                                      GCR_DBUS_PROMPTER_INTERFACE,
+                                      GCR_DBUS_PROMPTER_SIGNAL_READY,
+                                      g_variant_new ("()"),
+                                      &error);
+
+       if (error != NULL) {
+               g_warning ("couldn't emit prompter ready signal: %s", egg_error_message (error));
+               g_error_free (error);
+       }
+}
+
+static void
 on_owner_vanished (GDBusConnection *connection,
                    const gchar *name,
                    gpointer user_data)
@@ -675,9 +694,7 @@ on_owner_vanished (GDBusConnection *connection,
                gcr_system_prompter_respond_cancelled (self);
 
        finish_prompting (self);
-
-       /* Now let everyone else know, we're ready! */
-       _gcr_prompter_emit_prompter_ready (GCR_PROMPTER (self));
+       emit_prompter_ready (self);
 }
 
 static GVariant *
@@ -769,6 +786,8 @@ prompter_method_finish_prompting (GcrSystemPrompter *self,
        finish_prompting (self);
 
        g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+
+       emit_prompter_ready (self);
 }
 
 static void
@@ -1022,16 +1041,27 @@ gcr_system_prompter_set_choice_chosen (GcrSystemPrompter *self,
        prompt_emit_changed (self, GCR_DBUS_PROMPT_PROPERTY_CHOICE_CHOSEN);
 }
 
+/**
+ * gcr_system_prompter_get_caller_window:
+ * @self: a prompter
+ *
+ * Get a string containing the callers window identifier. If the prompter
+ * supports making its prompts transient for
+ */
 const gchar *
 gcr_system_prompter_get_caller_window (GcrSystemPrompter *self)
 {
        GVariant *variant;
+       const gchar *window;
 
        g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (self), NULL);
 
        variant = g_hash_table_lookup (self->pv->properties, GCR_DBUS_PROMPT_PROPERTY_CALLER_WINDOW);
        g_return_val_if_fail (variant != NULL, NULL);
-       return g_variant_get_string (variant, NULL);
+       window = g_variant_get_string (variant, NULL);
+       if (!window || !window[0])
+               return NULL;
+       return window;
 }
 
 /**
@@ -1149,6 +1179,14 @@ gcr_system_prompter_respond_confirmed (GcrSystemPrompter *self)
        g_signal_emit (self, signals[RESPONDED], 0);
 }
 
+/**
+ * gcr_system_prompter_new:
+ *
+ * Create a new system prompter service. This prompter won't do anything unless
+ * you connect to its signals and show appropriate prompts.
+ *
+ * Returns: (transfer full): a new prompter service
+ */
 GcrSystemPrompter *
 gcr_system_prompter_new (void)
 {
diff --git a/gcr/org.gnome.keyring.Prompter.service.in b/gcr/org.gnome.keyring.Prompter.service.in
new file mode 100644 (file)
index 0000000..5bf95eb
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.keyring.Prompter
+Exec=@libexecdir@/gcr-prompter
index 165c7ec..f4a945c 100644 (file)
@@ -67,6 +67,7 @@ noinst_PROGRAMS = \
        frob-openpgp \
        frob-parser \
        frob-request \
+       frob-system-prompt \
        frob-unlock \
        frob-unlock-options