1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
3
const Cairo = imports.cairo;
4
const Clutter = imports.gi.Clutter;
5
const Gio = imports.gi.Gio;
6
const GLib = imports.gi.GLib;
7
const GnomeDesktop = imports.gi.GnomeDesktop;
8
const Lang = imports.lang;
9
const Mainloop = imports.mainloop;
10
const Meta = imports.gi.Meta;
11
const Signals = imports.signals;
12
const Shell = imports.gi.Shell;
13
const St = imports.gi.St;
14
const TweenerEquations = imports.tweener.equations;
16
const GnomeSession = imports.misc.gnomeSession;
17
const Layout = imports.ui.layout;
18
const LoginManager = imports.misc.loginManager;
19
const Lightbox = imports.ui.lightbox;
20
const Main = imports.ui.main;
21
const Overview = imports.ui.overview;
22
const MessageTray = imports.ui.messageTray;
23
const ShellDBus = imports.ui.shellDBus;
24
const Tweener = imports.ui.tweener;
25
const Util = imports.misc.util;
27
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
28
const LOCK_ENABLED_KEY = 'lock-enabled';
29
const LOCK_DELAY_KEY = 'lock-delay';
31
const CURTAIN_SLIDE_TIME = 0.3;
32
// fraction of screen height the arrow must reach before completing
33
// the slide up automatically
34
const ARROW_DRAG_THRESHOLD = 0.1;
36
// Parameters for the arrow animation
38
const ARROW_ANIMATION_TIME = 0.6;
39
const ARROW_ANIMATION_PEAK_OPACITY = 0.4;
41
// The distance in px that the lock screen will move to when pressing
42
// a key that has no effect in the lock screen (bumping it)
44
const BUMP_TIME = 0.3;
46
const SUMMARY_ICON_SIZE = 48;
48
// Lightbox fading times
49
// STANDARD_FADE_TIME is used when the session goes idle, while
50
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
51
const STANDARD_FADE_TIME = 10;
52
const SHORT_FADE_TIME = 0.3;
54
const Clock = new Lang.Class({
55
Name: 'ScreenShieldClock',
57
CLOCK_FORMAT_KEY: 'clock-format',
58
CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
61
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
64
this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
65
this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
67
this.actor.add(this._time, { x_align: St.Align.MIDDLE });
68
this.actor.add(this._date, { x_align: St.Align.MIDDLE });
70
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
71
this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock));
76
_updateClock: function() {
77
this._time.text = this._wallClock.clock;
79
let date = new Date();
80
/* Translators: This is a time format for a date in
82
this._date.text = date.toLocaleFormat(_("%A, %B %d"));
87
this._wallClock.run_dispose();
91
const NotificationsBox = new Lang.Class({
92
Name: 'NotificationsBox',
95
this.actor = new St.BoxLayout({ vertical: true,
96
name: 'screenShieldNotifications',
97
style_class: 'screen-shield-notifications-box' });
99
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
100
style_class: 'screen-shield-notifications-box' });
101
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
102
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
103
style_class: 'screen-shield-notifications-box' });
104
scrollView.add_actor(this._persistentNotificationBox);
106
this.actor.add(this._residentNotificationBox, { x_fill: true });
107
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
110
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
111
this._summaryItemAdded(Main.messageTray, item, true);
113
this._updateVisibility();
115
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
118
destroy: function() {
119
if (this._summaryAddedId) {
120
Main.messageTray.disconnect(this._summaryAddedId);
121
this._summaryAddedId = 0;
124
for (let i = 0; i < this._items.length; i++)
125
this._removeItem(this._items[i]);
128
this.actor.destroy();
131
_updateVisibility: function() {
132
this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0;
133
this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) {
137
this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible;
140
_sourceIsResident: function(source) {
141
return source.hasResidentNotification() && !source.isChat;
144
_makeNotificationCountText: function(count, isChat) {
146
return ngettext("%d new message", "%d new messages", count).format(count);
148
return ngettext("%d new notification", "%d new notifications", count).format(count);
151
_makeNotificationSource: function(source) {
152
let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
154
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
155
box.add(sourceActor.actor, { y_fill: true });
157
let textBox = new St.BoxLayout({ vertical: true });
158
box.add(textBox, { y_fill: false, y_align: St.Align.START });
160
let label = new St.Label({ text: source.title,
161
style_class: 'screen-shield-notification-label' });
164
let count = source.unseenCount;
165
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
166
style_class: 'screen-shield-notification-count-text' });
167
textBox.add(countLabel);
169
box.visible = count != 0;
170
return [box, countLabel];
173
_summaryItemAdded: function(tray, item, dontUpdateVisibility) {
174
// Ignore transient sources, or sources explicitly marked not to show
175
// in the lock screen
176
if (item.source.isTransient || !item.source.showInLockScreen)
182
resident: this._sourceIsResident(item.source),
190
this._residentNotificationBox.add(item.notificationStackWidget);
191
item.closeButton.hide();
192
item.prepareNotificationStackForShowing();
194
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
195
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
198
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
199
obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
200
obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
201
obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
202
this._items.push(obj);
204
if (!dontUpdateVisibility)
205
this._updateVisibility();
208
_findSource: function(source) {
209
for (let i = 0; i < this._items.length; i++) {
210
if (this._items[i].source == source)
217
_onItemContentUpdated: function(item) {
218
let obj = this._items[this._findSource(item.source)];
219
this._updateItem(obj);
222
_onSourceChanged: function(source) {
223
let obj = this._items[this._findSource(source)];
224
this._updateItem(obj);
227
_updateItem: function(obj) {
228
let itemShouldBeResident = this._sourceIsResident(obj.source);
230
if (itemShouldBeResident && obj.resident) {
231
// Nothing to do here, the actor is already updated
235
if (obj.resident && !itemShouldBeResident) {
236
// make into a regular item
237
obj.item.doneShowingNotificationStack();
238
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
240
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
241
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
242
} else if (itemShouldBeResident && !obj.resident) {
243
// make into a resident item
244
obj.sourceBox.destroy();
245
obj.sourceBox = obj.countLabel = null;
248
this._residentNotificationBox.add(obj.item.notificationStackWidget);
249
obj.item.closeButton.hide();
250
obj.item.prepareNotificationStackForShowing();
252
// just update the counter
253
let count = obj.source.unseenCount;
254
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
255
obj.sourceBox.visible = count != 0;
258
this._updateVisibility();
261
_onSourceDestroy: function(source) {
262
let idx = this._findSource(source);
264
this._removeItem(this._items[idx]);
265
this._items.splice(idx, 1);
267
this._updateVisibility();
270
_removeItem: function(obj) {
272
obj.item.doneShowingNotificationStack();
273
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
275
obj.sourceBox.destroy();
278
obj.item.disconnect(obj.contentUpdatedId);
279
obj.source.disconnect(obj.sourceDestroyId);
280
obj.source.disconnect(obj.sourceCountChangedId);
284
const Arrow = new Lang.Class({
288
_init: function(params) {
290
this.x_fill = this.y_fill = true;
291
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
293
this._drawingArea = new St.DrawingArea();
294
this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow));
295
this.child = this._drawingArea;
297
this._shadowHelper = null;
298
this._shadowWidth = this._shadowHeight = 0;
301
_drawArrow: function(arrow) {
302
let cr = arrow.get_context();
303
let [w, h] = arrow.get_surface_size();
304
let node = this.get_theme_node();
305
let thickness = node.get_length('-arrow-thickness');
307
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
309
cr.setLineCap(Cairo.LineCap.ROUND);
310
cr.setLineWidth(thickness);
312
cr.moveTo(thickness / 2, h - thickness / 2);
313
cr.lineTo(w/2, thickness);
314
cr.lineTo(w - thickness / 2, h - thickness / 2);
318
vfunc_style_changed: function() {
319
let node = this.get_theme_node();
320
this._shadow = node.get_shadow('-arrow-shadow');
322
this._shadowHelper = St.ShadowHelper.new(this._shadow);
324
this._shadowHelper = null;
327
vfunc_paint: function() {
328
if (this._shadowHelper) {
329
this._shadowHelper.update(this._drawingArea);
331
let allocation = this._drawingArea.get_allocation_box();
332
let paintOpacity = this._drawingArea.get_paint_opacity();
333
this._shadowHelper.paint(allocation, paintOpacity);
336
this._drawingArea.paint();
341
* To test screen shield, make sure to kill gnome-screensaver.
343
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
344
* rather than through System Settings, you also need to set
345
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
346
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
347
* This will ensure that the screen blanks at the right time when it fades out.
348
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
350
const ScreenShield = new Lang.Class({
351
Name: 'ScreenShield',
354
this.actor = Main.layoutManager.screenShieldGroup;
356
this._lockScreenState = MessageTray.State.HIDDEN;
357
this._lockScreenGroup = new St.Widget({ x_expand: true,
361
name: 'lockScreenGroup',
363
this._lockScreenGroup.connect('key-release-event',
364
Lang.bind(this, this._onLockScreenKeyRelease));
365
this._lockScreenGroup.connect('scroll-event',
366
Lang.bind(this, this._onLockScreenScroll));
368
this._lockScreenContents = new St.Widget({ layout_manager: new Clutter.BinLayout(),
369
name: 'lockScreenContents' });
370
this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
372
this._background = new St.Bin({ style_class: 'screen-shield-background',
373
child: Meta.BackgroundActor.new_for_screen(global.screen) });
374
this._lockScreenGroup.add_actor(this._background);
375
this._lockScreenGroup.add_actor(this._lockScreenContents);
377
this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
379
x_align: Clutter.ActorAlign.CENTER,
380
y_align: Clutter.ActorAlign.END,
381
// HACK: without these, ClutterBinLayout
382
// ignores alignment properties on the actor
386
for (let i = 0; i < N_ARROWS; i++) {
387
let arrow = new Arrow({ opacity: 0 });
388
this._arrowContainer.add_actor(arrow);
390
this._lockScreenContents.add_actor(this._arrowContainer);
392
let dragArea = new Clutter.Rect({ origin: new Clutter.Point({ x: 0, y: -global.screen_height, }),
393
size: new Clutter.Size({ width: global.screen_width,
394
height: global.screen_height }) });
395
let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS,
396
drag_area: dragArea });
397
action.connect('drag-begin', Lang.bind(this, this._onDragBegin));
398
action.connect('drag-end', Lang.bind(this, this._onDragEnd));
399
this._lockScreenGroup.add_action(action);
401
this._lockDialogGroup = new St.Widget({ x_expand: true,
403
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
404
name: 'lockDialogGroup' });
406
this.actor.add_actor(this._lockDialogGroup);
407
this.actor.add_actor(this._lockScreenGroup);
409
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
411
logError(error, 'Error while reading gnome-session presence');
415
this._onStatusChanged(proxy.status);
417
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
418
this._onStatusChanged(status);
421
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
423
this._loginManager = LoginManager.getLoginManager();
424
this._loginSession = this._loginManager.getCurrentSessionProxy();
425
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
426
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
428
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
430
this._isModal = false;
431
this._hasLockScreen = false;
432
this._isGreeter = false;
433
this._isActive = false;
434
this._inUnlockAnimation = false;
435
this._activationTime = 0;
437
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
438
{ inhibitEvents: true,
439
fadeInTime: STANDARD_FADE_TIME,
442
this.idleMonitor = Shell.IdleMonitor.get();
445
_becomeModal: function() {
449
this._isModal = Main.pushModal(this.actor);
453
// We failed to get a pointer grab, it means that
454
// something else has it. Try with a keyboard grab only
455
this._isModal = Main.pushModal(this.actor, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED });
456
return this._isModal;
459
_onLockScreenKeyRelease: function(actor, event) {
460
let symbol = event.get_key_symbol();
462
// Do nothing if the lock screen is not fully shown.
463
// This avoids reusing the previous (and stale) unlock
464
// dialog if esc is pressed while the curtain is going
465
// down after cancel.
466
// Similarly, don't bump if the lock screen is not showing or is
467
// animating, as the bump overrides the animation and would
468
// remove any onComplete handler.
470
if (this._lockScreenState != MessageTray.State.SHOWN)
473
if (symbol == Clutter.KEY_Escape ||
474
symbol == Clutter.KEY_Return ||
475
symbol == Clutter.KEY_KP_Enter) {
476
this._ensureUnlockDialog(true, true);
477
this._hideLockScreen(true);
481
this._bumpLockScreen();
485
_onLockScreenScroll: function(actor, event) {
486
if (this._lockScreenState != MessageTray.State.SHOWN)
490
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
492
else if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH)
493
delta = Math.max(0, event.get_scroll_delta()[0]);
495
this._lockScreenScrollCounter += delta;
497
// 7 standard scrolls to lift up
498
if (this._lockScreenScrollCounter > 35) {
499
this._ensureUnlockDialog(false, true);
500
this._hideLockScreen(true);
506
_animateArrows: function() {
507
let arrows = this._arrowContainer.get_children();
508
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
509
let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY;
510
for (let i = 0; i < arrows.length; i++) {
512
Tweener.addTween(arrows[i],
514
delay: unitaryDelay * (N_ARROWS - (i + 1)),
515
time: ARROW_ANIMATION_TIME,
516
transition: function(t, b, c, d) {
518
return TweenerEquations.easeOutQuad(t, 0, maxOpacity, d/2);
520
return TweenerEquations.easeInQuad(t - d/2, maxOpacity, -maxOpacity, d/2);
528
_onDragBegin: function() {
529
Tweener.removeTweens(this._lockScreenGroup);
530
this._lockScreenState = MessageTray.State.HIDING;
531
this._ensureUnlockDialog(false, false);
534
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
535
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
536
// Complete motion automatically
537
this._hideLockScreen(true);
538
this._ensureUnlockDialog(false, true);
540
// restore the lock screen to its original place
541
// try to use the same speed as the normal animation
542
let h = global.stage.height;
543
let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
544
Tweener.removeTweens(this._lockScreenGroup);
545
Tweener.addTween(this._lockScreenGroup,
548
transition: 'easeInQuad',
549
onComplete: function() {
550
this._lockScreenGroup.fixed_position_set = false;
551
this._lockScreenState = MessageTray.State.SHOWN;
553
onCompleteScope: this,
556
// If we have a unlock dialog, cancel it
558
this._dialog.cancel();
559
if (!this._isGreeter) {
566
_onStatusChanged: function(status) {
567
if (status != GnomeSession.PresenceStatus.IDLE)
571
this._dialog.cancel();
572
if (!this._isGreeter) {
577
if (!this._becomeModal()) {
578
// We could not become modal, so we can't activate the
579
// screenshield. The user is probably very upset at this
580
// point, but any application using global grabs is broken
581
// Just tell him to stop using this app
583
// XXX: another option is to kick the user into the gdm login
584
// screen, where we're not affected by grabs
585
Main.notifyError("Unable to lock",
586
"Lock was blocked by an application");
590
if (!this._isActive) {
591
this._lightbox.show();
593
if (this._activationTime == 0)
594
this._activationTime = GLib.get_monotonic_time();
596
// What we want is a negative transition to 0, so we install a watch for
597
// 1 second and trigger it if the idle_time becomes lower than that
598
// The correct fix will come in GNOME 3.8 with GnomeDesktop.IdleMonitor
599
this._becameActiveId = this.idleMonitor.add_watch(1000, Lang.bind(this, function() {
600
if (this.idleMonitor.get_idletime() >= 1000)
603
this.idleMonitor.remove_watch(this._becameActiveId);
605
let lightboxWasShown = this._lightbox.shown;
606
this._lightbox.hide();
608
// GLib.get_monotonic_time() returns microseconds, convert to seconds
609
let elapsedTime = (GLib.get_monotonic_time() - this._activationTime) / 1000000;
610
let shouldLock = lightboxWasShown &&
611
this._settings.get_boolean(LOCK_ENABLED_KEY) &&
612
(elapsedTime >= this._settings.get_uint(LOCK_DELAY_KEY));
613
if (shouldLock || this._isLocked) {
615
} else if (this._isActive) {
620
this._isActive = true;
621
this.emit('lock-status-changed');
625
showDialog: function() {
626
// Ensure that the stage window is mapped, before taking a grab
627
// otherwise X errors out
628
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
629
if (!this._becomeModal()) {
630
// In the login screen, this is a hard error. Fail-whale
631
log('Could not acquire modal grab for the login screen. Aborting login process.');
632
Meta.quit(Meta.ExitCode.ERROR);
639
this._isGreeter = Main.sessionMode.isGreeter;
640
this._ensureUnlockDialog(true, true);
641
this._hideLockScreen(false);
644
_bumpLockScreen: function() {
645
Tweener.removeTweens(this._lockScreenGroup);
646
Tweener.addTween(this._lockScreenGroup,
649
transition: 'easeOutQuad',
650
onComplete: function() {
651
Tweener.addTween(this,
654
transition: 'easeInQuad' });
659
_hideLockScreen: function(animate) {
660
this._lockScreenState = MessageTray.State.HIDING;
663
// Tween the lock screen out of screen
664
// try to use the same speed regardless of original position
665
let h = global.stage.height;
666
let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
667
Tweener.removeTweens(this._lockScreenGroup);
668
Tweener.addTween(this._lockScreenGroup,
671
transition: 'easeInQuad',
672
onComplete: function() {
673
this._lockScreenState = MessageTray.State.HIDDEN;
674
this._lockScreenGroup.hide();
676
onCompleteScope: this,
679
this._lockScreenState = MessageTray.State.HIDDEN;
680
this._lockScreenGroup.hide();
683
if (Main.sessionMode.currentMode == 'lock-screen')
684
Main.sessionMode.popMode('lock-screen');
687
_ensureUnlockDialog: function(onPrimary, allowCancel) {
689
let constructor = Main.sessionMode.unlockDialog;
691
// This session mode has no locking capabilities
696
this._dialog = new constructor(this._lockDialogGroup);
699
let time = global.get_current_time();
700
this._dialog.connect('loaded', Lang.bind(this, function() {
701
if (!this._dialog.open(time, onPrimary)) {
702
log('Could not open login dialog: failed to acquire grab');
707
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
708
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
711
this._dialog.allowCancel = allowCancel;
714
_onUnlockFailed: function() {
715
this._resetLockScreen(true, false);
718
_onUnlockSucceded: function() {
719
this._tweenUnlocked();
722
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
723
this._ensureLockScreen();
724
this._lockDialogGroup.scale_x = 1;
725
this._lockDialogGroup.scale_y = 1;
727
this._lockScreenGroup.show();
728
this._lockScreenState = MessageTray.State.SHOWING;
730
if (animateLockScreen) {
731
this._lockScreenGroup.y = -global.screen_height;
732
Tweener.removeTweens(this._lockScreenGroup);
733
Tweener.addTween(this._lockScreenGroup,
735
time: SHORT_FADE_TIME,
736
transition: 'easeOutQuad',
737
onComplete: function() {
738
this._lockScreenShown();
740
onCompleteScope: this
743
this._lockScreenGroup.fixed_position_set = false;
744
this._lockScreenShown();
747
if (animateLockDialog) {
748
this._lockDialogGroup.opacity = 0;
749
Tweener.removeTweens(this._lockDialogGroup);
750
Tweener.addTween(this._lockDialogGroup,
752
time: SHORT_FADE_TIME,
753
transition: 'easeOutQuad' });
755
this._lockDialogGroup.opacity = 255;
758
this._lockScreenGroup.grab_key_focus();
760
if (Main.sessionMode.currentMode != 'lock-screen')
761
Main.sessionMode.pushMode('lock-screen');
764
_lockScreenShown: function() {
765
if (this._dialog && !this._isGreeter) {
766
this._dialog.destroy();
770
if (this._arrowAnimationId)
771
Mainloop.source_remove(this._arrowAnimationId);
772
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
773
this._animateArrows();
775
this._lockScreenState = MessageTray.State.SHOWN;
776
this._lockScreenGroup.fixed_position_set = false;
777
this._lockScreenScrollCounter = 0;
779
this.emit('lock-screen-shown');
782
// Some of the actors in the lock screen are heavy in
783
// resources, so we only create them when needed
784
_ensureLockScreen: function() {
785
if (this._hasLockScreen)
788
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
789
y_align: Clutter.ActorAlign.CENTER,
793
style_class: 'screen-shield-contents-box' });
794
this._clock = new Clock();
795
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
798
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
800
if (this._settings.get_boolean('show-notifications')) {
801
this._notificationsBox = new NotificationsBox();
802
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
807
this._hasLockScreen = true;
810
_clearLockScreen: function() {
811
this._clock.destroy();
814
if (this._notificationsBox) {
815
this._notificationsBox.destroy();
816
this._notificationsBox = null;
819
this._lockScreenContentsBox.destroy();
821
if (this._arrowAnimationId) {
822
Mainloop.source_remove(this._arrowAnimationId);
823
this._arrowAnimationId = 0;
826
this._hasLockScreen = false;
830
return this._isActive;
833
get activationTime() {
834
return this._activationTime;
837
_tweenUnlocked: function() {
838
this._inUnlockAnimation = true;
840
Tweener.addTween(this._lockDialogGroup, {
843
time: Overview.ANIMATION_TIME,
844
transition: 'easeOutQuad',
845
onComplete: function() {
847
this._dialog.destroy();
851
this._inUnlockAnimation = false;
853
onCompleteScope: this
858
if (this._hasLockScreen)
859
this._clearLockScreen();
861
if (this._dialog && !this._isGreeter) {
862
this._dialog.destroy();
866
this._lightbox.hide();
869
Main.popModal(this.actor);
870
this._isModal = false;
873
if (!this._inUnlockAnimation)
876
if (Main.sessionMode.currentMode == 'lock-screen')
877
Main.sessionMode.popMode('lock-screen');
878
if (Main.sessionMode.currentMode == 'unlock-dialog')
879
Main.sessionMode.popMode('unlock-dialog');
881
this._activationTime = 0;
882
this._isActive = false;
883
this._isLocked = false;
884
this.emit('lock-status-changed');
887
lock: function(animate) {
888
// Warn the user if we can't become modal
889
if (!this._becomeModal()) {
890
Main.notifyError("Unable to lock",
891
"Lock was blocked by an application");
895
if (this._activationTime == 0)
896
this._activationTime = GLib.get_monotonic_time();
900
if (Main.sessionMode.currentMode != 'unlock-dialog' &&
901
Main.sessionMode.currentMode != 'lock-screen') {
902
this._isGreeter = Main.sessionMode.isGreeter;
903
if (!this._isGreeter)
904
Main.sessionMode.pushMode('unlock-dialog');
907
this._resetLockScreen(animate, animate);
909
this._isActive = true;
910
this._isLocked = true;
911
this.emit('lock-status-changed');
914
Signals.addSignalMethods(ScreenShield.prototype);
916
/* Fallback code to handle session locking using gnome-screensaver,
917
in case the required GDM dependency is not there
919
const ScreenShieldFallback = new Lang.Class({
920
Name: 'ScreenShieldFallback',
923
Util.spawn(['gnome-screensaver']);
925
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
926
g_name: 'org.gnome.ScreenSaver',
927
g_object_path: '/org/gnome/ScreenSaver',
928
g_interface_name: 'org.gnome.ScreenSaver',
929
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
930
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
932
this._proxy.init(null);
934
this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
935
this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
938
_onNameOwnerChanged: function(object, pspec) {
939
if (this._proxy.g_name_owner)
940
[this._locked] = this._proxy.call_sync('GetActive', null,
941
Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
943
this._locked = false;
945
this.emit('lock-status-changed', this._locked);
948
_onSignal: function(proxy, senderName, signalName, params) {
949
if (signalName == 'ActiveChanged') {
950
[this._locked] = params.deep_unpack();
951
this.emit('lock-status-changed', this._locked);
960
this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
961
Lang.bind(this, function(proxy, result) {
962
proxy.call_finish(result);
964
this.emit('lock-screen-shown');
969
this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
970
Gio.DBusCallFlags.NONE, -1, null, null);
973
Signals.addSignalMethods(ScreenShieldFallback.prototype);