~darkxst/ubuntu/quantal/gnome-shell/lp1128804

« back to all changes in this revision

Viewing changes to js/ui/screenShield.js

  • Committer: Package Import Robot
  • Author(s): Tim Lunn
  • Date: 2012-10-09 20:42:33 UTC
  • mfrom: (57.1.7 quantal)
  • Revision ID: package-import@ubuntu.com-20121009204233-chcl8989muuzfpws
Tags: 3.6.0-0ubuntu3
* debian/patches/ubuntu-lightdm-user-switching.patch
  - Fix user switching when running lightdm.  LP: #1064269
 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 
2
 
 
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 St = imports.gi.St;
 
13
const TweenerEquations = imports.tweener.equations;
 
14
 
 
15
const GnomeSession = imports.misc.gnomeSession;
 
16
const Layout = imports.ui.layout;
 
17
const LoginManager = imports.misc.loginManager;
 
18
const Lightbox = imports.ui.lightbox;
 
19
const Main = imports.ui.main;
 
20
const Overview = imports.ui.overview;
 
21
const MessageTray = imports.ui.messageTray;
 
22
const ShellDBus = imports.ui.shellDBus;
 
23
const Tweener = imports.ui.tweener;
 
24
 
 
25
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
 
26
const LOCK_ENABLED_KEY = 'lock-enabled';
 
27
 
 
28
const CURTAIN_SLIDE_TIME = 0.5;
 
29
// fraction of screen height the arrow must reach before completing
 
30
// the slide up automatically
 
31
const ARROW_DRAG_THRESHOLD = 0.1;
 
32
 
 
33
// Parameters for the arrow animation
 
34
const N_ARROWS = 3;
 
35
const ARROW_ANIMATION_TIME = 0.6;
 
36
const ARROW_ANIMATION_PEAK_OPACITY = 0.4;
 
37
 
 
38
// The distance in px that the lock screen will move to when pressing
 
39
// a key that has no effect in the lock screen (bumping it)
 
40
const BUMP_SIZE = 25;
 
41
const BUMP_TIME = 0.3;
 
42
 
 
43
const SUMMARY_ICON_SIZE = 48;
 
44
 
 
45
// Lightbox fading times
 
46
// STANDARD_FADE_TIME is used when the session goes idle, while
 
47
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
 
48
const STANDARD_FADE_TIME = 10;
 
49
const SHORT_FADE_TIME = 0.8;
 
50
 
 
51
const Clock = new Lang.Class({
 
52
    Name: 'ScreenShieldClock',
 
53
 
 
54
    CLOCK_FORMAT_KEY: 'clock-format',
 
55
    CLOCK_SHOW_SECONDS_KEY: 'clock-show-seconds',
 
56
 
 
57
    _init: function() {
 
58
        this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
 
59
                                        vertical: true });
 
60
 
 
61
        this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
 
62
        this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
 
63
 
 
64
        this.actor.add(this._time, { x_align: St.Align.MIDDLE });
 
65
        this.actor.add(this._date, { x_align: St.Align.MIDDLE });
 
66
 
 
67
        this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
 
68
        this._wallClock.connect('notify::clock', Lang.bind(this, this._updateClock));
 
69
 
 
70
        this._updateClock();
 
71
    },
 
72
 
 
73
    _updateClock: function() {
 
74
        this._time.text = this._wallClock.clock;
 
75
 
 
76
        let date = new Date();
 
77
        /* Translators: This is a time format for a date in
 
78
           long format */
 
79
        this._date.text = date.toLocaleFormat(_("%A, %B %d"));
 
80
    },
 
81
 
 
82
    destroy: function() {
 
83
        this.actor.destroy();
 
84
        this._wallClock.run_dispose();
 
85
    }
 
86
});
 
87
 
 
88
const NotificationsBox = new Lang.Class({
 
89
    Name: 'NotificationsBox',
 
90
 
 
91
    _init: function() {
 
92
        this.actor = new St.BoxLayout({ vertical: true,
 
93
                                        name: 'screenShieldNotifications',
 
94
                                        style_class: 'screen-shield-notifications-box' });
 
95
 
 
96
        this._residentNotificationBox = new St.BoxLayout({ vertical: true,
 
97
                                                           style_class: 'screen-shield-notifications-box' });
 
98
        let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.MIDDLE });
 
99
        this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
 
100
                                                             style_class: 'screen-shield-notifications-box' });
 
