[mutter/wip/gcampax/background: 1/8] MetaBackgroundActor: import background rendering using libgnome-desktop



commit f3ec2579467c25078c53b1715e82d5fd79014f25
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sat Nov 10 20:49:23 2012 +0100

    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

 configure.ac                                   |    1 +
 src/Makefile.am                                |    1 +
 src/compositor/meta-background-actor-private.h |   13 ++
 src/compositor/meta-background-actor.c         |  209 +++++++++++------------
 src/compositor/meta-background.c               |  131 +++++++++++++++
 5 files changed, 246 insertions(+), 109 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 20715ed..82974d7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,6 +75,7 @@ MUTTER_PC_MODULES="
    xcomposite >= 0.2 xfixes xrender xdamage xi >= 1.6.0
    $CLUTTER_PACKAGE >= 1.13.2
    cogl-1.0 >= 1.9.6
+   gnome-desktop-3.0
 "
 
 GLIB_GSETTINGS
diff --git a/src/Makefile.am b/src/Makefile.am
index 9071d1a..b144869 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,6 +44,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		\
diff --git a/src/compositor/meta-background-actor-private.h b/src/compositor/meta-background-actor-private.h
index f5d656e..d46691c 100644
--- a/src/compositor/meta-background-actor-private.h
+++ b/src/compositor/meta-background-actor-private.h
@@ -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 */
diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c
index b039b58..00599dd 100644
--- a/src/compositor/meta-background-actor.c
+++ b/src/compositor/meta-background-actor.c
@@ -31,8 +31,6 @@
 #define CLUTTER_ENABLE_EXPERIMENTAL_API
 #include <clutter/clutter.h>
 
-#include <X11/Xatom.h>
-
 #include "cogl-utils.h"
 #include "compositor-private.h"
 #include <meta/errors.h>
@@ -53,11 +51,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
@@ -84,30 +87,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)
+free_screen_background (MetaScreenBackground *background)
 {
-  if (!background->have_pixmap)
-    set_texture_to_stage_color (background);
+  set_texture (background, COGL_INVALID_HANDLE);
+
+  g_cancellable_cancel (background->cancellable);
+  g_object_unref (background->cancellable);
+
+  g_object_unref (background->bg);
+  g_object_unref (background->settings);
 }
 
 static void
-free_screen_background (MetaScreenBackground *background)
+on_settings_changed (GSettings            *settings,
+                     const char           *key,
+                     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;
-    }
+  gnome_bg_load_from_preferences (background->bg,
+                                  background->settings);
 }
 
 static MetaScreenBackground *
@@ -118,18 +117,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);
     }
 
@@ -211,30 +226,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;
-
-  clutter_stage_get_color (CLUTTER_STAGE (stage), &color);
+  if (G_LIKELY (background->rendering_task == NULL ||
+                background->texture != COGL_INVALID_HANDLE))
+    return;
 
-  /* 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
@@ -302,6 +301,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);
@@ -489,82 +490,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)
-    {
-      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);
-        }
+  if (background->cancellable)
+    {
+      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);
 }
 
 /**
diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c
new file mode 100644
index 0000000..080dcef
--- /dev/null
+++ b/src/compositor/meta-background.c
@@ -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;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]