[gnome-shell] Bug 578584 - Use the wallpaper for the overlay background



commit 7c8cb8450ca6e1a7d5027e1fd63c4d9f9d87e59f
Author: Sander Dijkhuis <sander dijkhuis gmail com>
Date:   Wed Apr 22 02:23:06 2009 +0200

    Bug 578584 - Use the wallpaper for the overlay background
    
    The overlay looks nicer with the root window pixmap drawn on the
    background. It is scaled up to twice the size, with positioning
    based on the rule of thirds.
    
    The sideshow animations shown when entering or leaving the
    overlay and toggling the extended view were implemented by
    Marina Zhurakhinskaya. They replace the old method of having a
    black rectangle behind the workspaces that partly covers the
    sideshow during transitions.
    
    configure.ac: Add gdk-x11, clutter-x11 and clutter-glx modules.
    overlay.js: Add a root window pixmap actor, make sideshow width
        definitions more logical, replace the way the sideshow
        animates when entering or leaving the overlay.
    workspaces.js: Remove the backdrop, add helper functions for the
        overlay transitions.
    shell-global.[ch]: Add a method that creates an actor displaying
        the root window pixmap and returning clones of it.
---
 configure.ac        |    2 +-
 js/ui/overlay.js    |  214 +++++++++++++++++++++++++++++++--------------------
 js/ui/workspaces.js |   65 +++++-----------
 src/shell-global.c  |  133 +++++++++++++++++++++++++++++++
 src/shell-global.h  |    2 +
 5 files changed, 288 insertions(+), 128 deletions(-)

diff --git a/configure.ac b/configure.ac
index 8727802..e3da44d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,7 +38,7 @@ fi
 
 AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
 
-PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dbus-glib-1 metacity-plugins gjs-gi-1.0 libgnome-menu $recorder_modules)
+PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dbus-glib-1 metacity-plugins gjs-gi-1.0 libgnome-menu gdk-x11-2.0 clutter-x11-0.9 clutter-glx-0.9 $recorder_modules)
 PKG_CHECK_MODULES(TIDY, clutter-0.9)
 PKG_CHECK_MODULES(BIG, clutter-0.9 gtk+-2.0 librsvg-2.0)
 PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
diff --git a/js/ui/overlay.js b/js/ui/overlay.js
index 6c1793d..47097dd 100644
--- a/js/ui/overlay.js
+++ b/js/ui/overlay.js
@@ -18,8 +18,12 @@ const Panel = imports.ui.panel;
 const Tweener = imports.ui.tweener;
 const Workspaces = imports.ui.workspaces;
 
-const OVERLAY_BACKGROUND_COLOR = new Clutter.Color();
-OVERLAY_BACKGROUND_COLOR.from_pixel(0x000000ff);
+const ROOT_OVERLAY_COLOR = new Clutter.Color();
+ROOT_OVERLAY_COLOR.from_pixel(0x000000bb);
+
+// The factor to scale the overlay wallpaper with. This should not be less
+// than 3/2, because the rule of thirds is used for positioning (see below).
+const BACKGROUND_SCALE = 2;
 
 const LABEL_HEIGHT = 16;
 // We use SIDESHOW_PAD for the padding on the left side of the sideshow and as a gap
@@ -138,19 +142,23 @@ Sideshow.prototype = {
         let asideXFactor = wideScreen ? WORKSPACES_X_FACTOR_ASIDE_MODE_WIDE_SCREEN : WORKSPACES_X_FACTOR_ASIDE_MODE_REGULAR_SCREEN; 
         this._expandedSideshowColumns = wideScreen ? EXPANDED_SIDESHOW_COLUMNS_WIDE_SCREEN : EXPANDED_SIDESHOW_COLUMNS_REGULAR_SCREEN;       
 
-        this._width = displayGridColumnWidth - SIDESHOW_PAD;
+        this._width = displayGridColumnWidth;
+        this._displayWidth = this._width - SIDESHOW_PAD;
+
+        this._expandedWidth = displayGridColumnWidth * asideXFactor;
 
         // this figures out the additional width we can give to the display in the 'More' mode,
         // assuming that we want to keep the columns the same width in both modes
-        this._additionalWidth = ((this._width + SIDESHOW_PAD) / SIDESHOW_COLUMNS) * 
+        this._additionalWidth = (this._width / SIDESHOW_COLUMNS) *
                                 (this._expandedSideshowColumns - SIDESHOW_COLUMNS);
 
-        let previewWidth = displayGridColumnWidth * asideXFactor - this._width - 
-                           this._additionalWidth - SIDESHOW_SECTION_SPACING * 2; 
+        let previewWidth = this._expandedWidth - this._width -
+                           this._additionalWidth - SIDESHOW_SECTION_SPACING;
 
         let global = Shell.Global.get();
         this.actor = new Clutter.Group();
-        this._searchEntry = new SearchEntry(this._width);
+        this.actor.height = global.screen_height;
+        this._searchEntry = new SearchEntry(this._displayWidth);
         this.actor.add_actor(this._searchEntry.actor);
 
         this._searchEntry.actor.set_position(SIDESHOW_PAD, Panel.PANEL_HEIGHT + SIDESHOW_PAD);
@@ -246,8 +254,7 @@ Sideshow.prototype = {
             return false;
         });
 