101
        scrollView.add_actor(this._persistentNotificationBox);
 
102
 
 
103
        this.actor.add(this._residentNotificationBox, { x_fill: true });
 
104
        this.actor.add(scrollView, { x_fill: true, x_align: St.Align.MIDDLE });
 
105
 
 
106
        this._items = [];
 
107
        Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
 
108
            this._summaryItemAdded(Main.messageTray, item, true);
 
109
        }));
 
110
        this._updateVisibility();
 
111
 
 
112
        this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
 
113
    },
 
114
 
 
115
    destroy: function() {
 
116
        if (this._summaryAddedId) {
 
117
            Main.messageTray.disconnect(this._summaryAddedId);
 
118
            this._summaryAddedId = 0;
 
119
        }
 
120
 
 
121
        for (let i = 0; i < this._items.length; i++)
 
122
            this._removeItem(this._items[i]);
 
123
        this._items = [];
 
124
 
 
125
        this.actor.destroy();
 
126
    },
 
127
 
 
128
    _updateVisibility: function() {
 
129
        if (this._residentNotificationBox.get_n_children() > 0) {
 
130
            this.actor.show();
 
131
            return;
 
132
        }
 
133
 
 
134
        let children = this._persistentNotificationBox.get_children();
 
135
        this.actor.visible = children.some(function(a) { return a.visible; });
 
136
    },
 
137
 
 
138
    _sourceIsResident: function(source) {
 
139
        return source.hasResidentNotification() && !source.isChat;
 
140
    },
 
141
 
 
142
    _makeNotificationCountText: function(count, isChat) {
 
143
        if (isChat)
 
144
            return ngettext("%d new message", "%d new messages", count).format(count);
 
145
        else
 
146
            return ngettext("%d new notification", "%d new notifications", count).format(count);
 
147
    },
 
148
 
 
149
    _makeNotificationSource: function(source) {
 
150
        let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
 
151
 
 
152
        let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
 
153
        box.add(sourceActor.actor, { y_fill: true });
 
154
 
 
155
        let textBox = new St.BoxLayout({ vertical: true });
 
156
        box.add(textBox);
 
157
 
 
158
        let label = new St.Label({ text: source.title,
 
159
                                   style_class: 'screen-shield-notification-label' });
 
160
        textBox.add(label);
 
161
 
 
162
        let count = source.unseenCount;
 
163
        let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
 
164
                                        style_class: 'screen-shield-notification-count-text' });
 
165
        textBox.add(countLabel);
 
166
 
 
167
        box.visible = count != 0;
 
168
        return [box, countLabel];
 
169
    },
 
170
 
 
171
    _summaryItemAdded: function(tray, item, dontUpdateVisibility) {
 
172
        // Ignore transient sources, or sources explicitly marked not to show
 
173
        // in the lock screen
 
174
        if (item.source.isTransient || !item.source.showInLockScreen)
 
175
            return;
 
176
 
 
177
        let obj = {
 
178
            item: item,
 
179
            source: item.source,
 
180
            resident: this._sourceIsResident(item.source),
 
181
            contentUpdatedId: 0,
 
182
            sourceDestroyId: 0,
 
183
            sourceBox: null,
 
184
            countLabel: null,
 
185
        };
 
186
 
 
187
        if (obj.resident) {
 
188
            this._residentNotificationBox.add(item.notificationStackWidget);
 
189
            item.closeButtonVisible = false;
 
190
            item.prepareNotificationStackForShowing();
 
191
        } else {
 
192
            [obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
 
193
            this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.MIDDLE });
 
194
        }
 
195
 
 
196
        obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
 
197
        obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
 
198
        obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
 
199
        obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
 
200
        this._items.push(obj);
 
201
 
 
202
        if (!dontUpdateVisibility)
 
203
            this._updateVisibility();
 
204
    },
 
205
 
 
206
    _findSource: function(source) {
 
207
        for (let i = 0; i < this._items.length; i++) {
 
208
            if (this._items[i].source == source)
 
209
                return i;
 
210
        }
 
211
 
 
212
        return -1;
 
213
    },
 
214
 
 
215
    _onItemContentUpdated: function(item) {
 
216
        let obj = this._items[this._findSource(item.source)];
 
217
        this._updateItem(obj);
 
218
    },
 
219
 
 
220
    _onSourceChanged: function(source) {
 
221
        let obj = this._items[this._findSource(source)];
 
222
        this._updateItem(obj);
 
223
    },
 
