mutter/src/core/meta-context-main.c
Florian Müllner d17e9adc73 context: Add (hidden) --unsafe-mode option
When running gnome-shell, it is possible to toggle unsafe-mode in
looking glass. We'll eventually start using the property in mutter
as well, so to make stand-alone debugging easier, also expose it
as a hidden command line option.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3943

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1983>
2021-09-02 16:58:46 +00:00

710 lines
20 KiB
C

/*
* 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 <glib.h>
#include <gio/gio.h>
#if defined(HAVE_NATIVE_BACKEND) && defined(HAVE_WAYLAND)
#include <systemd/sd-login.h>
#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)
{
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_context_set_unsafe_mode (context, context_main->options.unsafe_mode);
meta_set_syncing (context_main->options.x11.sync || g_getenv ("MUTTER_SYNC"));
#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,
"headless", TRUE,
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)
{
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,
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
{
"unsafe-mode", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
&context_main->options.unsafe_mode,
"Run in unsafe mode"
},
{
"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->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;
}
static void
meta_context_main_init (MetaContextMain *context_main)
{
context_main->compositor_type = -1;
}