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

https://bugzilla.gnome.org/show_bug.cgi?id=682429
This commit is contained in:
Ray Strode 2012-12-24 15:20:39 +01:00
parent 5fa9581db3
commit 3c8325f1f3
8 changed files with 766 additions and 143 deletions

View File

@ -40,6 +40,7 @@ nobase_dist_js_DATA = \
ui/appDisplay.js \ ui/appDisplay.js \
ui/appFavorites.js \ ui/appFavorites.js \
ui/backgroundMenu.js \ ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \ ui/boxpointer.js \
ui/calendar.js \ ui/calendar.js \
ui/checkBox.js \ ui/checkBox.js \

506
js/ui/background.js Normal file
View File

@ -0,0 +1,506 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
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';
const FADE_ANIMATION_TIME = 1.0;
let _backgroundCache = null;
const BackgroundCache = new Lang.Class({
Name: 'BackgroundCache',
_init: function() {
this._patterns = [];
this._images = [];
this._fileMonitors = {};
},
getPatternContent: 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._patterns.length; i++) {
if (!this._patterns[i])
continue;
if (this._patterns[i].get_shading() != params.shadingType)
continue;
if (!params.color.equal(this._patterns[i].get_color()))
continue;
if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
!params.secondColor.equal(this._patterns[i].get_second_color()))
continue;
candidateContent = this._patterns[i];
if (params.effects != this._patterns[i].effects)
continue;
break;
}
if (candidateContent) {
content = candidateContent.copy(params.monitorIndex, params.effects);
} 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._patterns.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);
}
monitor.disconnect(signalId);
this.emit('file-changed', filename);
}));
this._fileMonitors[filename] = monitor;
},
_removeContent: function(contentList, content) {
let index = contentList.indexOf(content);
if (index >= 0)
contentList.splice(index, 1);
},
removePatternContent: function(content) {
this._removeContent(this._patterns, content);
},
removeImageContent: function(content) {
this._removeContent(this._images, content);
},
getImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,
filename: null,
effects: Meta.BackgroundEffects.NONE,
cancellable: null,
onFinished: null });
let content = null;
let candidateContent = null;
for (let i = 0; i < this._images.length; i++) {
if (!this._images[i])
continue;
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 != this._monitorIndex)
continue;
candidateContent = this._images[i];
if (params.effects != this._images[i].effects)
continue;
break;
}
if (candidateContent) {
content = candidateContent.copy(params.monitorIndex, params.effects);
if (params.onFinished)
params.onFinished(content);
} else {
content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
content.load_file_async(params.filename,
params.style,
params.cancellable,
Lang.bind(this,
function(object, result) {
try {
content.load_file_finish(result);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
content = null;
}
if (params.onFinished)
params.onFinished(content);
}));
}
}
});
Signals.addSignalMethods(BackgroundCache.prototype);
function getBackgroundCache() {
if (!_backgroundCache)
_backgroundCache = new BackgroundCache();
return _backgroundCache;
}
const Background = new Lang.Class({
Name: 'Background',
_init: function(params) {
params = Params.parse(params, { monitorIndex: 0,
effects: Meta.BackgroundEffects.NONE });
this.actor = new Meta.BackgroundGroup();
this.actor._delegate = this;
this._destroySignalId = this.actor.connect('destroy',
Lang.bind(this, this._destroy));
this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
this._monitorIndex = params.monitorIndex;
this._effects = params.effects;
this._fileWatches = {};
this._pattern = null;
this._image = null;
this._brightness = 1.0;
this._vignetteSharpness = 0.2;
this._saturation = 1.0;
this._cancellable = new Gio.Cancellable();
this.isLoaded = false;
this._settings.connect('changed', Lang.bind(this, function() {
this.emit('changed');
}));
this._load();
},
_destroy: function() {
this._cancellable.cancel();
this._cancellable = null;
let i;
let keys = Object.keys(this._fileWatches);
for (i = 0; i < keys.length; i++) {
this._cache.disconnect(this._fileWatches[keys[i]]);
}
this._fileWatches = null;
if (this._pattern) {
if (this._pattern.content)
this._cache.removePatternContent(this._pattern.content);
this._pattern.destroy();
this._pattern = null;
}
if (this._image) {
if (this._image.content)
this._cache.removeImageContent(this._image.content);
this._image.destroy();
this._image = null;
}
this.actor.disconnect(this._destroySignalId);
this._destroySignalId = 0;
this.actor.destroy();
},
_setLoaded: function() {
if (this.isLoaded)
return;
this.isLoaded = true;
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
},
_loadPattern: 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.getPatternContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
color: color,
secondColor: secondColor,
shadingType: shadingType });
this._pattern = new Meta.BackgroundActor();
this.actor.add_child(this._pattern);
this._pattern.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;
},
_setImage: function(content, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
this._image = new Meta.BackgroundActor();
this._image.content = content;
this.actor.add_child(this._image);
this._watchCacheFile(filename);
},
_loadFile: function(filename) {
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
style: this._style,
filename: filename,
cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content) {
if (!content) {
return;
}
this._setImage(content, filename);
this._setLoaded();
})
});
},
_load: function () {
if (!this._settings.get_boolean(DRAW_BACKGROUND_KEY)) {
this._setLoaded();
return;
}
this._cache = getBackgroundCache();
this._loadPattern(this._cache);
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
this._setLoaded();
return;
}
let uri = this._settings.get_string(PICTURE_URI_KEY);
let filename = Gio.File.new_for_uri(uri).get_path();
this._loadFile(filename);
},
get saturation() {
return this._saturation;
},
set saturation(saturation) {
this._saturation = saturation;
if (this._pattern && this._pattern.content)
this._pattern.content.saturation = saturation;
if (this._image && this._image.content)
this._image.content.saturation = saturation;
},
get brightness() {
return this._brightness;
},
set brightness(factor) {
this._brightness = factor;
if (this._pattern && this._pattern.content)
this._pattern.content.brightness = factor;
if (this._image && this._image.content)
this._image.content.brightness = factor;
},
get vignetteSharpness() {
return this._vignetteSharpness;
},
set vignetteSharpness(sharpness) {
this._vignetteSharpness = sharpness;
if (this._pattern && this._pattern.content)
this._pattern.content.vignette_sharpness = sharpness;
if (this._image && this._image.content)
this._image.content.vignette_sharpness = sharpness;
}
});
Signals.addSignalMethods(Background.prototype);
const StillFrame = new Lang.Class({
Name: 'StillFrame',
_init: function(monitorIndex) {
this.actor = new Meta.BackgroundActor();
this.actor._delegate = this;
let content = new Meta.Background({ meta_screen: global.screen,
monitor: monitorIndex,
effects: Meta.BackgroundEffects.NONE });
content.load_still_frame();
this.actor.content = content;
}
});
Signals.addSignalMethods(StillFrame.prototype);
const BackgroundManager = new Lang.Class({
Name: 'BackgroundManager',
_init: function(params) {
params = Params.parse(params, { container: null,
layoutManager: Main.layoutManager,
monitorIndex: null,
effects: Meta.BackgroundEffects.NONE });
this._container = params.container;
this._layoutManager = params.layoutManager;
this._effects = params.effects;
this._monitorIndex = params.monitorIndex;
this.background = this._createBackground();
this._newBackground = null;
this._loadedSignalId = 0;
this._changedSignalId = 0;
},
destroy: function() {
if (this._loadedSignalId)
this._newBackground.disconnect(this._loadedSignalId);
if (this._changedSignalId)
this.background.disconnect(this._changedSignalId);
if (this._newBackground) {
let container = this._newBackground.actor.get_parent();
if (container)
container.remove_actor(this._newBackground.actor);
this._newBackground = null;
}
if (this.background) {
let container = this.background.actor.get_parent();
if (container)
container.remove_actor(this.background.actor);
this.background = null;
}
},
_updateBackground: function(background, monitorIndex) {
let newBackground = this._createBackground(monitorIndex);
newBackground.vignetteSharpness = background.vignetteSharpness;
newBackground.brightness = background.brightness;
newBackground.saturation = background.saturation;
newBackground.visible = background.visible;
let signalId = newBackground.connect('loaded',
Lang.bind(this, function() {
newBackground.disconnect(signalId);
Tweener.addTween(background.actor,
{ opacity: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.background = newBackground;
this._newBackground = null;
this._container.remove_actor(background.actor);
this.emit('changed');
})
});
}));
this._loadedSignalId = signalId;
this._newBackground = newBackground;
},
_createBackground: function() {
let background = new Background({ monitorIndex: this._monitorIndex,
layoutManager: this._layoutManager,
effects: this._effects });
this._container.add_child(background.actor);
let monitor = this._layoutManager.monitors[this._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, this._monitorIndex);
}));
this._changedSignalId = signalId;
return background;
},
});
Signals.addSignalMethods(BackgroundManager.prototype);

