mirror of
https://github.com/brl/mutter.git
synced 2025-02-16 21:34:09 +00:00
MetaBackgroundActor: import background rendering using libgnome-desktop
Instead of relying on gnome-settings-daemon to set up _XROOTPMAP_ID properly, do rendering in process and stuff the background into a normal texture. This will allow to do more fancy transitions in the future, using Clutter to drive them. https://bugzilla.gnome.org/show_bug.cgi?id=682427
This commit is contained in:
parent
579bf2105e
commit
dbc9303efd
@ -75,6 +75,7 @@ MUTTER_PC_MODULES="
|
||||
xcomposite >= 0.2 xfixes xrender xdamage xi >= 1.6.0
|
||||
$CLUTTER_PACKAGE >= 1.13.5
|
||||
cogl-1.0 >= 1.13.3
|
||||
gnome-desktop-3.0
|
||||
"
|
||||
|
||||
GLIB_GSETTINGS
|
||||
|
@ -48,6 +48,7 @@ libmutter_la_SOURCES = \
|
||||
compositor/compositor-private.h \
|
||||
compositor/meta-background-actor.c \
|
||||
compositor/meta-background-actor-private.h \
|
||||
compositor/meta-background.c \
|
||||
compositor/meta-module.c \
|
||||
compositor/meta-module.h \
|
||||
compositor/meta-plugin.c \
|
||||
|
@ -3,6 +3,9 @@
|
||||
#ifndef META_BACKGROUND_ACTOR_PRIVATE_H
|
||||
#define META_BACKGROUND_ACTOR_PRIVATE_H
|
||||
|
||||
#define GNOME_DESKTOP_USE_UNSTABLE_API
|
||||
#include <libgnome-desktop/gnome-bg.h>
|
||||
|
||||
#include <meta/screen.h>
|
||||
#include <meta/meta-background-actor.h>
|
||||
|
||||
@ -12,4 +15,14 @@ void meta_background_actor_set_visible_region (MetaBackgroundActor *self,
|
||||
void meta_background_actor_update (MetaScreen *screen);
|
||||
void meta_background_actor_screen_size_changed (MetaScreen *screen);
|
||||
|
||||
GTask *meta_background_draw_async (MetaScreen *screen,
|
||||
GnomeBG *bg,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
CoglHandle meta_background_draw_finish (MetaScreen *screen,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
|
||||
#endif /* META_BACKGROUND_ACTOR_PRIVATE_H */
|
||||
|
@ -29,8 +29,6 @@
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include "cogl-utils.h"
|
||||
#include "compositor-private.h"
|
||||
#include <meta/errors.h>
|
||||
@ -51,11 +49,16 @@ struct _MetaScreenBackground
|
||||
MetaScreen *screen;
|
||||
GSList *actors;
|
||||
|
||||
GSettings *settings;
|
||||
GnomeBG *bg;
|
||||
GCancellable *cancellable;
|
||||
|
||||
float texture_width;
|
||||
float texture_height;
|
||||
CoglTexture *texture;
|
||||
CoglMaterialWrapMode wrap_mode;
|
||||
guint have_pixmap : 1;
|
||||
|
||||
GTask *rendering_task;
|
||||
};
|
||||
|
||||
struct _MetaBackgroundActorPrivate
|
||||
@ -82,30 +85,26 @@ G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR);
|
||||
|
||||
static void set_texture (MetaScreenBackground *background,
|
||||
CoglHandle texture);
|
||||
static void set_texture_to_stage_color (MetaScreenBackground *background);
|
||||
|
||||
static void
|
||||
on_notify_stage_color (GObject *stage,
|
||||
GParamSpec *pspec,
|
||||
MetaScreenBackground *background)
|
||||
{
|
||||
if (!background->have_pixmap)
|
||||
set_texture_to_stage_color (background);
|
||||
}
|
||||
|
||||
static void
|
||||
free_screen_background (MetaScreenBackground *background)
|
||||
{
|
||||
set_texture (background, COGL_INVALID_HANDLE);
|
||||
|
||||
if (background->screen != NULL)
|
||||
{
|
||||
ClutterActor *stage = meta_get_stage_for_screen (background->screen);
|
||||
g_signal_handlers_disconnect_by_func (stage,
|
||||
(gpointer) on_notify_stage_color,
|
||||
background);
|
||||
background->screen = NULL;
|
||||
}
|
||||
g_cancellable_cancel (background->cancellable);
|
||||
g_object_unref (background->cancellable);
|
||||
|
||||
g_object_unref (background->bg);
|
||||
g_object_unref (background->settings);
|
||||
}
|
||||
|
||||
static void
|
||||
on_settings_changed (GSettings *settings,
|
||||
const char *key,
|
||||
MetaScreenBackground *background)
|
||||
{
|
||||
gnome_bg_load_from_preferences (background->bg,
|
||||
background->settings);
|
||||
}
|
||||
|
||||
static MetaScreenBackground *
|
||||
@ -116,18 +115,34 @@ meta_screen_background_get (MetaScreen *screen)
|
||||
background = g_object_get_data (G_OBJECT (screen), "meta-screen-background");
|
||||
if (background == NULL)
|
||||
{
|
||||
ClutterActor *stage;
|
||||
|
||||
background = g_new0 (MetaScreenBackground, 1);
|
||||
|
||||
background->screen = screen;
|
||||
g_object_set_data_full (G_OBJECT (screen), "meta-screen-background",
|
||||
background, (GDestroyNotify) free_screen_background);
|
||||
|
||||
stage = meta_get_stage_for_screen (screen);
|
||||
g_signal_connect (stage, "notify::color",
|
||||
G_CALLBACK (on_notify_stage_color), background);
|
||||
background->settings = g_settings_new ("org.gnome.desktop.background");
|
||||
g_signal_connect (background->settings, "changed",
|
||||
G_CALLBACK (on_settings_changed), background);
|
||||
|
||||
background->bg = gnome_bg_new ();
|
||||
g_signal_connect_object (background->bg, "transitioned",
|
||||
G_CALLBACK (meta_background_actor_update),
|
||||
screen, G_CONNECT_SWAPPED);
|
||||
g_signal_connect_object (background->bg, "changed",
|
||||
G_CALLBACK (meta_background_actor_update),
|
||||
screen, G_CONNECT_SWAPPED);
|
||||
|
||||
background->texture_width = -1;
|
||||
background->texture_height = -1;
|
||||
background->wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT;
|
||||
|
||||
on_settings_changed (background->settings, NULL, background);
|
||||
|
||||
/* GnomeBG has queued a changed event, but we need to start rendering now,
|
||||
or it will be too late when we paint the first frame.
|
||||
*/
|
||||
g_object_set_data (G_OBJECT (background->bg), "ignore-pending-change", GINT_TO_POINTER (TRUE));
|
||||
meta_background_actor_update (screen);
|
||||
}
|
||||
|
||||
@ -209,30 +224,14 @@ set_texture (MetaScreenBackground *background,
|
||||
update_wrap_mode (background);
|
||||
}
|
||||
|
||||
/* Sets our pipeline to paint with a 1x1 texture of the stage's background
|
||||
* color; doing this when we have no pixmap allows the application to turn
|
||||
* off painting the stage. There might be a performance benefit to
|
||||
* painting in this case with a solid color, but the normal solid color
|
||||
* case is a 1x1 root pixmap, so we'd have to reverse-engineer that to
|
||||
* actually pick up the (small?) performance win. This is just a fallback.
|
||||
*/
|
||||
static void
|
||||
set_texture_to_stage_color (MetaScreenBackground *background)
|
||||
static inline void
|
||||
meta_background_ensure_rendered (MetaScreenBackground *background)
|
||||
{
|
||||
ClutterActor *stage = meta_get_stage_for_screen (background->screen);
|
||||
ClutterColor color;
|
||||
CoglHandle texture;
|
||||
if (G_LIKELY (background->rendering_task == NULL ||
|
||||
background->texture != COGL_INVALID_HANDLE))
|
||||
return;
|
||||
|
||||
clutter_stage_get_color (CLUTTER_STAGE (stage), &color);
|
||||
|
||||
/* Slicing will prevent COGL from using hardware texturing for
|
||||
* the tiled 1x1 pixmap, and will cause it to draw the window
|
||||
* background in millions of separate 1x1 rectangles */
|
||||
texture = meta_create_color_texture_4ub (color.red, color.green,
|
||||
color.blue, 0xff,
|
||||
COGL_TEXTURE_NO_SLICING);
|
||||
set_texture (background, texture);
|
||||
cogl_handle_unref (texture);
|
||||
g_task_wait_sync (background->rendering_task);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -300,6 +299,8 @@ meta_background_actor_paint (ClutterActor *actor)
|
||||
guint8 color_component;
|
||||
int width, height;
|
||||
|
||||
meta_background_ensure_rendered (priv->background);
|
||||
|
||||
meta_screen_get_size (priv->background->screen, &width, &height);
|
||||
|
||||
color_component = (int)(0.5 + opacity * priv->dim_factor);
|
||||
@ -487,82 +488,72 @@ meta_background_actor_new_for_screen (MetaScreen *screen)
|
||||
return CLUTTER_ACTOR (self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_background_drawn (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaScreen *screen = META_SCREEN (object);
|
||||
MetaScreenBackground *background;
|
||||
CoglHandle texture;
|
||||
GError *error;
|
||||
|
||||
background = meta_screen_background_get (screen);
|
||||
|
||||
g_clear_object (&background->rendering_task);
|
||||
g_clear_object (&background->cancellable);
|
||||
|
||||
error = NULL;
|
||||
texture = meta_background_draw_finish (screen, result, &error);
|
||||
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture != COGL_INVALID_HANDLE)
|
||||
{
|
||||
set_texture (background, texture);
|
||||
cogl_handle_unref (texture);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Failed to create background texture from pixmap: %s",
|
||||
error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_background_actor_update:
|
||||
* @screen: a #MetaScreen
|
||||
*
|
||||
* Refetches the _XROOTPMAP_ID property for the root window and updates
|
||||
* the contents of the background actor based on that. There's no attempt
|
||||
* to optimize out pixmap values that don't change (since a root pixmap
|
||||
* could be replaced by with another pixmap with the same ID under some
|
||||
* circumstances), so this should only be called when we actually receive
|
||||
* a PropertyNotify event for the property.
|
||||
* Forces a redraw of the background. The redraw happens asynchronously in
|
||||
* a thread, and the actual on screen change is therefore delayed until
|
||||
* the redraw is finished.
|
||||
*/
|
||||
void
|
||||
meta_background_actor_update (MetaScreen *screen)
|
||||
{
|
||||
MetaScreenBackground *background;
|
||||
MetaDisplay *display;
|
||||
MetaCompositor *compositor;
|
||||
Atom type;
|
||||
int format;
|
||||
gulong nitems;
|
||||
gulong bytes_after;
|
||||
guchar *data;
|
||||
Pixmap root_pixmap_id;
|
||||
|
||||
background = meta_screen_background_get (screen);
|
||||
display = meta_screen_get_display (screen);
|
||||
compositor = meta_display_get_compositor (display);
|
||||
|
||||
root_pixmap_id = None;
|
||||
if (!XGetWindowProperty (meta_display_get_xdisplay (display),
|
||||
meta_screen_get_xroot (screen),
|
||||
compositor->atom_x_root_pixmap,
|
||||
0, LONG_MAX,
|
||||
False,
|
||||
AnyPropertyType,
|
||||
&type, &format, &nitems, &bytes_after, &data) &&
|
||||
type != None)
|
||||
{
|
||||
/* Got a property. */
|
||||
if (type == XA_PIXMAP && format == 32 && nitems == 1)
|
||||
{
|
||||
/* Was what we expected. */
|
||||
root_pixmap_id = *(Pixmap *)data;
|
||||
}
|
||||
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
if (root_pixmap_id != None)
|
||||
if (background->cancellable)
|
||||
{
|
||||
CoglHandle texture;
|
||||
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||
GError *error = NULL;
|
||||
|
||||
meta_error_trap_push (display);
|
||||
texture = cogl_texture_pixmap_x11_new (ctx, root_pixmap_id, FALSE, &error);
|
||||
meta_error_trap_pop (display);
|
||||
|
||||
if (texture != COGL_INVALID_HANDLE)
|
||||
{
|
||||
set_texture (background, texture);
|
||||
cogl_handle_unref (texture);
|
||||
|
||||
background->have_pixmap = True;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Failed to create background texture from pixmap: %s",
|
||||
error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
g_cancellable_cancel (background->cancellable);
|
||||
g_object_unref (background->cancellable);
|
||||
}
|
||||
|
||||
background->have_pixmap = False;
|
||||
set_texture_to_stage_color (background);
|
||||
g_clear_object (&background->rendering_task);
|
||||
|
||||
background->cancellable = g_cancellable_new ();
|
||||
background->rendering_task = meta_background_draw_async (screen,
|
||||
background->bg,
|
||||
background->cancellable,
|
||||
on_background_drawn, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
131
src/compositor/meta-background.c
Normal file
131
src/compositor/meta-background.c
Normal file
@ -0,0 +1,131 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* meta-background.c: Utilities for drawing the background
|
||||
*
|
||||
* Copyright 2012 Giovanni Campagna <scampa.giovanni@gmail.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"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
#include "meta-background-actor-private.h"
|
||||
#include <core/screen-private.h>
|
||||
|
||||
typedef struct {
|
||||
GnomeBG *bg;
|
||||
GdkPixbuf *pixbuf;
|
||||
|
||||
GdkRectangle *monitors;
|
||||
int num_monitors;
|
||||
} TaskData;
|
||||
|
||||
static void
|
||||
task_data_free (gpointer task_data)
|
||||
{
|
||||
TaskData *td = task_data;
|
||||
|
||||
g_object_unref (td->bg);
|
||||
g_object_unref (td->pixbuf);
|
||||
|
||||
g_free (td->monitors);
|
||||
|
||||
g_slice_free (TaskData, td);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_background_draw_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
TaskData *td = task_data;
|
||||
|
||||
gnome_bg_draw_areas (td->bg,
|
||||
td->pixbuf,
|
||||
TRUE,
|
||||
td->monitors,
|
||||
td->num_monitors);
|
||||
|
||||
g_task_return_pointer (task, g_object_ref (td->pixbuf), g_object_unref);
|
||||
}
|
||||
|
||||
GTask *
|
||||
meta_background_draw_async (MetaScreen *screen,
|
||||
GnomeBG *bg,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
TaskData *td;
|
||||
int i;
|
||||
|
||||
g_return_val_if_fail (META_IS_SCREEN (screen), NULL);
|
||||
g_return_val_if_fail (GNOME_IS_BG (bg), NULL);
|
||||
|
||||
task = g_task_new (screen, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, meta_background_draw_async);
|
||||
g_task_set_return_on_cancel (task, TRUE);
|
||||
g_task_set_check_cancellable (task, TRUE);
|
||||
|
||||
td = g_slice_new (TaskData);
|
||||
td->bg = g_object_ref (bg);
|
||||
td->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
|
||||
FALSE, /* has alpha */
|
||||
8, /* bits per sample */
|
||||
screen->rect.width,
|
||||
screen->rect.height);
|
||||
|
||||
td->num_monitors = meta_screen_get_n_monitors (screen);
|
||||
td->monitors = g_new (GdkRectangle, td->num_monitors);
|
||||
for (i = 0; i < td->num_monitors; i++)
|
||||
meta_screen_get_monitor_geometry (screen, i, (MetaRectangle*)(td->monitors + i));
|
||||
|
||||
g_task_set_task_data (task, td, task_data_free);
|
||||
|
||||
g_task_run_in_thread (task, meta_background_draw_thread);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
CoglHandle
|
||||
meta_background_draw_finish (MetaScreen *screen,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
GdkPixbuf *pixbuf;
|
||||
CoglHandle handle;
|
||||
|
||||
pixbuf = g_task_propagate_pointer (G_TASK (result), error);
|
||||
if (pixbuf == NULL)
|
||||
return COGL_INVALID_HANDLE;
|
||||
|
||||
handle = cogl_texture_new_from_data (gdk_pixbuf_get_width (pixbuf),
|
||||
gdk_pixbuf_get_height (pixbuf),
|
||||
COGL_TEXTURE_NO_ATLAS | COGL_TEXTURE_NO_SLICING,
|
||||
COGL_PIXEL_FORMAT_RGB_888,
|
||||
COGL_PIXEL_FORMAT_RGB_888,
|
||||
gdk_pixbuf_get_rowstride (pixbuf),
|
||||
gdk_pixbuf_get_pixels (pixbuf));
|
||||
|
||||
g_object_unref (pixbuf);
|
||||
return handle;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user