/* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2006 Elijah Newren * Copyright (C) 2021 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 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. * */ #include "config.h" #include "core/meta-context-main.h" #include #include #if defined(HAVE_NATIVE_BACKEND) && defined(HAVE_WAYLAND) #include #endif /* HAVE_WAYLAND && HAVE_NATIVE_BACKEND */ #include "backends/meta-monitor-manager-private.h" #include "backends/meta-virtual-monitor.h" #include "backends/x11/cm/meta-backend-x11-cm.h" #include "meta/meta-backend.h" #include "wayland/meta-wayland.h" #include "x11/session.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #ifdef HAVE_WAYLAND #include "backends/x11/nested/meta-backend-x11-nested.h" #endif typedef struct _MetaContextMainOptions { struct { char *display_name; gboolean replace; gboolean sync; gboolean force; } x11; struct { char *save_file; char *client_id; gboolean disable; } sm; #ifdef HAVE_WAYLAND gboolean wayland; gboolean nested; gboolean no_x11; char *wayland_display; #endif #ifdef HAVE_NATIVE_BACKEND gboolean display_server; gboolean headless; #endif gboolean unsafe_mode; #ifdef HAVE_NATIVE_BACKEND GList *virtual_monitor_infos; #endif } MetaContextMainOptions; struct _MetaContextMain { GObject parent; MetaContextMainOptions options; MetaCompositorType compositor_type; GList *persistent_virtual_monitors; }; G_DEFINE_TYPE (MetaContextMain, meta_context_main, META_TYPE_CONTEXT) static gboolean check_configuration (MetaContextMain *context_main, GError **error) { #ifdef HAVE_WAYLAND if (context_main->options.x11.force && context_main->options.no_x11) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Can't run in X11 mode with no X11"); return FALSE; } if (context_main->options.x11.force && context_main->options.wayland) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Can't run in X11 mode with Wayland enabled"); return FALSE; } if (context_main->options.x11.force && context_main->options.nested) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Can't run in X11 mode nested"); return FALSE; } #endif /* HAVE_WAYLAND */ #ifdef HAVE_NATIVE_BACKEND if (context_main->options.x11.force && context_main->options.display_server) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Can't run in X11 mode as a display server"); return FALSE; } if (context_main->options.x11.force && context_main->options.headless) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Can't run in X11 mode headlessly"); return FALSE; } if (context_main->options.display_server && context_main->options.headless) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Can't run in display server mode headlessly"); return FALSE; } #endif /* HAVE_NATIVE_BACKEND */ if (context_main->options.sm.save_file && context_main->options.sm.client_id) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Can't specify both SM save file and SM client id"); return FALSE; } return TRUE; } #if defined(HAVE_WAYLAND) && defined(HAVE_NATIVE_BACKEND) static gboolean session_type_is_supported (const char *session_type) { return (g_strcmp0 (session_type, "x11") == 0) || (g_strcmp0 (session_type, "wayland") == 0); } static char * find_session_type (GError **error) { char **sessions = NULL; char *session_id; char *session_type; const char *session_type_env; gboolean is_tty = FALSE; int ret, i; ret = sd_pid_get_session (0, &session_id); if (ret == 0 && session_id != NULL) { ret = sd_session_get_type (session_id, &session_type); free (session_id); if (ret == 0) { if (session_type_is_supported (session_type)) goto out; else is_tty = g_strcmp0 (session_type, "tty") == 0; free (session_type); } } else if (sd_uid_get_sessions (getuid (), 1, &sessions) > 0) { for (i = 0; sessions[i] != NULL; i++) { ret = sd_session_get_type (sessions[i], &session_type); if (ret < 0) continue; if (session_type_is_supported (session_type)) { g_strfreev (sessions); goto out; } free (session_type); } } g_strfreev (sessions); session_type_env = g_getenv ("XDG_SESSION_TYPE"); if (session_type_is_supported (session_type_env)) { /* The string should be freeable */ session_type = strdup (session_type_env); goto out; } /* Legacy support for starting through xinit */ if (is_tty && (g_getenv ("MUTTER_DISPLAY") || g_getenv ("DISPLAY"))) { session_type = strdup ("x11"); goto out; } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unsupported session type"); return NULL; out: return session_type; } #else /* defined(HAVE_WAYLAND) && defined(HAVE_NATIVE_BACKEND) */ static char * find_session_type (GError **error) { return g_strdup ("x11"); } #endif /* defined(HAVE_WAYLAND) && defined(HAVE_NATIVE_BACKEND) */ static MetaCompositorType determine_compositor_type (MetaContextMain *context_main, GError **error) { g_autofree char *session_type = NULL; #ifdef HAVE_WAYLAND if (context_main->options.wayland || #ifdef HAVE_NATIVE_BACKEND context_main->options.display_server || context_main->options.headless || #endif /* HAVE_NATIVE_BACKEND */ context_main->options.nested) return META_COMPOSITOR_TYPE_WAYLAND; #endif /* HAVE_WAYLAND */ if (context_main->options.x11.force) return META_COMPOSITOR_TYPE_X11; session_type = find_session_type (error); if (!session_type) return -1; if (strcmp (session_type, "x11") == 0) return META_COMPOSITOR_TYPE_X11; #ifdef HAVE_WAYLAND else if (strcmp (session_type, "wayland") == 0) return META_COMPOSITOR_TYPE_WAYLAND; #endif else g_assert_not_reached (); } static gboolean meta_context_main_configure (MetaContext *context, int *argc, char ***argv, GError **error) { MetaContextMain *context_main = META_CONTEXT_MAIN (context); MetaContextClass *context_class = META_CONTEXT_CLASS (meta_context_main_parent_class); if (!context_class->configure (context, argc, argv, error)) return FALSE; if (!check_configuration (context_main, error)) return FALSE; context_main->compositor_type = determine_compositor_type (context_main, error); if (context_main->compositor_type == -1) return FALSE; #ifdef HAVE_WAYLAND if (context_main->options.wayland_display) meta_wayland_override_display_name (context_main->options.wayland_display); #endif return TRUE; } static MetaCompositorType meta_context_main_get_compositor_type (MetaContext *context) { MetaContextMain *context_main = META_CONTEXT_MAIN (context); return context_main->compositor_type; } static MetaX11DisplayPolicy meta_context_main_get_x11_display_policy (MetaContext *context) { MetaCompositorType compositor_type; #ifdef HAVE_WAYLAND MetaContextMain *context_main = META_CONTEXT_MAIN (context); char *unit; #endif compositor_type = meta_context_get_compositor_type (context); switch (compositor_type) { case META_COMPOSITOR_TYPE_X11: return META_X11_DISPLAY_POLICY_MANDATORY; case META_COMPOSITOR_TYPE_WAYLAND: #ifdef HAVE_WAYLAND if (context_main->options.no_x11) return META_X11_DISPLAY_POLICY_DISABLED; else if (sd_pid_get_user_unit (0, &unit) < 0) return META_X11_DISPLAY_POLICY_MANDATORY; else return META_X11_DISPLAY_POLICY_ON_DEMAND; #else /* HAVE_WAYLAND */ g_assert_not_reached (); #endif /* HAVE_WAYLAND */ } g_assert_not_reached (); } static gboolean meta_context_main_is_replacing (MetaContext *context) { MetaContextMain *context_main = META_CONTEXT_MAIN (context); return context_main->options.x11.replace; } #ifdef HAVE_NATIVE_BACKEND static gboolean add_persistent_virtual_monitors (MetaContextMain *context_main, GError **error) { MetaContext *context = META_CONTEXT (context_main); MetaBackend *backend = meta_context_get_backend (context); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *l; for (l = context_main->options.virtual_monitor_infos; l; l = l->next) { MetaVirtualMonitorInfo *info = l->data; MetaVirtualMonitor *virtual_monitor; virtual_monitor = meta_monitor_manager_create_virtual_monitor (monitor_manager, info, error); if (!virtual_monitor) { g_prefix_error (error, "Failed to add virtual monitor: "); return FALSE; } context_main->persistent_virtual_monitors = g_list_append (context_main->persistent_virtual_monitors, virtual_monitor); } if (context_main->options.virtual_monitor_infos) { g_list_free_full (context_main->options.virtual_monitor_infos, (GDestroyNotify) meta_virtual_monitor_info_free); context_main->options.virtual_monitor_infos = NULL; meta_monitor_manager_reload (monitor_manager); } return TRUE; } #endif static gboolean meta_context_main_setup (MetaContext *context, GError **error) { MetaContextMain *context_main = META_CONTEXT_MAIN (context); if (!META_CONTEXT_CLASS (meta_context_main_parent_class)->setup (context, error)) return FALSE; meta_context_set_unsafe_mode (context, context_main->options.unsafe_mode); #ifdef HAVE_NATIVE_BACKEND if (!add_persistent_virtual_monitors (context_main, error)) return FALSE; #endif return TRUE; } static MetaBackend * create_x11_cm_backend (MetaContext *context, GError **error) { MetaContextMain *context_main = META_CONTEXT_MAIN (context); #ifdef HAVE_NATIVE_BACKEND if (context_main->options.virtual_monitor_infos) g_warning ("Ignoring added virtual monitors in X11 session"); #endif return g_initable_new (META_TYPE_BACKEND_X11_CM, NULL, error, "context", context, "display-name", context_main->options.x11.display_name, NULL); } #ifdef HAVE_WAYLAND static MetaBackend * create_nested_backend (MetaContext *context, GError **error) { return g_initable_new (META_TYPE_BACKEND_X11_NESTED, NULL, error, "context", context, NULL); } #ifdef HAVE_NATIVE_BACKEND static MetaBackend * create_headless_backend (MetaContext *context, GError **error) { return g_initable_new (META_TYPE_BACKEND_NATIVE, NULL, error, "context", context, "mode", META_BACKEND_NATIVE_MODE_HEADLESS, NULL); } static MetaBackend * create_native_backend (MetaContext *context, GError **error) { return g_initable_new (META_TYPE_BACKEND_NATIVE, NULL, error, "context", context, NULL); } #endif /* HAVE_NATIVE_BACKEND */ #endif /* HAVE_WAYLAND */ static MetaBackend * meta_context_main_create_backend (MetaContext *context, GError **error) { #ifdef HAVE_WAYLAND MetaContextMain *context_main = META_CONTEXT_MAIN (context); #endif MetaCompositorType compositor_type; compositor_type = meta_context_get_compositor_type (context); switch (compositor_type) { #ifdef HAVE_X11 case META_COMPOSITOR_TYPE_X11: return create_x11_cm_backend (context, error); #endif case META_COMPOSITOR_TYPE_WAYLAND: #ifdef HAVE_WAYLAND if (context_main->options.nested) return create_nested_backend (context, error); #ifdef HAVE_NATIVE_BACKEND else if (context_main->options.headless) return create_headless_backend (context, error); else return create_native_backend (context, error); #endif /* HAVE_NATIVE_BACKEND */ #else /* HAVE_WAYLAND */ g_assert_not_reached (); #endif /* HAVE_WAYLAND */ } g_assert_not_reached (); } static void meta_context_main_notify_ready (MetaContext *context) { MetaContextMain *context_main = META_CONTEXT_MAIN (context); if (!context_main->options.sm.disable) { meta_session_init (context, context_main->options.sm.client_id, context_main->options.sm.save_file); } g_clear_pointer (&context_main->options.sm.client_id, g_free); g_clear_pointer (&context_main->options.sm.save_file, g_free); } #ifdef HAVE_X11 static gboolean meta_context_main_is_x11_sync (MetaContext *context) { MetaContextMain *context_main = META_CONTEXT_MAIN (context); return context_main->options.x11.sync || g_getenv ("MUTTER_SYNC"); } #endif #ifdef HAVE_NATIVE_BACKEND static gboolean add_virtual_monitor_cb (const char *option_name, const char *value, gpointer user_data, GError **error) { MetaContextMain *context_main = user_data; int width, height; float refresh_rate = 60.0; if (sscanf (value, "%dx%d@%f", &width, &height, &refresh_rate) == 3 || sscanf (value, "%dx%d", &width, &height) == 2) { g_autofree char *serial = NULL; MetaVirtualMonitorInfo *virtual_monitor; int n_existing_virtual_monitor_infos; n_existing_virtual_monitor_infos = g_list_length (context_main->options.virtual_monitor_infos); serial = g_strdup_printf ("0x%.2x", n_existing_virtual_monitor_infos); virtual_monitor = meta_virtual_monitor_info_new (width, height, refresh_rate, "MetaVendor", "MetaVirtualMonitor", serial); context_main->options.virtual_monitor_infos = g_list_append (context_main->options.virtual_monitor_infos, virtual_monitor); return TRUE; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Unrecognizable virtual monitor spec '%s'", value); return FALSE; } } #endif /* HAVE_NATIVE_BACKEND */ static void meta_context_main_add_option_entries (MetaContextMain *context_main) { MetaContext *context = META_CONTEXT (context_main); GOptionEntry options[] = { #ifdef HAVE_X11 { "replace", 'r', 0, G_OPTION_ARG_NONE, &context_main->options.x11.replace, N_("Replace the running window manager"), NULL }, { "display", 'd', 0, G_OPTION_ARG_STRING, &context_main->options.x11.display_name, N_("X Display to use"), "DISPLAY" }, { "sm-disable", 0, 0, G_OPTION_ARG_NONE, &context_main->options.sm.disable, N_("Disable connection to session manager"), NULL }, { "sm-client-id", 0, 0, G_OPTION_ARG_STRING, &context_main->options.sm.client_id, N_("Specify session management ID"), "ID" }, { "sm-save-file", 0, 0, G_OPTION_ARG_FILENAME, &context_main->options.sm.save_file, N_("Initialize session from savefile"), "FILE" }, { "sync", 0, 0, G_OPTION_ARG_NONE, &context_main->options.x11.sync, N_("Make X calls synchronous"), NULL }, #endif #ifdef HAVE_WAYLAND { "wayland", 0, 0, G_OPTION_ARG_NONE, &context_main->options.wayland, N_("Run as a wayland compositor"), NULL }, { "nested", 0, 0, G_OPTION_ARG_NONE, &context_main->options.nested, N_("Run as a nested compositor"), NULL }, { "no-x11", 0, 0, G_OPTION_ARG_NONE, &context_main->options.no_x11, N_("Run wayland compositor without starting Xwayland"), NULL }, { "wayland-display", 0, 0, G_OPTION_ARG_STRING, &context_main->options.wayland_display, N_("Specify Wayland display name to use"), NULL }, #endif #ifdef HAVE_NATIVE_BACKEND { "display-server", 0, 0, G_OPTION_ARG_NONE, &context_main->options.display_server, N_("Run as a full display server, rather than nested") }, { "headless", 0, 0, G_OPTION_ARG_NONE, &context_main->options.headless, N_("Run as a headless display server") }, { "virtual-monitor", 0, 0, G_OPTION_ARG_CALLBACK, add_virtual_monitor_cb, N_("Add persistent virtual monitor (WxH or WxH@R)") }, #endif { "unsafe-mode", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &context_main->options.unsafe_mode, "Run in unsafe mode" }, #ifdef HAVE_X11 { "x11", 0, 0, G_OPTION_ARG_NONE, &context_main->options.x11.force, N_("Run with X11 backend") }, #endif { NULL } }; meta_context_add_option_entries (context, options, GETTEXT_PACKAGE); } /** * meta_create_context: * @name: Human readable name of display server or window manager * * Create a context. * * Returns: (transfer full): A new context instance. */ MetaContext * meta_create_context (const char *name) { return g_object_new (META_TYPE_CONTEXT_MAIN, "name", name, NULL); } static void meta_context_main_finalize (GObject *object) { #ifdef HAVE_NATIVE_BACKEND MetaContextMain *context_main = META_CONTEXT_MAIN (object); g_list_free_full (context_main->persistent_virtual_monitors, g_object_unref); context_main->persistent_virtual_monitors = NULL; #endif G_OBJECT_CLASS (meta_context_main_parent_class)->finalize (object); } static void meta_context_main_constructed (GObject *object) { MetaContextMain *context_main = META_CONTEXT_MAIN (object); G_OBJECT_CLASS (meta_context_main_parent_class)->constructed (object); meta_context_main_add_option_entries (context_main); } static void meta_context_main_class_init (MetaContextMainClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaContextClass *context_class = META_CONTEXT_CLASS (klass); object_class->finalize = meta_context_main_finalize; object_class->constructed = meta_context_main_constructed; context_class->configure = meta_context_main_configure; context_class->get_compositor_type = meta_context_main_get_compositor_type; context_class->get_x11_display_policy = meta_context_main_get_x11_display_policy; context_class->is_replacing = meta_context_main_is_replacing; context_class->setup = meta_context_main_setup; context_class->create_backend = meta_context_main_create_backend; context_class->notify_ready = meta_context_main_notify_ready; #ifdef HAVE_X11 context_class->is_x11_sync = meta_context_main_is_x11_sync; #endif } static void meta_context_main_init (MetaContextMain *context_main) { context_main->compositor_type = -1; }