gnome-shell/js/ui/unlockDialog.js
Ray Strode ceee53aa0a unlockDialog: Properly reset auth prompt when showing it
If a user hits escape twice really fast when coming back to
their machine to unlock it, they made end up getting presented
with a non-functional unlock screen that doesn't show their
user icon and doesn't ask for a password.

This is because showPrompt assumes that if an auth prompt already
exists, it's ready to go. That may not be true, if it's in the
process of getting torn down at the time because it's in the middle
of a cancel animation.

This commit solves the problem by ensuring the auth prompt is always
in a fresh reset state before showing it.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1999>
2021-10-08 11:56:23 -04:00

910 lines
28 KiB
JavaScript

// -*- 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._sources = new Map();
Main.messageTray.getSources().forEach(source => {
this._sourceAdded(Main.messageTray, source, true);
});
this._updateVisibility();
this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
}
_onDestroy() {
if (this._sourceAddedId) {
Main.messageTray.disconnect(this._sourceAddedId);
this._sourceAddedId = 0;
}
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.toString(),
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>%s</b> %s'.format(n.title, 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);
}
_sourceAdded(tray, source, initial) {
let obj = {
visible: source.policy.showInLockScreen,
detailed: this._shouldShowDetails(source),
sourceDestroyId: 0,
sourceCountChangedId: 0,
sourceTitleChangedId: 0,
sourceUpdatedId: 0,
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);
obj.sourceCountChangedId = source.connect('notify::count', () => {
this._countChanged(source, obj);
});
obj.sourceTitleChangedId = source.connect('notify::title', () => {
this._titleChanged(source, obj);
});
obj.policyChangedId = source.policy.connect('notify', (policy, pspec) => {
if (pspec.name === 'show-in-lock-screen')
this._visibleChanged(source, obj);
else
this._detailedChanged(source, obj);
});
obj.sourceDestroyId = source.connect('destroy', () => {
this._onSourceDestroy(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();
if (obj.sourceBox.visible)
this.emit('wake-up-screen');
}
}
_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.toString();
obj.countLabel.visible = count > 1;
}
obj.sourceBox.visible = obj.visible && (source.unseenCount > 0);
this._updateVisibility();
if (obj.sourceBox.visible)
this.emit('wake-up-screen');
}
_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();
if (obj.sourceBox.visible)
this.emit('wake-up-screen');
}
_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);
}
_onSourceDestroy(source, obj) {
this._removeSource(source, obj);
this._updateVisibility();
}
_removeSource(source, obj) {
obj.sourceBox.destroy();
obj.sourceBox = obj.titleLabel = obj.countLabel = null;
source.disconnect(obj.sourceDestroyId);
source.disconnect(obj.sourceCountChangedId);
source.disconnect(obj.sourceTitleChangedId);
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._touchModeChangedId = this._seat.connect('notify::touch-mode',
this._updateHint.bind(this));
this._monitorManager = Meta.MonitorManager.get();
this._powerModeChangedId = this._monitorManager.connect(
'power-save-mode-changed', () => (this._hint.opacity = 0));
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._seat.disconnect(this._touchModeChangedId);
this._idleMonitor.remove_watch(this._idleWatchId);
this._monitorManager.disconnect(this._powerModeChangedId);
}
});
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();
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);
this._scaleChangedId = themeContext.connect('notify::scale-factor',
() => this._updateBackgroundEffects());
this._updateBackgrounds();
this._monitorsChangedId =
Main.layoutManager.connect('monitors-changed', this._updateBackgrounds.bind(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'),
reactive: false,
opacity: 0,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
child: new St.Icon({ 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._userSwitchEnabledId = this._screenSaverSettings.connect('changed::user-switch-enabled',
this._updateUserSwitchVisibility.bind(this));
this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
this._lockdownSettings.connect('changed::disable-user-switching',
this._updateUserSwitchVisibility.bind(this));
this._userLoadedId = this._user.connect('notify::is-loaded',
this._updateUserSwitchVisibility.bind(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;
}
_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._monitorsChangedId) {
Main.layoutManager.disconnect(this._monitorsChangedId);
delete this._monitorsChangedId;
}
let themeContext = St.ThemeContext.get_for_stage(global.stage);
if (this._scaleChangedId) {
themeContext.disconnect(this._scaleChangedId);
delete this._scaleChangedId;
}
if (this._gdmClient) {
this._gdmClient = null;
delete this._gdmClient;
}
if (this._userLoadedId) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userSwitchEnabledId) {
this._screenSaverSettings.disconnect(this._userSwitchEnabledId);
this._userSwitchEnabledId = 0;
}
}
_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,
};
if (!Main.pushModal(this, modalParams))
return false;
this._isModal = true;
return true;
}
activate() {
this._showPrompt();
}
popModal(timestamp) {
if (this._isModal) {
Main.popModal(this, timestamp);
this._isModal = false;
}
}
});