summaryrefslogtreecommitdiffstats
path: root/plugins/common/daemon-skeleton-gtk.h
blob: 4e67706ed777ce5fe8b04958755c4c4ef2fe6eda (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/**
 * Create a gnome-settings-daemon helper easily
 *
 * #define NEW gsd_media_keys_manager_new
 * #define START gsd_media_keys_manager_start
 * #define MANAGER GsdMediaKeysManager
 * #include "gsd-media-keys-manager.h"
 *
 * #include "daemon-skeleton-gtk.h"
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <locale.h>

#include <glib-unix.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>

#include "gnome-settings-bus.h"

#ifndef PLUGIN_NAME
#error Include PLUGIN_CFLAGS in the daemon s CFLAGS
#endif /* !PLUGIN_NAME */

#define GNOME_SESSION_DBUS_NAME                     "org.gnome.SessionManager"
#define GNOME_SESSION_CLIENT_PRIVATE_DBUS_INTERFACE "org.gnome.SessionManager.ClientPrivate"

static MANAGER *manager = NULL;
static int timeout = -1;
static char *dummy_name = NULL;
static gboolean verbose = FALSE;

static GOptionEntry entries[] = {
        { "exit-time", 0, 0, G_OPTION_ARG_INT, &timeout, "Exit after n seconds time", NULL },
        { "dummy-name", 0, 0, G_OPTION_ARG_STRING, &dummy_name, "Name when using the dummy daemon", NULL },
        { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Verbose", NULL },
        {NULL}
};

static void
respond_to_end_session (GDBusProxy *proxy)
{
        /* we must answer with "EndSessionResponse" */
        g_dbus_proxy_call (proxy, "EndSessionResponse",
                           g_variant_new ("(bs)", TRUE, ""),
                           G_DBUS_CALL_FLAGS_NONE,
                           -1, NULL, NULL, NULL);
}

static void
do_stop (void)
{
        gtk_main_quit ();
}

static void
client_proxy_signal_cb (GDBusProxy *proxy,
                        gchar *sender_name,
                        gchar *signal_name,
                        GVariant *parameters,
                        gpointer user_data)
{
        if (g_strcmp0 (signal_name, "QueryEndSession") == 0) {
                g_debug ("Got QueryEndSession signal");
                respond_to_end_session (proxy);
        } else if (g_strcmp0 (signal_name, "EndSession") == 0) {
                g_debug ("Got EndSession signal");
                respond_to_end_session (proxy);
        } else if (g_strcmp0 (signal_name, "Stop") == 0) {
                g_debug ("Got Stop signal");
                do_stop ();
        }
}

static void
on_client_registered (GObject             *source_object,
                      GAsyncResult        *res,
                      gpointer             user_data)
{
        GVariant *variant;
        GDBusProxy *client_proxy;
        GError *error = NULL;
        gchar *object_path = NULL;

        variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
        if (!variant) {
                g_warning ("Unable to register client: %s", error->message);
                g_error_free (error);
                return;
        }

        g_variant_get (variant, "(o)", &object_path);

        g_debug ("Registered client at path %s", object_path);

        client_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, 0, NULL,
                                                      GNOME_SESSION_DBUS_NAME,
                                                      object_path,
                                                      GNOME_SESSION_CLIENT_PRIVATE_DBUS_INTERFACE,
                                                      NULL,
                                                      &error);
        if (!client_proxy) {
                g_warning ("Unable to get the session client proxy: %s", error->message);
                g_error_free (error);
                return;
        }

        g_signal_connect (client_proxy, "g-signal",
                          G_CALLBACK (client_proxy_signal_cb), NULL);

        g_free (object_path);
        g_variant_unref (variant);
}

static void
register_with_gnome_session (void)
{
	GDBusProxy *proxy;
	const char *startup_id;

	proxy = G_DBUS_PROXY (gnome_settings_bus_get_session_proxy ());
	startup_id = g_getenv ("DESKTOP_AUTOSTART_ID");
	g_dbus_proxy_call (proxy,
			   "RegisterClient",
			   g_variant_new ("(ss)", dummy_name ? dummy_name : PLUGIN_NAME, startup_id ? startup_id : ""),
			   G_DBUS_CALL_FLAGS_NONE,
			   -1,
			   NULL,
			   (GAsyncReadyCallback) on_client_registered,
			   NULL);

	/* DESKTOP_AUTOSTART_ID must not leak into child processes, because
	 * it can't be reused. Child processes will not know whether this is
	 * a genuine value or erroneous already-used value. */
	g_unsetenv ("DESKTOP_AUTOSTART_ID");
}

static void
set_empty_gtk_theme (gboolean set)
{
	static char *old_gtk_theme = NULL;

	if (set) {
		/* Override GTK_THEME to reduce overhead of CSS engine. By using
		 * GTK_THEME environment variable, GtkSettings is not allowed to
		 * initially parse the Adwaita theme.
		 *
		 * https://bugzilla.gnome.org/show_bug.cgi?id=780555 */
		old_gtk_theme = g_strdup (g_getenv ("GTK_THEME"));
		g_setenv ("GTK_THEME", "Disabled", TRUE);
	} else {
		/* GtkSettings has loaded, so we can drop GTK_THEME used to initialize
		 * our internal theme. Only the main thread accesses the GTK_THEME
		 * environment variable, so this is safe to release. */
		if (old_gtk_theme != NULL)
			g_setenv ("GTK_THEME", old_gtk_theme, TRUE);
		else
			g_unsetenv ("GTK_THEME");
	}
}

static gboolean
handle_sigterm (gpointer user_data)
{
  g_debug ("Got SIGTERM; shutting down ...");

  if (gtk_main_level () > 0)
    gtk_main_quit ();

  return G_SOURCE_REMOVE;
}

static void
install_signal_handler (void)
{
  g_autoptr(GSource) source = NULL;

  source = g_unix_signal_source_new (SIGTERM);

  g_source_set_callback (source, handle_sigterm, NULL, NULL);
  g_source_attach (source, NULL);
}

static void
bus_acquired_cb (GDBusConnection *connection,
                 const gchar *name,
                 gpointer user_data G_GNUC_UNUSED)
{
        g_debug ("%s: acquired bus %p for name %s", G_STRFUNC, connection, name);
}

static void
name_acquired_cb (GDBusConnection *connection,
                  const gchar *name,
                  gpointer user_data G_GNUC_UNUSED)
{
        g_debug ("%s: acquired name %s on bus %p", G_STRFUNC, name, connection);
}

static void
name_lost_cb (GDBusConnection *connection,
              const gchar *name,
              gpointer user_data G_GNUC_UNUSED)
{
        g_debug ("%s: lost name %s on bus %p", G_STRFUNC, name, connection);
}

int
main (int argc, char **argv)
{
        GError  *error = NULL;
        guint name_own_id;

        bindtextdomain (GETTEXT_PACKAGE, GNOME_SETTINGS_LOCALEDIR);
        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
        textdomain (GETTEXT_PACKAGE);
        setlocale (LC_ALL, "");

        set_empty_gtk_theme (TRUE);

#ifdef GDK_BACKEND
	{
		const gchar *setup_display = getenv ("GNOME_SETUP_DISPLAY");
		if (setup_display && *setup_display != '\0')
			g_setenv ("DISPLAY", setup_display, TRUE);
	}

        gdk_set_allowed_backends (GDK_BACKEND);

        /* GDK would fail to initialize with e.g. GDK_BACKEND=wayland */
        g_unsetenv ("GDK_BACKEND");
#endif

        error = NULL;
        if (! gtk_init_with_args (&argc, &argv, PLUGIN_NAME, entries, NULL, &error)) {
                if (error != NULL) {
                        fprintf (stderr, "%s\n", error->message);
                        g_error_free (error);
                }
                exit (1);
        }

        set_empty_gtk_theme (FALSE);

        if (verbose) {
                g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
                /* Work around GLib not flushing the output for us by explicitly
                 * setting buffering to a sane behaviour. This is important
                 * during testing when the output is not going to a TTY and
                 * we are reading messages from g_debug on stdout.
                 *
                 * See also
                 *  https://bugzilla.gnome.org/show_bug.cgi?id=792432
                 */
                setlinebuf (stdout);
        }

	if (timeout > 0) {
		guint id;
		id = g_timeout_add_seconds (timeout, (GSourceFunc) gtk_main_quit, NULL);
		g_source_set_name_by_id (id, "[gnome-settings-daemon] gtk_main_quit");
	}

        install_signal_handler ();

        manager = NEW ();
	register_with_gnome_session ();

        if (!START (manager, &error)) {
                fprintf (stderr, "Failed to start: %s\n", error->message);
                g_error_free (error);
                exit (1);
        }

	name_own_id = g_bus_own_name (G_BUS_TYPE_SESSION,
				      PLUGIN_DBUS_NAME,
				      G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE,
				      bus_acquired_cb,
				      name_acquired_cb,
				      name_lost_cb,
				      NULL, /* user_data */
				      NULL /* user_data_free_func */);

        gtk_main ();

        STOP (manager);

        g_object_unref (manager);
        g_bus_unown_name (name_own_id);

        return 0;
}