224
 
 
225
    _updateItem: function(obj) {
 
226
        let itemShouldBeResident = this._sourceIsResident(obj.source);
 
227
 
 
228
        if (itemShouldBeResident && obj.resident) {
 
229
            // Nothing to do here, the actor is already updated
 
230
            return;
 
231
        }
 
232
 
 
233
        if (obj.resident && !itemShouldBeResident) {
 
234
            // make into a regular item
 
235
            obj.item.doneShowingNotificationStack();
 
236
            this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
 
237
 
 
238
            [obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
 
239
            this._persistentNotificationBox.add(obj.sourceBox);
 
240
        } else if (itemShouldBeResident && !obj.resident) {
 
241
            // make into a resident item
 
242
            obj.sourceBox.destroy();
 
243
            obj.sourceBox = obj.countLabel = null;
 
244
            obj.resident = true;
 
245
 
 
246
            this._residentNotificationBox.add(obj.item.notificationStackWidget);
 
247
            obj.item.closeButtonVisible = false;
 
248
            obj.item.prepareNotificationStackForShowing();
 
249
        } else {
 
250
            // just update the counter
 
251
            let count = obj.source.unseenCount;
 
252
            obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
 
253
            obj.sourceBox.visible = count != 0;
 
254
        }
 
255
 
 
256
        this._updateVisibility();
 
257
    },
 
258
 
 
259
    _onSourceDestroy: function(source) {
 
260
        let idx = this._findSource(source);
 
261
 
 
262
        this._removeItem(this._items[idx]);
 
263
        this._items.splice(idx, 1);
 
264
 
 
265
        this._updateVisibility();
 
266
    },
 
267
 
 
268
    _removeItem: function(obj) {
 
269
        if (obj.resident) {
 
270
            obj.item.doneShowingNotificationStack();
 
271
            this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
 
272
        } else {
 
273
            obj.sourceBox.destroy();
 
274
        }
 
275
 
 
276
        obj.item.disconnect(obj.contentUpdatedId);
 
277
        obj.source.disconnect(obj.sourceDestroyId);
 
278
        obj.source.disconnect(obj.sourceCountChangedId);
 
279
    },
 
280
});
 
281
 
 
282
const Arrow = new Lang.Class({
 
283
    Name: 'Arrow',
 
284
    Extends: St.Bin,
 
285
 
 
286
    _init: function(params) {
 
287
        this.parent(params);
 
288
        this.x_fill = this.y_fill = true;
 
289
        this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
 
290
 
 
291
        this._drawingArea = new St.DrawingArea();
 
292
        this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow));
 
293
        this.child = this._drawingArea;
 
294
 
 
295
        this._shadowHelper = null;
 
296
        this._shadowWidth = this._shadowHeight = 0;
 
297
    },
 
298
 
 
299
    _drawArrow: function(arrow) {
 
300
        let cr = arrow.get_context();
 
301
        let [w, h] = arrow.get_surface_size();
 
302
        let node = this.get_theme_node();
 
303
        let thickness = node.get_length('-arrow-thickness');
 
304
 
 
305
        Clutter.cairo_set_source_color(cr, node.get_foreground_color());
 
306
 
 
307
        cr.setLineCap(Cairo.LineCap.ROUND);
 
308
        cr.setLineWidth(thickness);
 
309
 
 
310
        cr.moveTo(thickness / 2, h - thickness / 2);
 
311
        cr.lineTo(w/2, thickness);
 
312
        cr.lineTo(w - thickness / 2, h - thickness / 2);
 
313
        cr.stroke();
 
314
    },
 
315
 
 
316
    vfunc_style_changed: function() {
 
317
        let node = this.get_theme_node();
 
318
        this._shadow = node.get_shadow('-arrow-shadow');
 
319
        if (this._shadow)
 
320
            this._shadowHelper = St.ShadowHelper.new(this._shadow);
 
321
        else
 
322
            this._shadowHelper = null;
 
323
    },
 
324
 
 
325
    vfunc_paint: function() {
 
326
        if (this._shadowHelper) {
 
327
            this._shadowHelper.update(this._drawingArea);
 
328
 
 
329
            let allocation = this._drawingArea.get_allocation_box();
 
330
            let paintOpacity = this._drawingArea.get_paint_opacity();
 
331
            this._shadowHelper.paint(allocation, paintOpacity);
 
332
        }
 
333
 
 
334
        this._drawingArea.paint();
 
335
    }
 
