diff options
Diffstat (limited to 'panels/online-accounts/gnome-control-center-goa-helper.c')
-rw-r--r-- | panels/online-accounts/gnome-control-center-goa-helper.c | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/panels/online-accounts/gnome-control-center-goa-helper.c b/panels/online-accounts/gnome-control-center-goa-helper.c new file mode 100644 index 0000000..1b40c84 --- /dev/null +++ b/panels/online-accounts/gnome-control-center-goa-helper.c @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2022 Endless OS Foundation, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: + * Georges Basile Stavracas Neto <georges.stavracas@gmail.com> + */ + +#include "config.h" + +#include <gtk/gtk.h> +#include <glib/gi18n.h> + +#define GOA_API_IS_SUBJECT_TO_CHANGE +#define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE +#include <goabackend/goabackend.h> + +#ifdef HAVE_GTK_X11 +#include <gdk/gdkx.h> +#endif +#ifdef HAVE_GTK_WAYLAND +#include <gdk/gdkwayland.h> +#endif + +static GdkDisplay * +get_wayland_display (void) +{ + static GdkDisplay *wayland_display = NULL; + + if (wayland_display) + return wayland_display; + + gdk_set_allowed_backends ("wayland"); + wayland_display = gdk_display_open (NULL); + gdk_set_allowed_backends (NULL); + if (!wayland_display) + g_warning ("Failed to open Wayland display"); + + return wayland_display; +} + +static GdkDisplay * +get_x11_display (void) +{ + static GdkDisplay *x11_display = NULL; + + if (x11_display) + return x11_display; + + gdk_set_allowed_backends ("x11"); + x11_display = gdk_display_open (NULL); + gdk_set_allowed_backends (NULL); + if (!x11_display) + g_warning ("Failed to open X11 display"); + + return x11_display; +} + +static void +set_external_parent_from_handle (GtkApplication *application, + GtkWindow *dialog, + const char *handle_str) +{ + GdkDisplay *display; + GtkWindow *fake_parent; + GdkScreen *screen; + +#ifdef HAVE_GTK_X11 + { + const char *x11_prefix = "x11:"; + if (g_str_has_prefix (handle_str, x11_prefix)) + { + display = get_x11_display (); + if (!display) + { + g_warning ("No X display connection, ignoring X11 parent"); + return; + } + } + } +#endif +#ifdef HAVE_GTK_WAYLAND + { + const char *wayland_prefix = "wayland:"; + + if (g_str_has_prefix (handle_str, wayland_prefix)) + { + display = get_wayland_display (); + if (!display) + { + g_warning ("No Wayland display connection, ignoring Wayland parent"); + return; + } + } + } +#endif + + screen = gdk_display_get_default_screen (gdk_display_get_default ()); + fake_parent = g_object_new (GTK_TYPE_APPLICATION_WINDOW, + "application", application, + "type", GTK_WINDOW_TOPLEVEL, + "screen", screen, + NULL); + g_object_ref_sink (fake_parent); + + gtk_window_set_transient_for (dialog, GTK_WINDOW (fake_parent)); + gtk_window_set_modal (dialog, TRUE); + gtk_widget_realize (GTK_WIDGET (dialog)); + +#ifdef HAVE_GTK_X11 + { + const char *x11_prefix = "x11:"; + if (g_str_has_prefix (handle_str, x11_prefix)) + { + GdkWindow *foreign_gdk_window; + int xid; + + errno = 0; + xid = strtol (handle_str + strlen (x11_prefix), NULL, 16); + if (errno != 0) + { + g_warning ("Failed to reference external X11 window, invalid XID %s", handle_str); + return; + } + + foreign_gdk_window = gdk_x11_window_foreign_new_for_display (display, xid); + if (!foreign_gdk_window) + { + g_warning ("Failed to create foreign window for XID %d", xid); + return; + } + + gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (dialog)), + foreign_gdk_window); + } + } +#endif +#ifdef HAVE_GTK_WAYLAND + { + const char *wayland_prefix = "wayland:"; + + if (g_str_has_prefix (handle_str, wayland_prefix)) + { + const char *wayland_handle_str = handle_str + strlen (wayland_prefix); + + if (!gdk_wayland_window_set_transient_for_exported (gtk_widget_get_window (GTK_WIDGET (dialog)), + (char *) wayland_handle_str)) + { + g_warning ("Failed to set window transient for external parent"); + return; + } + } + } +#endif + + gtk_window_present (dialog); +} + +/* create-account */ + +static void +on_application_activate_create_account_cb (GtkApplication *application, + char **argv) +{ + g_autoptr(GoaProvider) provider = NULL; + g_autoptr(GoaClient) client = NULL; + g_autoptr(GError) error = NULL; + GoaAccount *account; + GtkWidget *content_area; + GtkWidget *dialog; + GoaObject *object; + + client = goa_client_new_sync (NULL, &error); + if (error) + { + g_printerr ("Error retrieving online accounts client"); + exit (EXIT_FAILURE); + return; + } + + + /* Find the provider with a matching type */ + provider = goa_provider_get_for_provider_type (argv[2]); + if (!provider) + { + g_printerr ("Provider type not supported"); + exit (EXIT_FAILURE); + return; + } + + dialog = g_object_new (GTK_TYPE_DIALOG, + "use-header-bar", 1, + "default-width", 500, + "default-height", 350, + NULL); + g_signal_connect_swapped (dialog, "response", G_CALLBACK (g_application_quit), application); + set_external_parent_from_handle (application, GTK_WINDOW (dialog), argv[3]); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 0); + + object = goa_provider_add_account (provider, + client, + GTK_DIALOG (dialog), + GTK_BOX (content_area), + &error); + if (error) + { + g_printerr ("Failed to create account: %s", error->message); + exit (EXIT_FAILURE); + return; + } + + account = goa_object_peek_account (object); + g_print ("%s", goa_account_get_id (account)); +} + +static int +create_account (int argc, + char **argv) +{ + g_autoptr(GtkApplication) application = NULL; + + gtk_init (&argc, &argv); + + if (argc != 4) + { + g_printerr ("Not enough arguments"); + return EXIT_FAILURE; + } + + application = gtk_application_new ("org.gnome.Settings.GoaHelper", + G_APPLICATION_FLAGS_NONE); + g_signal_connect (application, "activate", G_CALLBACK (on_application_activate_create_account_cb), argv); + + return g_application_run (G_APPLICATION (application), 0, NULL); +} + +/* list-providers */ + +typedef struct { + GMainLoop *mainloop; + GList *providers; + GError *error; +} GetAllProvidersData; + +static void +get_all_providers_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + g_autolist(GoaProvider) providers = NULL; + GetAllProvidersData *data; + + data = user_data; + + goa_provider_get_all_finish (&providers, res, &data->error); + if (data->error) + goto out; + + data->providers = g_steal_pointer (&providers); + +out: + g_main_loop_quit (data->mainloop); +} + +static GList * +get_all_providers (GError **error) +{ + GetAllProvidersData data = (GetAllProvidersData) { + .mainloop = g_main_loop_new (NULL, FALSE), + .providers = NULL, + .error = NULL, + }; + + goa_provider_get_all (get_all_providers_cb, &data); + + g_main_loop_run (data.mainloop); + + if (data.error) + g_propagate_error (error, data.error); + + return data.providers; +} + +static int +list_providers (int argc, + char **argv) +{ + g_autofree char *serialized_result = NULL; + g_autolist(GoaProvider) providers = NULL; + g_autoptr(GVariant) result = NULL; + g_autoptr(GError) error = NULL; + GVariantBuilder b; + GList *l; + + providers = get_all_providers (&error); + + if (error) + { + g_printerr ("%s", error->message); + return EXIT_FAILURE; + } + + g_variant_builder_init (&b, G_VARIANT_TYPE ("a(ssviu)")); + for (l = providers; l; l = l->next) + { + GoaProvider *provider = l->data; + g_autofree char *name = NULL; + g_autoptr(GVariant) icon_variant = NULL; + g_autoptr(GIcon) icon = NULL; + + name = goa_provider_get_provider_name (provider, NULL); + icon = goa_provider_get_provider_icon (provider, NULL); + icon_variant = g_icon_serialize (icon); + + g_variant_builder_add (&b, "(ssviu)", + goa_provider_get_provider_type (provider), + name, + icon_variant, + goa_provider_get_provider_features (provider), + goa_provider_get_credentials_generation (provider)); + } + result = g_variant_builder_end (&b); + + serialized_result = g_variant_print (result, TRUE); + g_print ("%s", serialized_result); + + return EXIT_SUCCESS; +} + +/* show-account */ + +static void +on_remove_button_clicked_cb (GtkButton *button, + GApplication *application) +{ + g_print ("remove"); + g_application_quit (application); +} + +static void +on_application_activate_show_account_cb (GtkApplication *application, + char **argv) +{ + g_autoptr(GoaProvider) provider = NULL; + g_autoptr(GoaObject) object = NULL; + g_autoptr(GoaClient) client = NULL; + g_autoptr(GError) error = NULL; + g_autofree char *title = NULL; + GoaAccount *account; + GtkWidget *content_area; + GtkWidget *button; + GtkWidget *dialog; + GtkWidget *box; + const char *provider_type; + + client = goa_client_new_sync (NULL, &error); + if (error) + { + g_printerr ("Error retrieving online accounts client"); + exit (EXIT_FAILURE); + return; + } + + object = goa_client_lookup_by_id (client, argv[2]); + if (!object) + { + g_printerr ("Online account does not exist"); + exit (EXIT_FAILURE); + return; + } + + /* Find the provider with a matching type */ + account = goa_object_get_account (object); + provider_type = goa_account_get_provider_type (account); + provider = goa_provider_get_for_provider_type (provider_type); + if (!provider) + { + g_printerr ("Provider type not supported"); + exit (EXIT_FAILURE); + return; + } + + dialog = g_object_new (GTK_TYPE_DIALOG, + "use-header-bar", 1, + NULL); + /* Keep account alive so that the switches are still bound to it */ + g_object_set_data_full (G_OBJECT (dialog), "goa-account", account, g_object_unref); + g_signal_connect_swapped (dialog, "response", G_CALLBACK (g_application_quit), application); + set_external_parent_from_handle (application, GTK_WINDOW (dialog), argv[3]); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 42); + gtk_widget_set_margin_bottom (box, 24); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 0); + gtk_container_add (GTK_CONTAINER (content_area), box); + + goa_provider_show_account (provider, + client, + object, + GTK_BOX (box), + NULL, + NULL); + + /* + * The above call doesn't set any widgets to visible, so we have to do that. + * https://gitlab.gnome.org/GNOME/gnome-online-accounts/issues/56 + */ + gtk_widget_show_all (box); + + /* translators: This is the title of the "Show Account" dialog. The + * %s is the name of the provider. e.g., 'Google'. */ + title = g_strdup_printf (_("%s Account"), goa_account_get_provider_name (account)); + gtk_window_set_title (GTK_WINDOW (dialog), title); + + button = gtk_button_new_with_label (_("Remove Account")); + gtk_widget_set_margin_start (box, 24); + gtk_widget_set_margin_end (box, 24); + gtk_widget_set_halign (button, GTK_ALIGN_END); + gtk_widget_set_valign (button, GTK_ALIGN_END); + gtk_widget_set_visible (button, !goa_account_get_is_locked (account)); + gtk_style_context_add_class (gtk_widget_get_style_context (button), "destructive-action"); + gtk_container_add (GTK_CONTAINER (box), button); + g_signal_connect (button, "clicked", G_CALLBACK (on_remove_button_clicked_cb), application); +} + +static int +show_account (int argc, + char **argv) +{ + g_autoptr(GtkApplication) application = NULL; + + gtk_init (&argc, &argv); + + if (argc != 4) + { + g_printerr ("Not enough arguments"); + return EXIT_FAILURE; + } + + application = gtk_application_new ("org.gnome.Settings.GoaHelper", + G_APPLICATION_FLAGS_NONE); + g_signal_connect (application, "activate", G_CALLBACK (on_application_activate_show_account_cb), argv); + + return g_application_run (G_APPLICATION (application), 0, NULL); +} + +struct { + const char *command_name; + int (*command_func) (int argc, + char **argv); +} commands[] = { + { "create-account", create_account, }, + { "list-providers", list_providers, }, + { "show-account", show_account, }, +}; + + +static void +log_handler (const gchar *domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer user_data) +{ + g_printerr ("%s: %s", domain, message); +} + +int +main (int argc, + char **argv) +{ + gsize i; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + if (argc < 2) + return EXIT_FAILURE; + + g_log_set_default_handler (log_handler, NULL); + + for (i = 0; i < G_N_ELEMENTS (commands); i++) + { + if (g_strcmp0 (commands[i].command_name, argv[1]) == 0) + return commands[i].command_func (argc, argv); + } + + return EXIT_SUCCESS; +} |