diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 623206bae..b3dcb821e 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -1662,7 +1662,7 @@ StScrollBar StButton#vhandle:hover } .lightbox { - background-color: rgba(0, 0, 0, 0.4); + background-color: black; } .flashspot { diff --git a/js/Makefile.am b/js/Makefile.am index 02ab5d949..29a188857 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -76,6 +76,7 @@ nobase_dist_js_DATA = \ ui/popupMenu.js \ ui/remoteSearch.js \ ui/runDialog.js \ + ui/screenShield.js \ ui/scripting.js \ ui/search.js \ ui/searchDisplay.js \ diff --git a/js/ui/lightbox.js b/js/ui/lightbox.js index aa1516094..d6b0f5d1d 100644 --- a/js/ui/lightbox.js +++ b/js/ui/lightbox.js @@ -8,6 +8,8 @@ const St = imports.gi.St; const Params = imports.misc.params; const Tweener = imports.ui.tweener; +const DEFAULT_FADE_FACTOR = 0.4; + /** * Lightbox: * @container: parent Clutter.Container @@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener; * - inhibitEvents: whether to inhibit events for @container * - width: shade actor width * - height: shade actor height - * - fadeTime: seconds used to fade in/out + * - fadeInTime: seconds used to fade in + * - fadeOutTime: seconds used to fade out * * Lightbox creates a dark translucent "shade" actor to hide the * contents of @container, and allows you to specify particular actors @@ -38,12 +41,16 @@ const Lightbox = new Lang.Class({ params = Params.parse(params, { inhibitEvents: false, width: null, height: null, - fadeTime: null + fadeInTime: null, + fadeOutTime: null, + fadeFactor: DEFAULT_FADE_FACTOR }); this._container = container; this._children = container.get_children(); - this._fadeTime = params.fadeTime; + this._fadeInTime = params.fadeInTime; + this._fadeOutTime = params.fadeOutTime; + this._fadeFactor = params.fadeFactor; this.actor = new St.Bin({ x: 0, y: 0, style_class: 'lightbox', @@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({ container.add_actor(this.actor); this.actor.raise_top(); this.actor.hide(); + this.shown = false; this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); @@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({ }, show: function() { - if (this._fadeTime) { + if (this._fadeInTime) { + this.shown = false; this.actor.opacity = 0; Tweener.addTween(this.actor, - { opacity: 255, - time: this._fadeTime, - transition: 'easeOutQuad' + { opacity: 255 * this._fadeFactor, + time: this._fadeInTime, + transition: 'easeOutQuad', + onComplete: Lang.bind(this, function() { + this.shown = true; + }) }); } else { - this.actor.opacity = 255; + this.actor.opacity = 255 * this._fadeFactor; + this.shown = true; } this.actor.show(); }, hide: function() { - if (this._fadeTime) { + this.shown = false; + if (this._fadeOutTime) { Tweener.addTween(this.actor, { opacity: 0, - time: this._fadeTime, + time: this._fadeOutTime, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this.actor.hide(); diff --git a/js/ui/main.js b/js/ui/main.js index 0b12d7577..ee999dd65 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -30,6 +30,7 @@ const LookingGlass = imports.ui.lookingGlass; const NetworkAgent = imports.ui.networkAgent; const NotificationDaemon = imports.ui.notificationDaemon; const WindowAttentionHandler = imports.ui.windowAttentionHandler; +const ScreenShield = imports.ui.screenShield; const Scripting = imports.ui.scripting; const SessionMode = imports.ui.sessionMode; const ShellDBus = imports.ui.shellDBus; @@ -54,6 +55,7 @@ let runDialog = null; let lookingGlass = null; let wm = null; let messageTray = null; +let screenShield = null; let notificationDaemon = null; let windowAttentionHandler = null; let telepathyClient = null; @@ -204,6 +206,7 @@ function start() { overview = new Overview.Overview(); magnifier = new Magnifier.Magnifier(); statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher(); + screenShield = new ScreenShield.ScreenShield(); panel = new Panel.Panel(); wm = new WindowManager.WindowManager(); messageTray = new MessageTray.MessageTray(); @@ -642,6 +645,10 @@ function _findModal(actor) { return -1; } +function isInModalStack(actor) { + return _findModal(actor) != -1; +} + /** * pushModal: * @actor: #ClutterActor which will be given keyboard focus diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js new file mode 100644 index 000000000..1b963e302 --- /dev/null +++ b/js/ui/screenShield.js @@ -0,0 +1,96 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const Lang = imports.lang; +const Meta = imports.gi.Meta; +const St = imports.gi.St; + +const GnomeSession = imports.misc.gnomeSession; +const Lightbox = imports.ui.lightbox; +const LoginDialog = imports.gdm.loginDialog; +const Main = imports.ui.main; + +const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; +const LOCK_ENABLED_KEY = 'lock-enabled'; + +/** + * To test screen shield, make sure to kill gnome-screensaver. + * + * If you are setting org.gnome.desktop.session.idle-delay directly in dconf, + * rather than through System Settings, you also need to set + * org.gnome.settings-daemon.plugins.power.sleep-display-ac and + * org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value. + * This will ensure that the screen blanks at the right time when it fades out. + * https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance. + */ +const ScreenShield = new Lang.Class({ + Name: 'ScreenShield', + + _init: function() { + this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) { + if (error) { + logError(error, 'Error while reading gnome-session presence'); + return; + } + + this._onStatusChanged(proxy.status); + })); + this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) { + this._onStatusChanged(status); + })); + + this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); + + this._group = new St.Widget({ x: 0, + y: 0 }); + Main.uiGroup.add_actor(this._group); + let constraint = new Clutter.BindConstraint({ source: global.stage, + coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE }); + this._group.add_constraint(constraint); + this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); + this._group.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent)); + this._lightbox = new Lightbox.Lightbox(this._group, + { inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 }); + this._background = Meta.BackgroundActor.new_for_screen(global.screen); + this._background.hide(); + Main.uiGroup.add_actor(this._background); + }, + + _onStatusChanged: function(status) { + log ("in _onStatusChanged"); + if (status == GnomeSession.PresenceStatus.IDLE) { + log("session gone idle"); + this._group.reactive = true; + Main.pushModal(this._group); + this._lightbox.show(); + } else { + let lightboxWasShown = this._lightbox.shown; + log("this._lightbox.shown " + this._lightbox.shown); + this._lightbox.hide(); + if (lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY)) { + this._background.show(); + this._background.raise_top(); + } else { + this._popModal(); + } + } + }, + + _popModal: function() { + this._group.reactive = false; + if (Main.isInModalStack(this._group)) + Main.popModal(this._group); + this._background.hide(); + }, + + _onKeyPressEvent: function(object, keyPressEvent) { + log("in _onKeyPressEvent - lock is enabled: " + this._settings.get_boolean(LOCK_ENABLED_KEY)); + this._popModal(); + }, + + _onButtonPressEvent: function(object, buttonPressEvent) { + log("in _onButtonPressEvent - lock is enabled: " + this._settings.get_boolean(LOCK_ENABLED_KEY)); + this._popModal(); + }, +}); diff --git a/js/ui/workspace.js b/js/ui/workspace.js index e00a86c0b..2209e03db 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -301,7 +301,8 @@ const WindowClone = new Lang.Class({ if (!this._zoomLightbox) this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup, - { fadeTime: LIGHTBOX_FADE_TIME }); + { fadeInTime: LIGHTBOX_FADE_TIME, + fadeOutTime: LIGHTBOX_FADE_TIME }); this._zoomLightbox.show(); this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);