336
});
 
337
 
 
338
/**
 
339
 * To test screen shield, make sure to kill gnome-screensaver.
 
340
 *
 
341
 * If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
 
342
 * rather than through System Settings, you also need to set
 
343
 * org.gnome.settings-daemon.plugins.power.sleep-display-ac and
 
344
 * org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
 
345
 * This will ensure that the screen blanks at the right time when it fades out.
 
346
 * https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
 
347
 */
 
348
const ScreenShield = new Lang.Class({
 
349
    Name: 'ScreenShield',
 
350
 
 
351
    _init: function() {
 
352
        this.actor = Main.layoutManager.screenShieldGroup;
 
353
 
 
354
        this._lockScreenState = MessageTray.State.HIDDEN;
 
355
        this._lockScreenGroup = new St.Widget({ x_expand: true,
 
356
                                                y_expand: true,
 
357
                                                reactive: true,
 
358
                                                can_focus: true,
 
359
                                                name: 'lockScreenGroup',
 
360
                                              });
 
361
        this._lockScreenGroup.connect('key-release-event',
 
362
                                      Lang.bind(this, this._onLockScreenKeyRelease));
 
363
        this._lockScreenGroup.connect('scroll-event',
 
364
                                      Lang.bind(this, this._onLockScreenScroll));
 
365
 
 
366
        this._lockScreenContents = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 
367
                                                   name: 'lockScreenContents' });
 
368
        this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
 
369
 
 
370
        this._background = new St.Bin({ style_class: 'screen-shield-background',
 
371
                                        child: Meta.BackgroundActor.new_for_screen(global.screen) });
 
372
        this._lockScreenGroup.add_actor(this._background);
 
373
        this._lockScreenGroup.add_actor(this._lockScreenContents);
 
374
 
 
375
        this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
 
376
                                                  vertical: true,
 
377
                                                  x_align: Clutter.ActorAlign.CENTER,
 
378
                                                  y_align: Clutter.ActorAlign.END,
 
379
                                                  // HACK: without these, ClutterBinLayout
 
380
                                                  // ignores alignment properties on the actor
 
381
                                                  x_expand: true,
 
382
                                                  y_expand: true });
 
383
 
 
384
        for (let i = 0; i < N_ARROWS; i++) {
 
385
            let arrow = new Arrow({ opacity: 0 });
 
386
            this._arrowContainer.add_actor(arrow);
 
387
        }
 
388
        this._lockScreenContents.add_actor(this._arrowContainer);
 
389
 
 
390
        let dragArea = new Clutter.Rect({ origin: new Clutter.Point({ x: 0, y: -global.screen_height, }),
 
391
                                          size: new Clutter.Size({ width: global.screen_width,
 
392
                                                                   height: global.screen_height }) });
 
393
        let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS,
 
394
                                              drag_area: dragArea });
 
395
        action.connect('drag-begin', Lang.bind(this, this._onDragBegin));
 
396
        action.connect('drag-end', Lang.bind(this, this._onDragEnd));
 
397
        this._lockScreenGroup.add_action(action);
 
398
 
 
399
        this._lockDialogGroup = new St.Widget({ x_expand: true,
 
400
                                                y_expand: true,
 
401
                                                pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
 
402
                                                name: 'lockDialogGroup' });
 
403
 
 
404
        this.actor.add_actor(this._lockDialogGroup);
 
405
        this.actor.add_actor(this._lockScreenGroup);
 
406
 
 
407
        this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
 
408
            if (error) {
 
409
                logError(error, 'Error while reading gnome-session presence');
 
410
                return;
 
411
            }
 
412
 
 
413
            this._onStatusChanged(proxy.status);
 
414
        }));
 
415
        this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
 
416
            this._onStatusChanged(status);
 
417
        }));
 
418
 
 
419
        this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
 
420
 
 
421
        this._loginManager = LoginManager.getLoginManager();
 
422
        this._loginSession = this._loginManager.getCurrentSessionProxy();
 
423
        this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
 
424
        this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
 
425
 
 
426
        this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
 
427
 
 
428
        this._isModal = false;
 
429
        this._hasLockScreen = false;
 
430
        this._isGreeter = false;
 
431
        this._isActive = false;
 
432
        this._inUnlockAnimation = false;
 
433
 
 
434
        this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
 