View File

@ -9,15 +9,17 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Background = imports.ui.background;
const BackgroundMenu = imports.ui.backgroundMenu;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5; const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2; const STARTUP_ANIMATION_TIME = 0.5;
const KEYBOARD_ANIMATION_TIME = 0.15; const KEYBOARD_ANIMATION_TIME = 0.15;
const PLYMOUTH_TRANSITION_TIME = 1;
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff); const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
// The message tray takes this much pressure // The message tray takes this much pressure
@ -128,7 +130,6 @@ const LayoutManager = new Lang.Class({
this.primaryIndex = -1; this.primaryIndex = -1;
this._keyboardIndex = -1; this._keyboardIndex = -1;
this._hotCorners = []; this._hotCorners = [];
this._background = null;
this._leftPanelBarrier = null; this._leftPanelBarrier = null;
this._rightPanelBarrier = null; this._rightPanelBarrier = null;
this._trayBarrier = null; this._trayBarrier = null;
@ -207,6 +208,14 @@ const LayoutManager = new Lang.Class({
global.stage.remove_actor(global.top_window_group); global.stage.remove_actor(global.top_window_group);
this.uiGroup.add_actor(global.top_window_group); this.uiGroup.add_actor(global.top_window_group);
this._consoleBackgroundGroup = new Meta.BackgroundGroup();
global.stage.insert_child_below(this._consoleBackgroundGroup, null);
this._backgroundGroup = new Meta.BackgroundGroup();
global.window_group.add_child(this._backgroundGroup);
this._backgroundGroup.lower_bottom();
this._bgManagers = [];
// This blocks the XDND picks from finding the activities button // This blocks the XDND picks from finding the activities button
// and we never attempt to pick anything from it anyway so make // and we never attempt to pick anything from it anyway so make
// it invisible from picks // it invisible from picks
@ -220,6 +229,7 @@ const LayoutManager = new Lang.Class({
global.screen.connect('monitors-changed', global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged)); Lang.bind(this, this._monitorsChanged));
this._monitorsChanged(); this._monitorsChanged();
this._prepareStartupAnimation();
}, },
// This is called by Main after everything else is constructed; // This is called by Main after everything else is constructed;
@ -328,6 +338,56 @@ const LayoutManager = new Lang.Class({
} }
}, },
_createBackground: function(monitorIndex) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
layoutManager: this,
monitorIndex: monitorIndex });
bgManager.connect('changed', Lang.bind(this, function() {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
}));
this._bgManagers.push(bgManager);
return bgManager.background;
},
_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: BACKGROUND_FADE_ANIMATION_TIME,
transition: 'easeOutQuad' });
}
}
},
_createPrimaryBackground: function() {
this._createBackground(this.primaryIndex);
},
_updateBackgrounds: function() {
let i;
for (i = 0; i < this._bgManagers.length; i++)
this._bgManagers[i].destroy();
this._bgManagers = [];
if (Main.sessionMode.isGreeter)
return;
if (this._startingUp)
return;
for (let i = 0; i < this.monitors.length; i++) {
this._createBackground(i);
}
},
_updateBoxes: function() { _updateBoxes: function() {
this.screenShieldGroup.set_position(0, 0); this.screenShieldGroup.set_position(0, 0);
this.screenShieldGroup.set_size(global.screen_width, global.screen_height); this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
@ -402,6 +462,7 @@ const LayoutManager = new Lang.Class({
this._updateBoxes(); this._updateBoxes();
this._updateTrayBarrier(); this._updateTrayBarrier();
this._updateHotCorners(); this._updateHotCorners();
this._updateBackgrounds();
this._updateFullscreen(); this._updateFullscreen();
this._updateVisibility(); this._updateVisibility();
this._queueUpdateRegions(); this._queueUpdateRegions();
@ -464,53 +525,95 @@ const LayoutManager = new Lang.Class({
return this._keyboardIndex; return this._keyboardIndex;
}, },
prepareStartupAnimation: function() { // Startup Animations
this.panelBox.translation_y = -this.panelBox.height; //
// 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, and fade up the background.
// 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() {
// 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._consoleBackgroundGroup.add_child(stillFrame.actor);
stillFrame.actor.set_size(this.monitors[i].width, this.monitors[i].height);
stillFrame.actor.set_position(this.monitors[i].x, this.monitors[i].y);
}
if (Main.sessionMode.isGreeter) {
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;
}
}, },
startupAnimation: function() { startupAnimation: function() {
let plymouthTransitionRunning = false; if (Main.sessionMode.isGreeter)
this._startupAnimationGreeter();
// If we're the greeter, put up the xrootpmap actor else
// and fade it out to have a nice transition from plymouth this._startupAnimationSession();
// 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.uiGroup.add_actor(this._background);
Tweener.addTween(this._background,
{ opacity: 0,
time: PLYMOUTH_TRANSITION_TIME,
transition: 'linear',
onComplete: this._fadeBackgroundComplete,
onCompleteScope: this });
plymouthTransitionRunning = true;
}
}
if (!plymouthTransitionRunning)
this._fadeBackgroundComplete();
}, },
_fadeBackgroundComplete: function() { _startupAnimationGreeter: function() {
this._freezeUpdateRegions();
Tweener.addTween(this.panelBox,
{ translation_y: 0,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._startupAnimationComplete,
onCompleteScope: this });
},
_startupAnimationSession: function() {
this._freezeUpdateRegions(); this._freezeUpdateRegions();
this._createPrimaryBackground();
if (this._background != null) { Tweener.addTween(this.uiGroup,
this._background.destroy(); { scale_x: 1,
this._background = null; scale_y: 1,
}
Tweener.addTween(this.panelBox,
{ translation_y: 0,
time: STARTUP_ANIMATION_TIME, time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: this._startupAnimationComplete, onComplete: this._startupAnimationComplete,
onCompleteScope: this onCompleteScope: this });
});
}, },
_startupAnimationComplete: function() { _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._consoleBackgroundGroup.destroy();
if (Main.sessionMode.isGreeter)
this._createSecondaryBackgrounds();
this.emit('panel-box-changed'); this.emit('panel-box-changed');
this._thawUpdateRegions(); this._thawUpdateRegions();
}, },

