0eeb62794d
Add an event when we receive an event on buffer swap completion; we'll only get this if Clutter is using the INTEL_swap_event GLX extension, but it's useful to see the actual timing of video frames. The recorded event includes the actual timestamp of the swap, since we are given that in the GLX event - on my system it tends to be consistently 80-100us before we log the event, but if something was going wrong in event handling (too much synchronous work), then that could could show up as a longer delay. https://bugzilla.gnome.org/show_bug.cgi?id=619516
513 lines
18 KiB
C
513 lines
18 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#define MUTTER_BUILDING_PLUGIN 1
|
|
#include <mutter-plugin.h>
|
|
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include <clutter/clutter.h>
|
|
#include <clutter/x11/clutter-x11.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdk.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <gjs/gjs.h>
|
|
#include <girepository.h>
|
|
#include <gmodule.h>
|
|
#include <malloc.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <GL/glx.h>
|
|
#include <GL/glxext.h>
|
|
|
|
#include "display.h"
|
|
|
|
#include "shell-global-private.h"
|
|
#include "shell-perf-log.h"
|
|
#include "shell-wm.h"
|
|
#include "st.h"
|
|
|
|
static void gnome_shell_plugin_dispose (GObject *object);
|
|
static void gnome_shell_plugin_finalize (GObject *object);
|
|
|
|
static void gnome_shell_plugin_start (MutterPlugin *plugin);
|
|
static void gnome_shell_plugin_minimize (MutterPlugin *plugin,
|
|
MutterWindow *actor);
|
|
static void gnome_shell_plugin_maximize (MutterPlugin *plugin,
|
|
MutterWindow *actor,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height);
|
|
static void gnome_shell_plugin_unmaximize (MutterPlugin *plugin,
|
|
MutterWindow *actor,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height);
|
|
static void gnome_shell_plugin_map (MutterPlugin *plugin,
|
|
MutterWindow *actor);
|
|
static void gnome_shell_plugin_destroy (MutterPlugin *plugin,
|
|
MutterWindow *actor);
|
|
|
|
static void gnome_shell_plugin_switch_workspace (MutterPlugin *plugin,
|
|
const GList **actors,
|
|
gint from,
|
|
gint to,
|
|
MetaMotionDirection direction);
|
|
static void gnome_shell_plugin_kill_effect (MutterPlugin *plugin,
|
|
MutterWindow *actor,
|
|
gulong events);
|
|
|
|
static gboolean gnome_shell_plugin_xevent_filter (MutterPlugin *plugin,
|
|
XEvent *event);
|
|
static const MutterPluginInfo *gnome_shell_plugin_plugin_info (MutterPlugin *plugin);
|
|
|
|
#define GNOME_TYPE_SHELL_PLUGIN (gnome_shell_plugin_get_type ())
|
|
#define GNOME_SHELL_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_SHELL_PLUGIN, GnomeShellPlugin))
|
|
#define GNOME_SHELL_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_SHELL_PLUGIN, GnomeShellPluginClass))
|
|
#define GNOME_IS_SHELL_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_SHELL_PLUGIN_TYPE))
|
|
#define GNOME_IS_SHELL_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_SHELL_PLUGIN))
|
|
#define GNOME_SHELL_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_SHELL_PLUGIN, GnomeShellPluginClass))
|
|
|
|
typedef struct _GnomeShellPlugin GnomeShellPlugin;
|
|
typedef struct _GnomeShellPluginClass GnomeShellPluginClass;
|
|
|
|
struct _GnomeShellPlugin
|
|
{
|
|
MutterPlugin parent;
|
|
|
|
GjsContext *gjs_context;
|
|
Atom panel_action;
|
|
Atom panel_action_run_dialog;
|
|
Atom panel_action_main_menu;
|
|
|
|
int glx_error_base;
|
|
int glx_event_base;
|
|
guint have_swap_event : 1;
|
|
};
|
|
|
|
struct _GnomeShellPluginClass
|
|
{
|
|
MutterPluginClass parent_class;
|
|
};
|
|
|
|
/*
|
|
* Create the plugin struct; function pointers initialized in
|
|
* g_module_check_init().
|
|
*/
|
|
MUTTER_PLUGIN_DECLARE(GnomeShellPlugin, gnome_shell_plugin);
|
|
|
|
static void
|
|
gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
MutterPluginClass *plugin_class = MUTTER_PLUGIN_CLASS (klass);
|
|
|
|
gobject_class->dispose = gnome_shell_plugin_dispose;
|
|
gobject_class->finalize = gnome_shell_plugin_finalize;
|
|
|
|
plugin_class->start = gnome_shell_plugin_start;
|
|
plugin_class->map = gnome_shell_plugin_map;
|
|
plugin_class->minimize = gnome_shell_plugin_minimize;
|
|
plugin_class->maximize = gnome_shell_plugin_maximize;
|
|
plugin_class->unmaximize = gnome_shell_plugin_unmaximize;
|
|
plugin_class->destroy = gnome_shell_plugin_destroy;
|
|
|
|
plugin_class->switch_workspace = gnome_shell_plugin_switch_workspace;
|
|
plugin_class->kill_effect = gnome_shell_plugin_kill_effect;
|
|
|
|
plugin_class->xevent_filter = gnome_shell_plugin_xevent_filter;
|
|
plugin_class->plugin_info = gnome_shell_plugin_plugin_info;
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_init (GnomeShellPlugin *shell_plugin)
|
|
{
|
|
meta_prefs_override_preference_location ("/apps/metacity/general/button_layout",
|
|
"/desktop/gnome/shell/windows/button_layout");
|
|
}
|
|
|
|
static void
|
|
update_font_options (GtkSettings *settings)
|
|
{
|
|
StThemeContext *context;
|
|
ClutterStage *stage;
|
|
ClutterBackend *backend;
|
|
gint dpi;
|
|
gint hinting;
|
|
gchar *hint_style_str;
|
|
cairo_hint_style_t hint_style = CAIRO_HINT_STYLE_NONE;
|
|
gint antialias;
|
|
cairo_antialias_t antialias_mode = CAIRO_ANTIALIAS_NONE;
|
|
cairo_font_options_t *options;
|
|
|
|
/* Disable text mipmapping; it causes problems on pre-GEM Intel
|
|
* drivers and we should just be rendering text at the right
|
|
* size rather than scaling it. If we do effects where we dynamically
|
|
* zoom labels, then we might want to reconsider.
|
|
*/
|
|
clutter_set_font_flags (clutter_get_font_flags () & ~CLUTTER_FONT_MIPMAPPING);
|
|
|
|
g_object_get (settings,
|
|
"gtk-xft-dpi", &dpi,
|
|
"gtk-xft-antialias", &antialias,
|
|
"gtk-xft-hinting", &hinting,
|
|
"gtk-xft-hintstyle", &hint_style_str,
|
|
NULL);
|
|
|
|
stage = CLUTTER_STAGE (clutter_stage_get_default ());
|
|
context = st_theme_context_get_for_stage (stage);
|
|
|
|
if (dpi != -1)
|
|
/* GTK stores resolution as 1024 * dots/inch */
|
|
st_theme_context_set_resolution (context, dpi / 1024);
|
|
else
|
|
st_theme_context_set_default_resolution (context);
|
|
|
|
/* Clutter (as of 0.9) passes comprehensively wrong font options
|
|
* override whatever set_font_flags() did above.
|
|
*
|
|
* http://bugzilla.openedhand.com/show_bug.cgi?id=1456
|
|
*/
|
|
backend = clutter_get_default_backend ();
|
|
options = cairo_font_options_create ();
|
|
|
|
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
|
|
|
|
if (hinting >= 0 && !hinting)
|
|
{
|
|
hint_style = CAIRO_HINT_STYLE_NONE;
|
|
}
|
|
else if (hint_style_str)
|
|
{
|
|
if (strcmp (hint_style_str, "hintnone") == 0)
|
|
hint_style = CAIRO_HINT_STYLE_NONE;
|
|
else if (strcmp (hint_style_str, "hintslight") == 0)
|
|
hint_style = CAIRO_HINT_STYLE_SLIGHT;
|
|
else if (strcmp (hint_style_str, "hintmedium") == 0)
|
|
hint_style = CAIRO_HINT_STYLE_MEDIUM;
|
|
else if (strcmp (hint_style_str, "hintfull") == 0)
|
|
hint_style = CAIRO_HINT_STYLE_FULL;
|
|
}
|
|
|
|
g_free (hint_style_str);
|
|
|
|
cairo_font_options_set_hint_style (options, hint_style);
|
|
|
|
/* We don't want to turn on subpixel anti-aliasing; since Clutter
|
|
* doesn't currently have the code to support ARGB masks,
|
|
* generating them then squashing them back to A8 is pointless.
|
|
*/
|
|
antialias_mode = (antialias < 0 || antialias) ? CAIRO_ANTIALIAS_GRAY
|
|
: CAIRO_ANTIALIAS_NONE;
|
|
|
|
cairo_font_options_set_antialias (options, antialias_mode);
|
|
|
|
clutter_backend_set_font_options (backend, options);
|
|
cairo_font_options_destroy (options);
|
|
}
|
|
|
|
static void
|
|
settings_notify_cb (GtkSettings *settings,
|
|
GParamSpec *pspec,
|
|
gpointer data)
|
|
{
|
|
update_font_options (settings);
|
|
}
|
|
|
|
static void
|
|
malloc_statistics_callback (ShellPerfLog *perf_log,
|
|
gpointer data)
|
|
{
|
|
struct mallinfo info = mallinfo ();
|
|
|
|
shell_perf_log_update_statistic_i (perf_log,
|
|
"malloc.arenaSize",
|
|
info.arena);
|
|
shell_perf_log_update_statistic_i (perf_log,
|
|
"malloc.mmapSize",
|
|
info.hblkhd);
|
|
shell_perf_log_update_statistic_i (perf_log,
|
|
"malloc.usedSize",
|
|
info.uordblks);
|
|
}
|
|
|
|
static void
|
|
add_statistics (GnomeShellPlugin *shell_plugin)
|
|
{
|
|
ShellPerfLog *perf_log = shell_perf_log_get_default ();
|
|
|
|
/* For probably historical reasons, mallinfo() defines the returned values,
|
|
* even those in bytes as int, not size_t. We're determined not to use
|
|
* more than 2G of malloc'ed memory, so are OK with that.
|
|
*/
|
|
shell_perf_log_define_statistic (perf_log,
|
|
"malloc.arenaSize",
|
|
"Amount of memory allocated by malloc() with brk(), in bytes",
|
|
"i");
|
|
shell_perf_log_define_statistic (perf_log,
|
|
"malloc.mmapSize",
|
|
"Amount of memory allocated by malloc() with mmap(), in bytes",
|
|
"i");
|
|
shell_perf_log_define_statistic (perf_log,
|
|
"malloc.usedSize",
|
|
"Amount of malloc'ed memory currently in use",
|
|
"i");
|
|
|
|
shell_perf_log_add_statistics_callback (perf_log,
|
|
malloc_statistics_callback,
|
|
NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_start (MutterPlugin *plugin)
|
|
{
|
|
GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
|
|
MetaScreen *screen;
|
|
MetaDisplay *display;
|
|
Display *xdisplay;
|
|
GtkSettings *settings;
|
|
GError *error = NULL;
|
|
int status;
|
|
const char *shell_js;
|
|
char **search_path;
|
|
ShellGlobal *global;
|
|
const char *glx_extensions;
|
|
|
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
|
|
settings = gtk_settings_get_default ();
|
|
g_object_connect (settings,
|
|
"signal::notify::gtk-xft-dpi",
|
|
G_CALLBACK (settings_notify_cb), NULL,
|
|
"signal::notify::gtk-xft-antialias",
|
|
G_CALLBACK (settings_notify_cb), NULL,
|
|
"signal::notify::gtk-xft-hinting",
|
|
G_CALLBACK (settings_notify_cb), NULL,
|
|
"signal::notify::gtk-xft-hintstyle",
|
|
G_CALLBACK (settings_notify_cb), NULL,
|
|
NULL);
|
|
update_font_options (settings);
|
|
|
|
screen = mutter_plugin_get_screen (plugin);
|
|
display = meta_screen_get_display (screen);
|
|
|
|
xdisplay = meta_display_get_xdisplay (display);
|
|
|
|
glXQueryExtension (xdisplay,
|
|
&shell_plugin->glx_error_base,
|
|
&shell_plugin->glx_event_base);
|
|
|
|
glx_extensions = glXQueryExtensionsString (xdisplay,
|
|
meta_screen_get_screen_number (screen));
|
|
shell_plugin->have_swap_event = strstr (glx_extensions, "GLX_INTEL_swap_event") != NULL;
|
|
|
|
shell_perf_log_define_event (shell_perf_log_get_default (),
|
|
"glx.swapComplete",
|
|
"GL buffer swap complete event received (with timestamp of completion)",
|
|
"x");
|
|
|
|
g_irepository_prepend_search_path (GNOME_SHELL_PKGLIBDIR);
|
|
|
|
shell_js = g_getenv("GNOME_SHELL_JS");
|
|
if (!shell_js)
|
|
shell_js = JSDIR;
|
|
|
|
search_path = g_strsplit(shell_js, ":", -1);
|
|
shell_plugin->gjs_context = gjs_context_new_with_search_path(search_path);
|
|
g_strfreev(search_path);
|
|
|
|
/* Initialize the global object here. */
|
|
global = shell_global_get ();
|
|
|
|
_shell_global_set_plugin (global, MUTTER_PLUGIN(shell_plugin));
|
|
_shell_global_set_gjs_context (global, shell_plugin->gjs_context);
|
|
|
|
add_statistics (shell_plugin);
|
|
|
|
if (!gjs_context_eval (shell_plugin->gjs_context,
|
|
"const Main = imports.ui.main; Main.start();",
|
|
-1,
|
|
"<main>",
|
|
&status,
|
|
&error))
|
|
{
|
|
g_message ("Execution of main.js threw exception: %s", error->message);
|
|
g_error_free (error);
|
|
/* We just exit() here, since in a development environment you'll get the
|
|
* error in your shell output, and it's way better than a busted WM,
|
|
* which typically manifests as a white screen.
|
|
*
|
|
* In production, we shouldn't crash =) But if we do, we should get
|
|
* restarted by the session infrastructure, which is likely going
|
|
* to be better than some undefined state.
|
|
*
|
|
* If there was a generic "hook into bug-buddy for non-C crashes"
|
|
* infrastructure, here would be the place to put it.
|
|
*/
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_dispose (GObject *object)
|
|
{
|
|
G_OBJECT_CLASS(gnome_shell_plugin_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_finalize (GObject *object)
|
|
{
|
|
G_OBJECT_CLASS(gnome_shell_plugin_parent_class)->finalize (object);
|
|
}
|
|
|
|
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 (MutterPlugin *plugin,
|
|
MutterWindow *actor)
|
|
{
|
|
_shell_wm_minimize (get_shell_wm (),
|
|
actor);
|
|
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_maximize (MutterPlugin *plugin,
|
|
MutterWindow *actor,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
_shell_wm_maximize (get_shell_wm (),
|
|
actor, x, y, width, height);
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_unmaximize (MutterPlugin *plugin,
|
|
MutterWindow *actor,
|
|
gint x,
|
|
gint y,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
_shell_wm_unmaximize (get_shell_wm (),
|
|
actor, x, y, width, height);
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_map (MutterPlugin *plugin,
|
|
MutterWindow *actor)
|
|
{
|
|
_shell_wm_map (get_shell_wm (),
|
|
actor);
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_destroy (MutterPlugin *plugin,
|
|
MutterWindow *actor)
|
|
{
|
|
_shell_wm_destroy (get_shell_wm (),
|
|
actor);
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_switch_workspace (MutterPlugin *plugin,
|
|
const GList **actors,
|
|
gint from,
|
|
gint to,
|
|
MetaMotionDirection direction)
|
|
{
|
|
_shell_wm_switch_workspace (get_shell_wm(),
|
|
actors, from, to, direction);
|
|
}
|
|
|
|
static void
|
|
gnome_shell_plugin_kill_effect (MutterPlugin *plugin,
|
|
MutterWindow *actor,
|
|
gulong events)
|
|
{
|
|
_shell_wm_kill_effect (get_shell_wm(),
|
|
actor, events);
|
|
}
|
|
|
|
static gboolean
|
|
gnome_shell_plugin_xevent_filter (MutterPlugin *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)
|
|
shell_perf_log_event_x (shell_perf_log_get_default (),
|
|
"glx.swapComplete",
|
|
swap_complete_event->ust);
|
|
}
|
|
#endif
|
|
|
|
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
|
|
}
|
|
|
|
static const
|
|
MutterPluginInfo *gnome_shell_plugin_plugin_info (MutterPlugin *plugin)
|
|
{
|
|
static const MutterPluginInfo info = {
|
|
.name = "GNOME Shell",
|
|
.version = "0.1",
|
|
.author = "Various",
|
|
.license = "GPLv2+",
|
|
.description = "Provides GNOME Shell core functionality"
|
|
};
|
|
|
|
return &info;
|
|
}
|