diff options
Diffstat (limited to 'js/ui/unlockDialog.js')
-rw-r--r-- | js/ui/unlockDialog.js | 899 |
1 files changed, 899 insertions, 0 deletions
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js new file mode 100644 index 0000000..24c8160 --- /dev/null +++ b/js/ui/unlockDialog.js @@ -0,0 +1,899 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* exported UnlockDialog */ + +const { + AccountsService, Atk, Clutter, Gdm, Gio, + GnomeDesktop, GLib, GObject, Meta, Shell, St, +} = imports.gi; + +const Background = imports.ui.background; +const Layout = imports.ui.layout; +const Main = imports.ui.main; +const MessageTray = imports.ui.messageTray; +const SwipeTracker = imports.ui.swipeTracker; + +const AuthPrompt = imports.gdm.authPrompt; + +// The timeout before going back automatically to the lock screen (in seconds) +const IDLE_TIMEOUT = 2 * 60; + +// The timeout before showing the unlock hint (in seconds) +const HINT_TIMEOUT = 4; + +const CROSSFADE_TIME = 300; +const FADE_OUT_TRANSLATION = 200; +const FADE_OUT_SCALE = 0.3; + +const BLUR_BRIGHTNESS = 0.55; +const BLUR_SIGMA = 60; + +const SUMMARY_ICON_SIZE = 32; + +var NotificationsBox = GObject.registerClass({ + Signals: { 'wake-up-screen': {} }, +}, class NotificationsBox extends St.BoxLayout { + _init() { + super._init({ + vertical: true, + name: 'unlockDialogNotifications', + style_class: 'unlock-dialog-notifications-container', + }); + + this._scrollView = new St.ScrollView({ hscrollbar_policy: St.PolicyType.NEVER }); + this._notificationBox = new St.BoxLayout({ + vertical: true, + style_class: 'unlock-dialog-notifications-container', + }); + this._scrollView.add_actor(this._notificationBox); + + this.add_child(this._scrollView); + + this._settings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.notifications', + }); + + this._sources = new Map(); + Main.messageTray.getSources().forEach(source => { + this._sourceAdded(Main.messageTray, source, true); + }); + this._updateVisibility(); + + Main.messageTray.connectObject('source-added', + this._sourceAdded.bind(this), this); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + let items = this._sources.entries(); + for (let [source, obj] of items) + this._removeSource(source, obj); + } + + _updateVisibility() { + this._notificationBox.visible = + this._notificationBox.get_children().some(a => a.visible); + + this.visible = this._notificationBox.visible; + } + + _makeNotificationSource(source, box) { + let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); + box.add_child(sourceActor); + + let textBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + box.add_child(textBox); + + let title = new St.Label({ + text: source.title, + style_class: 'unlock-dialog-notification-label', + x_expand: true, + x_align: Clutter.ActorAlign.START, + }); + textBox.add(title); + + let count = source.unseenCount; + let countLabel = new St.Label({ + text: `${count}`, + visible: count > 1, + style_class: 'unlock-dialog-notification-count-text', + }); + textBox.add(countLabel); + + box.visible = count !== 0; + return [title, countLabel]; + } + + _makeNotificationDetailedSource(source, box) { + let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); + let sourceBin = new St.Bin({ child: sourceActor }); + box.add(sourceBin); + + let textBox = new St.BoxLayout({ vertical: true }); + box.add_child(textBox); + + let title = new St.Label({ + text: source.title.replace(/\n/g, ' '), + style_class: 'unlock-dialog-notification-label', + }); + textBox.add(title); + + let visible = false; + for (let i = 0; i < source.notifications.length; i++) { + let n = source.notifications[i]; + + if (n.acknowledged) + continue; + + let body = ''; + if (n.bannerBodyText) { + const bodyText = n.bannerBodyText.replace(/\n/g, ' '); + body = n.bannerBodyMarkup + ? bodyText + : GLib.markup_escape_text(bodyText, -1); + } + + let label = new St.Label({ style_class: 'unlock-dialog-notification-count-text' }); + label.clutter_text.set_markup(`<b>${n.title}</b> ${body}`); + textBox.add(label); + + visible = true; + } + + box.visible = visible; + return [title, null]; + } + + _shouldShowDetails(source) { + return source.policy.detailsInLockScreen || + source.narrowestPrivacyScope === MessageTray.PrivacyScope.SYSTEM; + } + + _updateSourceBoxStyle(source, obj, box) { + let hasCriticalNotification = + source.notifications.some(n => n.urgency === MessageTray.Urgency.CRITICAL); + + if (hasCriticalNotification !== obj.hasCriticalNotification) { + obj.hasCriticalNotification = hasCriticalNotification; + + if (hasCriticalNotification) + box.add_style_class_name('critical'); + else + box.remove_style_class_name('critical'); + } + } + + _showSource(source, obj, box) { + if (obj.detailed) + [obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box); + else + [obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box); + + box.visible = obj.visible && (source.unseenCount > 0); + + this._updateSourceBoxStyle(source, obj, box); + } + + _wakeUpScreenForSource(source) { + if (!this._settings.get_boolean('show-banners')) + return; + const obj = this._sources.get(source); + if (obj?.sourceBox.visible) + this.emit('wake-up-screen'); + } + + _sourceAdded(tray, source, initial) { + let obj = { + visible: source.policy.showInLockScreen, + detailed: this._shouldShowDetails(source), + sourceBox: null, + titleLabel: null, + countLabel: null, + hasCriticalNotification: false, + }; + + obj.sourceBox = new St.BoxLayout({ + style_class: 'unlock-dialog-notification-source', + x_expand: true, + }); + this._showSource(source, obj, obj.sourceBox); + this._notificationBox.add_child(obj.sourceBox); + + source.connectObject( + 'notify::count', () => this._countChanged(source, obj), + 'notify::title', () => this._titleChanged(source, obj), + 'destroy', () => { + this._removeSource(source, obj); + this._updateVisibility(); + }, this); + obj.policyChangedId = source.policy.connect('notify', (policy, pspec) => { + if (pspec.name === 'show-in-lock-screen') + this._visibleChanged(source, obj); + else + this._detailedChanged(source, obj); + }); + + this._sources.set(source, obj); + + if (!initial) { + // block scrollbars while animating, if they're not needed now + let boxHeight = this._notificationBox.height; + if (this._scrollView.height >= boxHeight) + this._scrollView.vscrollbar_policy = St.PolicyType.NEVER; + + let widget = obj.sourceBox; + let [, natHeight] = widget.get_preferred_height(-1); + widget.height = 0; + widget.ease({ + height: natHeight, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + duration: 250, + onComplete: () => { + this._scrollView.vscrollbar_policy = St.PolicyType.AUTOMATIC; + widget.set_height(-1); + }, + }); + + this._updateVisibility(); + this._wakeUpScreenForSource(source); + } + } + + _titleChanged(source, obj) { + obj.titleLabel.text = source.title; + } + + _countChanged(source, obj) { + // A change in the number of notifications may change whether we show + // details. + let newDetailed = this._shouldShowDetails(source); + let oldDetailed = obj.detailed; + + obj.detailed = newDetailed; + + if (obj.detailed || oldDetailed !== newDetailed) { + // A new notification was pushed, or a previous notification was destroyed. + // Give up, and build the list again. + + obj.sourceBox.destroy_all_children(); + obj.titleLabel = obj.countLabel = null; + this._showSource(source, obj, obj.sourceBox); + } else { + let count = source.unseenCount; + obj.countLabel.text = `${count}`; + obj.countLabel.visible = count > 1; + } + + obj.sourceBox.visible = obj.visible && (source.unseenCount > 0); + + this._updateVisibility(); + this._wakeUpScreenForSource(source); + } + + _visibleChanged(source, obj) { + if (obj.visible === source.policy.showInLockScreen) + return; + + obj.visible = source.policy.showInLockScreen; + obj.sourceBox.visible = obj.visible && source.unseenCount > 0; + + this._updateVisibility(); + this._wakeUpScreenForSource(source); + } + + _detailedChanged(source, obj) { + let newDetailed = this._shouldShowDetails(source); + if (obj.detailed === newDetailed) + return; + + obj.detailed = newDetailed; + + obj.sourceBox.destroy_all_children(); + obj.titleLabel = obj.countLabel = null; + this._showSource(source, obj, obj.sourceBox); + } + + _removeSource(source, obj) { + obj.sourceBox.destroy(); + obj.sourceBox = obj.titleLabel = obj.countLabel = null; + + source.policy.disconnect(obj.policyChangedId); + + this._sources.delete(source); + } +}); + +var Clock = GObject.registerClass( +class UnlockDialogClock extends St.BoxLayout { + _init() { + super._init({ style_class: 'unlock-dialog-clock', vertical: true }); + + this._time = new St.Label({ + style_class: 'unlock-dialog-clock-time', + x_align: Clutter.ActorAlign.CENTER, + }); + this._date = new St.Label({ + style_class: 'unlock-dialog-clock-date', + x_align: Clutter.ActorAlign.CENTER, + }); + this._hint = new St.Label({ + style_class: 'unlock-dialog-clock-hint', + x_align: Clutter.ActorAlign.CENTER, + opacity: 0, + }); + + this.add_child(this._time); + this.add_child(this._date); + this.add_child(this._hint); + + this._wallClock = new GnomeDesktop.WallClock({ time_only: true }); + this._wallClock.connect('notify::clock', this._updateClock.bind(this)); + + this._seat = Clutter.get_default_backend().get_default_seat(); + this._seat.connectObject('notify::touch-mode', + this._updateHint.bind(this), this); + + this._monitorManager = Meta.MonitorManager.get(); + this._monitorManager.connectObject('power-save-mode-changed', + () => (this._hint.opacity = 0), this); + + this._idleMonitor = global.backend.get_core_idle_monitor(); + this._idleWatchId = this._idleMonitor.add_idle_watch(HINT_TIMEOUT * 1000, () => { + this._hint.ease({ + opacity: 255, + duration: CROSSFADE_TIME, + }); + }); + + this._updateClock(); + this._updateHint(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _updateClock() { + this._time.text = this._wallClock.clock; + + let date = new Date(); + /* Translators: This is a time format for a date in + long format */ + let dateFormat = Shell.util_translate_time_string(N_('%A %B %-d')); + this._date.text = date.toLocaleFormat(dateFormat); + } + + _updateHint() { + this._hint.text = this._seat.touch_mode + ? _('Swipe up to unlock') + : _('Click or press a key to unlock'); + } + + _onDestroy() { + this._wallClock.run_dispose(); + + this._idleMonitor.remove_watch(this._idleWatchId); + } +}); + +var UnlockDialogLayout = GObject.registerClass( +class UnlockDialogLayout extends Clutter.LayoutManager { + _init(stack, notifications, switchUserButton) { + super._init(); + + this._stack = stack; + this._notifications = notifications; + this._switchUserButton = switchUserButton; + } + + vfunc_get_preferred_width(container, forHeight) { + return this._stack.get_preferred_width(forHeight); + } + + vfunc_get_preferred_height(container, forWidth) { + return this._stack.get_preferred_height(forWidth); + } + + vfunc_allocate(container, box) { + let [width, height] = box.get_size(); + + let tenthOfHeight = height / 10.0; + let thirdOfHeight = height / 3.0; + + let [, , stackWidth, stackHeight] = + this._stack.get_preferred_size(); + + let [, , notificationsWidth, notificationsHeight] = + this._notifications.get_preferred_size(); + + let columnWidth = Math.max(stackWidth, notificationsWidth); + + let columnX1 = Math.floor((width - columnWidth) / 2.0); + let actorBox = new Clutter.ActorBox(); + + // Notifications + let maxNotificationsHeight = Math.min( + notificationsHeight, + height - tenthOfHeight - stackHeight); + + actorBox.x1 = columnX1; + actorBox.y1 = height - maxNotificationsHeight; + actorBox.x2 = columnX1 + columnWidth; + actorBox.y2 = actorBox.y1 + maxNotificationsHeight; + + this._notifications.allocate(actorBox); + + // Authentication Box + let stackY = Math.min( + thirdOfHeight, + height - stackHeight - maxNotificationsHeight); + + actorBox.x1 = columnX1; + actorBox.y1 = stackY; + actorBox.x2 = columnX1 + columnWidth; + actorBox.y2 = stackY + stackHeight; + + this._stack.allocate(actorBox); + + // Switch User button + if (this._switchUserButton.visible) { + let [, , natWidth, natHeight] = + this._switchUserButton.get_preferred_size(); + + const textDirection = this._switchUserButton.get_text_direction(); + if (textDirection === Clutter.TextDirection.RTL) + actorBox.x1 = box.x1 + natWidth; + else + actorBox.x1 = box.x2 - (natWidth * 2); + + actorBox.y1 = box.y2 - (natHeight * 2); + actorBox.x2 = actorBox.x1 + natWidth; + actorBox.y2 = actorBox.y1 + natHeight; + + this._switchUserButton.allocate(actorBox); + } + } +}); + +var UnlockDialog = GObject.registerClass({ + Signals: { + 'failed': {}, + 'wake-up-screen': {}, + }, +}, class UnlockDialog extends St.Widget { + _init(parentActor) { + super._init({ + accessible_role: Atk.Role.WINDOW, + style_class: 'unlock-dialog', + visible: false, + reactive: true, + }); + + parentActor.add_child(this); + + this._gdmClient = new Gdm.Client(); + + try { + this._gdmClient.set_enabled_extensions([ + Gdm.UserVerifierChoiceList.interface_info().name, + ]); + } catch (e) { + } + + this._adjustment = new St.Adjustment({ + actor: this, + lower: 0, + upper: 2, + page_size: 1, + page_increment: 1, + }); + this._adjustment.connect('notify::value', () => { + this._setTransitionProgress(this._adjustment.value); + }); + + this._swipeTracker = new SwipeTracker.SwipeTracker(this, + Clutter.Orientation.VERTICAL, + Shell.ActionMode.UNLOCK_SCREEN); + this._swipeTracker.connect('begin', this._swipeBegin.bind(this)); + this._swipeTracker.connect('update', this._swipeUpdate.bind(this)); + this._swipeTracker.connect('end', this._swipeEnd.bind(this)); + + this.connect('scroll-event', (o, event) => { + if (this._swipeTracker.canHandleScrollEvent(event)) + return Clutter.EVENT_PROPAGATE; + + let direction = event.get_scroll_direction(); + if (direction === Clutter.ScrollDirection.UP) + this._showClock(); + else if (direction === Clutter.ScrollDirection.DOWN) + this._showPrompt(); + return Clutter.EVENT_STOP; + }); + + this._activePage = null; + + let tapAction = new Clutter.TapAction(); + tapAction.connect('tap', this._showPrompt.bind(this)); + this.add_action(tapAction); + + // Background + this._backgroundGroup = new Clutter.Actor(); + this.add_child(this._backgroundGroup); + + this._bgManagers = []; + + const themeContext = St.ThemeContext.get_for_stage(global.stage); + themeContext.connectObject('notify::scale-factor', + () => this._updateBackgroundEffects(), this); + + this._updateBackgrounds(); + Main.layoutManager.connectObject('monitors-changed', + this._updateBackgrounds.bind(this), this); + + this._userManager = AccountsService.UserManager.get_default(); + this._userName = GLib.get_user_name(); + this._user = this._userManager.get_user(this._userName); + + // Authentication & Clock stack + this._stack = new Shell.Stack(); + + this._promptBox = new St.BoxLayout({ vertical: true }); + this._promptBox.set_pivot_point(0.5, 0.5); + this._promptBox.hide(); + this._stack.add_child(this._promptBox); + + this._clock = new Clock(); + this._clock.set_pivot_point(0.5, 0.5); + this._stack.add_child(this._clock); + this._showClock(); + + this.allowCancel = false; + + Main.ctrlAltTabManager.addGroup(this, _('Unlock Window'), 'dialog-password-symbolic'); + + // Notifications + this._notificationsBox = new NotificationsBox(); + this._notificationsBox.connect('wake-up-screen', () => this.emit('wake-up-screen')); + + // Switch User button + this._otherUserButton = new St.Button({ + style_class: 'modal-dialog-button button switch-user-button', + accessible_name: _('Log in as another user'), + button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, + reactive: false, + opacity: 0, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.END, + icon_name: 'system-users-symbolic', + }); + this._otherUserButton.set_pivot_point(0.5, 0.5); + this._otherUserButton.connect('clicked', this._otherUserClicked.bind(this)); + + this._screenSaverSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.screensaver' }); + + this._screenSaverSettings.connectObject('changed::user-switch-enabled', + this._updateUserSwitchVisibility.bind(this), this); + + this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' }); + this._lockdownSettings.connect('changed::disable-user-switching', + this._updateUserSwitchVisibility.bind(this)); + + this._user.connectObject('notify::is-loaded', + this._updateUserSwitchVisibility.bind(this), this); + + this._updateUserSwitchVisibility(); + + // Main Box + let mainBox = new St.Widget(); + mainBox.add_constraint(new Layout.MonitorConstraint({ primary: true })); + mainBox.add_child(this._stack); + mainBox.add_child(this._notificationsBox); + mainBox.add_child(this._otherUserButton); + mainBox.layout_manager = new UnlockDialogLayout( + this._stack, + this._notificationsBox, + this._otherUserButton); + this.add_child(mainBox); + + this._idleMonitor = global.backend.get_core_idle_monitor(); + this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, this._escape.bind(this)); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + vfunc_key_press_event(keyEvent) { + if (this._activePage === this._promptBox || + (this._promptBox && this._promptBox.visible)) + return Clutter.EVENT_PROPAGATE; + + const { keyval } = keyEvent; + if (keyval === Clutter.KEY_Shift_L || + keyval === Clutter.KEY_Shift_R || + keyval === Clutter.KEY_Shift_Lock || + keyval === Clutter.KEY_Caps_Lock) + return Clutter.EVENT_PROPAGATE; + + let unichar = keyEvent.unicode_value; + + this._showPrompt(); + + if (GLib.unichar_isgraph(unichar)) + this._authPrompt.addCharacter(unichar); + + return Clutter.EVENT_PROPAGATE; + } + + vfunc_captured_event(event) { + if (Main.keyboard.maybeHandleEvent(event)) + return Clutter.EVENT_STOP; + + return Clutter.EVENT_PROPAGATE; + } + + _createBackground(monitorIndex) { + let monitor = Main.layoutManager.monitors[monitorIndex]; + let widget = new St.Widget({ + style_class: 'screen-shield-background', + x: monitor.x, + y: monitor.y, + width: monitor.width, + height: monitor.height, + effect: new Shell.BlurEffect({ name: 'blur' }), + }); + + let bgManager = new Background.BackgroundManager({ + container: widget, + monitorIndex, + controlPosition: false, + }); + + this._bgManagers.push(bgManager); + + this._backgroundGroup.add_child(widget); + } + + _updateBackgroundEffects() { + const themeContext = St.ThemeContext.get_for_stage(global.stage); + + for (const widget of this._backgroundGroup) { + const effect = widget.get_effect('blur'); + + if (effect) { + effect.set({ + brightness: BLUR_BRIGHTNESS, + sigma: BLUR_SIGMA * themeContext.scale_factor, + }); + } + } + } + + _updateBackgrounds() { + for (let i = 0; i < this._bgManagers.length; i++) + this._bgManagers[i].destroy(); + + this._bgManagers = []; + this._backgroundGroup.destroy_all_children(); + + for (let i = 0; i < Main.layoutManager.monitors.length; i++) + this._createBackground(i); + this._updateBackgroundEffects(); + } + + _ensureAuthPrompt() { + if (!this._authPrompt) { + this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient, + AuthPrompt.AuthPromptMode.UNLOCK_ONLY); + this._authPrompt.connect('failed', this._fail.bind(this)); + this._authPrompt.connect('cancelled', this._fail.bind(this)); + this._authPrompt.connect('reset', this._onReset.bind(this)); + this._promptBox.add_child(this._authPrompt); + } + + this._authPrompt.reset(); + this._authPrompt.updateSensitivity(true); + } + + _maybeDestroyAuthPrompt() { + let focus = global.stage.key_focus; + if (focus === null || + (this._authPrompt && this._authPrompt.contains(focus)) || + (this._otherUserButton && focus === this._otherUserButton)) + this.grab_key_focus(); + + if (this._authPrompt) { + this._authPrompt.destroy(); + this._authPrompt = null; + } + } + + _showClock() { + if (this._activePage === this._clock) + return; + + this._activePage = this._clock; + + this._adjustment.ease(0, { + duration: CROSSFADE_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => this._maybeDestroyAuthPrompt(), + }); + } + + _showPrompt() { + this._ensureAuthPrompt(); + + if (this._activePage === this._promptBox) + return; + + this._activePage = this._promptBox; + + this._adjustment.ease(1, { + duration: CROSSFADE_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + _setTransitionProgress(progress) { + this._promptBox.visible = progress > 0; + this._clock.visible = progress < 1; + + this._otherUserButton.set({ + reactive: progress > 0, + can_focus: progress > 0, + }); + + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); + + this._promptBox.set({ + opacity: 255 * progress, + scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress, + scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress, + translation_y: FADE_OUT_TRANSLATION * (1 - progress) * scaleFactor, + }); + + this._clock.set({ + opacity: 255 * (1 - progress), + scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * (1 - progress), + scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * (1 - progress), + translation_y: -FADE_OUT_TRANSLATION * progress * scaleFactor, + }); + + this._otherUserButton.set({ + opacity: 255 * progress, + scale_x: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress, + scale_y: FADE_OUT_SCALE + (1 - FADE_OUT_SCALE) * progress, + }); + } + + _fail() { + this._showClock(); + this.emit('failed'); + } + + _onReset(authPrompt, beginRequest) { + let userName; + if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { + this._authPrompt.setUser(this._user); + userName = this._userName; + } else { + userName = null; + } + + this._authPrompt.begin({ userName }); + } + + _escape() { + if (this._authPrompt && this.allowCancel) + this._authPrompt.cancel(); + } + + _swipeBegin(tracker, monitor) { + if (monitor !== Main.layoutManager.primaryIndex) + return; + + this._adjustment.remove_transition('value'); + + this._ensureAuthPrompt(); + + let progress = this._adjustment.value; + tracker.confirmSwipe(this._stack.height, + [0, 1], + progress, + Math.round(progress)); + } + + _swipeUpdate(tracker, progress) { + this._adjustment.value = progress; + } + + _swipeEnd(tracker, duration, endProgress) { + this._activePage = endProgress + ? this._promptBox + : this._clock; + + this._adjustment.ease(endProgress, { + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, + duration, + onComplete: () => { + if (this._activePage === this._clock) + this._maybeDestroyAuthPrompt(); + }, + }); + } + + _otherUserClicked() { + Gdm.goto_login_session_sync(null); + + this._authPrompt.cancel(); + } + + _onDestroy() { + this.popModal(); + + if (this._idleWatchId) { + this._idleMonitor.remove_watch(this._idleWatchId); + this._idleWatchId = 0; + } + + if (this._gdmClient) { + this._gdmClient = null; + delete this._gdmClient; + } + } + + _updateUserSwitchVisibility() { + this._otherUserButton.visible = this._userManager.can_switch() && + this._screenSaverSettings.get_boolean('user-switch-enabled') && + !this._lockdownSettings.get_boolean('disable-user-switching'); + } + + cancel() { + if (this._authPrompt) + this._authPrompt.cancel(); + } + + finish(onComplete) { + if (!this._authPrompt) { + onComplete(); + return; + } + + this._authPrompt.finish(onComplete); + } + + open(timestamp) { + this.show(); + + if (this._isModal) + return true; + + let modalParams = { + timestamp, + actionMode: Shell.ActionMode.UNLOCK_SCREEN, + }; + let grab = Main.pushModal(Main.uiGroup, modalParams); + if (grab.get_seat_state() !== Clutter.GrabState.ALL) { + Main.popModal(grab); + return false; + } + + this._grab = grab; + this._isModal = true; + + return true; + } + + activate() { + this._showPrompt(); + } + + popModal(timestamp) { + if (this._isModal) { + Main.popModal(this._grab, timestamp); + this._grab = null; + this._isModal = false; + } + } +}); |