435
                                               { inhibitEvents: true,
 
436
                                                 fadeInTime: STANDARD_FADE_TIME,
 
437
                                                 fadeFactor: 1 });
 
438
    },
 
439
 
 
440
    _onLockScreenKeyRelease: function(actor, event) {
 
441
        let symbol = event.get_key_symbol();
 
442
 
 
443
        if (symbol == Clutter.KEY_Escape ||
 
444
            symbol == Clutter.KEY_Return ||
 
445
            symbol == Clutter.KEY_KP_Enter) {
 
446
            this._ensureUnlockDialog();
 
447
            this._hideLockScreen(true);
 
448
            return true;
 
449
        }
 
450
 
 
451
        // Don't bump if the lock screen is not showing or is
 
452
        // animating, as the bump overrides the animation and would
 
453
        // remove any onComplete handler
 
454
        if (this._lockScreenState == MessageTray.State.SHOWN)
 
455
            this._bumpLockScreen();
 
456
        return true;
 
457
    },
 
458
 
 
459
    _onLockScreenScroll: function(actor, event) {
 
460
        if (this._lockScreenState != MessageTray.State.SHOWN)
 
461
            return false;
 
462
 
 
463
        let delta = 0;
 
464
        if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
 
465
            delta = 5;
 
466
        else if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH)
 
467
            delta = Math.max(0, event.get_scroll_delta()[0]);
 
468
 
 
469
        this._lockScreenScrollCounter += delta;
 
470
 
 
471
        // 7 standard scrolls to lift up
 
472
        if (this._lockScreenScrollCounter > 35) {
 
473
            this._ensureUnlockDialog();
 
474
            this._hideLockScreen(true);
 
475
        }
 
476
 
 
477
        return true;
 
478
    },
 
479
 
 
480
    _animateArrows: function() {
 
481
        let arrows = this._arrowContainer.get_children();
 
482
        let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
 
483
        let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY;
 
484
        for (let i = 0; i < arrows.length; i++) {
 
485
            arrows.opacity = 0;
 
486
            Tweener.addTween(arrows[i],
 
487
                             { opacity: 0,
 
488
                               delay: unitaryDelay * (N_ARROWS - (i + 1)),
 
489
                               time: ARROW_ANIMATION_TIME,
 
490
                               transition: function(t, b, c, d) {
 
491
                                 if (t < d/2)
 
492
                                     return TweenerEquations.easeOutQuad(t, 0, maxOpacity, d/2);
 
493
                                 else
 
494
                                     return TweenerEquations.easeInQuad(t - d/2, maxOpacity, -maxOpacity, d/2);
 
495
                               }
 
496
                             });
 
497
        }
 
498
 
 
499
        return true;
 
500
    },
 
501
 
 
502
    _onDragBegin: function() {
 
503
        Tweener.removeTweens(this._lockScreenGroup);
 
504
        this._lockScreenState = MessageTray.State.HIDING;
 
505
        this._ensureUnlockDialog();
 
506
    },
 
507
 
 
508
    _onDragEnd: function(action, actor, eventX, eventY, modifiers) {
 
509
        if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
 
510
            // Complete motion automatically
 
511
            this._hideLockScreen(true);
 
512
        } else {
 
513
            // restore the lock screen to its original place
 
514
            // try to use the same speed as the normal animation
 
515
            let h = global.stage.height;
 
516
            let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
 
517
            Tweener.removeTweens(this._lockScreenGroup);
 
518
            Tweener.addTween(this._lockScreenGroup,
 
519
                             { y: 0,
 
520
                               time: time,
 
521
                               transition: 'linear',
 
522
                               onComplete: function() {
 
523
                                   this._lockScreenGroup.fixed_position_set = false;
 
524
                                   this._lockScreenState = MessageTray.State.SHOWN;
 
525
                               },
 
526
                               onCompleteScope: this,
 
527
                             });
 
528
 
 
529
            // If we have a unlock dialog, cancel it
 
530
            if (this._dialog) {
 
531
                this._dialog.cancel();
 
532
                if (!this._isGreeter) {
 
533
                    this._dialog = null;
 
534
                }
 
535
            }
 
536
        }
 
537
    },
 
