Compare commits

...

6 Commits

Author SHA1 Message Date
Giovanni Campagna
352ad8f558 Modal stack: fix handling of destroyed actors
Destroyed modal actors should be completely removed from the modal
stack automatically, including leaving modality if needed.
This allows for destroying modal dialogs without calling close().
2012-05-21 18:47:47 +02:00
Giovanni Campagna
d5dc886748 ScreenShield: improve locking/modal policy
Ensure that the lightbox is above everything (including the screenlock
itself) when fading in - this allows for fading while showing the
unlock dialog. Also, don't pushModal again when already locked, or
we won't get out of it.
2012-05-21 18:47:47 +02:00
Giovanni Campagna
17044adf96 Add UnlockDialog for unlocking the screen shield
When the screenshield is deactivated, instead of going back to the
session immediately, prompt the user for authentication.
This essentially reinstates what used to be provided by gnome-screensaver.
2012-05-21 18:47:44 +02:00
Marina Zhurakhinskaya
9dfd805be2 lightbox: make sure this.shown set to false when start showing 2012-05-21 18:47:44 +02:00
Marina Zhurakhinskaya
bbbb6b685f screenShield: add initial functionality
We are replacing the gnome-screensaver module with with a screen shield
that is part of gnome-shell.

This patch fades out the screen on idle and shows a shield with a background
image when there is activity again. The shield can be removed with a key or
button press.
2012-05-21 18:47:40 +02:00
Giovanni Campagna
71c0d5f82d Login dialog: update for GDM port to GDBus
libgdmgreeter changed interface slightly due to the port to GDBus.
Update for that.

https://bugzilla.gnome.org/show_bug.cgi?id=676401
2012-05-20 17:16:17 +02:00
8 changed files with 567 additions and 46 deletions

View File

