No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 #include "config.h"
4
5 #ifdef HAVE_MALLINFO
6 #include <malloc.h>
7 #endif
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include <clutter/clutter.h>
12 #include <clutter/x11/clutter-x11.h>
13 #include <gdk/gdk.h>
14 #include <gdk/gdkx.h>
15 #include <gtk/gtk.h>
16 #include <glib/gi18n-lib.h>
17 #include <girepository.h>
18 #include <meta/main.h>
19 #include <meta/meta-plugin.h>
20 #include <meta/prefs.h>
21 #include <atk-bridge.h>
22 #include <telepathy-glib/debug.h>
23 #include <telepathy-glib/debug-sender.h>
24
25 #include "shell-global.h"
26 #include "shell-global-private.h"
27 #include "shell-js.h"
28 #include "shell-perf-log.h"
29 #include "st.h"
30
31 #include <jsapi.h>
32
33 extern GType gnome_shell_plugin_get_type (void);
34
35 #define SHELL_DBUS_SERVICE "org.gnome.Shell"
36 #define MAGNIFIER_DBUS_SERVICE "org.gnome.Magnifier"
37
38 #define OVERRIDES_SCHEMA "org.gnome.shell.overrides"
39
40 #define WM_NAME "GNOME Shell"
41 #define GNOME_WM_KEYBINDINGS "Metacity,Mutter,GNOME Shell"
42
43 static gboolean is_gdm_mode = FALSE;
44 static char *session_mode = NULL;
45
46 #define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
47 #define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
48
49 static void
50 shell_dbus_acquire_name (GDBusProxy *bus,
51 guint32 request_name_flags,
52 guint32 *request_name_result,
53 gchar *name,
54 gboolean fatal)
55 {
56 GError *error = NULL;
57 GVariant *request_name_variant;
58
59 if (!(request_name_variant = g_dbus_proxy_call_sync (bus,
60 "RequestName",
61 g_variant_new ("(su)", name, request_name_flags),
62 0, /* call flags */
63 -1, /* timeout */
64 NULL, /* cancellable */
65 &error)))
66 {
67 g_printerr ("failed to acquire %s: %s\n", name, error->message);
68 if (!fatal)
69 return;
70 exit (1);
71 }
72 g_variant_get (request_name_variant, "(u)", request_name_result);
73 }
74
75 static void
76 shell_dbus_acquire_names (GDBusProxy *bus,
77 guint32 request_name_flags,
78 gchar *name,
79 gboolean fatal, ...) G_GNUC_NULL_TERMINATED;
80
81 static void
82 shell_dbus_acquire_names (GDBusProxy *bus,
83 guint32 request_name_flags,
84 gchar *name,
85 gboolean fatal, ...)
86 {
87 va_list al;
88 guint32 request_name_result;
89 va_start (al, fatal);
90 for (;;)
91 {
92 shell_dbus_acquire_name (bus,
93 request_name_flags,
94 &request_name_result,
95 name, fatal);
96 name = va_arg (al, gchar *);
97 if (!name)
98 break;
99 fatal = va_arg (al, gboolean);
100 }
101 va_end (al);
102 }
103
104 static void
105 shell_dbus_init (gboolean replace)
106 {
107 GDBusConnection *session;
108 GDBusProxy *bus;
109 GError *error = NULL;
110 guint32 request_name_flags;
111 guint32 request_name_result;
112
113 session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
114
115 if (error) {
116 g_printerr ("Failed to connect to session bus: %s", error->message);
117 exit (1);
118 }
119
120 bus = g_dbus_proxy_new_sync (session,
121 G_DBUS_PROXY_FLAGS_NONE,
122 NULL, /* interface info */
123 "org.freedesktop.DBus",
124 "/org/freedesktop/DBus",
125 "org.freedesktop.DBus",
126 NULL, /* cancellable */
127 &error);
128
129 request_name_flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
130 if (replace)
131 request_name_flags |= DBUS_NAME_FLAG_REPLACE_EXISTING;
132
133 shell_dbus_acquire_name (bus,
134 request_name_flags,
135 &request_name_result,
136 SHELL_DBUS_SERVICE, TRUE);
137 if (!(request_name_result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
138 || request_name_result == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER))
139 {
140 g_printerr (SHELL_DBUS_SERVICE " already exists on bus and --replace not specified\n");
141 exit (1);
142 }
143
144 /*
145 * We always specify REPLACE_EXISTING to ensure we kill off
146 * the existing service if it was running.
147 */
148 request_name_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
149
150 shell_dbus_acquire_names (bus,
151 request_name_flags,
152 /* Also grab org.gnome.Panel to replace any existing panel process */
153 "org.gnome.Panel", TRUE,
154 /* ...and the org.gnome.Magnifier service. */
155 MAGNIFIER_DBUS_SERVICE, FALSE,
156 /* ...and the org.freedesktop.Notifications service. */
157 "org.freedesktop.Notifications", FALSE,
158 NULL);
159 /* ...and the on-screen keyboard service */
160 shell_dbus_acquire_name (bus,
161 DBUS_NAME_FLAG_REPLACE_EXISTING,
162 &request_name_result,
163 "org.gnome.Caribou.Keyboard", FALSE);
164 g_object_unref (bus);
165 g_object_unref (session);
166 }
167
168 static void
169 shell_prefs_init (void)
170 {
171 meta_prefs_override_preference_schema ("attach-modal-dialogs",
172 OVERRIDES_SCHEMA);
173 meta_prefs_override_preference_schema ("dynamic-workspaces",
174 OVERRIDES_SCHEMA);
175 meta_prefs_override_preference_schema ("workspaces-only-on-primary",
176 OVERRIDES_SCHEMA);
177 meta_prefs_override_preference_schema ("button-layout",
178 OVERRIDES_SCHEMA);
179 meta_prefs_override_preference_schema ("edge-tiling",
180 OVERRIDES_SCHEMA);
181 meta_prefs_override_preference_schema ("focus-change-on-pointer-rest",
182 OVERRIDES_SCHEMA);
183 }
184
185 static void
186 shell_introspection_init (void)
187 {
188
189 g_irepository_prepend_search_path (MUTTER_TYPELIB_DIR);
190 g_irepository_prepend_search_path (GNOME_SHELL_PKGLIBDIR);
191 #if HAVE_BLUETOOTH
192 g_irepository_prepend_search_path (BLUETOOTH_DIR);
193 #endif
194
195 }
196
197 static void
198 malloc_statistics_callback (ShellPerfLog *perf_log,
199 gpointer data)
200 {
201 #ifdef HAVE_MALLINFO
202 struct mallinfo info = mallinfo ();
203
204 shell_perf_log_update_statistic_i (perf_log,
205 "malloc.arenaSize",
206 info.arena);
207 shell_perf_log_update_statistic_i (perf_log,
208 "malloc.mmapSize",
209 info.hblkhd);
210 shell_perf_log_update_statistic_i (perf_log,
211 "malloc.usedSize",
212 info.uordblks);
213 #endif
214 }
215
216 static void
217 shell_perf_log_init (void)
218 {
219 ShellPerfLog *perf_log = shell_perf_log_get_default ();
220
221 /* For probably historical reasons, mallinfo() defines the returned values,
222 * even those in bytes as int, not size_t. We're determined not to use
223 * more than 2G of malloc'ed memory, so are OK with that.
224 */
225 shell_perf_log_define_statistic (perf_log,
226 "malloc.arenaSize",
227 "Amount of memory allocated by malloc() with brk(), in bytes",
228 "i");
229 shell_perf_log_define_statistic (perf_log,
230 "malloc.mmapSize",
231 "Amount of memory allocated by malloc() with mmap(), in bytes",
232 "i");
233 shell_perf_log_define_statistic (perf_log,
234 "malloc.usedSize",
235 "Amount of malloc'ed memory currently in use",
236 "i");
237
238 shell_perf_log_add_statistics_callback (perf_log,
239 malloc_statistics_callback,
240 NULL, NULL);
241 }
242
243 static void
244 shell_a11y_init (void)
245 {
246 if (clutter_get_accessibility_enabled () == FALSE)
247 {
248 g_warning ("Accessibility: clutter has no accessibility enabled"
249 " skipping the atk-bridge load");
250 }
251 else
252 {
253 atk_bridge_adaptor_init (NULL, NULL);
254 }
255 }
256
257 static void
258 default_log_handler (const char *log_domain,
259 GLogLevelFlags log_level,
260 const char *message,
261 gpointer data)
262 {
263 TpDebugSender *sender = data;
264 GTimeVal now;
265
266 g_get_current_time (&now);
267
268 tp_debug_sender_add_message (sender, &now, log_domain, log_level, message);
269
270 /* Filter out telepathy-glib logs, we don't want to flood Shell's output
271 * with those. */
272 if (!g_str_has_prefix (log_domain, "tp-glib"))
273 g_log_default_handler (log_domain, log_level, message, data);
274 }
275
276 static void
277 shut_up (const char *domain,
278 GLogLevelFlags level,
279 const char *message,
280 gpointer user_data)
281 {
282 }
283
284 static gboolean
285 list_modes (const char *option_name,
286 const char *value,
287 gpointer data,
288 GError **error)
289 {
290 ShellGlobal *global;
291 GjsContext *context;
292 const char *script;
293 int status;
294
295 /* Many of our imports require global to be set, so rather than
296 * tayloring our imports carefully here to avoid that dependency,
297 * we just set it.
298 * ShellGlobal has some GTK+ dependencies, so initialize GTK+; we
299 * don't really care if it fails though (e.g. when running from a tty),
300 * so we mute all warnings */
301 g_log_set_default_handler (shut_up, NULL);
302 gtk_init_check (NULL, NULL);
303
304 _shell_global_init (NULL);
305 global = shell_global_get ();
306 context = _shell_global_get_gjs_context (global);
307
308 shell_introspection_init ();
309
310 script = "imports.ui.environment.init();"
311 "imports.ui.sessionMode.listModes();";
312 if (!gjs_context_eval (context, script, -1, "<main>", &status, NULL))
313 g_message ("Retrieving list of available modes failed.");
314
315 exit (status);
316 }
317
318 static gboolean
319 print_version (const gchar *option_name,
320 const gchar *value,
321 gpointer data,
322 GError **error)
323 {
324 g_print ("GNOME Shell %s\n", VERSION);
325 exit (0);
326 }
327
328 GOptionEntry gnome_shell_options[] = {
329 {
330 "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
331 print_version,
332 N_("Print version"),
333 NULL
334 },
335 {
336 "gdm-mode", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
337 &is_gdm_mode,
338 N_("Mode used by GDM for login screen"),
339 NULL
340 },
341 {
342 "mode", 0, 0, G_OPTION_ARG_STRING,
343 &session_mode,
344 N_("Use a specific mode, e.g. \"gdm\" for login screen"),
345 "MODE"
346 },
347 {
348 "list-modes", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
349 list_modes,
350 N_("List possible modes"),
351 NULL
352 },
353 { NULL }
354 };
355
356 int
357 main (int argc, char **argv)
358 {
359 GOptionContext *ctx;
360 GError *error = NULL;
361 int ecode;
362 TpDebugSender *sender;
363
364 g_type_init ();
365
366 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
367 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
368 textdomain (GETTEXT_PACKAGE);
369
370 ctx = meta_get_option_context ();
371 g_option_context_add_main_entries (ctx, gnome_shell_options, GETTEXT_PACKAGE);
372 if (!g_option_context_parse (ctx, &argc, &argv, &error))
373 {
374 g_printerr ("%s: %s\n", argv[0], error->message);
375 exit (1);
376 }
377
378 g_option_context_free (ctx);
379
380 meta_plugin_manager_set_plugin_type (gnome_shell_plugin_get_type ());
381
382 meta_set_wm_name (WM_NAME);
383 meta_set_gnome_wm_keybindings (GNOME_WM_KEYBINDINGS);
384
385 /* Prevent meta_init() from causing gtk to load gail and at-bridge */
386 g_setenv ("NO_AT_BRIDGE", "1", TRUE);
387 meta_init ();
388 g_unsetenv ("NO_AT_BRIDGE");
389
390 /* FIXME: Add gjs API to set this stuff and don't depend on the
391 * environment. These propagate to child processes.
392 */
393 g_setenv ("GJS_DEBUG_OUTPUT", "stderr", TRUE);
394 g_setenv ("GJS_DEBUG_TOPICS", "JS ERROR;JS LOG", TRUE);
395
396 shell_dbus_init (meta_get_replace_current_wm ());
397 shell_a11y_init ();
398 shell_perf_log_init ();
399 shell_prefs_init ();
400 shell_introspection_init ();
401
402 /* Turn on telepathy-glib debugging but filter it out in
403 * default_log_handler. This handler also exposes all the logs over D-Bus
404 * using TpDebugSender. */
405 tp_debug_set_flags ("all");
406
407 sender = tp_debug_sender_dup ();
408 g_log_set_default_handler (default_log_handler, sender);
409
410 /* Initialize the global object */
411 if (session_mode == NULL)
412 session_mode = is_gdm_mode ? "gdm" : "user";
413
414 _shell_global_init ("session-mode", session_mode, NULL);
415
416 ecode = meta_run ();
417
418 if (g_getenv ("GNOME_SHELL_ENABLE_CLEANUP"))
419 {
420 g_printerr ("Doing final cleanup...\n");
421 g_object_unref (shell_global_get ());
422 }
423
424 g_object_unref (sender);
425
426 return ecode;
427 }
428
429 /* HACK:
430 Add a dummy function that calls into libgnome-shell-js.so to ensure it's
431 linked to /usr/bin/gnome-shell even when linking with --as-needed.
432 This function is never actually called.
433 https://bugzilla.gnome.org/show_bug.cgi?id=670477
434 */
435 void _shell_link_to_shell_js (void);
436
437 void
438 _shell_link_to_shell_js (void)
439 {
440 shell_js_add_extension_importer (NULL, NULL, NULL, NULL);
441 }