538
 
 
539
    _onStatusChanged: function(status) {
 
540
        if (status == GnomeSession.PresenceStatus.IDLE) {
 
541
            if (this._dialog) {
 
542
                this._dialog.cancel();
 
543
                if (!this._isGreeter) {
 
544
                    this._dialog = null;
 
545
                }
 
546
            }
 
547
 
 
548
            if (!this._isModal) {
 
549
                Main.pushModal(this.actor);
 
550
                this._isModal = true;
 
551
            }
 
552
 
 
553
            if (!this._isActive)
 
554
                this._lightbox.show();
 
555
        } else {
 
556
            let lightboxWasShown = this._lightbox.shown;
 
557
            this._lightbox.hide();
 
558
 
 
559
            let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
 
560
            if (shouldLock || this._isActive) {
 
561
                this.lock(false);
 
562
            } else if (this._isModal) {
 
563
                this.unlock();
 
564
            }
 
565
        }
 
566
    },
 
567
 
 
568
    showDialog: function() {
 
569
        // Ensure that the stage window is mapped, before taking a grab
 
570
        // otherwise X errors out
 
571
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
 
572
            if (!this._isModal) {
 
573
                Main.pushModal(this.actor);
 
574
                this._isModal = true;
 
575
            }
 
576
 
 
577
            return false;
 
578
        }));
 
579
 
 
580
        this.actor.show();
 
581
        this._isGreeter = Main.sessionMode.isGreeter;
 
582
        this._ensureUnlockDialog();
 
583
        this._hideLockScreen(false);
 
584
    },
 
585
 
 
586
    _bumpLockScreen: function() {
 
587
        Tweener.removeTweens(this._lockScreenGroup);
 
588
        Tweener.addTween(this._lockScreenGroup,
 
589
                         { y: -BUMP_SIZE,
 
590
                           time: BUMP_TIME / 2,
 
591
                           transition: 'easeOutQuad',
 
592
                           onComplete: function() {
 
593
                               Tweener.addTween(this,
 
594
                                                { y: 0,
 
595
                                                  time: BUMP_TIME / 2,
 
596
                                                  transition: 'easeInQuad' });
 
597
                           }
 
598
                         });
 
599
    },
 
600
 
 
601
    _hideLockScreen: function(animate) {
 
602
        this._lockScreenState = MessageTray.State.HIDING;
 
603
 
 
604
        if (animate) {
 
605
            // Tween the lock screen out of screen
 
606
            // try to use the same speed regardless of original position
 
607
            let h = global.stage.height;
 
608
            let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
 
609
            Tweener.removeTweens(this._lockScreenGroup);
 
610
            Tweener.addTween(this._lockScreenGroup,
 
611
                             { y: -h,
 
612
                               time: time,
 
613
                               transition: 'linear',
 
614
                               onComplete: function() {
 
615
                                   this._lockScreenState = MessageTray.State.HIDDEN;
 
616
                                   this._lockScreenGroup.hide();
 
617
                               },
 
618
                               onCompleteScope: this,
 
619
                             });
 
620
        } else {
 
621
            this._lockScreenState = MessageTray.State.HIDDEN;
 
622
            this._lockScreenGroup.hide();
 
623
        }
 
624
 
 
625
        if (Main.sessionMode.currentMode == 'lock-screen')
 
626
            Main.sessionMode.popMode('lock-screen');
 
627
    },
 
628
 
 
629
    _ensureUnlockDialog: function() {
 
630
        if (!this._dialog) {
 
631
            let constructor = Main.sessionMode.unlockDialog;
 
632
            this._dialog = new constructor(this._lockDialogGroup);
 
633
            if (!this._dialog) {
 
634
                // This session mode has no locking capabilities
 
635
                this.unlock();
 
636
                return;
 
637
            }
 
638
 
 
639
            this._dialog.connect('loaded', Lang.bind(this, function() {
 
640
                if (!this._dialog.open()) {
 
641
                    log('Could not open login dialog: failed to acquire grab');
 
642
                    this.unlock();
 
643
                }
 
644
            }));
 
645
 
 
646
            this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
 
647
            this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
 
648
        }
 
649
    },
 
650
 
 
651
    _onUnlockFailed: function() {
 
652
        this._resetLockScreen(true, false);
 
653
    },
 
654
 
 
655
    _onUnlockSucceded: function() {
 
656
        this._tweenUnlocked();
 
657
    },
 
