gnome-shell/src/gnome-shell-plugin.c
Evan Welsh 8fb8f7f827 init: Move Meta main loop into JavaScript after GJS context is initialized
gjs now has an internal mainloop that it can spin to resolve
module imports. That loop uses the thread default context,
so its possible that other sources, namely from mutter, get
dispatched when iterating the context. If that happens before
mutter is properly initialized, this will lead to a crash.

GjsContext needs to iterate its internal mainloop when initializing
to resolve internal modules, to avoid iterating Meta's mainloop and
triggering events before Meta is ready we will initialize the Shell
global and thus the GjsContext (js_context) before Meta.

Once GjsContext is initialized, we can call meta_context_setup().
Once Meta is setup and started, we'll run init.js which uses GJS'
internal promises API to set a "mainloop hook". The mainloop hook
is run immediately after the module returns so GJS will not attempt
to iterate the main loop again before exiting.

Also adjust the 'headlessStart' test to not wait for the
MetaContext::started signal, as that signal has now already
been emitted when the code is executed.

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

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2781>
2023-06-07 19:23:27 +00:00

368 lines
12 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (c) 2008 Red Hat, Inc.
* Copyright (c) 2008 Intel Corp.
*
* Based on plugin skeleton by:
* Author: Tomas Frydrych <tf@linux.intel.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* GnomeShellPlugin is the entry point for for GNOME Shell into and out of
* Mutter. By registering itself into Mutter using
* meta_plugin_manager_set_plugin_type(), Mutter will call the vfuncs of the
* plugin at the appropriate time.
*
* The functions in in GnomeShellPlugin are all just stubs, which just call the
* similar methods in GnomeShellWm.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <clutter/clutter.h>
#include <gjs/gjs.h>
#include <meta/display.h>
#include <meta/meta-plugin.h>
#include <meta/meta-x11-display.h>
#include <meta/util.h>
#include "shell-global-private.h"
#include "shell-perf-log.h"
#include "shell-wm-private.h"
#define GNOME_TYPE_SHELL_PLUGIN (gnome_shell_plugin_get_type ())
G_DECLARE_FINAL_TYPE (GnomeShellPlugin, gnome_shell_plugin,
GNOME, SHELL_PLUGIN,
MetaPlugin)
struct _GnomeShellPlugin
{
MetaPlugin parent;
int glx_error_base;
int glx_event_base;
guint have_swap_event : 1;
CoglContext *cogl_context;
ShellGlobal *global;
};
G_DEFINE_TYPE (GnomeShellPlugin, gnome_shell_plugin, META_TYPE_PLUGIN)
static gboolean
gnome_shell_plugin_has_swap_event (GnomeShellPlugin *shell_plugin)
{
CoglDisplay *cogl_display =
cogl_context_get_display (shell_plugin->cogl_context);
CoglRenderer *renderer = cogl_display_get_renderer (cogl_display);
const char * (* query_extensions_string) (Display *dpy, int screen);
Bool (* query_extension) (Display *dpy, int *error, int *event);
MetaDisplay *display = meta_plugin_get_display (META_PLUGIN (shell_plugin));
MetaX11Display *x11_display = meta_display_get_x11_display (display);
Display *xdisplay;
int screen_number;
const char *glx_extensions;
/* We will only get swap events if Cogl is using GLX */
if (cogl_renderer_get_winsys_id (renderer) != COGL_WINSYS_ID_GLX)
return FALSE;
xdisplay = meta_x11_display_get_xdisplay (x11_display);
query_extensions_string =
(void *) cogl_get_proc_address ("glXQueryExtensionsString");
query_extension =
(void *) cogl_get_proc_address ("glXQueryExtension");
query_extension (xdisplay,
&shell_plugin->glx_error_base,
&shell_plugin->glx_event_base);
screen_number = XDefaultScreen (xdisplay);
glx_extensions = query_extensions_string (xdisplay, screen_number);
return strstr (glx_extensions, "GLX_INTEL_swap_event") != NULL;
}
static void
gnome_shell_plugin_start (MetaPlugin *plugin)
{
GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
ClutterBackend *backend;
backend = clutter_get_default_backend ();
shell_plugin->cogl_context = clutter_backend_get_cogl_context (backend);
shell_plugin->have_swap_event =
gnome_shell_plugin_has_swap_event (shell_plugin);
shell_perf_log_define_event (shell_perf_log_get_default (),
"glx.swapComplete",
"GL buffer swap complete event received (with timestamp of completion)",
"x");
shell_plugin->global = shell_global_get ();
_shell_global_set_plugin (shell_plugin->global, META_PLUGIN (shell_plugin));
}
static ShellWM *
get_shell_wm (void)
{
ShellWM *wm;
g_object_get (shell_global_get (),
"window-manager", &wm,
NULL);
/* drop extra ref added by g_object_get */
g_object_unref (wm);
return wm;
}
static void
gnome_shell_plugin_minimize (MetaPlugin *plugin,
MetaWindowActor *actor)
{
_shell_wm_minimize (get_shell_wm (),
actor);
}
static void
gnome_shell_plugin_unminimize (MetaPlugin *plugin,
MetaWindowActor *actor)
{
_shell_wm_unminimize (get_shell_wm (),
actor);
}
static void
gnome_shell_plugin_size_changed (MetaPlugin *plugin,
MetaWindowActor *actor)
{
_shell_wm_size_changed (get_shell_wm (), actor);
}
static void
gnome_shell_plugin_size_change (MetaPlugin *plugin,
MetaWindowActor *actor,
MetaSizeChange which_change,
MetaRectangle *old_frame_rect,
MetaRectangle *old_buffer_rect)
{
_shell_wm_size_change (get_shell_wm (), actor, which_change, old_frame_rect, old_buffer_rect);
}
static void
gnome_shell_plugin_map (MetaPlugin *plugin,
MetaWindowActor *actor)
{
_shell_wm_map (get_shell_wm (),
actor);
}
static void
gnome_shell_plugin_destroy (MetaPlugin *plugin,
MetaWindowActor *actor)
{
_shell_wm_destroy (get_shell_wm (),
actor);
}
static void
gnome_shell_plugin_switch_workspace (MetaPlugin *plugin,
gint from,
gint to,
MetaMotionDirection direction)
{
_shell_wm_switch_workspace (get_shell_wm(), from, to, direction);
}
static void
gnome_shell_plugin_kill_window_effects (MetaPlugin *plugin,
MetaWindowActor *actor)
{
_shell_wm_kill_window_effects (get_shell_wm(), actor);
}
static void
gnome_shell_plugin_kill_switch_workspace (MetaPlugin *plugin)
{
_shell_wm_kill_switch_workspace (get_shell_wm());
}
static void
gnome_shell_plugin_show_tile_preview (MetaPlugin *plugin,
MetaWindow *window,
MetaRectangle *tile_rect,
int tile_monitor)
{
_shell_wm_show_tile_preview (get_shell_wm (), window, tile_rect, tile_monitor);
}
static void
gnome_shell_plugin_hide_tile_preview (MetaPlugin *plugin)
{
_shell_wm_hide_tile_preview (get_shell_wm ());
}
static void
gnome_shell_plugin_show_window_menu (MetaPlugin *plugin,
MetaWindow *window,
MetaWindowMenuType menu,
int x,
int y)
{
_shell_wm_show_window_menu (get_shell_wm (), window, menu, x, y);
}
static void
gnome_shell_plugin_show_window_menu_for_rect (MetaPlugin *plugin,
MetaWindow *window,
MetaWindowMenuType menu,
MetaRectangle *rect)
{
_shell_wm_show_window_menu_for_rect (get_shell_wm (), window, menu, rect);
}
static gboolean
gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
XEvent *xev)
{
#ifdef GLX_INTEL_swap_event
GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
if (shell_plugin->have_swap_event &&
xev->type == (shell_plugin->glx_event_base + GLX_BufferSwapComplete))
{
GLXBufferSwapComplete *swap_complete_event;
swap_complete_event = (GLXBufferSwapComplete *)xev;
/* Buggy early versions of the INTEL_swap_event implementation in Mesa
* can send this with a ust of 0. Simplify life for consumers
* by ignoring such events */
if (swap_complete_event->ust != 0)
{
gboolean frame_timestamps;
g_object_get (shell_plugin->global,
"frame-timestamps", &frame_timestamps,
NULL);
if (frame_timestamps)
shell_perf_log_event_x (shell_perf_log_get_default (),
"glx.swapComplete",
swap_complete_event->ust);
}
}
#endif
return FALSE;
}
static gboolean
gnome_shell_plugin_keybinding_filter (MetaPlugin *plugin,
MetaKeyBinding *binding)
{
return _shell_wm_filter_keybinding (get_shell_wm (), binding);
}
static void
gnome_shell_plugin_confirm_display_change (MetaPlugin *plugin)
{
_shell_wm_confirm_display_change (get_shell_wm ());
}
static const MetaPluginInfo *
gnome_shell_plugin_plugin_info (MetaPlugin *plugin)
{
static const MetaPluginInfo info = {
.name = "GNOME Shell",
.version = "0.1",
.author = "Various",
.license = "GPLv2+",
.description = "Provides GNOME Shell core functionality"
};
return &info;
}
static MetaCloseDialog *
gnome_shell_plugin_create_close_dialog (MetaPlugin *plugin,
MetaWindow *window)
{
return _shell_wm_create_close_dialog (get_shell_wm (), window);
}
static MetaInhibitShortcutsDialog *
gnome_shell_plugin_create_inhibit_shortcuts_dialog (MetaPlugin *plugin,
MetaWindow *window)
{
return _shell_wm_create_inhibit_shortcuts_dialog (get_shell_wm (), window);
}
static void
gnome_shell_plugin_locate_pointer (MetaPlugin *plugin)
{
GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
_shell_global_locate_pointer (shell_plugin->global);
}
static void
gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
{
MetaPluginClass *plugin_class = META_PLUGIN_CLASS (klass);
plugin_class->start = gnome_shell_plugin_start;
plugin_class->map = gnome_shell_plugin_map;
plugin_class->minimize = gnome_shell_plugin_minimize;
plugin_class->unminimize = gnome_shell_plugin_unminimize;
plugin_class->size_changed = gnome_shell_plugin_size_changed;
plugin_class->size_change = gnome_shell_plugin_size_change;
plugin_class->destroy = gnome_shell_plugin_destroy;
plugin_class->switch_workspace = gnome_shell_plugin_switch_workspace;
plugin_class->kill_window_effects = gnome_shell_plugin_kill_window_effects;
plugin_class->kill_switch_workspace = gnome_shell_plugin_kill_switch_workspace;
plugin_class->show_tile_preview = gnome_shell_plugin_show_tile_preview;
plugin_class->hide_tile_preview = gnome_shell_plugin_hide_tile_preview;
plugin_class->show_window_menu = gnome_shell_plugin_show_window_menu;
plugin_class->show_window_menu_for_rect = gnome_shell_plugin_show_window_menu_for_rect;
plugin_class->xevent_filter = gnome_shell_plugin_xevent_filter;
plugin_class->keybinding_filter = gnome_shell_plugin_keybinding_filter;
plugin_class->confirm_display_change = gnome_shell_plugin_confirm_display_change;
plugin_class->plugin_info = gnome_shell_plugin_plugin_info;
plugin_class->create_close_dialog = gnome_shell_plugin_create_close_dialog;
plugin_class->create_inhibit_shortcuts_dialog = gnome_shell_plugin_create_inhibit_shortcuts_dialog;
plugin_class->locate_pointer = gnome_shell_plugin_locate_pointer;
}
static void
gnome_shell_plugin_init (GnomeShellPlugin *shell_plugin)
{
}