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.
This commit is contained in:
Sander Dijkhuis 2009-04-22 02:23:06 +02:00
parent 822ef09350
commit 7c8cb8450c
5 changed files with 288 additions and 128 deletions

View File

@ -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)

View File

@ -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,
@ -451,6 +457,13 @@ Sideshow.prototype = {
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())
@ -575,6 +596,13 @@ Sideshow.prototype = {
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,20 +886,19 @@ 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() {
@ -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);
}

View File

@ -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(); });
}

View File

@ -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;
}

View File

@ -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__ */