diff --git a/data/theme/gdm.css b/data/theme/gdm.css deleted file mode 100644 index daaa57546..000000000 --- a/data/theme/gdm.css +++ /dev/null @@ -1,188 +0,0 @@ -/* Copyright 2011, Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU Lesser General Public License, - * version 2.1, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* 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-button { - padding-top: 2em; -} -.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: white; -} - -.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; -} diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index b3dcb821e..588117258 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 cd4136b3b..cf2353c81 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 1b963e302..b83debbfa 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 8c259302b..dff6c8422 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 000000000..4a849c7f7 --- /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(); + }, +});