diff --git a/js/Makefile.am b/js/Makefile.am index 8b19bcbac..908d515a2 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -40,6 +40,7 @@ nobase_dist_js_DATA = \ ui/appDisplay.js \ ui/appFavorites.js \ ui/backgroundMenu.js \ + ui/background.js \ ui/boxpointer.js \ ui/calendar.js \ ui/checkBox.js \ diff --git a/js/ui/background.js b/js/ui/background.js new file mode 100644 index 000000000..e6a8d232f --- /dev/null +++ b/js/ui/background.js @@ -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); diff --git a/js/ui/layout.js b/js/ui/layout.js index 357a98601..12a9e6e36 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -9,15 +9,17 @@ const Shell = imports.gi.Shell; const Signals = imports.signals; const St = imports.gi.St; +const Background = imports.ui.background; +const BackgroundMenu = imports.ui.backgroundMenu; + const DND = imports.ui.dnd; const Main = imports.ui.main; const Params = imports.misc.params; const Tweener = imports.ui.tweener; const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5; -const STARTUP_ANIMATION_TIME = 0.2; +const STARTUP_ANIMATION_TIME = 0.5; const KEYBOARD_ANIMATION_TIME = 0.15; -const PLYMOUTH_TRANSITION_TIME = 1; const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff); // The message tray takes this much pressure @@ -128,7 +130,6 @@ const LayoutManager = new Lang.Class({ this.primaryIndex = -1; this._keyboardIndex = -1; this._hotCorners = []; - this._background = null; this._leftPanelBarrier = null; this._rightPanelBarrier = null; this._trayBarrier = null; @@ -207,6 +208,14 @@ const LayoutManager = new Lang.Class({ global.stage.remove_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 // and we never attempt to pick anything from it anyway so make // it invisible from picks @@ -220,6 +229,7 @@ const LayoutManager = new Lang.Class({ global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged)); this._monitorsChanged(); + this._prepareStartupAnimation(); }, // 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() { this.screenShieldGroup.set_position(0, 0); this.screenShieldGroup.set_size(global.screen_width, global.screen_height); @@ -402,6 +462,7 @@ const LayoutManager = new Lang.Class({ this._updateBoxes(); this._updateTrayBarrier(); this._updateHotCorners(); + this._updateBackgrounds(); this._updateFullscreen(); this._updateVisibility(); this._queueUpdateRegions(); @@ -464,53 +525,95 @@ const LayoutManager = new Lang.Class({ return this._keyboardIndex; }, - prepareStartupAnimation: function() { - this.panelBox.translation_y = -this.panelBox.height; + // Startup Animations + // + // We have two different animations, depending on whether we're a greeter + // or a normal session. + // + // In the greeter, we want to animate the panel from the top, and smoothly + // fade the login dialog on top of whatever plymouth left on screen which + // we get as a still frame background before drawing anything else. + // + // Here we just have the code to animate the panel, 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() { - let plymouthTransitionRunning = false; - - // If we're the greeter, put up the xrootpmap actor - // and fade it out to have a nice transition from plymouth - // to the greeter. Otherwise, we'll just animate the panel, - // as usual. - if (Main.sessionMode.isGreeter) { - this._background = Meta.BackgroundActor.new_for_screen(global.screen); - if (this._background != null) { - this.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(); + if (Main.sessionMode.isGreeter) + this._startupAnimationGreeter(); + else + this._startupAnimationSession(); }, - _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(); - - if (this._background != null) { - this._background.destroy(); - this._background = null; - } - - Tweener.addTween(this.panelBox, - { translation_y: 0, + this._createPrimaryBackground(); + Tweener.addTween(this.uiGroup, + { scale_x: 1, + scale_y: 1, time: STARTUP_ANIMATION_TIME, transition: 'easeOutQuad', onComplete: this._startupAnimationComplete, - onCompleteScope: this - }); + onCompleteScope: this }); }, _startupAnimationComplete: function() { + // At this point, the UI group is covering everything, so + // we no longer need to clear the stage + global.stage.no_clear_hint = true; + + global.stage_input_mode = Shell.StageInputMode.NORMAL; + + this._consoleBackgroundGroup.destroy(); + + if (Main.sessionMode.isGreeter) + this._createSecondaryBackgrounds(); + this.emit('panel-box-changed'); this._thawUpdateRegions(); }, diff --git a/js/ui/main.js b/js/ui/main.js index 22edfb904..fa42f2553 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -10,7 +10,6 @@ const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; const St = imports.gi.St; -const BackgroundMenu = imports.ui.backgroundMenu; const Components = imports.ui.components; const CtrlAltTab = imports.ui.ctrlAltTab; const EndSessionDialog = imports.ui.endSessionDialog; @@ -152,10 +151,7 @@ function startSession() { windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); componentManager = new Components.ComponentManager(); - BackgroundMenu.addBackgroundMenu(global.background_actor); - layoutManager.init(); - layoutManager.prepareStartupAnimation(); overview.init(); global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, diff --git a/js/ui/overview.js b/js/ui/overview.js index 263e2b763..755dfb4b2 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -10,6 +10,7 @@ const St = imports.gi.St; const Shell = imports.gi.Shell; const Gdk = imports.gi.Gdk; +const Background = imports.ui.background; const Dash = imports.ui.dash; const DND = imports.ui.dnd; const Main = imports.ui.main; @@ -24,25 +25,12 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail; // Time for initial animation going into Overview mode 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 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 DND_WINDOW_SWITCH_TIMEOUT = 1250; const ShellInfo = new Lang.Class({ Name: 'ShellInfo', @@ -118,18 +106,12 @@ const Overview = new Lang.Class({ this._overviewCreated = true; - // The main BackgroundActor is inside global.window_group which is + // The main Background actors are inside global.window_group which are // hidden when displaying the overview, so we create a new // one. Instances of this class share a single CoglTexture behind the // scenes which allows us to show the background with different // rendering options without duplicating the texture data. - this._background = Meta.BackgroundActor.new_for_screen(global.screen); - this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT, - GLSL_DIM_EFFECT_DECLARATIONS, - GLSL_DIM_EFFECT_CODE, - false); - this._background.hide(); - global.overlay_group.add_actor(this._background); + let monitor = Main.layoutManager.primaryMonitor; this._desktopFade = new St.Bin(); global.overlay_group.add_actor(this._desktopFade); @@ -145,6 +127,11 @@ const Overview = new Lang.Class({ this._group = new St.BoxLayout({ name: 'overview-group', 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._buttonPressId = 0; @@ -188,6 +175,56 @@ const Overview = new Lang.Class({ 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() { this.isDummy = !Main.sessionMode.hasOverview; this._createOverview(); @@ -379,6 +416,8 @@ const Overview = new Lang.Class({ this._coverPane.set_position(0, workArea.y); this._coverPane.set_size(workArea.width, workArea.height); + + this._updateBackgrounds(); }, _onRestacked: function() { @@ -477,7 +516,7 @@ const Overview = new Lang.Class({ global.window_group.hide(); global.top_window_group.hide(); this._overview.show(); - this._background.show(); + this._backgroundGroup.show(); this._viewSelector.show(); this._overview.opacity = 0; @@ -488,12 +527,7 @@ const Overview = new Lang.Class({ onComplete: this._showDone, onCompleteScope: this }); - - Tweener.addTween(this._background, - { dim_factor: 0.8, - time: ANIMATION_TIME, - transition: 'easeOutQuad' - }); + this._shadeBackgrounds(); this._coverPane.raise_top(); this._coverPane.show(); @@ -612,12 +646,7 @@ const Overview = new Lang.Class({ onComplete: this._hideDone, onCompleteScope: this }); - - Tweener.addTween(this._background, - { dim_factor: 1.0, - time: ANIMATION_TIME, - transition: 'easeOutQuad' - }); + this._unshadeBackgrounds(); this._coverPane.raise_top(); this._coverPane.show(); @@ -647,7 +676,7 @@ const Overview = new Lang.Class({ this._viewSelector.hide(); this._desktopFade.hide(); - this._background.hide(); + this._backgroundGroup.hide(); this._overview.hide(); this.visible = false; diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js index a2f482a94..0edb7c3ea 100644 --- a/js/ui/screenShield.js +++ b/js/ui/screenShield.js @@ -13,6 +13,7 @@ const Signals = imports.signals; const St = imports.gi.St; const TweenerEquations = imports.tweener.equations; +const Background = imports.ui.background; const GnomeSession = imports.misc.gnomeSession; const Hash = imports.misc.hash; const Layout = imports.ui.layout; @@ -49,45 +50,15 @@ const SUMMARY_ICON_SIZE = 48; // - STANDARD_FADE_TIME is used when the session goes idle // - MANUAL_FADE_TIME is used for lowering the shield when asked by the user, // 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 // - INITIAL_FADE_IN_TIME is used for the initial fade in at startup const STANDARD_FADE_TIME = 10; const MANUAL_FADE_TIME = 0.8; +const BACKGROUND_FADE_TIME = 1.0; const CURTAIN_SLIDE_TIME = 0.3; 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({ Name: 'ScreenShieldClock', @@ -474,22 +445,17 @@ const ScreenShield = new Lang.Class({ name: 'lockScreenContents' }); this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true })); - let backgroundActor = Meta.BackgroundActor.new_for_screen(global.screen); - backgroundActor.add_glsl_snippet(Meta.SnippetHook.TEXTURE_LOOKUP, - GLSL_BLUR_EFFECT_DECLARATIONS, - GLSL_BLUR_EFFECT_CODE, - true); - backgroundActor.set_uniform_float('desaturation', - 1, 1, [0.6]); - backgroundActor.connect('notify::size', function(actor) { - actor.set_uniform_float('pixel_step', 2, 1, [1/actor.width, 1/actor.height]); - }); - - this._background = new St.Bin({ style_class: 'screen-shield-background', - child: backgroundActor }); - this._lockScreenGroup.add_actor(this._background); this._lockScreenGroup.add_actor(this._lockScreenContents); + this._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', vertical: true, x_align: Clutter.ActorAlign.CENTER, @@ -571,6 +537,31 @@ const ScreenShield = new Lang.Class({ 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) { if (this._isLocked) { this._ensureUnlockDialog(onPrimary, true /* allowCancel */); diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js index a087f3a2e..f90877766 100644 --- a/js/ui/workspaceThumbnail.js +++ b/js/ui/workspaceThumbnail.js @@ -9,6 +9,7 @@ const Shell = imports.gi.Shell; const Signals = imports.signals; const St = imports.gi.St; +const Background = imports.ui.background; const DND = imports.ui.dnd; const Main = imports.ui.main; const Tweener = imports.ui.tweener; @@ -170,8 +171,7 @@ const WorkspaceThumbnail = new Lang.Class({ this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - this._background = Meta.BackgroundActor.new_for_screen(global.screen); - this._contents.add_actor(this._background); + this._createBackground(); let monitor = Main.layoutManager.primaryMonitor; this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height); @@ -213,6 +213,12 @@ const WorkspaceThumbnail = new Lang.Class({ 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) { this._portholeX = x; this._portholeY = y; @@ -236,7 +242,7 @@ const WorkspaceThumbnail = new Lang.Class({ let clone = this._windows[i]; let metaWindow = clone.metaWindow; if (i == 0) { - clone.setStackAbove(this._background); + clone.setStackAbove(this._bgManager.background.actor); } else { let previousClone = this._windows[i - 1]; clone.setStackAbove(previousClone.actor); @@ -356,6 +362,8 @@ const WorkspaceThumbnail = new Lang.Class({ destroy : function() { this.actor.destroy(); + this._bgManager.destroy(); + this._bgManager = null; }, workspaceRemoved : function() { @@ -417,7 +425,7 @@ const WorkspaceThumbnail = new Lang.Class({ this._contents.add_actor(clone.actor); if (this._windows.length == 0) - clone.setStackAbove(this._background); + clone.setStackAbove(this._bgManager.actor); else clone.setStackAbove(this._windows[this._windows.length - 1].actor); diff --git a/src/shell-global.c b/src/shell-global.c index 817706735..6f20e8c2c 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -108,7 +108,6 @@ enum { PROP_STAGE_INPUT_MODE, PROP_WINDOW_GROUP, PROP_TOP_WINDOW_GROUP, - PROP_BACKGROUND_ACTOR, PROP_WINDOW_MANAGER, PROP_SETTINGS, PROP_DATADIR, @@ -207,9 +206,6 @@ shell_global_get_property(GObject *object, case PROP_TOP_WINDOW_GROUP: g_value_set_object (value, meta_get_top_window_group_for_screen (global->meta_screen)); break; - case PROP_BACKGROUND_ACTOR: - g_value_set_object (value, meta_get_background_actor_for_screen (global->meta_screen)); - break; case PROP_WINDOW_MANAGER: g_value_set_object (value, global->wm); break; @@ -436,13 +432,6 @@ shell_global_class_init (ShellGlobalClass *klass) CLUTTER_TYPE_ACTOR, G_PARAM_READABLE)); - g_object_class_install_property (gobject_class, - PROP_BACKGROUND_ACTOR, - g_param_spec_object ("background-actor", - "Background Actor", - "Actor drawing root window background", - CLUTTER_TYPE_ACTOR, - G_PARAM_READABLE)); g_object_class_install_property (gobject_class, PROP_WINDOW_MANAGER, g_param_spec_object ("window-manager",