@ -1715,7 +1715,7 @@ StScrollBar StButton#vhandle:hover
} }
.lightbox { .lightbox {
background-color: rgba(0, 0, 0, 0.4); background-color: black;
} }
.flashspot { .flashspot {

View File

@ -78,6 +78,7 @@ nobase_dist_js_DATA = \
ui/popupMenu.js \ ui/popupMenu.js \
ui/remoteSearch.js \ ui/remoteSearch.js \
ui/runDialog.js \ ui/runDialog.js \
ui/screenShield.js \
ui/scripting.js \ ui/scripting.js \
ui/search.js \ ui/search.js \
ui/searchDisplay.js \ ui/searchDisplay.js \
@ -91,6 +92,7 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \ ui/status/bluetooth.js \
ui/telepathyClient.js \ ui/telepathyClient.js \
ui/tweener.js \ ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \ ui/userMenu.js \
ui/viewSelector.js \ ui/viewSelector.js \
ui/wanda.js \ ui/wanda.js \

View File

@ -39,8 +39,8 @@ const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const _PASSWORD_SERVICE_NAME = 'gdm-password'; const PASSWORD_SERVICE_NAME = 'gdm-password';
const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const _FADE_ANIMATION_TIME = 0.16; const _FADE_ANIMATION_TIME = 0.16;
const _RESIZE_ANIMATION_TIME = 0.25; const _RESIZE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 2.0; const _SCROLL_ANIMATION_TIME = 2.0;
@ -747,15 +747,13 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onOpened)); Lang.bind(this, this._onOpened));
this._userManager = AccountsService.UserManager.get_default() this._userManager = AccountsService.UserManager.get_default()
this._greeterClient = new GdmGreeter.Client(); this._greeterClient = GdmGreeter.Server.new_for_greeter_sync(null);
this._greeterClient.open_connection(); this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null);
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
this._greeterClient.connect('reset', this._greeterClient.connect('reset',
Lang.bind(this, this._onReset)); Lang.bind(this, this._onReset));
this._greeterClient.connect('default-session-changed', this._greeterClient.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged)); Lang.bind(this, this._onDefaultSessionChanged));
this._greeterClient.connect('info', this._greeterClient.connect('info',
Lang.bind(this, this._onInfo)); Lang.bind(this, this._onInfo));
@ -769,8 +767,6 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onSessionOpened)); Lang.bind(this, this._onSessionOpened));
this._greeterClient.connect('timed-login-requested', this._greeterClient.connect('timed-login-requested',
Lang.bind(this, this._onTimedLoginRequested)); Lang.bind(this, this._onTimedLoginRequested));
this._greeterClient.connect('authentication-failed',
Lang.bind(this, this._onAuthenticationFailed));
this._greeterClient.connect('conversation-stopped', this._greeterClient.connect('conversation-stopped',
Lang.bind(this, this._onConversationStopped)); Lang.bind(this, this._onConversationStopped));
@ -899,7 +895,7 @@ const LoginDialog = new Lang.Class({
this._haveFingerprintReader = true; this._haveFingerprintReader = true;
if (this._haveFingerprintReader) if (this._haveFingerprintReader)
this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME); this._greeterClient.call_start_conversation_sync(FINGERPRINT_SERVICE_NAME, null);
})); }));
}, },
@ -918,7 +914,7 @@ const LoginDialog = new Lang.Class({
}, },
_onReset: function(client, serviceName) { _onReset: function(client, serviceName) {
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME); this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null);
this._startFingerprintConversationIfNeeded(); this._startFingerprintConversationIfNeeded();
let tasks = [this._hidePrompt, let tasks = [this._hidePrompt,
@ -954,7 +950,7 @@ const LoginDialog = new Lang.Class({
// We don't display fingerprint messages, because they // We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages // have words like UPEK in them. Instead we use the messages
// as a cue to display our own message. // as a cue to display our own message.
if (serviceName == _FINGERPRINT_SERVICE_NAME && if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader && this._haveFingerprintReader &&
(!this._promptFingerprintMessage.visible || (!this._promptFingerprintMessage.visible ||
this._promptFingerprintMessage.opacity != 255)) { this._promptFingerprintMessage.opacity != 255)) {
@ -963,7 +959,7 @@ const LoginDialog = new Lang.Class({
return; return;
} }
if (serviceName != _PASSWORD_SERVICE_NAME) if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
Main.notifyError(info); Main.notifyError(info);
}, },
@ -971,13 +967,13 @@ const LoginDialog = new Lang.Class({
_onProblem: function(client, serviceName, problem) { _onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to // we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint. // users who haven't enrolled their fingerprint.
if (serviceName != _PASSWORD_SERVICE_NAME) if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
Main.notifyError(problem); Main.notifyError(problem);
}, },
_onCancel: function(client) { _onCancel: function(client) {
this._greeterClient.call_cancel(); this._greeterClient.call_cancel_sync(null);
}, },
_fadeInPrompt: function() { _fadeInPrompt: function() {
@ -1084,7 +1080,7 @@ const LoginDialog = new Lang.Class({
let _text = this._promptEntry.get_text(); let _text = this._promptEntry.get_text();
this._promptEntry.reactive = false; this._promptEntry.reactive = false;
this._promptEntry.add_style_pseudo_class('insensitive'); this._promptEntry.add_style_pseudo_class('insensitive');
this._greeterClient.call_answer_query(serviceName, _text); this._greeterClient.call_answer_query_sync(serviceName, _text, null);
}]; }];
let batch = new Batch.ConsecutiveBatch(this, tasks); let batch = new Batch.ConsecutiveBatch(this, tasks);
@ -1092,7 +1088,7 @@ const LoginDialog = new Lang.Class({
}, },
_onInfoQuery: function(client, serviceName, question) { _onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service // We only expect questions to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME) if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
this._promptEntry.set_text(''); this._promptEntry.set_text('');
@ -1102,7 +1098,7 @@ const LoginDialog = new Lang.Class({
_onSecretInfoQuery: function(client, serviceName, secretQuestion) { _onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service // We only expect secret requests to come from the main auth service
if (serviceName != _PASSWORD_SERVICE_NAME) if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
this._promptEntry.set_text(''); this._promptEntry.set_text('');
@ -1111,7 +1107,7 @@ const LoginDialog = new Lang.Class({
}, },
_onSessionOpened: function(client, serviceName) { _onSessionOpened: function(client, serviceName) {
this._greeterClient.call_start_session_when_ready(serviceName, true); this._greeterClient.call_start_session_when_ready_sync(serviceName, true, null);
}, },
_waitForItemForUser: function(userName) { _waitForItemForUser: function(userName) {
@ -1193,7 +1189,7 @@ const LoginDialog = new Lang.Class({
function() { function() {
this._timedLoginBatch = null; this._timedLoginBatch = null;
this._greeterClient.call_begin_auto_login(userName); this._greeterClient.call_begin_auto_login_sync(userName, null);
}]; }];
this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks); this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
@ -1236,17 +1232,13 @@ const LoginDialog = new Lang.Class({
})); }));
}, },
_onAuthenticationFailed: function(client) {
this._greeterClient.call_cancel();
},
_onConversationStopped: function(client, serviceName) { _onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything. // if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give // But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed // password authentication a chance to succeed
if (serviceName == _PASSWORD_SERVICE_NAME) { if (serviceName == PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel(); this._greeterClient.call_cancel_sync(null);
} else if (serviceName == _FINGERPRINT_SERVICE_NAME) { } else if (serviceName == FINGERPRINT_SERVICE_NAME) {
_fadeOutActor(this._promptFingerprintMessage); _fadeOutActor(this._promptFingerprintMessage);
} }
}, },
@ -1269,7 +1261,7 @@ const LoginDialog = new Lang.Class({
this._fadeOutLogo]), this._fadeOutLogo]),
function() { function() {
this._greeterClient.call_begin_verification(_PASSWORD_SERVICE_NAME); this._greeterClient.call_begin_verification_sync(PASSWORD_SERVICE_NAME, null);
}]; }];
let batch = new Batch.ConsecutiveBatch(this, tasks); let batch = new Batch.ConsecutiveBatch(this, tasks);
@ -1328,11 +1320,11 @@ const LoginDialog = new Lang.Class({
function() { function() {
let userName = activatedItem.user.get_user_name(); let userName = activatedItem.user.get_user_name();
this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME, this._greeterClient.call_begin_verification_for_user_sync(PASSWORD_SERVICE_NAME,
userName); userName, null);
if (this._haveFingerprintReader) if (this._haveFingerprintReader)
this._greeterClient.call_begin_verification_for_user(_FINGERPRINT_SERVICE_NAME, userName); this._greeterClient.call_begin_verification_for_user_sync(_FINGERPRINT_SERVICE_NAME, userName, null);
}]; }];
this._user = activatedItem.user; this._user = activatedItem.user;