View File

@ -10,7 +10,6 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const BackgroundMenu = imports.ui.backgroundMenu;
const Components = imports.ui.components; const Components = imports.ui.components;
const CtrlAltTab = imports.ui.ctrlAltTab; const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog; const EndSessionDialog = imports.ui.endSessionDialog;
@ -152,10 +151,7 @@ function startSession() {
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
componentManager = new Components.ComponentManager(); componentManager = new Components.ComponentManager();
BackgroundMenu.addBackgroundMenu(global.background_actor);
layoutManager.init(); layoutManager.init();
layoutManager.prepareStartupAnimation();
overview.init(); overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,

View File

@ -10,6 +10,7 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk; const Gdk = imports.gi.Gdk;
const Background = imports.ui.background;
const Dash = imports.ui.dash; const Dash = imports.ui.dash;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Main = imports.ui.main; const Main = imports.ui.main;
@ -24,25 +25,12 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode // Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25; const ANIMATION_TIME = 0.25;
const DND_WINDOW_SWITCH_TIMEOUT = 1250; // Must be less than ANIMATION_TIME, since we switch to
// or from the overview completely after ANIMATION_TIME,
// and don't want the shading animation to get cut off
const SHADE_ANIMATION_TIME = .20;
const GLSL_DIM_EFFECT_DECLARATIONS = ''; const DND_WINDOW_SWITCH_TIMEOUT = 1250;
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({ const ShellInfo = new Lang.Class({
Name: 'ShellInfo', Name: 'ShellInfo',
@ -118,18 +106,12 @@ const Overview = new Lang.Class({
this._overviewCreated = true; 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 // hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the // one. Instances of this class share a single CoglTexture behind the
// scenes which allows us to show the background with different // scenes which allows us to show the background with different
// rendering options without duplicating the texture data. // rendering options without duplicating the texture data.
this._background = Meta.BackgroundActor.new_for_screen(global.screen); let monitor = Main.layoutManager.primaryMonitor;
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);
this._desktopFade = new St.Bin(); this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade); global.overlay_group.add_actor(this._desktopFade);
@ -145,6 +127,11 @@ const Overview = new Lang.Class({
this._group = new St.BoxLayout({ name: 'overview-group', this._group = new St.BoxLayout({ name: 'overview-group',
clip_to_allocation: true }); clip_to_allocation: true });
this._backgroundGroup = new Meta.BackgroundGroup();
global.overlay_group.add_child(this._backgroundGroup);
this._backgroundGroup.hide();
this._bgManagers = [];
this._capturedEventId = 0; this._capturedEventId = 0;
this._buttonPressId = 0; this._buttonPressId = 0;
@ -188,6 +175,56 @@ const Overview = new Lang.Class({
this.init(); this.init();
}, },
_updateBackgrounds: function() {
for (let i = 0; i < this._bgManagers.length; i++)
this._bgManagers[i].destroy();
this._bgManagers = [];
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
monitorIndex: i,
effects: Meta.BackgroundEffects.VIGNETTE });
this._bgManagers.push(bgManager);
}
},
_unshadeBackgrounds: function() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 1.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
},
_shadeBackgrounds: function() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 0.8,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.7,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
},
_sessionUpdated: function() { _sessionUpdated: function() {
this.isDummy = !Main.sessionMode.hasOverview; this.isDummy = !Main.sessionMode.hasOverview;
this._createOverview(); this._createOverview();
@ -379,6 +416,8 @@ const Overview = new Lang.Class({
this._coverPane.set_position(0, workArea.y); this._coverPane.set_position(0, workArea.y);
this._coverPane.set_size(workArea.width, workArea.height); this._coverPane.set_size(workArea.width, workArea.height);
this._updateBackgrounds();
}, },
_onRestacked: function() { _onRestacked: function() {
@ -477,7 +516,7 @@ const Overview = new Lang.Class({
global.window_group.hide(); global.window_group.hide();
global.top_window_group.hide(); global.top_window_group.hide();
this._overview.show(); this._overview.show();
this._background.show(); this._backgroundGroup.show();
this._viewSelector.show(); this._viewSelector.show();
this._overview.opacity = 0; this._overview.opacity = 0;
@ -488,12 +527,7 @@ const Overview = new Lang.Class({
onComplete: this._showDone, onComplete: this._showDone,
onCompleteScope: this onCompleteScope: this
}); });
this._shadeBackgrounds();
Tweener.addTween(this._background,
{ dim_factor: 0.8,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top(); this._coverPane.raise_top();
this._coverPane.show(); this._coverPane.show();
@ -612,12 +646,7 @@ const Overview = new Lang.Class({
onComplete: this._hideDone, onComplete: this._hideDone,
onCompleteScope: this onCompleteScope: this
}); });
this._unshadeBackgrounds();
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top(); this._coverPane.raise_top();
this._coverPane.show(); this._coverPane.show();
@ -647,7 +676,7 @@ const Overview = new Lang.Class({
this._viewSelector.hide(); this._viewSelector.hide();
this._desktopFade.hide(); this._desktopFade.hide();
this._background.hide(); this._backgroundGroup.hide();
this._overview.hide(); this._overview.hide();
this.visible = false; this.visible = false;

View File

@ -13,6 +13,7 @@ const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const TweenerEquations = imports.tweener.equations; const TweenerEquations = imports.tweener.equations;
const Background = imports.ui.background;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash; const Hash = imports.misc.hash;
const Layout = imports.ui.layout; const Layout = imports.ui.layout;
@ -49,45 +50,15 @@ const SUMMARY_ICON_SIZE = 48;
// - STANDARD_FADE_TIME is used when the session goes idle // - STANDARD_FADE_TIME is used when the session goes idle
// - MANUAL_FADE_TIME is used for lowering the shield when asked by the user, // - MANUAL_FADE_TIME is used for lowering the shield when asked by the user,
// or when cancelling the dialog // or when cancelling the dialog
// - BACKGROUND_FADE_TIME is used when the background changes to crossfade to new background
// - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking // - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking
// - INITIAL_FADE_IN_TIME is used for the initial fade in at startup // - INITIAL_FADE_IN_TIME is used for the initial fade in at startup
const STANDARD_FADE_TIME = 10; const STANDARD_FADE_TIME = 10;
const MANUAL_FADE_TIME = 0.8; const MANUAL_FADE_TIME = 0.8;
const BACKGROUND_FADE_TIME = 1.0;
const CURTAIN_SLIDE_TIME = 0.3; const CURTAIN_SLIDE_TIME = 0.3;
const INITIAL_FADE_IN_TIME = 0.25; const INITIAL_FADE_IN_TIME = 0.25;
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({ const Clock = new Lang.Class({
Name: 'ScreenShieldClock', Name: 'ScreenShieldClock',
@ -474,22 +445,17 @@ const ScreenShield = new Lang.Class({
name: 'lockScreenContents' }); name: 'lockScreenContents' });
this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true })); 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._lockScreenGroup.add_actor(this._lockScreenContents);
this._backgroundGroup = new Meta.BackgroundGroup();
this._lockScreenGroup.add_actor(this._backgroundGroup);
this._backgroundGroup.lower_bottom();
this._bgManagers = [];
this._updateBackgrounds();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateBackgrounds));
this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows', this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
vertical: true, vertical: true,
x_align: Clutter.ActorAlign.CENTER, x_align: Clutter.ActorAlign.CENTER,
@ -571,6 +537,31 @@ const ScreenShield = new Lang.Class({
this.idleMonitor = new GnomeDesktop.IdleMonitor(); this.idleMonitor = new GnomeDesktop.IdleMonitor();
}, },
_createBackground: function(monitorIndex) {
let bin = new St.Bin({ style_class: 'screen-shield-background' });
let bgManager = new Background.BackgroundManager({ container: bin,
monitorIndex: monitorIndex,
effects: Meta.BackgroundEffects.BLUR | Meta.BackgroundEffects.DESATURATE });
bgManager.background.saturation = 0.6;
this._bgManagers.push(bgManager);
this._backgroundGroup.add_child(bin);
bin.lower_bottom();
},
_updateBackgrounds: function() {
for (let i = 0; i < this._bgManagers.length; i++)
this._bgManagers[i].destroy();
this._bgManagers = [];
for (let i = 0; i < Main.layoutManager.monitors.length; i++)
this._createBackground(i);
},
_liftShield: function(onPrimary, velocity) { _liftShield: function(onPrimary, velocity) {
if (this._isLocked) { if (this._isLocked) {
this._ensureUnlockDialog(onPrimary, true /* allowCancel */); this._ensureUnlockDialog(onPrimary, true /* allowCancel */);

View File

@ -9,6 +9,7 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Background = imports.ui.background;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
@ -170,8 +171,7 @@ const WorkspaceThumbnail = new Lang.Class({
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._background = Meta.BackgroundActor.new_for_screen(global.screen); this._createBackground();
this._contents.add_actor(this._background);
let monitor = Main.layoutManager.primaryMonitor; let monitor = Main.layoutManager.primaryMonitor;
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height); this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
@ -213,6 +213,12 @@ const WorkspaceThumbnail = new Lang.Class({
this._collapseFraction = 0; // Not collapsed this._collapseFraction = 0; // Not collapsed
}, },
_createBackground: function() {
this._bgManager = new Background.BackgroundManager({ monitorIndex: Main.layoutManager.primaryIndex,
container: this._contents,
effects: Meta.BackgroundEffects.NONE });
},
setPorthole: function(x, y, width, height) { setPorthole: function(x, y, width, height) {
this._portholeX = x; this._portholeX = x;
this._portholeY = y; this._portholeY = y;
@ -236,7 +242,7 @@ const WorkspaceThumbnail = new Lang.Class({
let clone = this._windows[i]; let clone = this._windows[i];
let metaWindow = clone.metaWindow; let metaWindow = clone.metaWindow;
if (i == 0) { if (i == 0) {
clone.setStackAbove(this._background); clone.setStackAbove(this._bgManager.background.actor);
} else { } else {
let previousClone = this._windows[i - 1]; let previousClone = this._windows[i - 1];
clone.setStackAbove(previousClone.actor); clone.setStackAbove(previousClone.actor);
@ -356,6 +362,8 @@ const WorkspaceThumbnail = new Lang.Class({
destroy : function() { destroy : function() {
this.actor.destroy(); this.actor.destroy();
this._bgManager.destroy();
this._bgManager = null;
}, },
workspaceRemoved : function() { workspaceRemoved : function() {
@ -417,7 +425,7 @@ const WorkspaceThumbnail = new Lang.Class({
this._contents.add_actor(clone.actor); this._contents.add_actor(clone.actor);
if (this._windows.length == 0) if (this._windows.length == 0)
clone.setStackAbove(this._background); clone.setStackAbove(this._bgManager.actor);
else else
clone.setStackAbove(this._windows[this._windows.length - 1].actor); clone.setStackAbove(this._windows[this._windows.length - 1].actor);

View File

@ -108,7 +108,6 @@ enum {
PROP_STAGE_INPUT_MODE, PROP_STAGE_INPUT_MODE,
PROP_WINDOW_GROUP, PROP_WINDOW_GROUP,
PROP_TOP_WINDOW_GROUP, PROP_TOP_WINDOW_GROUP,
PROP_BACKGROUND_ACTOR,
PROP_WINDOW_MANAGER, PROP_WINDOW_MANAGER,
PROP_SETTINGS, PROP_SETTINGS,
PROP_DATADIR, PROP_DATADIR,
@ -207,9 +206,6 @@ shell_global_get_property(GObject *object,
case PROP_TOP_WINDOW_GROUP: case PROP_TOP_WINDOW_GROUP:
g_value_set_object (value, meta_get_top_window_group_for_screen (global->meta_screen)); g_value_set_object (value, meta_get_top_window_group_for_screen (global->meta_screen));
break; break;
case PROP_BACKGROUND_ACTOR:
g_value_set_object (value, meta_get_background_actor_for_screen (global->meta_screen));
break;
case PROP_WINDOW_MANAGER: case PROP_WINDOW_MANAGER:
g_value_set_object (value, global->wm); g_value_set_object (value, global->wm);
break; break;
@ -436,13 +432,6 @@ shell_global_class_init (ShellGlobalClass *klass)
CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_ACTOR,
G_PARAM_READABLE)); 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, g_object_class_install_property (gobject_class,
PROP_WINDOW_MANAGER, PROP_WINDOW_MANAGER,
g_param_spec_object ("window-manager", g_param_spec_object ("window-manager",