From aa306ac3ca0842ba9183cf0ede8c5ec964204de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 3 Mar 2021 11:31:24 +0100 Subject: [PATCH] Introduce MetaContextMain This object intends to replace the scattered functions that are used to make up what is effectively a "mutter context". It takes care of the command line arguments that is now done in main.c, persistant virtual monitors, and the like. Part-of: --- src/core/meta-context-main.c | 663 ++++++++++++++++++++++++++++++++ src/core/meta-context-main.h | 31 ++ src/core/meta-context-private.h | 1 - src/core/meta-context.c | 2 +- src/meson.build | 2 + src/meta/meta-context.h | 7 + 6 files changed, 704 insertions(+), 2 deletions(-) create mode 100644 src/core/meta-context-main.c create mode 100644 src/core/meta-context-main.h diff --git a/src/core/meta-context-main.c b/src/core/meta-context-main.c new file mode 100644 index 000000000..60e507c24 --- /dev/null +++ b/src/core/meta-context-main.c @@ -0,0 +1,663 @@ +/* + * 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 "core/main-private.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 +#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; + +#ifdef HAVE_WAYLAND + if (context_main->options.no_x11) + meta_override_x11_display_policy (META_X11_DISPLAY_POLICY_DISABLED); +#endif + + 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; +} + +#ifdef HAVE_NATIVE_BACKEND +static gboolean +add_persistent_virtual_monitors (MetaContextMain *context_main, + GError **error) +{ + MetaBackend *backend = meta_get_backend (); + 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_set_syncing (context_main->options.x11.sync || g_getenv ("MUTTER_SYNC")); + meta_set_replace_current_wm (context_main->options.x11.replace); + +#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, + "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, + NULL); +} + +#ifdef HAVE_NATIVE_BACKEND +static MetaBackend * +create_headless_backend (MetaContext *context, + GError **error) +{ + return g_initable_new (META_TYPE_BACKEND_NATIVE, + NULL, error, + "headless", TRUE, + NULL); +} + +static MetaBackend * +create_native_backend (MetaContext *context, + GError **error) +{ + return g_initable_new (META_TYPE_BACKEND_NATIVE, + NULL, error, + 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) + { + case META_COMPOSITOR_TYPE_X11: + return create_x11_cm_backend (context, error); + 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_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_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[] = { + { + "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 + }, +#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 + { + "x11", 0, 0, G_OPTION_ARG_NONE, + &context_main->options.x11.force, + N_("Run with X11 backend") + }, + { 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->setup = meta_context_main_setup; + context_class->create_backend = meta_context_main_create_backend; + context_class->notify_ready = meta_context_main_notify_ready; +} + +static void +meta_context_main_init (MetaContextMain *context_main) +{ + context_main->compositor_type = -1; +} diff --git a/src/core/meta-context-main.h b/src/core/meta-context-main.h new file mode 100644 index 000000000..9a8ce902b --- /dev/null +++ b/src/core/meta-context-main.h @@ -0,0 +1,31 @@ +/* + * 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. + * + */ + +#ifndef META_CONTEXT_MAIN_H +#define META_CONTEXT_MAIN_H + +#include "core/meta-context-private.h" + +#define META_TYPE_CONTEXT_MAIN (meta_context_main_get_type ()) +G_DECLARE_FINAL_TYPE (MetaContextMain, meta_context_main, + META, CONTEXT_MAIN, + MetaContext) + +#endif /* META_CONTEXT_MAIN_H */ diff --git a/src/core/meta-context-private.h b/src/core/meta-context-private.h index 8a3a6171e..ca38ddddb 100644 --- a/src/core/meta-context-private.h +++ b/src/core/meta-context-private.h @@ -23,7 +23,6 @@ #include "meta/meta-backend.h" #include "meta/meta-context.h" -#include "meta/meta-enums.h" struct _MetaContextClass { diff --git a/src/core/meta-context.c b/src/core/meta-context.c index a7a29d241..c28c03f3b 100644 --- a/src/core/meta-context.c +++ b/src/core/meta-context.c @@ -86,7 +86,7 @@ meta_context_notify_ready (MetaContext *context) META_CONTEXT_GET_CLASS (context)->notify_ready (context); } -static MetaCompositorType +MetaCompositorType meta_context_get_compositor_type (MetaContext *context) { return META_CONTEXT_GET_CLASS (context)->get_compositor_type (context); diff --git a/src/meson.build b/src/meson.build index 817cfa2c3..aba2dc952 100644 --- a/src/meson.build +++ b/src/meson.build @@ -371,6 +371,8 @@ mutter_sources = [ 'core/meta-close-dialog.c', 'core/meta-close-dialog-default.c', 'core/meta-close-dialog-default-private.h', + 'core/meta-context-main.c', + 'core/meta-context-main.h', 'core/meta-context-private.h', 'core/meta-context.c', 'core/meta-fraction.c', diff --git a/src/meta/meta-context.h b/src/meta/meta-context.h index 003e863e9..b872e1ca5 100644 --- a/src/meta/meta-context.h +++ b/src/meta/meta-context.h @@ -24,11 +24,15 @@ #include #include "meta/common.h" +#include "meta/meta-enums.h" #define META_TYPE_CONTEXT (meta_context_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaContext, meta_context, META, CONTEXT, GObject) +META_EXPORT +MetaContext * meta_create_context (const char *name); + META_EXPORT void meta_context_add_option_entries (MetaContext *context, const GOptionEntry *entries, @@ -66,4 +70,7 @@ META_EXPORT void meta_context_terminate_with_error (MetaContext *context, GError *error); +META_EXPORT +MetaCompositorType meta_context_get_compositor_type (MetaContext *context); + #endif /* META_CONTEXT_H */