View File

@ -8,6 +8,8 @@ const St = imports.gi.St;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const DEFAULT_FADE_FACTOR = 0.4;
/** /**
* Lightbox: * Lightbox:
* @container: parent Clutter.Container * @container: parent Clutter.Container
@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener;
* - inhibitEvents: whether to inhibit events for @container * - inhibitEvents: whether to inhibit events for @container
* - width: shade actor width * - width: shade actor width
* - height: shade actor height * - height: shade actor height
* - fadeTime: seconds used to fade in/out * - fadeInTime: seconds used to fade in
* - fadeOutTime: seconds used to fade out
* *
* Lightbox creates a dark translucent "shade" actor to hide the * Lightbox creates a dark translucent "shade" actor to hide the
* contents of @container, and allows you to specify particular actors * contents of @container, and allows you to specify particular actors
@ -38,12 +41,16 @@ const Lightbox = new Lang.Class({
params = Params.parse(params, { inhibitEvents: false, params = Params.parse(params, { inhibitEvents: false,
width: null, width: null,
height: null, height: null,
fadeTime: null fadeInTime: null,
fadeOutTime: null,
fadeFactor: DEFAULT_FADE_FACTOR
}); });
this._container = container; this._container = container;
this._children = container.get_children(); this._children = container.get_children();
this._fadeTime = params.fadeTime; this._fadeInTime = params.fadeInTime;
this._fadeOutTime = params.fadeOutTime;
this._fadeFactor = params.fadeFactor;
this.actor = new St.Bin({ x: 0, this.actor = new St.Bin({ x: 0,
y: 0, y: 0,
style_class: 'lightbox', style_class: 'lightbox',
@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({
container.add_actor(this.actor); container.add_actor(this.actor);
this.actor.raise_top(); this.actor.raise_top();
this.actor.hide(); this.actor.hide();
this.shown = false;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({
}, },
show: function() { show: function() {
if (this._fadeTime) { if (this._fadeInTime) {
this.shown = false;
this.actor.opacity = 0; this.actor.opacity = 0;
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 255, { opacity: 255 * this._fadeFactor,
time: this._fadeTime, time: this._fadeInTime,
transition: 'easeOutQuad' transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.shown = true;
})
}); });
} else { } else {
this.actor.opacity = 255; this.actor.opacity = 255 * this._fadeFactor;
this.shown = true;
} }
this.actor.show(); this.actor.show();
}, },
hide: function() { hide: function() {
if (this._fadeTime) { this.shown = false;
if (this._fadeOutTime) {
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 0, { opacity: 0,
time: this._fadeTime, time: this._fadeOutTime,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
this.actor.hide(); this.actor.hide();

View File

@ -29,6 +29,7 @@ const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent; const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon; const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler; const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting; const Scripting = imports.ui.scripting;
const ShellDBus = imports.ui.shellDBus; const ShellDBus = imports.ui.shellDBus;
const TelepathyClient = imports.ui.telepathyClient; const TelepathyClient = imports.ui.telepathyClient;
@ -51,6 +52,7 @@ let runDialog = null;
let lookingGlass = null; let lookingGlass = null;
let wm = null; let wm = null;
let messageTray = null; let messageTray = null;
let screenShield = null;
let notificationDaemon = null; let notificationDaemon = null;
let windowAttentionHandler = null; let windowAttentionHandler = null;
let telepathyClient = null; let telepathyClient = null;
@ -216,6 +218,7 @@ function start() {
panel = new Panel.Panel(); panel = new Panel.Panel();
wm = new WindowManager.WindowManager(); wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
screenShield = new ScreenShield.ScreenShield();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon(); notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
@ -491,8 +494,8 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet }); let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (global.session_type == Shell.SessionType.GDM) // FIXME: merge back into main stylesheet
theme.load_stylesheet(_gdmCssStylesheet); theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) { if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets(); let customStylesheets = previousTheme.get_custom_stylesheets();
@ -621,6 +624,10 @@ function _findModal(actor) {
return -1; return -1;
} }
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/** /**
* pushModal: * pushModal:
* @actor: #ClutterActor which will be given keyboard focus * @actor: #ClutterActor which will be given keyboard focus
@ -661,7 +668,7 @@ function pushModal(actor, timestamp, options) {
let actorDestroyId = actor.connect('destroy', function() { let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor); let index = _findModal(actor);
if (index >= 0) if (index >= 0)
modalActorFocusStack.splice(index, 1); popModal(actor);
}); });
let curFocus = global.stage.get_key_focus(); let curFocus = global.stage.get_key_focus();
let curFocusDestroyId; let curFocusDestroyId;

142
js/ui/screenShield.js Normal file
View File

@ -0,0 +1,142 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const UnlockDialog = imports.ui.unlockDialog;
const Main = imports.ui.main;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
/**
* To test screen shield, make sure to kill gnome-screensaver.
*
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
* rather than through System Settings, you also need to set
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
* This will ensure that the screen blanks at the right time when it fades out.
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
*/
const ScreenShield = new Lang.Class({
Name: 'ScreenShield',
_init: function() {
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
this._onStatusChanged(proxy.status);
}));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._isModal = false;
this._isLocked = false;
this._group = new St.Widget({ x: 0,
y: 0 });
Main.uiGroup.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
this._group.add_constraint(constraint);
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 });
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background.hide();
Main.uiGroup.add_actor(this._background);
},
_onStatusChanged: function(status) {
log ("in _onStatusChanged");
if (status == GnomeSession.PresenceStatus.IDLE) {
log("session gone idle");
if (this._dialog) {
log('canceling existing dialog');
this._dialog.cancel();
this._dialog = null;
}
this._group.reactive = true;
if (!this._isModal) {
Main.pushModal(this._group);
this._isModal = true;
}
this._group.raise_top();
this._lightbox.show();
} else {
log('status is now ' + status);
let lightboxWasShown = this._lightbox.shown;
log("this._lightbox.shown " + this._lightbox.shown);
this._lightbox.hide();
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
if (shouldLock || this._isLocked) {
this._isLocked = true;
this._background.show();
this._background.raise_top();
this._showUnlockDialog();
} else if (this._isModal) {
this._popModal();
}
}
},
_popModal: function() {
this._group.reactive = false;
Main.popModal(this._group);
this._background.hide();
},
_showUnlockDialog: function() {
if (this._dialog) {
log ('_showUnlockDialog called again when the dialog was already there');
return;
}
try {
this._dialog = new UnlockDialog.UnlockDialog();
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
if (!this._dialog.open(global.get_current_time()))
throw new Error('open failed!')
this._dialog._group.raise_top();
} catch(e) {
// FIXME: this is for debugging purposes
logError(e, 'error while creating unlock dialog');
this._popModal();
}
},
_onUnlockFailed: function() {
// FIXME: for now, on failure we just destroy the dialog and create a new
// one (this is what gnome-screensaver does)
// in the future, we may want to go back to the lock screen instead
this._dialog.destroy();
this._dialog = null;
this._showUnlockDialog();
},
_onUnlockSucceded: function() {
this._dialog.destroy();
this._dialog = null;
this._popModal();
},
});

