[gnome-shell] Add UnlockDialog for unlocking the screen shield



commit 2c3ec7846fa563831aea4ef695ec3c16e89e1054
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.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=619955

 data/theme/gdm.css         |  188 -------------------------------------
 data/theme/gnome-shell.css |  186 +++++++++++++++++++++++++++++++++++++
 js/Makefile.am             |    1 +
 js/ui/screenShield.js      |   36 ++++++--
 js/ui/sessionMode.js       |    2 +-
 js/ui/unlockDialog.js      |  221 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 437 insertions(+), 197 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index b3dcb82..5881172 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -2008,3 +2008,189 @@ StScrollBar StButton#vhandle:hover
     border-radius: 4px;
     background-color: rgba(255,255,255,0.33);
 }
+
+/* Login Dialog */
+
+.login-dialog-banner {
+    font-size: 10pt;
+    font-weight: bold;
+    text-align: center;
+    color: #666666;
+    padding-bottom: 1em;
+}
+
+.login-dialog-title {
+    font-size: 14pt;
+    font-weight: bold;
+    color: #666666;
+    padding-bottom: 2em;
+}
+
+.login-dialog {
+    border-radius: 16px;
+    min-height: 150px;
+    max-height: 700px;
+    min-width: 350px;
+}
+
+.login-dialog-prompt-fingerprint-message {
+    font-size: 10.5pt;
+}
+
+.login-dialog-user-list-view {
+    -st-vfade-offset: 1em;
+}
+
+.login-dialog-user-list {
+    spacing: 12px;
+}
+
+.login-dialog-user-list-item {
+    color: #666666;
+}
+
+.login-dialog-user-list-item:ltr {
+    padding-right: 1em;
+}
+
+.login-dialog-user-list-item:rtl {
+    padding-left: 1em;
+}
+
+.login-dialog-user-list-item .login-dialog-user-list-item-name {
+    font-size: 20pt;
+    padding-left: 1em;
+    color: #666666;
+}
+
+.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
+    color: white;
+}
+
+.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
+    color: white;
+    text-shadow: black 0px 2px 2px;
+}
+
+.login-dialog-user-list-item-vertical-layout {
+    spacing: 2px;
+}
+
+.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
+    background-color: rgba(0,0,0,0.0);
+    height: 2px;
+}
+
+.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
+    background-color: #666666;
+}
+
+.login-dialog-user-list-item-icon {
+    border: 2px solid #8b8b8b;
+    border-radius: 8px;
+    width: 64px;
+    height: 64px;
+}
+
+.login-dialog-not-listed-label {
+    font-size: 14pt;
+    font-weight: bold;
+    color: #666666;
+}
+
+.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
+    color: #E8E8E8;
+}
+
+.login-dialog-prompt-layout {
+    padding-bottom: 32px;
+}
+.login-dialog-prompt-label {
+    color: white;
+    font-size: 20pt;
+}
+
+.login-dialog-prompt-entry {
+    padding: 4px;
+    border-radius: 4px;
+    border: 2px solid #5656cc;
+    color: black;
+    background-color: white;
+    caret-color: black;
+    caret-size: 1px;
+    width: 15em;
+}
+
+.login-dialog-prompt-entry .capslock-warning {
+    icon-size: 16px;
+    warning-color: #999;
+}
+
+.login-dialog-prompt-entry:insensitive {
+    color: rgba(0,0,0,0.7);
+    border: 2px solid #565656;
+}
+
+.login-dialog-session-list {
+    color: #ffffff;
+    font-size: 10.5pt;
+}
+
+.login-dialog-session-list-button {
+    padding: 4px;
+}
+
+.login-dialog-session-list-button:focus {
+    background-color: #4c4c4c;
+}
+
+.login-dialog-session-list-button:active {
+    background-color: #4c4c4c;
+}
+
+.login-dialog-session-list-button:hover {
+    font-weight: bold;
+}
+
+.login-dialog-session-list-scroll-view {
+    background-gradient-start: rgba(80,80,80,0.3);
+    background-gradient-end: rgba(80,80,80,0.7);
+    background-gradient-direction: vertical;
+    box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
+    border-radius: 8px;
+    border: 1px solid rgba(80,80,80,1.0);
+    padding: .5em;
+}
+
+.login-dialog-session-list-item:focus {
+    background-color: #666666;
+}
+
+.login-dialog-session-list-triangle {
+    padding-right: .5em;
+}
+
+.login-dialog-session-list-item-box {
+    spacing: .25em;
+}
+
+.login-dialog-session-list-item-dot {
+    width: .75em;
+    height: .75em;
+}
+
+.unlock-dialog-user-name {
+    padding: 4px;
+    border-radius: 4px;
+    border: 2px solid #5656cc;
+    color: black;
+    background-color: white;
+    caret-color: black;
+    caret-size: 1px;
+    width: 15em;
+    text-align: right;
+}
+
+.unlock-dialog-user-name-container {
+    spacing: .4em;
+}
diff --git a/js/Makefile.am b/js/Makefile.am
index cd4136b..cf2353c 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -91,6 +91,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/ui/screenShield.js b/js/ui/screenShield.js
index 1b963e3..b83debb 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';
@@ -48,8 +48,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);
@@ -71,6 +70,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();
             }
