/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter main() */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2006 Elijah Newren * * 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, see . */ /** * SECTION:main * @title: Main * @short_description: Program startup. * * Functions which parse the command-line arguments, create the display, * kick everything off and then close down Mutter when it's time to go. * * * * Mutter - a boring window manager for the adult in you * * Many window managers are like Marshmallow Froot Loops; Mutter * is like Frosted Flakes: it's still plain old corn, but dusted * with some sugar. * * The best way to get a handle on how the whole system fits together * is discussed in doc/code-overview.txt; if you're looking for functions * to investigate, read main(), meta_display_open(), and event_callback(). */ #define _GNU_SOURCE #define _XOPEN_SOURCE /* for putenv() and some signal-related functions */ #include #include #include "util-private.h" #include "display-private.h" #include #include "ui.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_INTROSPECTION #include #endif #include "x11/session.h" #include "wayland/meta-wayland.h" #include "backends/meta-backend.h" /* * The exit code we'll return to our parent process when we eventually die. */ static MetaExitCode meta_exit_code = META_EXIT_SUCCESS; /* * Handle on the main loop, so that we have an easy way of shutting Mutter * down. */ static GMainLoop *meta_main_loop = NULL; static void prefs_changed_callback (MetaPreference pref, gpointer data); /** * meta_print_compilation_info: * * Prints a list of which configure script options were used to * build this copy of Mutter. This is actually always called * on startup, but it's all no-op unless we're in verbose mode * (see meta_set_verbose()). */ static void meta_print_compilation_info (void) { #ifdef HAVE_RANDR meta_verbose ("Compiled with randr extension\n"); #else meta_verbose ("Compiled without randr extension\n"); #endif #ifdef HAVE_STARTUP_NOTIFICATION meta_verbose ("Compiled with startup notification\n"); #else meta_verbose ("Compiled without startup notification\n"); #endif } /** * meta_print_self_identity: * * Prints the version number, the current timestamp (not the * build date), the locale, the character encoding, and a list * of configure script options that were used to build this * copy of Mutter. This is actually always called * on startup, but it's all no-op unless we're in verbose mode * (see meta_set_verbose()). */ static void meta_print_self_identity (void) { char buf[256]; GDate d; const char *charset; /* Version and current date. */ g_date_clear (&d, 1); g_date_set_time_t (&d, time (NULL)); g_date_strftime (buf, sizeof (buf), "%x", &d); meta_verbose ("Mutter version %s running on %s\n", VERSION, buf); /* Locale and encoding. */ g_get_charset (&charset); meta_verbose ("Running in locale \"%s\" with encoding \"%s\"\n", setlocale (LC_ALL, NULL), charset); /* Compilation settings. */ meta_print_compilation_info (); } /* * The set of possible options that can be set on Mutter's * command line. */ static gchar *opt_save_file; static gchar *opt_display_name; static gchar *opt_client_id; static gboolean opt_replace_wm; static gboolean opt_disable_sm; static gboolean opt_sync; static gboolean opt_wayland; #ifdef HAVE_NATIVE_BACKEND static gboolean opt_display_server; #endif static GOptionEntry meta_options[] = { { "sm-disable", 0, 0, G_OPTION_ARG_NONE, &opt_disable_sm, N_("Disable connection to session manager"), NULL }, { "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace_wm, N_("Replace the running window manager"), NULL }, { "sm-client-id", 0, 0, G_OPTION_ARG_STRING, &opt_client_id, N_("Specify session management ID"), "ID" }, { "display", 'd', 0, G_OPTION_ARG_STRING, &opt_display_name, N_("X Display to use"), "DISPLAY" }, { "sm-save-file", 0, 0, G_OPTION_ARG_FILENAME, &opt_save_file, N_("Initialize session from savefile"), "FILE" }, { "sync", 0, 0, G_OPTION_ARG_NONE, &opt_sync, N_("Make X calls synchronous"), NULL }, { "wayland", 0, 0, G_OPTION_ARG_NONE, &opt_wayland, N_("Run as a wayland compositor"), NULL }, #ifdef HAVE_NATIVE_BACKEND { "display-server", 0, 0, G_OPTION_ARG_NONE, &opt_display_server, N_("Run as a full display server, rather than nested") }, #endif {NULL} }; /** * meta_get_option_context: (skip) * * Returns a #GOptionContext initialized with mutter-related options. * Parse the command-line args with this before calling meta_init(). * * Return value: the #GOptionContext */ GOptionContext * meta_get_option_context (void) { GOptionContext *ctx; if (setlocale (LC_ALL, "") == NULL) meta_warning ("Locale not understood by C library, internationalization will not work\n"); bindtextdomain (GETTEXT_PACKAGE, MUTTER_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); ctx = g_option_context_new (NULL); g_option_context_add_main_entries (ctx, meta_options, GETTEXT_PACKAGE); return ctx; } /** * meta_select_display: * * Selects which display Mutter should use. It first tries to use * @display_name as the display. If @display_name is %NULL then * try to use the environment variable MUTTER_DISPLAY. If that * also is %NULL, use the default - :0.0 */ static void meta_select_display (char *display_arg) { const char *display_name; if (display_arg) display_name = (const char *) display_arg; else display_name = g_getenv ("MUTTER_DISPLAY"); if (display_name) g_setenv ("DISPLAY", display_name, TRUE); } static void meta_finalize (void) { MetaDisplay *display = meta_get_display (); if (display) meta_display_close (display, CurrentTime); /* I doubt correct timestamps matter here */ if (meta_is_wayland_compositor ()) meta_wayland_finalize (); } static gboolean on_sigterm (gpointer user_data) { meta_quit (EXIT_SUCCESS); return G_SOURCE_REMOVE; } /** * meta_init: (skip) * * Initialize mutter. Call this after meta_get_option_context() and * meta_plugin_manager_set_plugin_type(), and before meta_run(). */ void meta_init (void) { struct sigaction act; sigset_t empty_mask; ClutterSettings *clutter_settings; sigemptyset (&empty_mask); act.sa_handler = SIG_IGN; act.sa_mask = empty_mask; act.sa_flags = 0; if (sigaction (SIGPIPE, &act, NULL) < 0) g_printerr ("Failed to register SIGPIPE handler: %s\n", g_strerror (errno)); #ifdef SIGXFSZ if (sigaction (SIGXFSZ, &act, NULL) < 0) g_printerr ("Failed to register SIGXFSZ handler: %s\n", g_strerror (errno)); #endif g_unix_signal_add (SIGTERM, on_sigterm, NULL); if (g_getenv ("MUTTER_VERBOSE")) meta_set_verbose (TRUE); if (g_getenv ("MUTTER_DEBUG")) meta_set_debugging (TRUE); #if defined(CLUTTER_WINDOWING_EGL) && defined(HAVE_NATIVE_BACKEND) if (opt_display_server) clutter_set_windowing_backend (CLUTTER_WINDOWING_EGL); #endif meta_set_is_wayland_compositor (opt_wayland); if (g_get_home_dir ()) if (chdir (g_get_home_dir ()) < 0) meta_warning ("Could not change to home directory %s.\n", g_get_home_dir ()); meta_print_self_identity (); #ifdef HAVE_INTROSPECTION g_irepository_prepend_search_path (MUTTER_PKGLIBDIR); #endif if (meta_is_wayland_compositor ()) { /* NB: When running as a hybrid wayland compositor we run our own headless X * server so the user can't control the X display to connect too. */ meta_wayland_init (); } else meta_select_display (opt_display_name); meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL)); if (opt_replace_wm) meta_set_replace_current_wm (TRUE); if (opt_save_file && opt_client_id) meta_fatal ("Can't specify both SM save file and SM client id\n"); meta_main_loop = g_main_loop_new (NULL, FALSE); meta_ui_init (); /* If we are running with wayland then we don't wait until we have * an X connection before initializing clutter we instead initialize * it earlier since we need to initialize the GL driver so the driver * can register any needed wayland extensions. */ if (!meta_is_wayland_compositor ()) { /* * Clutter can only be initialized after the UI. */ meta_clutter_init (); } meta_restart_init (); /* * XXX: We cannot handle high dpi scaling yet, so fix the scale to 1 * for now. */ clutter_settings = clutter_settings_get_default (); g_object_set (clutter_settings, "window-scaling-factor", 1, NULL); } /** * meta_register_with_session: * * Registers mutter with the session manager. Call this after completing your own * initialization. * * This should be called when the session manager can safely continue to the * next phase of startup and potentially display windows. */ void meta_register_with_session (void) { if (!opt_disable_sm) { if (opt_client_id == NULL) { const gchar *desktop_autostart_id; desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); if (desktop_autostart_id != NULL) opt_client_id = g_strdup (desktop_autostart_id); } /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to * use the same client id. */ g_unsetenv ("DESKTOP_AUTOSTART_ID"); meta_session_init (opt_client_id, opt_save_file); } /* Free memory possibly allocated by the argument parsing which are * no longer needed. */ g_free (opt_save_file); g_free (opt_display_name); g_free (opt_client_id); } /** * meta_run: (skip) * * Runs mutter. Call this after completing initialization that doesn't require * an event loop. * * Return value: mutter's exit status */ int meta_run (void) { /* Load prefs */ meta_prefs_init (); meta_prefs_add_listener (prefs_changed_callback, NULL); meta_ui_set_current_theme (meta_prefs_get_theme ()); /* Try to find some theme that'll work if the theme preference * doesn't exist. First try Simple (the default theme) then just * try anything in the themes directory. */ if (!meta_ui_have_a_theme ()) meta_ui_set_current_theme ("Simple"); if (!meta_ui_have_a_theme ()) { const char *dir_entry = NULL; GError *err = NULL; GDir *themes_dir = NULL; if (!(themes_dir = g_dir_open (MUTTER_DATADIR"/themes", 0, &err))) { meta_fatal (_("Failed to scan themes directory: %s\n"), err->message); g_error_free (err); } else { while (((dir_entry = g_dir_read_name (themes_dir)) != NULL) && (!meta_ui_have_a_theme ())) { meta_ui_set_current_theme (dir_entry); } g_dir_close (themes_dir); } } if (!meta_ui_have_a_theme ()) meta_fatal (_("Could not find a theme! Be sure %s exists and contains the usual themes.\n"), MUTTER_DATADIR"/themes"); if (!meta_display_open ()) meta_exit (META_EXIT_ERROR); g_main_loop_run (meta_main_loop); meta_finalize (); return meta_exit_code; } /** * meta_quit: * @code: The success or failure code to return to the calling process. * * Stops Mutter. This tells the event loop to stop processing; it is * rather dangerous to use this because this will leave the user with * no window manager. We generally do this only if, for example, the * session manager asks us to; we assume the session manager knows * what it's talking about. */ void meta_quit (MetaExitCode code) { if (g_main_loop_is_running (meta_main_loop)) { meta_exit_code = code; g_main_loop_quit (meta_main_loop); } } /** * prefs_changed_callback: * @pref: Which preference has changed * @data: Arbitrary data (which we ignore) * * Called on pref changes. (One of several functions of its kind and purpose.) * * FIXME: Why are these particular prefs handled in main.c and not others? * Should they be? */ static void prefs_changed_callback (MetaPreference pref, gpointer data) { switch (pref) { case META_PREF_THEME: case META_PREF_DRAGGABLE_BORDER_WIDTH: meta_ui_set_current_theme (meta_prefs_get_theme ()); meta_display_retheme_all (); break; default: /* handled elsewhere or otherwise */ break; } }