658
 
 
659
    _resetLockScreen: function(animateLockScreen, animateLockDialog) {
 
660
        this._ensureLockScreen();
 
661
        this._lockDialogGroup.scale_x = 1;
 
662
        this._lockDialogGroup.scale_y = 1;
 
663
 
 
664
        this._lockScreenGroup.show();
 
665
        this._lockScreenState = MessageTray.State.SHOWING;
 
666
 
 
667
        if (animateLockScreen) {
 
668
            this._lockScreenGroup.y = -global.screen_height;
 
669
            Tweener.removeTweens(this._lockScreenGroup);
 
670
            Tweener.addTween(this._lockScreenGroup,
 
671
                             { y: 0,
 
672
                               time: SHORT_FADE_TIME,
 
673
                               transition: 'linear',
 
674
                               onComplete: function() {
 
675
                                   this._lockScreenShown();
 
676
                               },
 
677
                               onCompleteScope: this
 
678
                             });
 
679
        } else {
 
680
            this._lockScreenGroup.fixed_position_set = false;
 
681
            this._lockScreenShown();
 
682
        }
 
683
 
 
684
        if (animateLockDialog) {
 
685
            this._lockDialogGroup.opacity = 0;
 
686
            Tweener.removeTweens(this._lockDialogGroup);
 
687
            Tweener.addTween(this._lockDialogGroup,
 
688
                             { opacity: 255,
 
689
                               time: SHORT_FADE_TIME,
 
690
                               transition: 'easeOutQuad' });
 
691
        } else {
 
692
            this._lockDialogGroup.opacity = 255;
 
693
        }
 
694
 
 
695
        this._lockScreenGroup.grab_key_focus();
 
696
 
 
697
        if (Main.sessionMode.currentMode != 'lock-screen')
 
698
            Main.sessionMode.pushMode('lock-screen');
 
699
    },
 
700
 
 
701
    _lockScreenShown: function() {
 
702
        if (this._dialog && !this._isGreeter) {
 
703
            this._dialog.destroy();
 
704
            this._dialog = null;
 
705
        }
 
706
 
 
707
        if (this._arrowAnimationId)
 
708
            Mainloop.source_remove(this._arrowAnimationId);
 
709
        this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
 
710
        this._animateArrows();
 
711
 
 
712
        this._lockScreenState = MessageTray.State.SHOWN;
 
713
        this._lockScreenGroup.fixed_position_set = false;
 
714
        this._lockScreenScrollCounter = 0;
 
715
 
 
716
        this.emit('lock-screen-shown');
 
717
    },
 
718
 
 
719
    // Some of the actors in the lock screen are heavy in
 
720
    // resources, so we only create them when needed
 
721
    _ensureLockScreen: function() {
 
722
        if (this._hasLockScreen)
 
723
            return;
 
724
 
 
725
        this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
 
726
                                                         y_align: Clutter.ActorAlign.CENTER,
 
727
                                                         x_expand: true,
 
728
                                                         y_expand: true,
 
729
                                                         vertical: true });
 
730
        this._clock = new Clock();
 
731
        this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
 
732
                                                             y_fill: true });
 
733
 
 
734
        this._lockScreenContents.add_actor(this._lockScreenContentsBox);
 
735
 
 
736
        if (this._settings.get_boolean('show-notifications')) {
 
737
            this._notificationsBox = new NotificationsBox();
 
738
            this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
 
739
                                                                            y_fill: true,
 
740
                                                                            expand: true });
 
741
        }
 
742
 
 
743
        this._hasLockScreen = true;
 
744
    },
 
745
 
 
746
    _clearLockScreen: function() {
 
747
        this._clock.destroy();
 
748
        this._clock = null;
 
749
 
 
750
        if (this._notificationsBox) {
 
751
            this._notificationsBox.destroy();
 
752
            this._notificationsBox = null;
 
753
        }
 
754
 
 
755
        this._lockScreenContentsBox.destroy();
 
756
 
 
757
        if (this._arrowAnimationId) {
 
758
            Mainloop.source_remove(this._arrowAnimationId);
 
759
            this._arrowAnimationId = 0;
 
760
        }
 
761
 
 
762
        this._hasLockScreen = false;
 
763
    },
 
764
 
 
765
    get locked() {
 
766
        return this._isActive;
 
767
    },
 