@@ -84,13 +85,32 @@ 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());
     },
 
-    _onButtonPressEvent: function(object, buttonPressEvent) {
-        log("in _onButtonPressEvent - lock is enabled: " + this._settings.get_boolean(LOCK_ENABLED_KEY));
+    _onUnlockFailed: function() {
+        // 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();
     },
 });
diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
index 8c25930..dff6c84 100644
--- a/js/ui/sessionMode.js
+++ b/js/ui/sessionMode.js
@@ -39,7 +39,7 @@ const _modes = {
              hasRunDialog: false,
              hasWorkspaces: false,
              createSession: Main.createGDMSession,
-             extraStylesheet: global.datadir + '/theme/gdm.css',
+             extraStylesheet: null,
              statusArea: {
                  order: [
                      'a11y', 'display', 'keyboard',
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
new file mode 100644
index 0000000..4a849c7
--- /dev/null
+++ b/js/ui/unlockDialog.js
@@ -0,0 +1,221 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const AccountsService = imports.gi.AccountsService;
+const Clutter = imports.gi.Clutter;
+const Gdm = imports.gi.Gdm;
+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 Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+const Tweener = imports.ui.tweener;
+const UserMenu = imports.ui.userMenu;
+
+const Batch = imports.gdm.batch;
+const GdmUtil = imports.gdm.util;
+
+// 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: 'unlock-dialog-user-name-container',
+                                        vertical: false });
+
+        this._avatar = new UserMenu.UserAvatarWidget(user);
+        this.actor.add(this._avatar.actor,
+                       { x_fill: true, y_fill: true });
+
+        this._label = new St.Label({ style_class: 'unlock-dialog-user-name' });
+        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));
+        if (this._user.is_loaded)
+            this._updateUser();
+    },
+
+    destroy: function() {
+        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() {
+        if (this._user.is_loaded)
+            this._label.text = this._user.get_real_name();
+        else
+            this._label.text = '';
+
+        this._avatar.update();
+    }
+});
+
+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 = new Gdm.Client();
+        this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
+
+        this._userVerifier.connect('reset', Lang.bind(this, this._reset));
+        this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
+        this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
+        this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
+
+        this._userVerifier.connect('show-fingerprint-prompt', Lang.bind(this, this._showFingerprintPrompt));
+        this._userVerifier.connect('hide-fingerprint-prompt', Lang.bind(this, this._hideFingerprintPrompt));
+
+        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 });
+        ShellEntry.addContextMenu(this._promptEntry);
+        this.setInitialKeyFocus(this._promptEntry);
+        this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
+
+        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);
+
+        let otherUserLabel = new St.Label({ text: _("Login as another user"),
+                                            style_class: 'login-dialog-not-listed-label' });
+        this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
+                                                can_focus: true,
+                                                child: otherUserLabel,
+                                                reactive: true,
+                                                x_align: St.Align.START,
+                                                x_fill: true });
+        this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
+        this.contentLayout.add(this._otherUserButton,
+                               { x_align: St.Align.START,
+                                 x_fill: false });
+
+        this._okButton = { label: _("Unlock"),
+                           action: Lang.bind(this, this._doUnlock),
+                           default: true };
+        this.setButtons([this._okButton]);
+
+        this._updateOkButton(false);
+        this._reset();
+    },
+
+    _updateOkButton: function(sensitive) {
+        this._okButton.button.reactive = sensitive;
+    },
+
+    _reset: function() {
+        this._userVerifier.begin(this._userName, new Batch.Hold());
+    },
+
+    _onAskQuestion: function(verifier, serviceName, question, passwordChar) {
+        this._promptLabel.text = question;
+
+        this._promptEntry.text = '';
+        this._promptEntry.clutter_text.set_password_char(passwordChar);
+        this._promptEntry.menu.isPassword = passwordChar != '';
+
+        this._currentQuery = serviceName;
+        this._updateOkButton(true);
+    },
+
+    _showFingerprintPrompt: function() {
+        GdmUtil.fadeInActor(this._promptFingerprintMessage);
+    },
+
+    _hideFingerprintPrompt: function() {
+        GdmUtil.fadeOutActor(this._promptFingerprintMessage);
+    },
+
+    _doUnlock: function() {
+        if (!this._currentQuery)
+            return;
+
+        let query = this._currentQuery;
+        this._currentQuery = null;
+
+        this._updateOkButton(false);
+
+        this._userVerifier.answerQuery(query, this._promptEntry.text);
+    },
+
+    _onVerificationComplete: function() {
+        this._userVerifier.clear();
+        this.emit('unlocked');
+    },
+
+    _onVerificationFailed: function() {
+        this._userVerifier.cancel();
+        this.emit('failed');
+    },
+
+    _otherUserClicked: function(button, event) {
+        this._userManager.goto_login_session();
+
+        this._userVerifier.cancel();
+        this.emit('failed');
+    },
+
+    destroy: function() {
+        this._userVerifier.clear();
+        this.parent();
+    },
+
+    cancel: function() {
+        this._userVerifier.cancel(null);
+
+        this.destroy();
+    },
+});



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]