-        this._appsSection = new Big.Box({ background_color: OVERLAY_BACKGROUND_COLOR,
-                                          x: SIDESHOW_PAD,
+        this._appsSection = new Big.Box({ x: SIDESHOW_PAD,
                                           y: this._searchEntry.actor.y + this._searchEntry.actor.height,
                                           padding_top: SIDESHOW_SECTION_PADDING_TOP,
                                           spacing: SIDESHOW_SECTION_SPACING});
@@ -265,7 +272,7 @@ Sideshow.prototype = {
         
         this._appsContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
         this._appsSection.append(this._appsContent, Big.BoxPackFlags.EXPAND);
-        this._appDisplay = new AppDisplay.AppDisplay(this._width, this._itemDisplayHeight / 2, SIDESHOW_COLUMNS, SIDESHOW_PAD);
+        this._appDisplay = new AppDisplay.AppDisplay(this._displayWidth, this._itemDisplayHeight / 2, SIDESHOW_COLUMNS, SIDESHOW_PAD);
         let sideArea = this._appDisplay.getSideArea();
         sideArea.hide();
         this._appsContent.append(sideArea, Big.BoxPackFlags.NONE);
@@ -286,8 +293,7 @@ Sideshow.prototype = {
         this._appsDisplayControlBox = new Big.Box({x_align: Big.BoxAlignment.CENTER});
         this._appsDisplayControlBox.append(this._appDisplay.displayControl, Big.BoxPackFlags.NONE);
 
-        this._docsSection = new Big.Box({ background_color: OVERLAY_BACKGROUND_COLOR,
-                                          x: SIDESHOW_PAD,
+        this._docsSection = new Big.Box({ x: SIDESHOW_PAD,
                                           y: this._appsSection.y + this._appsSection.height,
                                           padding_top: SIDESHOW_SECTION_PADDING_TOP,
                                           spacing: SIDESHOW_SECTION_SPACING});
@@ -298,7 +304,7 @@ Sideshow.prototype = {
                                             height: LABEL_HEIGHT});
         this._docsSection.append(this._docsText, Big.BoxPackFlags.EXPAND);
 
-        this._docDisplay = new DocDisplay.DocDisplay(this._width, this._itemDisplayHeight - this._appsContent.height, SIDESHOW_COLUMNS, SIDESHOW_PAD);
+        this._docDisplay = new DocDisplay.DocDisplay(this._displayWidth, this._itemDisplayHeight - this._appsContent.height, SIDESHOW_COLUMNS, SIDESHOW_PAD);
         this._docsSection.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND);
 
         let moreDocsBox = new Big.Box({x_align: Big.BoxAlignment.END});
@@ -316,7 +322,7 @@ Sideshow.prototype = {
         this._docsDisplayControlBox = new Big.Box({x_align: Big.BoxAlignment.CENTER});
         this._docsDisplayControlBox.append(this._docDisplay.displayControl, Big.BoxPackFlags.NONE);
 
-        this._details = new Big.Box({ x: SIDESHOW_PAD + this._width + this._additionalWidth + SIDESHOW_SECTION_SPACING,
+        this._details = new Big.Box({ x: this._width + this._additionalWidth + SIDESHOW_SECTION_SPACING,
                                       y: Panel.PANEL_HEIGHT + SIDESHOW_PAD,
                                       width: previewWidth,
                                       height: global.screen_height - Panel.PANEL_HEIGHT - SIDESHOW_PAD - bottomHeight,
@@ -449,8 +455,15 @@ Sideshow.prototype = {
                            transition: "easeOutQuad",
                            onComplete: this._onAppsSectionExpanded,
                            onCompleteScope: this
-                         }); 
-                   
+                         });
+
+        this.actor.set_clip(0, 0, this.actor.width, this.actor.height);
+        Tweener.addTween(this.actor,
+                         { clipWidthRight: this._expandedWidth,
+                           time: ANIMATION_TIME,
+                           transition: "easeOutQuad"
+                         });
+
         this.emit('more-activated'); 
     },
 
@@ -464,7 +477,7 @@ Sideshow.prototype = {
         this._moreAppsLink.actor.hide();
 
         this._appsSection.set_clip(0, 0, this._appsSection.width, this._appsSection.height);
-
+        this.actor.set_clip(0, 0, this.actor.width, this.actor.height);
         this._docDisplay.show();
 
         // We need to be reducing the clip on the applications section so that the last application to
@@ -483,7 +496,11 @@ Sideshow.prototype = {
                            time: ANIMATION_TIME,
                            transition: "easeOutQuad"
                          });  
- 
+        Tweener.addTween(this.actor,
+                         { clipWidthRight: this._width,
+                           time: ANIMATION_TIME,
+                           transition: "easeOutQuad"
+                         });
         this.emit('less-activated');
     },
 
@@ -492,6 +509,7 @@ Sideshow.prototype = {
     _onAppsSectionExpanded: function() {
         this._appsSection.remove_clip(); 
         this._docDisplay.hide();
+        this.actor.remove_clip();
     },
 
     // Updates the applications section to contain fewer items. Selects the first item in the 
@@ -500,6 +518,7 @@ Sideshow.prototype = {
     // Removes the clip from the documents section, so that the clip does not limit the size of 
     // the section if it is expanded later.
     _onAppsSectionReduced: function() {
+        this.actor.remove_clip();
         if (this._moreAppsMode != STATE_PENDING_INACTIVE)
             return;
         this._moreAppsMode = STATE_INACTIVE;
@@ -516,7 +535,7 @@ Sideshow.prototype = {
     _updateAppsSection: function() {
         if (this._moreAppsMode) {
             // Subtract one from columns since we are displaying menus
-            this._appDisplay.setExpanded(true, this._width, this._additionalWidth,
+            this._appDisplay.setExpanded(true, this._displayWidth, this._additionalWidth,
                                          this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT,
                                          this._expandedSideshowColumns - 1);
             this._moreAppsLink.setText("Less...");
@@ -524,7 +543,7 @@ Sideshow.prototype = {
             this.actor.add_actor(this._details);
             this._details.append(this._appDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
         } else {
-            this._appDisplay.setExpanded(false, this._width, 0,
+            this._appDisplay.setExpanded(false, this._displayWidth, 0,
                                          this._appsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT,
                                          SIDESHOW_COLUMNS);
             this._moreAppsLink.setText("More...");
@@ -550,6 +569,8 @@ Sideshow.prototype = {
         this._moreDocsLink.actor.hide();
         this._docsSection.set_clip(0, 0, this._docsSection.width, this._docsSection.height);
 
+        this.actor.set_clip(0, 0, this.actor.width, this.actor.height);
+
         // Move the selection to the docs section if it was in the apps section.
         this._appDisplay.unsetSelected();
         if (!this._docDisplay.hasSelected())
@@ -574,7 +595,14 @@ Sideshow.prototype = {
                            transition: "easeOutQuad",
                            onComplete: this._onDocsSectionExpanded,
                            onCompleteScope: this
-                         });                   
+                         });
+
+        Tweener.addTween(this.actor,
+                         { clipWidthRight: this._expandedWidth,
+                           time: ANIMATION_TIME,
+                           transition: "easeOutQuad"
+                         });
+
         this.emit('more-activated'); 
     },
 
@@ -589,7 +617,7 @@ Sideshow.prototype = {
         this._moreDocsLink.actor.hide();
          
         this._docsSection.set_clip(0, 0, this._docsSection.width, this._docsSection.height);
-
+        this.actor.set_clip(0, 0, this.actor.width, this.actor.height);
         this._appsContent.show();
 
         Tweener.addTween(this._docsSection,
@@ -606,6 +634,12 @@ Sideshow.prototype = {
                            time: ANIMATION_TIME,
                            transition: "easeOutQuad"
                          });  
+        Tweener.addTween(this.actor,
+                         { clipWidthRight: this._width,
+                           time: ANIMATION_TIME,
+                           transition: "easeOutQuad"
+                         });
+
         this.emit('less-activated');
     },
 
@@ -614,6 +648,7 @@ Sideshow.prototype = {
     _onDocsSectionExpanded: function() {
         this._docsSection.remove_clip(); 
         this._appsContent.hide();
+        this.actor.remove_clip();
     },
 
     // Updates the documents section to contain fewer items. Selects the first item in the 
@@ -622,6 +657,7 @@ Sideshow.prototype = {
     // Removes the clip from the applications section, so that the clip does not limit the size of 
     // the section if it is expanded later.
     _onDocsSectionReduced: function() {
+        this.actor.remove_clip();
         this._updateDocsSection();  
         if (!this._docDisplay.hasItems())
             this._appDisplay.selectFirstItem();
@@ -634,7 +670,7 @@ Sideshow.prototype = {
     // changed, which is ensured by _setMoreDocsMode() and _unsetMoreDocsMode() functions. 
     _updateDocsSection: function() {
         if (this._moreDocsMode) {
-            this._docDisplay.setExpanded(true, this._width, this._additionalWidth,
+            this._docDisplay.setExpanded(true, this._displayWidth, this._additionalWidth,
                                          this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT,
                                          this._expandedSideshowColumns);
             this._moreDocsLink.setText("Less...");
@@ -642,7 +678,7 @@ Sideshow.prototype = {
             this.actor.add_actor(this._details);
             this._details.append(this._docDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
         } else {
-            this._docDisplay.setExpanded(false, this._width, 0,
+            this._docDisplay.setExpanded(false, this._displayWidth, 0,
                                          this._docsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT,
                                          SIDESHOW_COLUMNS);
             this._moreDocsLink.setText("More...");
@@ -684,14 +720,25 @@ Overlay.prototype = {
         this.visible = false;
         this._hideInProgress = false;
 
-        let background = new Clutter.Rectangle({ color: OVERLAY_BACKGROUND_COLOR,
-                                                 reactive: true,
-                                                 x: 0,
-                                                 y: 0,
-                                                 width: global.screen_width,
-                                                 height: global.screen_width });
+        // A scaled root pixmap actor is used as a background. It is zoomed in
+        // to the lower right intersection of the lines that divide the image
+        // evenly in a 3x3 grid. This is based on the rule of thirds, a
+        // compositional rule of thumb in visual arts. The choice for the
+        // lower right point is based on a quick survey of GNOME wallpapers.
+        let background = global.create_root_pixmap_actor();
+        background.width = global.screen_width * BACKGROUND_SCALE;
+        background.height = global.screen_height * BACKGROUND_SCALE;
+        background.x = -global.screen_width * (4 * BACKGROUND_SCALE - 3) / 6;
+        background.y = -global.screen_height * (4 * BACKGROUND_SCALE - 3) / 6;
         this._group.add_actor(background);
 
+        // Draw a semitransparent rectangle over the background for readability.
+        let backOver = new Clutter.Rectangle({ color: ROOT_OVERLAY_COLOR,
+                                               width: global.screen_width,
+                                               height: global.screen_height - Panel.PANEL_HEIGHT,
+                                               y: Panel.PANEL_HEIGHT });
+        this._group.add_actor(backOver);
+
         this._group.hide();
         global.overlay_group.add_actor(this._group);
 
@@ -699,7 +746,6 @@ Overlay.prototype = {
         this._sideshow = new Sideshow();
         this._group.add_actor(this._sideshow.actor); 
         this._workspaces = null;
-        this._workspacesBackground = null;
         this._sideshow.connect('activated', function(sideshow) {
             // TODO - have some sort of animation/effect while
             // transitioning to the new app.  We definitely need
@@ -713,17 +759,6 @@ Overlay.prototype = {
                 let workspacesX = displayGridColumnWidth * asideXFactor + WORKSPACE_GRID_PADDING;
                 me._workspaces.addButton.hide();
                 me._workspaces.updatePosition(workspacesX, null);
-                // lower the sideshow below the workspaces background, so that the workspaces
-                // background covers the parts of the sideshow that are gradually being 
-                // revealed from underneath it
-                me._sideshow.actor.lower(me._workspacesBackground);
-                Tweener.addTween(me._workspacesBackground,
-                                 { x: displayGridColumnWidth * asideXFactor,
-                                   time: ANIMATION_TIME,
-                                   transition: "easeOutQuad",
-                                   onComplete: me._animationDone,
-                                   onCompleteScope: me
-                                 });
             }    
         });
         this._sideshow.connect('less-activated', function(sideshow) {
@@ -731,16 +766,6 @@ Overlay.prototype = {
                 let workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
                 me._workspaces.addButton.show();
                 me._workspaces.updatePosition(workspacesX, null);
-                // lower the sideshow below the workspaces background, so that the workspaces
-                // background covers the parts of the sideshow as it slides in over them
-                me._sideshow.actor.lower(me._workspacesBackground);
-                Tweener.addTween(me._workspacesBackground,
-                                 { x: displayGridColumnWidth,
-                                   time: ANIMATION_TIME,
-                                   transition: "easeOutQuad",
-                                   onComplete: me._animationDone,
-                                   onCompleteScope: me
-                                 });  
             }
         });
     },
@@ -790,18 +815,6 @@ Overlay.prototype = {
         let addButtonX = workspacesX + workspacesWidth - addButtonSize;
         let addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
 
-        // We use the workspaces background to have it fill the full height of the overlay when we are sliding
-        // the workspaces out to uncover the expanded items display and also when we are sliding the
-        // workspaces back in to gradually cover the expanded items display. If we don't have such background,
-        // we get a few items above or below the workspaces display that disappear or appear abruptly.  
-        this._workspacesBackground = new Clutter.Rectangle({ color: OVERLAY_BACKGROUND_COLOR,
-                                                             reactive: false,
-                                                             x: displayGridColumnWidth,
-                                                             y: Panel.PANEL_HEIGHT,
-                                                             width: displayGridColumnWidth * columnsUsed,
-                                                             height: global.screen_height - Panel.PANEL_HEIGHT });
-        this._group.add_actor(this._workspacesBackground);
-
         this._workspaces = new Workspaces.Workspaces(workspacesWidth, workspacesHeight, workspacesX, workspacesY, 
                                                      addButtonSize, addButtonX, addButtonY);
         this._group.add_actor(this._workspaces.actor);
@@ -816,11 +829,26 @@ Overlay.prototype = {
         global.window_group.hide();
         this._group.show();
 
-        // Dummy tween, just waiting for the workspace animation
-        Tweener.addTween(this,
-                         { time: ANIMATION_TIME,
-                           onComplete: this._animationDone,
+        // Try to make the menu not too visible behind the empty space between
+        // the workspace previews by sliding in its clipping rectangle.
+        // We want to finish drawing the sideshow just before the top workspace fully
+        // slides in on the top. Which means that we have more time to wait before
+        // drawing the sideshow if the active workspace is displayed on the bottom of
+        // the workspaces grid, and almost no time to wait if it is displayed in the top
+        // row of the workspaces grid. The calculations used below try to roughly
+        // capture the animation ratio for when workspaces are covering the top of the overlay
+        // vs. when workspaces are already below the top of the overlay, and apply it
+        // to clipping the sideshow. The clipping is removed in this._showDone().
+        this._sideshow.actor.set_clip(0, 0,
+                                      this._workspaces.getFullSizeX(),
+                                      this._sideshow.actor.height);
+        Tweener.addTween(this._sideshow.actor,
+                         { clipWidthRight: this._sideshow._width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
+                           time: ANIMATION_TIME,
+                           transition: "easeOutQuad",
+                           onComplete: this._showDone,
                            onCompleteScope: this
+
                          });
     },
 
@@ -828,14 +856,28 @@ Overlay.prototype = {
         if (!this.visible || this._hideInProgress)
             return;
 
+        let global = Shell.Global.get();
+
         this._hideInProgress = true;
         // lower the sideshow, so that workspaces display is on top and covers the sideshow while it is sliding out
-        this._sideshow.actor.lower(this._workspacesBackground);
+        this._sideshow.actor.lower(this._workspaces.actor);
         this._workspaces.hide();
 
-        // Dummy tween, just waiting for the workspace animation
-        Tweener.addTween(this,
-                         { time: ANIMATION_TIME,
+        // Try to make the menu not too visible behind the empty space between
+        // the workspace previews by sliding in its clipping rectangle.
+        // The logic used is the same as described in this.show(). If the active workspace
+        // is displayed in the top row, than almost full animation time is needed for it
+        // to reach the top of the overlay and cover the sideshow fully, while if the
+        // active workspace is in the lower row, than the top left workspace reaches the
+        // top of the overlay sooner as it is moving out of the way.
+        // The clipping is removed in this._hideDone().
+        this._sideshow.actor.set_clip(0, 0,
+                                      this._sideshow.actor.width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
+                                      this._sideshow.actor.height);
+        Tweener.addTween(this._sideshow.actor,
+                         { clipWidthRight: this._workspaces.getFullSizeX() + this._workspaces.getWidthToTopActiveWorkspace() - global.screen_width,
+                           time: ANIMATION_TIME,
+                           transition: "easeOutQuad",
                            onComplete: this._hideDone,
                            onCompleteScope: this
                          });
@@ -844,21 +886,20 @@ Overlay.prototype = {
     //// Private methods ////
 
     // Raises the sideshow to the top, so that we can tell if the pointer is above one of its items.
-    // We need to do this every time animation of the workspaces is done bacause the workspaces actor
-    // currently covers the whole screen, regardless of where the workspaces are actually displayed. 
-    // On the other hand, we need the workspaces to be on top when they are sliding in, out,
-    // and to the side because we want them to cover the sideshow as they do that.
+    // We need to do this once the workspaces are shown because the workspaces actor currently covers
+    // the whole screen, regardless of where the workspaces are actually displayed.
     //
     // Once we rework the workspaces actor to only cover the area it actually needs, we can
     // remove this workaround. Also http://bugzilla.openedhand.com/show_bug.cgi?id=1513 requests being
     // able to pick only a reactive actor at a certain position, rather than any actor. Being able
     // to do that would allow us to not have to raise the sideshow.  
-    _animationDone: function() {
+    _showDone: function() {
         if (this._hideInProgress)
             return;
 
-       this._sideshow.actor.raise_top();
-    }, 
+        this._sideshow.actor.raise_top();
+        this._sideshow.actor.remove_clip();
+    },
 
     _hideDone: function() {
         let global = Shell.Global.get();
@@ -868,9 +909,7 @@ Overlay.prototype = {
         this._workspaces.destroy();
         this._workspaces = null;
 
-        this._workspacesBackground.destroy();
-        this._workspacesBackground = null;
-
+        this._sideshow.actor.remove_clip();
         this._sideshow.hide();
         this._group.hide();
 
@@ -904,3 +943,14 @@ function _clipHeightTopGet(actor) {
 function _clipHeightTopSet(actor, clipHeight) {
     actor.set_clip(0, actor.height - clipHeight, actor.width, clipHeight);
 }
+
+Tweener.registerSpecialProperty("clipWidthRight", _clipWidthRightGet, _clipWidthRightSet);
+
+function _clipWidthRightGet(actor) {
+    let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip();
+    return clipWidth;
+}
+
+function _clipWidthRightSet(actor, clipWidth) {
+    actor.set_clip(0, 0, clipWidth, actor.height);
+}
diff --git a/js/ui/workspaces.js b/js/ui/workspaces.js
index 37a9c1d..cb560a8 100644
--- a/js/ui/workspaces.js
+++ b/js/ui/workspaces.js
@@ -800,26 +800,6 @@ Workspaces.prototype = {
         activeWorkspace.actor.raise_top();
         this._positionWorkspaces(global, activeWorkspace);
 
-        // Create a backdrop rectangle, so that you don't see the
-        // other parts of the overlay (eg, sidebar) through the gaps
-        // between the workspaces when they're zooming in/out
-        this._backdrop = new Clutter.Rectangle({ color: Overlay.OVERLAY_BACKGROUND_COLOR,
-                                                 x: this._backdropX,
-                                                 y: this._backdropY,
-                                                 width: this._backdropWidth,
-                                                 height: this._backdropHeight
-                                               });
-        this.actor.add_actor(this._backdrop);
-        this._backdrop.lower_bottom();
-        Tweener.addTween(this._backdrop,
-                         { x: this._x,
-                           y: this._y,
-                           width: this._width,
-                           height: this._height,
-                           time: Overlay.ANIMATION_TIME,
-                           transition: "easeOutQuad"
-                         });
-
         // Create (+) button
         buttonSize = addButtonSize;
         this.addButton = new Clutter.Texture({ x: addButtonX,
@@ -870,15 +850,6 @@ Workspaces.prototype = {
 
         for (let w = 0; w < this._workspaces.length; w++)
             this._workspaces[w].zoomFromOverlay();
-
-        Tweener.addTween(this._backdrop,
-                         { x: this._backdropX,
-                           y: this._backdropY,
-                           width: this._backdropWidth,
-                           height: this._backdropHeight,
-                           time: Overlay.ANIMATION_TIME,
-                           transition: "easeOutQuad"
-                         });
     },
 
     destroy : function() {
@@ -890,25 +861,36 @@ Workspaces.prototype = {
 
         this.actor.destroy();
         this.actor = null;
-        this._backdrop = null;
 
         global.screen.disconnect(this._nWorkspacesNotifyId);
         global.window_manager.disconnect(this._switchWorkspaceNotifyId);
     },
 
+    getFullSizeX : function() {
+        return this._workspaces[0].fullSizeX;
+    },
+
+    // If j-th workspace in the i-th row is active, returns the full width
+    // of j workspaces including empty space if i = 1, or the width of one
+    // workspace.
+    // Used in overlay.js to determine when it is ok to remove the sideshow
+    // during animations for entering and leaving the overlay.
+    getWidthToTopActiveWorkspace : function() {
+        let global = Shell.Global.get();
+        let activeWorkspaceIndex = global.screen.get_active_workspace_index();
+        let activeWorkspace = this._workspaces[activeWorkspaceIndex];
+
+        if (activeWorkspace.gridRow == 0)
+            return (activeWorkspace.gridCol + 1) * global.screen_width + activeWorkspace.gridCol * GRID_SPACING;
+        else
+            return global.screen_width;
+    },
+
     // Updates the workspaces display based on the current dimensions and position.
     _updateInOverlay : function() {
         let global = Shell.Global.get();  
   
         this._positionWorkspaces(global);
-        Tweener.addTween(this._backdrop,
-                         { x: this._x,
-                           y: this._y,
-                           width: this._width,
-                           height: this._height,
-                           time: Overlay.ANIMATION_TIME,
-                           transition: "easeOutQuad"
-                         });
 
         // Position/scale the desktop windows and their children
         for (let w = 0; w < this._workspaces.length; w++)
@@ -973,12 +955,6 @@ Workspaces.prototype = {
             workspace.fullSizeX = (workspace.gridCol - activeWorkspace.gridCol) * (global.screen_width + GRID_SPACING);
             workspace.fullSizeY = (workspace.gridRow - activeWorkspace.gridRow) * (global.screen_height + GRID_SPACING);
         }
-
-        // And the backdrop
-        this._backdropX = this._workspaces[0].fullSizeX;
-        this._backdropY = this._workspaces[0].fullSizeY;
-        this._backdropWidth = gridWidth * (global.screen_width + GRID_SPACING) - GRID_SPACING;
-        this._backdropHeight = gridHeight * (global.screen_height + GRID_SPACING) - GRID_SPACING;
     },
 
     _workspacesChanged : function() {
@@ -1036,7 +1012,6 @@ Workspaces.prototype = {
             // Slide old workspaces out
             for (let w = 0; w < lostWorkspaces.length; w++) {
                 let workspace = lostWorkspaces[w];
-                workspace.actor.raise(this._backdrop);
                 workspace.slideOut(function () { workspace.destroy(); });
             }
 
diff --git a/src/shell-global.c b/src/shell-global.c
index 6bbe40c..2a2b747 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -4,7 +4,9 @@
 #include "shell-wm.h"
 
 #include "display.h"
+#include <clutter/glx/clutter-glx.h>
 #include <clutter/x11/clutter-x11.h>
+#include <gdk/gdkx.h>
 #include <unistd.h>
 #include <errno.h>
 #include <stdlib.h>
@@ -37,6 +39,9 @@ struct _ShellGlobal {
   ShellWM *wm;
   gboolean keyboard_grabbed;
   const char *imagedir;
+
+  /* Displays the root window; see shell_global_create_root_pixmap_actor() */
+  ClutterGLXTexturePixmap *root_pixmap;
 };
 
 enum {
@@ -153,6 +158,8 @@ shell_global_init (ShellGlobal *global)
   global->grab_notifier = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
   g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global);
   global->grab_active = FALSE;
+
+  global->root_pixmap = NULL;
 }
 
 static void
@@ -728,3 +735,129 @@ shell_global_create_vertical_gradient (ClutterColor *top,
 
   return texture;
 }
+
+/*
+ * Updates the global->root_pixmap actor with the root window's pixmap or fails
+ * with a warning.
+ */
+static void
+update_root_window_pixmap (ShellGlobal *global)
+{
+  Atom type;
+  int format;
+  gulong nitems;
+  gulong bytes_after;
+  guchar *data;
+
+  if (!XGetWindowProperty (gdk_x11_get_default_xdisplay (),
+                           gdk_x11_get_default_root_xwindow (),
+                           gdk_x11_get_xatom_by_name ("_XROOTPMAP_ID"),
+                           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. */
+         clutter_x11_texture_pixmap_set_pixmap (CLUTTER_X11_TEXTURE_PIXMAP (global->root_pixmap),
+                                                *(Pixmap *)data);
+       }
+     else
+       {
+         g_warning ("Could not get the root window pixmap");
+       }
+
+     XFree(data);
+  }
+}
+
+/*
+ * Called when the X server emits a root window change event. If the event is
+ * about a new pixmap, update the global->root_pixmap actor.
+ */
+static GdkFilterReturn
+root_window_filter (GdkXEvent *native, GdkEvent *event, gpointer data)
+{
+  XEvent *xevent = (XEvent *)native;
+
+  if ((xevent->type == PropertyNotify) &&
+      (xevent->xproperty.window == gdk_x11_get_default_root_xwindow ()) &&
+      (xevent->xproperty.atom == gdk_x11_get_xatom_by_name ("_XROOTPMAP_ID")))
+    update_root_window_pixmap (SHELL_GLOBAL (data));
+
+  return GDK_FILTER_CONTINUE;
+}
+
+/*
+ * Called when the root window pixmap actor is destroyed.
+ */
+static void
+root_pixmap_destroy (GObject *sender, gpointer data)
+{
+  ShellGlobal *global = SHELL_GLOBAL (data);
+
+  gdk_window_remove_filter (gdk_get_default_root_window (),
+                            root_window_filter, global);
+  global->root_pixmap = NULL;
+}
+
+/**
+ * shell_global_create_root_pixmap_actor:
+ * @global: a #ShellGlobal
+ *
+ * Creates an actor showing the root window pixmap.
+ *
+ * Return value: (transfer none): a #ClutterActor with the root window pixmap.
+ *               The actor is floating, hence (transfer none).
+ */
+ClutterActor *
+shell_global_create_root_pixmap_actor (ShellGlobal *global)
+{
+  GdkWindow *window;
+  GdkEventMask events;
+  gboolean created_new_pixmap = FALSE;
+  ClutterActor *clone;
+
+  /* The actor created is actually a ClutterClone of global->root_pixmap. */
+
+  if (global->root_pixmap == NULL)
+    {
+      global->root_pixmap = CLUTTER_GLX_TEXTURE_PIXMAP (clutter_glx_texture_pixmap_new ());
+
+      /* The low and medium quality filters give nearest-neighbor resizing. */
+      clutter_texture_set_filter_quality (CLUTTER_TEXTURE (global->root_pixmap),
+                                          CLUTTER_TEXTURE_QUALITY_HIGH);
+
+      /* The pixmap actor is only referenced by its clones. */
+      g_object_ref_sink (global->root_pixmap);
+
+      g_signal_connect (G_OBJECT (global->root_pixmap), "destroy",
+                        G_CALLBACK (root_pixmap_destroy), global);
+
+      /* Watch the root window for changes. */
+      window = gdk_get_default_root_window ();
+      events = gdk_window_get_events (window);
+      events |= GDK_PROPERTY_CHANGE_MASK;
+      gdk_window_set_events (window, events);
+      /* Metacity handles some root window property updates in its global
+       * event filter, though not this one. For all root window property
+       * updates, the global filter returns GDK_FILTER_CONTINUE, so our
+       * window specific filter will be called.
+       */
+      gdk_window_add_filter (window, root_window_filter, global);
+
+      update_root_window_pixmap (global);
+
+      created_new_pixmap = TRUE;
+    }
+
+  clone = clutter_clone_new (CLUTTER_ACTOR (global->root_pixmap));
+
+  if (created_new_pixmap)
+    g_object_unref(global->root_pixmap);
+
+  return clone;
+}
diff --git a/src/shell-global.h b/src/shell-global.h
index 70cc962..9ce837f 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -69,6 +69,8 @@ void shell_global_reexec_self (ShellGlobal *global);
 ClutterCairoTexture *shell_global_create_vertical_gradient (ClutterColor *top,
 							    ClutterColor *bottom);
 
+ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
+
 G_END_DECLS
 
 #endif /* __SHELL_GLOBAL_H__ */



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