[gnome-shell/wip/screen-shield-rebase1: 4/6] Add UnlockDialog for unlocking the screen shield
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/screen-shield-rebase1: 4/6] Add UnlockDialog for unlocking the screen shield
- Date: Mon, 21 May 2012 16:49:21 +0000 (UTC)
commit 17044adf96bfb92beae576ada23d9ae9af534489
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Sun May 20 18:30:14 2012 +0200
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.
js/Makefile.am | 1 +
js/gdm/loginDialog.js | 28 ++--
js/ui/main.js | 4 +-
js/ui/screenShield.js | 34 ++++-
js/ui/unlockDialog.js | 363 +++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 406 insertions(+), 24 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index a422abc..826b054 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -92,6 +92,7 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
+ ui/unlockDialog.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/wanda.js \
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
index f49230d..a6a02df 100644
--- a/js/gdm/loginDialog.js
+++ b/js/gdm/loginDialog.js
@@ -39,8 +39,8 @@ const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
-const _PASSWORD_SERVICE_NAME = 'gdm-password';
-const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
+const PASSWORD_SERVICE_NAME = 'gdm-password';
+const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const _FADE_ANIMATION_TIME = 0.16;
const _RESIZE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 2.0;
@@ -749,7 +749,7 @@ const LoginDialog = new Lang.Class({
this._userManager = AccountsService.UserManager.get_default()
this._greeterClient = GdmGreeter.Server.new_for_greeter_sync(null);
- this._greeterClient.call_start_conversation_sync(_PASSWORD_SERVICE_NAME, null);
+ this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null);
this._greeterClient.connect('reset',
Lang.bind(this, this._onReset));
@@ -895,7 +895,7 @@ const LoginDialog = new Lang.Class({
this._haveFingerprintReader = true;
if (this._haveFingerprintReader)
- this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME);
+ this._greeterClient.call_start_conversation_sync(FINGERPRINT_SERVICE_NAME, null);
}));
},
@@ -914,7 +914,7 @@ const LoginDialog = new Lang.Class({
},
_onReset: function(client, serviceName) {
- this._greeterClient.call_start_conversation_sync(_PASSWORD_SERVICE_NAME, null);
+ this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null);
this._startFingerprintConversationIfNeeded();
let tasks = [this._hidePrompt,
@@ -950,7 +950,7 @@ const LoginDialog = new Lang.Class({
// 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 == _FINGERPRINT_SERVICE_NAME &&
+ if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader &&
(!this._promptFingerprintMessage.visible ||
this._promptFingerprintMessage.opacity != 255)) {
@@ -959,7 +959,7 @@ const LoginDialog = new Lang.Class({
return;
}
- if (serviceName != _PASSWORD_SERVICE_NAME)
+ if (serviceName != PASSWORD_SERVICE_NAME)
return;
Main.notifyError(info);
},
@@ -967,7 +967,7 @@ const LoginDialog = new Lang.Class({
_onProblem: function(client, serviceName, problem) {
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
- if (serviceName != _PASSWORD_SERVICE_NAME)
+ if (serviceName != PASSWORD_SERVICE_NAME)
return;
Main.notifyError(problem);
},
@@ -1088,7 +1088,7 @@ const LoginDialog = new Lang.Class({
},
_onInfoQuery: function(client, serviceName, question) {
// We only expect questions to come from the main auth service
- if (serviceName != _PASSWORD_SERVICE_NAME)
+ if (serviceName != PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
@@ -1098,7 +1098,7 @@ const LoginDialog = new Lang.Class({
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
// We only expect secret requests to come from the main auth service
- if (serviceName != _PASSWORD_SERVICE_NAME)
+ if (serviceName != PASSWORD_SERVICE_NAME)
return;
this._promptEntry.set_text('');
@@ -1236,9 +1236,9 @@ const LoginDialog = new Lang.Class({
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
- if (serviceName == _PASSWORD_SERVICE_NAME) {
+ if (serviceName == PASSWORD_SERVICE_NAME) {
this._greeterClient.call_cancel_sync(null);
- } else if (serviceName == _FINGERPRINT_SERVICE_NAME) {
+ } else if (serviceName == FINGERPRINT_SERVICE_NAME) {
_fadeOutActor(this._promptFingerprintMessage);
}
},
@@ -1261,7 +1261,7 @@ const LoginDialog = new Lang.Class({
this._fadeOutLogo]),
function() {
- this._greeterClient.call_begin_verification_sync(_PASSWORD_SERVICE_NAME, null);
+ this._greeterClient.call_begin_verification_sync(PASSWORD_SERVICE_NAME, null);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
@@ -1320,7 +1320,7 @@ const LoginDialog = new Lang.Class({
function() {
let userName = activatedItem.user.get_user_name();
- this._greeterClient.call_begin_verification_for_user_sync(_PASSWORD_SERVICE_NAME,
+ this._greeterClient.call_begin_verification_for_user_sync(PASSWORD_SERVICE_NAME,
userName, null);
if (this._haveFingerprintReader)
diff --git a/js/ui/main.js b/js/ui/main.js
index 30e3f6d..8942de2 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -494,8 +494,8 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
- if (global.session_type == Shell.SessionType.GDM)
- theme.load_stylesheet(_gdmCssStylesheet);
+ // FIXME: merge back into main stylesheet
+ theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index be3315e..6602bc5 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -8,7 +8,7 @@ const St = imports.gi.St;
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
-const LoginDialog = imports.gdm.loginDialog;
+const UnlockDialog = imports.ui.unlockDialog;
const Main = imports.ui.main;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
@@ -43,8 +43,7 @@ const ScreenShield = new Lang.Class({
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
this._group.add_constraint(constraint);
- this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
- this._group.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
+
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 });
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
@@ -66,6 +65,8 @@ const ScreenShield = new Lang.Class({
if (lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY)) {
this._background.show();
this._background.raise_top();
+
+ this._showUnlockDialog();
} else {
this._popModal();
}
@@ -79,13 +80,30 @@ const ScreenShield = new Lang.Class({
this._background.hide();
},
- _onKeyPressEvent: function(object, keyPressEvent) {
- log("in _onKeyPressEvent - lock is enabled: " + this._settings.get_boolean(LOCK_ENABLED_KEY));
- this._popModal();
+ _showUnlockDialog: function() {
+ if (this._dialog)
+ return;
+
+ this._dialog = new UnlockDialog.UnlockDialog();
+ this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
+ this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
+
+ this._dialog.open(global.get_current_time());
+ },
+
+ _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();
},
- _onButtonPressEvent: function(object, buttonPressEvent) {
- log("in _onButtonPressEvent - lock is enabled: " + this._settings.get_boolean(LOCK_ENABLED_KEY));
+ _onUnlockSucceded: function() {
+ this._dialog.destroy();
this._popModal();
},
});
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
new file mode 100644
index 0000000..66b7328
--- /dev/null
+++ b/js/ui/unlockDialog.js
@@ -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();
+ },
+});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]