[gnome-shell/wip/background-rework: 4/13] layout: rework background handling



commit 24334ed8b2fb6f9f9d8d54a77cab2ecb38433b70
Author: Ray Strode <rstrode redhat com>
Date:   Mon Dec 24 15:20:39 2012 +0100

    layout: rework background handling
    
    This commit updates the code to use mutter's new background
    api, and changes the shell's startup animation to be closer
    to the mockups.
    
    Based on initial work by Giovanni Campagna

 js/Makefile.am              |    1 +
 js/ui/background.js         |  314 +++++++++++++++++++++++++++++++++++++++++++
 js/ui/layout.js             |  201 +++++++++++++++++++++++-----
 js/ui/main.js               |    9 ++
 js/ui/overview.js           |   58 ++++----
 js/ui/screenShield.js       |   66 ++++------
 js/ui/workspaceThumbnail.js |   24 +++-
 src/Makefile.am             |    2 +-
 src/shell-global.c          |   11 --
 src/shell-util.c            |   40 ++++++
 src/shell-util.h            |    3 +
 11 files changed, 605 insertions(+), 124 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 29187da..b7bfa9e 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -38,6 +38,7 @@ nobase_dist_js_DATA = 	\
 	ui/altTab.js		\
 	ui/appDisplay.js	\
 	ui/appFavorites.js	\
+ 	ui/background.js	\
 	ui/boxpointer.js	\
 	ui/calendar.js		\
 	ui/checkBox.js		\