768
 
 
769
    _tweenUnlocked: function() {
 
770
        this._inUnlockAnimation = true;
 
771
        this.unlock();
 
772
        Tweener.addTween(this._lockDialogGroup, {
 
773
            scale_x: 0,
 
774
            scale_y: 0,
 
775
            time: Overview.ANIMATION_TIME,
 
776
            transition: 'easeOutQuad',
 
777
            onComplete: function() {
 
778
                if (this._dialog) {
 
779
                    this._dialog.destroy();
 
780
                    this._dialog = null;
 
781
                }
 
782
                this.actor.hide();
 
783
                this._inUnlockAnimation = false;
 
784
            },
 
785
            onCompleteScope: this
 
786
        });
 
787
    },
 
788
 
 
789
    unlock: function() {
 
790
        if (this._hasLockScreen)
 
791
            this._clearLockScreen();
 
792
 
 
793
        if (this._dialog && !this._isGreeter) {
 
794
            this._dialog.destroy();
 
795
            this._dialog = null;
 
796
        }
 
797
 
 
798
        this._lightbox.hide();
 
799
 
 
800
        if (this._isModal) {
 
801
            Main.popModal(this.actor);
 
802
            this._isModal = false;
 
803
        }
 
804
 
 
805
        if (!this._inUnlockAnimation)
 
806
            this.actor.hide();
 
807
 
 
808
        if (Main.sessionMode.currentMode == 'lock-screen')
 
809
            Main.sessionMode.popMode('lock-screen');
 
810
        if (Main.sessionMode.currentMode == 'unlock-dialog')
 
811
            Main.sessionMode.popMode('unlock-dialog');
 
812
 
 
813
        this._isActive = false;
 
814
        this.emit('lock-status-changed');
 
815
    },
 
816
 
 
817
    lock: function(animate) {
 
818
        if (!this._isModal) {
 
819
            Main.pushModal(this.actor);
 
820
            this._isModal = true;
 
821
        }
 
822
 
 
823
        this.actor.show();
 
824
 
 
825
        if (Main.sessionMode.currentMode != 'unlock-dialog' &&
 
826
            Main.sessionMode.currentMode != 'lock-screen') {
 
827
            this._isGreeter = Main.sessionMode.isGreeter;
 
828
            if (!this._isGreeter)
 
829
                Main.sessionMode.pushMode('unlock-dialog');
 
830
        }
 
831
 
 
832
        this._resetLockScreen(animate, animate);
 
833
 
 
834
        this._isActive = true;
 
835
        this.emit('lock-status-changed');
 
836
    },
 
837
});
 
838
Signals.addSignalMethods(ScreenShield.prototype);
 
839
 
 
840
/* Fallback code to handle session locking using gnome-screensaver,
 
841
   in case the required GDM dependency is not there
 
842
*/
 
843
const ScreenShieldFallback = new Lang.Class({
 
844
    Name: 'ScreenShieldFallback',
 
845
 
 
846
    _init: function() {
 
847
        this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
 
848
                                          g_name: 'org.gnome.ScreenSaver',
 
849
                                          g_object_path: '/org/gnome/ScreenSaver',
 
850
                                          g_interface_name: 'org.gnome.ScreenSaver',
 
851
                                          g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
 
852
                                                    Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
 
853
                                        });
 
854
        this._proxy.init(null);
 
855
 
 
856
        this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
 
857
        this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
 
858
    },
 
859
 
 
860
    _onNameOwnerChanged: function(object, pspec) {
 
861
        if (this._proxy.g_name_owner)
 
862
            [this._locked] = this._proxy.call_sync('GetActive', null,
 
863
                                                   Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
 
864
        else
 
865
            this._locked = false;
 
866
 
 
867
        this.emit('lock-status-changed', this._locked);
 
868
    },
 
869
 
 
870
    _onSignal: function(proxy, senderName, signalName, params) {
 
871
        if (signalName == 'ActiveChanged') {
 
872
            [this._locked] = params.deep_unpack();
 
873
            this.emit('lock-status-changed', this._locked);
 
874
        }
 
875
    },
 
876
 
 
877
    get locked() {
 
878
        return this._locked;
 
879
    },
 
880
 
 
881
    lock: function() {
 
882
        this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
 
883
                         Lang.bind(this, function(proxy, result) {
 
884
                             proxy.call_finish(result);
 
885
 
 
886
                             this.emit('lock-screen-shown');
 
887
                         }));
 
888
    },
 
889
 
 
890
    unlock: function() {
 
891
        this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
 
892
                         Gio.DBusCallFlags.NONE, -1, null, null);
 
893
    },
 
894
});
 
895
Signals.addSignalMethods(ScreenShieldFallback.prototype);