363
js/ui/unlockDialog.js Normal file
View File

@ -0,0 +1,363 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const GdmGreeter = imports.gi.GdmGreeter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const ModalDialog = imports.ui.modalDialog;
const Fprint = imports.gdm.fingerprint;
const GdmLoginDialog = imports.gdm.loginDialog;
function _fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible)
return;
actor.show();
let [minHeight, naturalHeight] = actor.get_preferred_height(-1);
actor.opacity = 0;
actor.set_height(0);
Tweener.addTween(actor,
{ opacity: 255,
height: naturalHeight,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.set_height(-1);
},
});
}
function _fadeOutActor(actor) {
if (!actor.visible || actor.opacity == 0) {
actor.opacity = 0;
actor.hide();
}
Tweener.addTween(actor,
{ opacity: 0,
height: 0,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
this.hide();
this.set_height(-1);
},
});
}
// A widget showing the user avatar and name
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'status-chooser',
vertical: false,
reactive: false
});
this._iconBin = new St.Bin({ style_class: 'status-chooser-user-icon' });
this.actor.add(this._iconBin,
{ x_fill: true,
y_fill: true });
this._label = new St.Label({ style_class: 'login-dialog-prompt-label',
// FIXME:
style: 'text-align: right' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: true
});
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this,
this._updateUser));
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._updateUser();
}));
},
destroy: function() {
// clean up signal handlers
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.actor.destroy();
},
_updateUser: function() {
let iconFile = null;
if (this._user.is_loaded) {
this._label.text = this._user.get_real_name();
iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
} else {
this._label.text = "";
}
if (iconFile)
this._setIconFromFile(iconFile);
else
this._setIconFromName('avatar-default');
},
// XXX: a GFileIcon instead?
_setIconFromFile: function(iconFile) {
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
this._iconBin.child = null;
},
_setIconFromName: function(iconName) {
this._iconBin.set_style(null);
if (iconName != null) {
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: DIALOG_ICON_SIZE });
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
}
});
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function() {
this.parent({ shellReactive: true,
styleClass: 'login-dialog' });
this._userManager = AccountsService.UserManager.get_default();
this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName);
this._greeterClient = GdmGreeter.Server.new_for_display_sync(null, null);
this._greeterClient.call_start_conversation_sync(GdmLoginDialog.PASSWORD_SERVICE_NAME, null);
this._greeterClient.connect('reset',
Lang.bind(this, this._onReset));
this._greeterClient.connect('ready',
Lang.bind(this, this._onReady));
this._greeterClient.connect('info',
Lang.bind(this, this._onInfo));
this._greeterClient.connect('problem',
Lang.bind(this, this._onProblem));
this._greeterClient.connect('info-query',
Lang.bind(this, this._onInfoQuery));
this._greeterClient.connect('secret-info-query',
Lang.bind(this, this._onSecretInfoQuery));
this._greeterClient.connect('session-opened',
Lang.bind(this, this._onSessionOpened));
this._greeterClient.connect('conversation-stopped',
Lang.bind(this, this._onConversationStopped));
this._fprintManager = new Fprint.FprintManager();
this._startFingerprintConversationIfNeeded();
this._userWidget = new UserWidget(this._user);
this.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: false
});
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptLayout.add(this._promptLabel,
{ expand: false,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptLayout.add(this._promptEntry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this.contentLayout.add_actor(this._promptLayout);
// Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead
this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"),
style_class: 'login-dialog-prompt-fingerprint-message' });
this._promptFingerprintMessage.hide();
this.contentLayout.add_actor(this._promptFingerprintMessage);
this._okButton = { label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
key: Clutter.KEY_Return,
};
this.setButtons([this._okButton]);
this._updateOkButton(true);
},
_updateOkButton: function(sensitive) {
this._okButton.button.reactive = sensitive;
this._okButton.button.can_focus = sensitive;
if (sensitive)
this._okButton.button.remove_style_pseudo_class('disabled');
else
this._okButton.button.add_style_pseudo_class('disabled');
},
_onReset: function() {
// I'm not sure this is emitted for external greeters...
this._greeterClient.call_start_conversation_sync(GdmLoginDialog.PASSWORD_SERVICE_NAME, null);
this._startFingerprintConversationIfNeeded();
},
_startFingerprintConversationIfNeeded: function() {
this._haveFingerprintReader = false;
// FIXME: the greeter has a GSettings key for disabling fingerprint auth
this._fprintManager.GetDefaultDeviceRemote(Lang.bind(this,
function(device, error) {
if (!error && device)
this._haveFingerprintReader = true;
if (this._haveFingerprintReader)
this._greeterClient.call_start_conversation_sync(GdmLoginDialog.FINGERPRINT_SERVICE_NAME, null);
}));
},
_onReady: function(greeter, serviceName) {
greeter.call_begin_verification_for_user_sync(serviceName, this._userName, null);
},
_onInfo: function(greeter, serviceName, info) {
// We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == GdmLoginDialog.FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader &&
(!this._promptFingerprintMessage.visible ||
this._promptFingerprintMessage.opacity != 255)) {
_fadeInActor(this._promptFingerprintMessage);
return;
}
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
Main.notify(info);
},
_onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
Main.notifyError(problem);
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
this._promptLabel.text = question;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('');
this._currentQuery = serviceName;
this._updateOkButton(true);
},
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME)
return;
this._promptLabel.text = secretQuestion;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('\u25cf');
this._currentQuery = serviceName;
this._updateOkButton(true);
},
_doUnlock: function() {
if (!this._currentQuery)
return;
let query = this._currentQuery;
this._currentQuery = null;
this._updateOkButton(false);
this._greeterClient.call_answer_query_sync(query, this._promptEntry.text, null);
},
_onConversationStopped: function(client, serviceName) {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (serviceName == GdmLoginDialog.PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel_sync(null);
this.emit('failed');
} else if (serviceName == GdmLoginDialog.FINGERPRINT_SERVICE_NAME) {
_fadeOutActor(this._promptFingerprintMessage);
}
},
_onSessionOpened: function(client, serviceName) {
// For external greeters, SessionOpened means we succeded
// in the authentication process
// Close the greeter proxy
this._greeterClient.run_dispose();
this._greeterClient = null;
this.emit('unlocked');
},
destroy: function() {
if (this._greeterClient) {
this._greeterClient.run_dispose();
this._greeterClient = null;
}
this.parent();
},
cancel: function() {
this._greeterClient.call_cancel_sync(null);
this.destroy();
},
});

View File

@ -301,7 +301,8 @@ const WindowClone = new Lang.Class({
if (!this._zoomLightbox) if (!this._zoomLightbox)
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup, this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
{ fadeTime: LIGHTBOX_FADE_TIME }); { fadeInTime: LIGHTBOX_FADE_TIME,
fadeOutTime: LIGHTBOX_FADE_TIME });
this._zoomLightbox.show(); this._zoomLightbox.show();
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y); this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);