diff --git a/js/ui/background.js b/js/ui/background.js
new file mode 100644
index 0000000..b658d1f
--- /dev/null
+++ b/js/ui/background.js
@@ -0,0 +1,314 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Cairo = imports.cairo;
+const Clutter = imports.gi.Clutter;
+const GdkPixbuf = imports.gi.GdkPixbuf;
+const GDesktopEnums = imports.gi.GDesktopEnums;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const St = imports.gi.St;
+
+const Layout = imports.ui.layout;
+const Main = imports.ui.main
+const Params = imports.misc.params;
+const Util = imports.misc.util;
+
+const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
+const DRAW_BACKGROUND_KEY = 'draw-background';
+const PRIMARY_COLOR_KEY = 'primary-color';
+const SECONDARY_COLOR_KEY = 'secondary-color';
+const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
+const BACKGROUND_STYLE_KEY = 'picture-options';
+const PICTURE_OPACITY_KEY = 'picture-opacity';
+const PICTURE_URI_KEY = 'picture-uri';
+
+let _backgroundCache = null;
+
+const BackgroundCache = new Lang.Class({
+    Name: 'BackgroundCache',
+
+    _init: function(layoutManager) {
+       this._mats = [];
+       this._images = [];
+       this._fileMonitors = {};
+       this._layoutManager = layoutManager;
+    },
+
+    getMatContent: function(params) {
+        params = Params.parse(params, { monitorIndex: 0,
+                                        color: null,
+                                        secondColor: null,
+                                        shadingType: null,
+                                        effects: Meta.BackgroundEffects.NONE });
+
+        let content = null;
+        let candidateContent = null;
+        for (let i = 0; i < this._mats.length; i++) {
+            if (this._mats[i].get_shading() != params.shadingType)
+                continue;
+
+            if (!params.color.equal(this._mats[i].get_color()))
+                continue;
+
+            if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
+                !params.secondColor.equal(this._mats[i].get_second_color()))
+                continue;
+
+            candidateContent = this._mats[i];
+
+            if (params.effects != this._mats[i].effects)
+                continue;
+
+            break;
+        }
+
+        if (candidateContent) {
+            content = candidateContent.copy(params.monitorIndex, params.effects);
+            content.saturation = 1.0;
+        } else {
+            content = new Meta.Background({ meta_screen: global.screen,
+                                            monitor: params.monitorIndex,
+                                            effects: params.effects });
+
+            if (params.shadingType == GDesktopEnums.BackgroundShading.SOLID) {
+                content.load_color(params.color);
+            } else {
+                content.load_gradient(params.shadingType, params.color, params.secondColor);
+            }
+
+            this._mats.push(content);
+        }
+
+        return content;
+    },
+
+    _monitorFile: function(filename) {
+        if (this._fileMonitors[filename])
+            return;
+
+        let file = Gio.File.new_for_path(filename);
+        let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
+
+        let signalId = monitor.connect('changed',
+                                       Lang.bind(this, function() {
+                                           for (let i = 0; i < this._images.length; i++) {
+                                               if (this._images[i].get_filename() == filename)
+                                                   this._images.splice(i, 1);
+                                           }
+
+                                           GLib.source_remove(signalId);
+
+                                           this.emit('file-changed', filename);
+                                       }));
+
+        this._fileMonitors[filename] = monitor;
+    },
+
+    getImageContent: function(params) {
+        params = Params.parse(params, { monitorIndex: 0,
+                                        style: null,
+                                        filename: null,
+                                        effects: Meta.BackgroundEffects.NONE });
+
+        let content = null;
+        let candidateContent = null;
+        for (let i = 0; i < this._images.length; i++) {
+            if (this._images[i].get_style() != params.style)
+                continue;
+
+            if (this._images[i].get_filename() != params.filename)
+                continue;
+
+            if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
+                this._images[i].monitor_index != monitorIndex)
+                continue;
+
+            candidateContent = this._images[i];
+
+            if (params.effects != this._images[i].effects)
+                continue;
+
+            break;
+        }
+
+        if (candidateContent) {
+            content = candidateContent.copy(params.monitorIndex, params.effects);
+            content.saturation = 1.0;
+        } else {
+            content = new Meta.Background({ meta_screen: global.screen,
+                                            monitor: params.monitorIndex,
+                                            effects: params.effects });
+
+            if (content.load_file(params.filename, params.style)) {
+                this._monitorFile(params.filename);
+                this._images.push(content);
+            } else {
+                content = null;
+            }
+        }
+
+        return content;
+    }
+});
+Signals.addSignalMethods(BackgroundCache.prototype);
+
+function getBackgroundCache(layoutManager) {
+    if (!_backgroundCache)
+        _backgroundCache = new BackgroundCache(layoutManager);
+    return _backgroundCache;
+}
+
+const Background = new Lang.Class({
+    Name: 'Background',
+
+    _init: function(params) {
+        params = Params.parse(params, { layoutManager: Main.layoutManager,
+                                        monitorIndex: 0,
+                                        effects: Meta.BackgroundEffects.NONE });
+        this.actor = new Meta.BackgroundGroup();
+
+        this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
+        this._layoutManager = params.layoutManager;
+        this._monitorIndex = params.monitorIndex;
+        this._effects = params.effects;
+        this._fileWatches = {};
+
+        this._dimFactor = 1.0;
+
+        this._load();
+    },
+
+    destroy: function() {
+        if (this._slideUpdateTimeoutId) {
+            GLib.source_remove(this._slideUpdateTimeoutId);
+            this._slideUpdateTimeoutId = 0;
+        }
+
+        let keys = Object.keys(this._fileWatches);
+        for (let i = 0; i < keys.length; i++) {
+            this._cache.disconnect(this._fileWatches[keys[i]]);
+        }
+        this._fileWatches = null;
+    },
+
+    _loadMat: function() {
+        let colorString, res, color, secondColor;
+
+        colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
+        [res, color] = Clutter.Color.from_string(colorString);
+        colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
+        [res, secondColor] = Clutter.Color.from_string(colorString);
+
+        let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
+
+        let content = this._cache.getMatContent({ monitorIndex: this._monitorIndex,
+                                                  effects: this._effects,
+                                                  color: color,
+                                                  secondColor: secondColor,
+                                                  shadingType: shadingType });
+
+        this._mat = new Meta.BackgroundActor(global.screen, this._monitorIndex);
+        this.actor.add_child(this._mat);
+
+        this._mat.content = content;
+    },
+
+    _watchCacheFile: function(filename) {
+        if (this._fileWatches[filename])
+            return;
+
+        let signalId = this._cache.connect('file-changed',
+                                           Lang.bind(this, function(cache, changedFile) {
+                                               if (changedFile == filename) {
+                                                   this.emit('changed');
+                                               }
+                                           }));
+        this._fileWatches[filename] = signalId;
+    },
+
+    _loadImage: function(filename, style) {
+        let content = this._cache.getImageContent({ monitorIndex: this._monitorIndex,
+                                                    effects: this._effects,
+                                                    style: style,
+                                                    filename: filename });
+
+        if (content) {
+            this._image = new Meta.BackgroundActor(global.screen, this._monitorIndex);
+            this.actor.add_child(this._image);
+            this._image.content = content;
+            this._watchCacheFile(filename);
+
+            return true;
+        }
+
+        return false;
+    },
+
+    _load: function () {
+        this._cache = getBackgroundCache(this._layoutManager);
+
+        this._loadMat(this._cache);
+
+        let uri = this._settings.get_string(PICTURE_URI_KEY);
+        let filename = Gio.File.new_for_uri(uri).get_path();
+
+        let style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
+        if (style != GDesktopEnums.BackgroundStyle.NONE) {
+            this._loadImage(filename, style);
+        }
+
+        if (!this._settings.get_boolean(DRAW_BACKGROUND_KEY)) {
+            this.actor.opacity = 0;
+        } else {
+            this.actor.opacity = 255;
+        }
+
+        this._settings.connect('changed', Lang.bind(this, function() {
+                                   this.emit('changed');
+                               }));
+    },
+
+    set saturation(saturation) {
+       this._saturation = saturation;
+       this._mat.get_content().saturation = saturation;
+       if (this._image && this._image.get_content())
+           this._image.get_content().saturation = saturation;
+    },
+
+    get saturation() {
+       return this._saturation;
+    },
+
+    get dimFactor() {
+        return this._dimFactor;
+    },
+
+    set dimFactor(factor) {
+        this._dimFactor = factor;
+        this._mat.get_content().dim_factor = factor;
+        if (this._image && this._image.get_content())
+           this._image.get_content().dim_factor = factor;
+    }
+});
+Signals.addSignalMethods(Background.prototype);
+
+const StillFrame = new Lang.Class({
+    Name: 'StillFrame',
+
+    _init: function(monitor) {
+        this.actor = new Meta.BackgroundActor();
+
+        let content = new Meta.Background({ meta_screen: global.screen,
+                                            monitor: monitor,
+                                            effects: Meta.BackgroundEffects.NONE });
+        content.load_still_frame();
+
+        this.actor.content = content;
+    }
+});
+Signals.addSignalMethods(StillFrame.prototype);
diff --git a/js/ui/layout.js b/js/ui/layout.js
index eb909af..51c337c 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -9,15 +9,16 @@ const Shell = imports.gi.Shell;
 const Signals = imports.signals;
 const St = imports.gi.St;
 
