summaryrefslogtreecommitdiffstats
path: root/gnome-session/gsm-shell.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:49:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:49:37 +0000
commit35504d91654321ff2b378229ff13150f53d5aad2 (patch)
treecb85edefc751b37c8423d78c5e5888f42cc01e4b /gnome-session/gsm-shell.c
parentInitial commit. (diff)
downloadgnome-session-35504d91654321ff2b378229ff13150f53d5aad2.tar.xz
gnome-session-35504d91654321ff2b378229ff13150f53d5aad2.zip
Adding upstream version 43.0.upstream/43.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gnome-session/gsm-shell.c')
-rw-r--r--gnome-session/gsm-shell.c507
1 files changed, 507 insertions, 0 deletions
diff --git a/gnome-session/gsm-shell.c b/gnome-session/gsm-shell.c
new file mode 100644
index 0000000..04cfa2f
--- /dev/null
+++ b/gnome-session/gsm-shell.c
@@ -0,0 +1,507 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc
+ *
+ * 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, 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "gsm-inhibitor.h"
+#include "gsm-shell.h"
+
+#define SHELL_NAME "org.gnome.Shell"
+#define SHELL_PATH "/org/gnome/Shell"
+#define SHELL_INTERFACE "org.gnome.Shell"
+
+#define SHELL_END_SESSION_DIALOG_PATH "/org/gnome/SessionManager/EndSessionDialog"
+#define SHELL_END_SESSION_DIALOG_INTERFACE "org.gnome.SessionManager.EndSessionDialog"
+
+#define AUTOMATIC_ACTION_TIMEOUT 60
+
+#define GSM_SHELL_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_SHELL, GsmShellPrivate))
+
+struct _GsmShellPrivate
+{
+ GDBusProxy *end_session_dialog_proxy;
+ GsmStore *inhibitors;
+
+ guint32 is_running : 1;
+
+ gboolean dialog_is_open;
+ GsmShellEndSessionDialogType end_session_dialog_type;
+
+ guint update_idle_id;
+ guint watch_id;
+};
+
+enum {
+ PROP_0,
+ PROP_IS_RUNNING
+};
+
+enum {
+ END_SESSION_DIALOG_OPENED = 0,
+ END_SESSION_DIALOG_OPEN_FAILED,
+ END_SESSION_DIALOG_CLOSED,
+ END_SESSION_DIALOG_CANCELED,
+ END_SESSION_DIALOG_CONFIRMED_LOGOUT,
+ END_SESSION_DIALOG_CONFIRMED_SHUTDOWN,
+ END_SESSION_DIALOG_CONFIRMED_REBOOT,
+ NUMBER_OF_SIGNALS
+};
+
+static guint signals[NUMBER_OF_SIGNALS] = { 0 };
+
+static void gsm_shell_class_init (GsmShellClass *klass);
+static void gsm_shell_init (GsmShell *ck);
+static void gsm_shell_finalize (GObject *object);
+
+static void queue_end_session_dialog_update (GsmShell *shell);
+
+G_DEFINE_TYPE (GsmShell, gsm_shell, G_TYPE_OBJECT);
+
+static void
+gsm_shell_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GsmShell *shell = GSM_SHELL (object);
+
+ switch (prop_id) {
+ case PROP_IS_RUNNING:
+ g_value_set_boolean (value,
+ shell->priv->is_running);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
+ prop_id,
+ pspec);
+ }
+}
+
+static void
+gsm_shell_class_init (GsmShellClass *shell_class)
+{
+ GObjectClass *object_class;
+ GParamSpec *param_spec;
+
+ object_class = G_OBJECT_CLASS (shell_class);
+
+ object_class->finalize = gsm_shell_finalize;
+ object_class->get_property = gsm_shell_get_property;
+
+ param_spec = g_param_spec_boolean ("is-running",
+ "Is running",
+ "Whether GNOME Shell is running in the session",
+ FALSE,
+ G_PARAM_READABLE);
+
+ g_object_class_install_property (object_class, PROP_IS_RUNNING,
+ param_spec);
+
+ signals [END_SESSION_DIALOG_OPENED] =
+ g_signal_new ("end-session-dialog-opened",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_opened),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals [END_SESSION_DIALOG_OPEN_FAILED] =
+ g_signal_new ("end-session-dialog-open-failed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_open_failed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals [END_SESSION_DIALOG_CLOSED] =
+ g_signal_new ("end-session-dialog-closed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_closed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals [END_SESSION_DIALOG_CANCELED] =
+ g_signal_new ("end-session-dialog-canceled",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_canceled),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals [END_SESSION_DIALOG_CONFIRMED_LOGOUT] =
+ g_signal_new ("end-session-dialog-confirmed-logout",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_confirmed_logout),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals [END_SESSION_DIALOG_CONFIRMED_SHUTDOWN] =
+ g_signal_new ("end-session-dialog-confirmed-shutdown",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_confirmed_shutdown),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ signals [END_SESSION_DIALOG_CONFIRMED_REBOOT] =
+ g_signal_new ("end-session-dialog-confirmed-reboot",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_confirmed_reboot),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (shell_class, sizeof (GsmShellPrivate));
+}
+
+static void
+on_shell_name_vanished (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GsmShell *shell = user_data;
+ shell->priv->is_running = FALSE;
+}
+
+static void
+on_shell_name_appeared (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ GsmShell *shell = user_data;
+ shell->priv->is_running = TRUE;
+}
+
+static void
+gsm_shell_ensure_connection (GsmShell *shell)
+{
+ if (shell->priv->watch_id != 0) {
+ return;
+ }
+
+ shell->priv->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+ SHELL_NAME,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_shell_name_appeared,
+ on_shell_name_vanished,
+ shell, NULL);
+}
+
+static void
+gsm_shell_init (GsmShell *shell)
+{
+ shell->priv = GSM_SHELL_GET_PRIVATE (shell);
+
+ gsm_shell_ensure_connection (shell);
+}
+
+static void
+gsm_shell_finalize (GObject *object)
+{
+ GsmShell *shell;
+ GObjectClass *parent_class;
+
+ shell = GSM_SHELL (object);
+
+ parent_class = G_OBJECT_CLASS (gsm_shell_parent_class);
+
+ g_object_unref (shell->priv->inhibitors);
+
+ if (shell->priv->watch_id != 0) {
+ g_bus_unwatch_name (shell->priv->watch_id);
+ shell->priv->watch_id = 0;
+ }
+
+ if (parent_class->finalize != NULL) {
+ parent_class->finalize (object);
+ }
+}
+
+GsmShell *
+gsm_shell_new (void)
+{
+ GsmShell *shell;
+
+ shell = g_object_new (GSM_TYPE_SHELL, NULL);
+
+ return shell;
+}
+
+GsmShell *
+gsm_get_shell (void)
+{
+ static GsmShell *shell = NULL;
+
+ if (shell == NULL) {
+ shell = gsm_shell_new ();
+ }
+
+ return g_object_ref (shell);
+}
+
+gboolean
+gsm_shell_is_running (GsmShell *shell)
+{
+ gsm_shell_ensure_connection (shell);
+
+ return shell->priv->is_running;
+}
+
+static gboolean
+add_inhibitor_to_array (const char *id,
+ GsmInhibitor *inhibitor,
+ GVariantBuilder *builder)
+{
+ g_variant_builder_add (builder, "o", gsm_inhibitor_peek_id (inhibitor));
+ return FALSE;
+}
+
+static GVariant *
+get_array_from_store (GsmStore *inhibitors)
+{
+ GVariantBuilder builder;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
+ gsm_store_foreach (inhibitors,
+ (GsmStoreFunc) add_inhibitor_to_array,
+ &builder);
+
+ return g_variant_builder_end (&builder);
+}
+
+static void
+on_open_finished (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GsmShell *shell = user_data;
+ GError *error;
+
+ if (shell->priv->update_idle_id != 0) {
+ g_source_remove (shell->priv->update_idle_id);
+ shell->priv->update_idle_id = 0;
+ }
+
+ shell->priv->dialog_is_open = FALSE;
+
+ error = NULL;
+ g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
+
+ if (error != NULL) {
+ g_warning ("Unable to open shell end session dialog: %s", error->message);
+ g_error_free (error);
+
+ g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_OPEN_FAILED], 0);
+ return;
+ }
+
+ g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_OPENED], 0);
+}
+
+static void
+on_end_session_dialog_dbus_signal (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ GsmShell *shell)
+{
+ struct {
+ const char *name;
+ int index;
+ } signal_map[] = {
+ { "Closed", END_SESSION_DIALOG_CLOSED },
+ { "Canceled", END_SESSION_DIALOG_CANCELED },
+ { "ConfirmedLogout", END_SESSION_DIALOG_CONFIRMED_LOGOUT },
+ { "ConfirmedReboot", END_SESSION_DIALOG_CONFIRMED_REBOOT },
+ { "ConfirmedShutdown", END_SESSION_DIALOG_CONFIRMED_SHUTDOWN },
+ { NULL, -1 }
+ };
+ int signal_index = -1;
+ int i;
+
+ for (i = 0; signal_map[i].name != NULL; i++) {
+ if (g_strcmp0 (signal_map[i].name, signal_name) == 0) {
+ signal_index = signal_map[i].index;
+ break;
+ }
+ }
+
+ if (signal_index == -1)
+ return;
+
+ shell->priv->dialog_is_open = FALSE;
+
+ if (shell->priv->update_idle_id != 0) {
+ g_source_remove (shell->priv->update_idle_id);
+ shell->priv->update_idle_id = 0;
+ }
+
+ g_signal_handlers_disconnect_by_func (shell->priv->inhibitors,
+ G_CALLBACK (queue_end_session_dialog_update),
+ shell);
+
+ g_signal_emit (G_OBJECT (shell), signals[signal_index], 0);
+}
+
+static void
+on_end_session_dialog_name_owner_changed (GDBusProxy *proxy,
+ GParamSpec *pspec,
+ GsmShell *shell)
+{
+ gchar *name_owner;
+
+ name_owner = g_dbus_proxy_get_name_owner (proxy);
+ if (name_owner == NULL) {
+ g_clear_object (&shell->priv->end_session_dialog_proxy);
+ }
+
+ g_free (name_owner);
+}
+
+static gboolean
+on_need_end_session_dialog_update (GsmShell *shell)
+{
+ /* No longer need an update */
+ if (shell->priv->update_idle_id == 0)
+ return FALSE;
+
+ shell->priv->update_idle_id = 0;
+
+ gsm_shell_open_end_session_dialog (shell,
+ shell->priv->end_session_dialog_type,
+ shell->priv->inhibitors);
+ return FALSE;
+}
+
+static void
+queue_end_session_dialog_update (GsmShell *shell)
+{
+ if (shell->priv->update_idle_id != 0)
+ return;
+
+ shell->priv->update_idle_id = g_idle_add ((GSourceFunc) on_need_end_session_dialog_update,
+ shell);
+}
+
+gboolean
+gsm_shell_open_end_session_dialog (GsmShell *shell,
+ GsmShellEndSessionDialogType type,
+ GsmStore *inhibitors)
+{
+ GDBusProxy *proxy;
+ GError *error;
+
+ error = NULL;
+
+ if (shell->priv->dialog_is_open) {
+ g_return_val_if_fail (shell->priv->end_session_dialog_type == type,
+ FALSE);
+
+ return TRUE;
+ }
+
+ if (shell->priv->end_session_dialog_proxy == NULL) {
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ SHELL_NAME,
+ SHELL_END_SESSION_DIALOG_PATH,
+ SHELL_END_SESSION_DIALOG_INTERFACE,
+ NULL, &error);
+
+ if (error != NULL) {
+ g_critical ("Could not connect to the shell: %s",
+ error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ shell->priv->end_session_dialog_proxy = proxy;
+
+ g_signal_connect (proxy, "notify::g-name-owner",
+ G_CALLBACK (on_end_session_dialog_name_owner_changed),
+ shell);
+ g_signal_connect (proxy, "g-signal",
+ G_CALLBACK (on_end_session_dialog_dbus_signal),
+ shell);
+ }
+
+ g_dbus_proxy_call (shell->priv->end_session_dialog_proxy,
+ "Open",
+ g_variant_new ("(uuu@ao)",
+ type,
+ 0,
+ AUTOMATIC_ACTION_TIMEOUT,
+ get_array_from_store (inhibitors)),
+ G_DBUS_CALL_FLAGS_NONE,
+ G_MAXINT, NULL,
+ on_open_finished, shell);
+
+ g_object_ref (inhibitors);
+
+ if (shell->priv->inhibitors != NULL) {
+ g_signal_handlers_disconnect_by_func (shell->priv->inhibitors,
+ G_CALLBACK (queue_end_session_dialog_update),
+ shell);
+ g_object_unref (shell->priv->inhibitors);
+ }
+
+ shell->priv->inhibitors = inhibitors;
+
+ g_signal_connect_swapped (inhibitors, "added",
+ G_CALLBACK (queue_end_session_dialog_update),
+ shell);
+
+ g_signal_connect_swapped (inhibitors, "removed",
+ G_CALLBACK (queue_end_session_dialog_update),
+ shell);
+
+ shell->priv->dialog_is_open = TRUE;
+ shell->priv->end_session_dialog_type = type;
+
+ return TRUE;
+}
+
+void
+gsm_shell_close_end_session_dialog (GsmShell *shell)
+{
+ if (!shell->priv->end_session_dialog_proxy)
+ return;
+
+ shell->priv->dialog_is_open = FALSE;
+
+ g_dbus_proxy_call (shell->priv->end_session_dialog_proxy,
+ "Close",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL, NULL);
+}