+const Background = imports.ui.background;
 const DND = imports.ui.dnd;
 const Main = imports.ui.main;
 const Params = imports.misc.params;
 const Tweener = imports.ui.tweener;
 
 const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
-const STARTUP_ANIMATION_TIME = 0.2;
+const STARTUP_ANIMATION_TIME = 0.5;
+const FADE_ANIMATION_TIME = 1.0;
 const KEYBOARD_ANIMATION_TIME = 0.15;
-const PLYMOUTH_TRANSITION_TIME = 1;
 const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
 
 const MonitorConstraint = new Lang.Class({
@@ -106,10 +107,13 @@ const LayoutManager = new Lang.Class({
         this.primaryIndex = -1;
         this._keyboardIndex = -1;
         this._hotCorners = [];
-        this._background = null;
+        this._stillFrames = [];
+        this._backgrounds = [];
         this._leftPanelBarrier = 0;
         this._rightPanelBarrier = 0;
         this._trayBarrier = 0;
+        this._startupPrepared = false;
+        this._startingUp = true;
 
         // Normally, the stage is always covered so Clutter doesn't need to clear
         // it; however it becomes visible during the startup animation
@@ -187,8 +191,6 @@ const LayoutManager = new Lang.Class({
     // yet when the LayoutManager was constructed.
     init: function() {
         this._chrome.init();
-
-        this._startupAnimation();
     },
 
     _updateMonitors: function() {
@@ -271,6 +273,83 @@ const LayoutManager = new Lang.Class({
         }
     },
 
+    _destroyBackgrounds: function() {
+        for (let i = 0; i < this._backgrounds.length; i++) {
+            if (this._backgrounds[i])
+                this._backgrounds[i].actor.destroy();
+        }
+        this._backgrounds = [];
+    },
+
+    _updateBackground: function(background, monitorIndex) {
+        let newBackground = this._createBackground(monitorIndex);
+
+        this._backgrounds[monitorIndex] = null;
+
+        // New background is at the bottom of the stack, so we just
+        // have to fade out the old one to expose it.
+        Tweener.addTween(background.actor,
+                         { opacity: 0,
+                           time: FADE_ANIMATION_TIME,
+                           transition: 'easeOutQuad',
+                           onComplete: function() {
+                               background.actor.destroy();
+                           },
+                           onCompleteScope: this });
+    },
+
+    _createSecondaryBackgrounds: function() {
+        for (let i = 0; i < this.monitors.length; i++) {
+            if (i != this.primaryIndex) {
+                let background = this._createBackground(i);
+
+                background.actor.opacity = 0;
+                Tweener.addTween(background.actor,
+                                 { opacity: 255,
+                                   time: FADE_ANIMATION_TIME,
+                                   transition: 'easeOutQuad' });
+            }
+        }
+    },
+
+    _createPrimaryBackground: function() {
+        this._createBackground(this.primaryIndex);
+    },
+
+    _createBackground: function(monitorIndex) {
+        let background = new Background.Background({ layoutManager: this,
+                                                     monitorIndex: monitorIndex });
+        this._backgrounds.push(background);
+        global.window_group.add_child(background.actor);
+
+        let monitor = this.monitors[monitorIndex];
+        background.actor.set_position(monitor.x, monitor.y);
+        background.actor.set_size(monitor.width, monitor.height);
+        background.actor.lower_bottom();
+
+        let signalId = background.connect('changed', Lang.bind(this, function() {
+            background.disconnect(signalId);
+
+            this._updateBackground(background, monitorIndex);
+        }));
+
+        return background;
+    },
+
+    _updateBackgrounds: function() {
+        this._destroyBackgrounds();
+
+        if (Main.sessionMode.isGreeter)
+            return;
+
+        if (this._startingUp)
+            return;
+
+        for (let i = 0; i < this.monitors.length; i++) {
+            this._createBackground(i);
+        }
+    },
+
     _updateBoxes: function() {
         this.screenShieldGroup.set_position(0, 0);
         this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
@@ -324,6 +403,7 @@ const LayoutManager = new Lang.Class({
         this._updateMonitors();
         this._updateBoxes();
         this._updateHotCorners();
+        this._updateBackgrounds();
 
         this.emit('monitors-changed');
     },
@@ -383,52 +463,101 @@ const LayoutManager = new Lang.Class({
         return this._keyboardIndex;
     },
 
-    _startupAnimation: function() {
-        this.panelBox.translation_y = -this.panelBox.height;
+    // Startup Animations
+    //
+    // We have two different animations, depending on whether we're a greeter
+    // or a normal session.
+    //
+    // In the greeter, we want to animate the panel from the top, and smoothly
+    // fade the login dialog on top of whatever plymouth left on screen which
+    // we get as a still frame background before drawing anything else.
+    //
+    // Here we just have the code to animate the panel, the login dialog animation
+    // is handled by modalDialog.js
+    //
+    // When starting a normal user session, we want to grow it out of the middle
+    // of the screen.
+    //
+    // Usually, we don't want to paint the stage background color because the
+    // MetaBackgroundActor inside global.window_group covers the entirety of the
+    // screen. So, we set no_clear_hint at the end of the animation.
+
+    prepareStartupAnimation: function() {
+        if (this._startupPrepared)
+            return;
+
+        // Set ourselves to FULLSCREEN input mode while the animation is running
+        // so events don't get delivered to X11 windows (which are distorted by the animation)
+        global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
+
+        // build new backgrounds
+        for (let i = 0; i < this.monitors.length; i++) {
+
+            let monitor = this.monitors[i];
+
+            let stillFrame = new Background.StillFrame(i);
+            this._stillFrames.push(stillFrame);
 
-        let plymouthTransitionRunning = false;
+            stillFrame.actor.set_size(this.monitors[i].width, this.monitors[i].height);
+            stillFrame.actor.set_position(this.monitors[i].x, this.monitors[i].y);
+            global.stage.insert_child_below(stillFrame.actor, null);
+        }
 
-        // If we're the greeter, put up the xrootpmap actor
-        // and fade it out to have a nice transition from plymouth
-        // to the greeter. Otherwise, we'll just animate the panel,
-        // as usual.
         if (Main.sessionMode.isGreeter) {
-            this._background = Meta.BackgroundActor.new_for_screen(global.screen);
-            if (this._background != null) {
-                this._layoutManager.add_actor(this._background);
-                Tweener.addTween(this._background,
-                                 { opacity: 0,
-                                   time: PLYMOUTH_TRANSITION_TIME,
-                                   transition: 'linear',
-                                   onComplete: this._fadeBackgroundComplete,
-                                   onCompleteScope: this });
-                plymouthTransitionRunning = true;
-            }
+            this.panelBox.translation_y = -this.panelBox.height;
+        } else {
+            let monitor = this.primaryMonitor;
+            let x = monitor.x + monitor.width / 2.0;
+            let y = monitor.y + monitor.height / 2.0;
+
+            this.uiGroup.set_pivot_point(x / global.screen_width,
+                                         y / global.screen_height);
+            this.uiGroup.scale_x = this.uiGroup.scale_y = 0;
         }
 
-        if (!plymouthTransitionRunning)
-            this._fadeBackgroundComplete();
+        this._startupPrepared = true;
     },
 
-    _fadeBackgroundComplete: function() {
-        // Don't animate the strut
-        this._chrome.freezeUpdateRegions();
+    startupAnimation: function() {
+        if (Main.sessionMode.isGreeter)
+            this._startupAnimationGreeter();
+        else
+            this._startupAnimationSession();
+    },
 
-        if (this._background != null) {
-            this._background.destroy();
-            this._background = null;
-        }
+    _startupAnimationGreeter: function() {
+         this._chrome.freezeUpdateRegions();
+         Tweener.addTween(this.panelBox,
+                          { translation_y: 0,
+                            time: STARTUP_ANIMATION_TIME,
+                            transition: 'easeOutQuad',
+                            onComplete: this._startupAnimationComplete,
+                            onCompleteScope: this });
+    },
 
-        Tweener.addTween(this.panelBox,
-                         { translation_y: 0,
+    _startupAnimationSession: function() {
+        this._chrome.freezeUpdateRegions();
+        this._createPrimaryBackground();
+        Tweener.addTween(this.uiGroup,
+                         { scale_x: 1,
+                           scale_y: 1,
                            time: STARTUP_ANIMATION_TIME,
                            transition: 'easeOutQuad',
                            onComplete: this._startupAnimationComplete,
-                           onCompleteScope: this
-                         });
+                           onCompleteScope: this });
     },
 
     _startupAnimationComplete: function() {
+        // At this point, the UI group is covering everything, so
+        // we no longer need to clear the stage
+        global.stage.no_clear_hint = true;
+
+        global.stage_input_mode = Shell.StageInputMode.NORMAL;
+
+        this._stillFrames = [];
+        this._startingUp = false;
+        this._createSecondaryBackgrounds();
+
         this.emit('panel-box-changed');
         this._chrome.thawUpdateRegions();
     },
diff --git a/js/ui/main.js b/js/ui/main.js
index c15b6da..f7b7b65 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -81,6 +81,7 @@ let _cssStylesheet = null;
 let _overridesSettings = null;
 
 function _sessionUpdated() {
+    layoutManager.prepareStartupAnimation()
     wm.setCustomKeybindingHandler('panel-main-menu',
                                   KeybindingMode.NORMAL |
                                   KeybindingMode.OVERVIEW,
@@ -184,6 +185,14 @@ function start() {
 
     ExtensionDownloader.init();
     ExtensionSystem.init();
+
+    // Run the startup animation as soon as the mainloop is idle enough
+    // This is necessary to have it smooth and without interruptions from
+    // completed IO tasks
+    GLib.idle_add(GLib.PRIORITY_LOW, function() {
+        layoutManager.startupAnimation();
+        return false;
+    });
 }
 
 let _workspaces = [];
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 84cd48c..82aaa85 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -10,6 +10,7 @@ const St = imports.gi.St;
 const Shell = imports.gi.Shell;
 const Gdk = imports.gi.Gdk;
 
+const Background = imports.ui.background;
 const Dash = imports.ui.dash;
 const DND = imports.ui.dnd;
 const Main = imports.ui.main;
@@ -25,24 +26,6 @@ const ANIMATION_TIME = 0.25;
 
 const DND_WINDOW_SWITCH_TIMEOUT = 1250;
 
-const GLSL_DIM_EFFECT_DECLARATIONS = '';
-const GLSL_DIM_EFFECT_CODE = '\
-   vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
-   float elipse_radius = 0.5; \
-   /* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
-      the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
-   float y = 250.0 / 255.0; \
-   float x = 180.0 / 255.0; \
-   /* interpolate darkening value, based on distance from screen center */ \
-   float val = min(length(dist), elipse_radius); \
-   float a = mix(x, y, val / elipse_radius); \
-   /* dim_factor varies from [1.0 -> 0.5] when overview is showing \
-      We use it to smooth value, then we clamp it to valid color interval */ \
-   a = clamp(a - cogl_color_in.r + 0.5, 0.0, 1.0); \
-   /* We\'re blending between: color and black color (obviously omitted in the equation) */ \
-   cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
-   cogl_color_out.a = 1.0;';
-
 const ShellInfo = new Lang.Class({
     Name: 'ShellInfo',
 
@@ -108,6 +91,24 @@ const Overview = new Lang.Class({
         this._sessionUpdated();
     },
 
+    _updateBackground: function() {
+        this._background = new Background.Background({ monitorIndex: Main.layoutManager.primaryIndex,
+                                                       effects: Meta.BackgroundEffects.VIGNETTE });
+        global.overlay_group.add_child(this._background.actor);
+        this._background.actor.lower_bottom();
+
+        let signalId = this._background.connect('changed',
+                                                Lang.bind(this, function() {
+                                                    this._background.disconnect(signalId);
+                                                    let isVisible = this._background.actor.visible;
+
+                                                    this._background.actor.destroy();
+
+                                                    this._updateBackground();
+                                                    this._background.visible = isVisible;
+                                                }));
+    },
+
     _createOverview: function() {
         if (this._overviewCreated)
             return;
@@ -117,18 +118,15 @@ const Overview = new Lang.Class({
 
         this._overviewCreated = true;
 
-        // The main BackgroundActor is inside global.window_group which is
+        // The main Background actors are inside global.window_group which are
         // hidden when displaying the overview, so we create a new
         // one. Instances of this class share a single CoglTexture behind the
         // scenes which allows us to show the background with different
         // rendering options without duplicating the texture data.
-        this._background = Meta.BackgroundActor.new_for_screen(global.screen);
-        this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT,
-                                          GLSL_DIM_EFFECT_DECLARATIONS,
-                                          GLSL_DIM_EFFECT_CODE,
-                                          false);
-        this._background.hide();
-        global.overlay_group.add_actor(this._background);
+        let monitor = Main.layoutManager.primaryMonitor;
+
+        this._updateBackground();
+        this._background.actor.hide();
 
         this._desktopFade = new St.Bin();
         global.overlay_group.add_actor(this._desktopFade);
@@ -479,7 +477,7 @@ const Overview = new Lang.Class({
         Meta.disable_unredirect_for_screen(global.screen);
         global.window_group.hide();
         this._overview.show();
-        this._background.show();
+        this._background.actor.show();
         this._viewSelector.show();
 
         this._overview.opacity = 0;
@@ -492,7 +490,7 @@ const Overview = new Lang.Class({
                          });
 
         Tweener.addTween(this._background,
-                         { dim_factor: 0.8,
+                         { dimFactor: 0.8,
                            time: ANIMATION_TIME,
                            transition: 'easeOutQuad'
                          });
@@ -616,7 +614,7 @@ const Overview = new Lang.Class({
                          });
 
         Tweener.addTween(this._background,
-                         { dim_factor: 1.0,
+                         { dimFactor: 1.0,
                            time: ANIMATION_TIME,
                            transition: 'easeOutQuad'
                          });
@@ -648,7 +646,7 @@ const Overview = new Lang.Class({
 
         this._viewSelector.hide();
         this._desktopFade.hide();
-        this._background.hide();
+        this._background.actor.hide();
         this._overview.hide();
 
         this.visible = false;
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index 699fb96..ddfea2c 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -12,6 +12,7 @@ const Signals = imports.signals;
 const St = imports.gi.St;
 const TweenerEquations = imports.tweener.equations;
 
+const Background = imports.ui.background;
 const GnomeSession = imports.misc.gnomeSession;
 const Layout = imports.ui.layout;
 const LoginManager = imports.misc.loginManager;
@@ -54,34 +55,6 @@ function sample(offx, offy) {
     return 'texel += texture2D (sampler, tex_coord.st + pixel_step * ' +
         'vec2 (' + offx + ',' + offy + '));\n'
 }
-const GLSL_BLUR_EFFECT_DECLARATIONS = ' \
-uniform vec2 pixel_step;\n \
-uniform float desaturation;\n \
-vec4 apply_blur(in sampler2D sampler, in vec2 tex_coord) {\n \
-  vec4 texel;\n \
-  texel = texture2D (sampler, tex_coord.st);\n'
-  + sample(-1.0, -1.0)
-  + sample( 0.0, -1.0)
-  + sample(+1.0, -1.0)
-  + sample(-1.0,  0.0)
-  + sample(+1.0,  0.0)
-  + sample(-1.0, +1.0)
-  + sample( 0.0, +1.0)
-  + sample(+1.0, +1.0) + ' \
-   texel /= 9.0;\n \
-   return texel;\n \
-}\n \
-vec3 desaturate (const vec3 color)\n \
-{\n \
-   const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n \
-   vec3 gray = vec3 (dot (gray_conv, color));\n \
-   return vec3 (mix (color.rgb, gray, desaturation));\n \
-}';
-const GLSL_BLUR_EFFECT_CODE = ' \
-cogl_texel = apply_blur(cogl_sampler, cogl_tex_coord.st);\n \
-cogl_texel.rgb = desaturate(cogl_texel.rgb);\n';
-
-
 const Clock = new Lang.Class({
     Name: 'ScreenShieldClock',
 
@@ -406,22 +379,14 @@ const ScreenShield = new Lang.Class({
                                                    name: 'lockScreenContents' });
         this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
 
-        let backgroundActor = Meta.BackgroundActor.new_for_screen(global.screen);
-        backgroundActor.add_glsl_snippet(Meta.SnippetHook.TEXTURE_LOOKUP,
-                                         GLSL_BLUR_EFFECT_DECLARATIONS,
-                                         GLSL_BLUR_EFFECT_CODE,
-                                         true);
-        backgroundActor.set_uniform_float('desaturation',
-                                          1, 1, [0.6]);
-        backgroundActor.connect('notify::size', function(actor) {
-            actor.set_uniform_float('pixel_step', 2, 1, [1/actor.width, 1/actor.height]);
-        });
-
-        this._background = new St.Bin({ style_class: 'screen-shield-background',
-                                        child: backgroundActor });
-        this._lockScreenGroup.add_actor(this._background);
         this._lockScreenGroup.add_actor(this._lockScreenContents);
 
+        this._background = new St.Bin({ style_class: 'screen-shield-background' });
+        this._updateBackground();
+
+	this._lockScreenGroup.add_actor(this._background);
+	this._background.lower_bottom();
+
         this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
                                                   vertical: true,
                                                   x_align: Clutter.ActorAlign.CENTER,
@@ -487,6 +452,23 @@ const ScreenShield = new Lang.Class({
         this.idleMonitor = new GnomeDesktop.IdleMonitor();
     },
 
+    _updateBackground: function() {
+	let background = new Background.Background({ monitorIndex: Main.layoutManager.primaryIndex,
+						     effects: Meta.BackgroundEffects.BLUR | Meta.BackgroundEffects.DESATURATE });
+	background.actor.add_constraint(new Layout.MonitorConstraint({primary: true }));
+
+	background.saturation = 0.6;
+        this._background.child = background.actor;
+
+        let signalId = background.connect('changed',
+                                          Lang.bind(this, function() {
+                                              background.disconnect(signalId);
+                                              background.actor.destroy();
+
+                                              this._updateBackground();
+                                          }));
+    },
+
     _onLockScreenKeyRelease: function(actor, event) {
         let symbol = event.get_key_symbol();
 
diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js
index fd68ce5..59ebc01 100644
--- a/js/ui/workspaceThumbnail.js
+++ b/js/ui/workspaceThumbnail.js
@@ -9,6 +9,7 @@ const Shell = imports.gi.Shell;
 const Signals = imports.signals;
 const St = imports.gi.St;
 
+const Background = imports.ui.background;
 const DND = imports.ui.dnd;
 const Main = imports.ui.main;
 const Tweener = imports.ui.tweener;
@@ -170,10 +171,10 @@ const WorkspaceThumbnail = new Lang.Class({
 
         this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 
-        this._background = Meta.BackgroundActor.new_for_screen(global.screen);
-        this._contents.add_actor(this._background);
+        this._updateBackground();
 
         let monitor = Main.layoutManager.primaryMonitor;
+
         this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
 
         let windows = global.get_window_actors().filter(this._isWorkspaceWindow, this);
@@ -210,6 +211,21 @@ const WorkspaceThumbnail = new Lang.Class({
         this._collapseFraction = 0; // Not collapsed
     },
 
+    _updateBackground: function() {
+        this._background = new Background.Background({ monitorIndex: Main.layoutManager.primaryIndex,
+						       effects: Meta.BackgroundEffects.NONE });
+        this._contents.add_actor(this._background.actor);
+
+        let signalId = this._background.connect('changed',
+                                                Lang.bind(this, function() {
+                                                    this._background.disconnect(signalId);
+                                                    this._background.actor.destroy();
+
+                                                    this._updateBackground();
+                                                }));
+
+    },
+
     setPorthole: function(x, y, width, height) {
         this._portholeX = x;
         this._portholeY = y;
@@ -233,7 +249,7 @@ const WorkspaceThumbnail = new Lang.Class({
             let clone = this._windows[i];
             let metaWindow = clone.metaWindow;
             if (i == 0) {
-                clone.setStackAbove(this._background);
+                clone.setStackAbove(this._background.actor);
             } else {
                 let previousClone = this._windows[i - 1];
                 clone.setStackAbove(previousClone.actor);
@@ -418,7 +434,7 @@ const WorkspaceThumbnail = new Lang.Class({
         this._contents.add_actor(clone.actor);
 
         if (this._windows.length == 0)
-            clone.setStackAbove(this._background);
+            clone.setStackAbove(this._background.actor);
         else
             clone.setStackAbove(this._windows[this._windows.length - 1].actor);
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 0e035ba..852df89 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -291,7 +291,7 @@ libgnome_shell_la_LIBADD =		\
 libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 Shell-0.1.gir: libgnome-shell.la St-1.0.gir
-Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0
+Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0 xlib-2.0
 Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
 Shell_0_1_gir_LIBS = libgnome-shell.la
 Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)
diff --git a/src/shell-global.c b/src/shell-global.c
index 10b1fc1..23d5648 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -107,7 +107,6 @@ enum {
   PROP_STAGE,
   PROP_STAGE_INPUT_MODE,
   PROP_WINDOW_GROUP,
-  PROP_BACKGROUND_ACTOR,
   PROP_WINDOW_MANAGER,
   PROP_SETTINGS,
   PROP_DATADIR,
@@ -203,9 +202,6 @@ shell_global_get_property(GObject         *object,
     case PROP_WINDOW_GROUP:
       g_value_set_object (value, meta_get_window_group_for_screen (global->meta_screen));
       break;
-    case PROP_BACKGROUND_ACTOR:
-      g_value_set_object (value, meta_get_background_actor_for_screen (global->meta_screen));
-      break;
     case PROP_WINDOW_MANAGER:
       g_value_set_object (value, global->wm);
       break;
@@ -419,13 +415,6 @@ shell_global_class_init (ShellGlobalClass *klass)
                                                         CLUTTER_TYPE_ACTOR,
                                                         G_PARAM_READABLE));
   g_object_class_install_property (gobject_class,
-                                   PROP_BACKGROUND_ACTOR,
-                                   g_param_spec_object ("background-actor",
-                                                        "Background Actor",
-                                                        "Actor drawing root window background",
-                                                        CLUTTER_TYPE_ACTOR,
-                                                        G_PARAM_READABLE));
-  g_object_class_install_property (gobject_class,
                                    PROP_WINDOW_MANAGER,
                                    g_param_spec_object ("window-manager",
                                                         "Window Manager",
diff --git a/src/shell-util.c b/src/shell-util.c
index 3821b3a..8a6d6c3 100644
--- a/src/shell-util.c
+++ b/src/shell-util.c
@@ -9,6 +9,8 @@
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
 
 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
 #include <langinfo.h>
@@ -430,3 +432,41 @@ shell_util_create_pixbuf_from_data (const guchar      *data,
                                    bits_per_sample, width, height, rowstride,
                                    (GdkPixbufDestroyNotify) g_free, NULL);
 }
+
+Pixmap
+shell_util_get_still_frame (void)
+{
+  Display *display;
+  Pixmap pixmap;
+  Window rootwin;
+  GC gc;
+  XGCValues values;
+  int width, height, depth;
+
+  display = gdk_x11_get_default_xdisplay ();
+  rootwin = gdk_x11_get_default_root_xwindow ();
+
+  width = DisplayWidth (display, gdk_x11_get_default_screen ());
+  height = DisplayHeight (display, gdk_x11_get_default_screen ());
+  depth = DefaultDepth (display, gdk_x11_get_default_screen ());
+  pixmap = XCreatePixmap (display,
+                          rootwin,
+                          width, height, depth);
+
+  values.function = GXcopy;
+  values.plane_mask = AllPlanes;
+  values.fill_style = FillSolid;
+  values.subwindow_mode = IncludeInferiors;
+
+  gc = XCreateGC (display,
+                  rootwin,
+                  GCFunction | GCPlaneMask | GCFillStyle | GCSubwindowMode,
+                  &values);
+
+  XCopyArea (display,
+             rootwin, pixmap, gc, 0, 0,
+             width, height, 0, 0);
+
+  XFreeGC (display, gc);
+  return pixmap;
+}
diff --git a/src/shell-util.h b/src/shell-util.h
index 41ba96f..0476cbd 100644
--- a/src/shell-util.h
+++ b/src/shell-util.h
@@ -7,6 +7,7 @@
 #include <clutter/clutter.h>
 #include <libsoup/soup.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdkx.h>
 
 G_BEGIN_DECLS
 
@@ -46,6 +47,8 @@ GdkPixbuf *shell_util_create_pixbuf_from_data (const guchar      *data,
                                                int                height,
                                                int                rowstride);
 
+Pixmap  shell_util_get_still_frame            (void);
+
 G_END_DECLS
 
 #endif /* __SHELL_UTIL_H__ */


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