From d097327bd820eb6fe612fbd730f2fab3394634ac Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Mon, 18 Mar 2013 00:59:56 -0400 Subject: [PATCH] loginDialog,unlockDialog: Give user time to read messages Right now, if multiple messages come in, they just sort of clobber each other. This commit sets up a message queue, and introduces pauses long enough for the user to hopefully be able to read those messages. https://bugzilla.gnome.org/show_bug.cgi?id=694688 --- js/gdm/loginDialog.js | 14 +++++- js/gdm/util.js | 114 ++++++++++++++++++++++++++++++++++++------ js/ui/unlockDialog.js | 16 +++++- 3 files changed, 127 insertions(+), 17 deletions(-) diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js index 0abd233cd..77bd2eb0e 100644 --- a/js/gdm/loginDialog.js +++ b/js/gdm/loginDialog.js @@ -930,7 +930,7 @@ const LoginDialog = new Lang.Class({ return batch.run(); }, - _onSessionOpened: function(client, serviceName) { + _startSession: function(serviceName) { Tweener.addTween(this.dialogLayout, { opacity: 0, time: _FADE_ANIMATION_TIME, @@ -953,6 +953,18 @@ const LoginDialog = new Lang.Class({ onCompleteScope: this }); }, + _onSessionOpened: function(client, serviceName) { + if (!this._userVerifier.hasPendingMessages) { + this._startSession(serviceName); + } else { + let signalId = this._userVerifier.connect('no-more-messages', + Lang.bind(this, function() { + this._userVerifier.disconnect(signalId); + this._startSession(serviceName); + })); + } + }, + _waitForItemForUser: function(userName) { let item = this._userList.getItemFromUserName(userName); diff --git a/js/gdm/util.js b/js/gdm/util.js index 4de22e416..36ee4abed 100644 --- a/js/gdm/util.js +++ b/js/gdm/util.js @@ -2,6 +2,7 @@ const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; const Lang = imports.lang; const Mainloop = imports.mainloop; const Signals = imports.signals; @@ -27,6 +28,9 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures'; const LOGO_KEY = 'logo'; const DISABLE_USER_LIST_KEY = 'disable-user-list'; +// Give user 16ms to read each character of a PAM message +const USER_READ_TIME = 16 + function fadeInActor(actor) { if (actor.opacity == 255 && actor.visible) return null; @@ -114,6 +118,9 @@ const ShellUserVerifier = new Lang.Class({ this._fprintManager = new Fprint.FprintManager(); this._realmManager = new Realmd.Manager(); + this._messageQueue = []; + this._messageQueueTimeoutId = 0; + this.hasPendingMessages = false; this._failCounter = 0; }, @@ -153,13 +160,73 @@ const ShellUserVerifier = new Lang.Class({ this._userVerifier.run_dispose(); this._userVerifier = null; } + + this._clearMessageQueue(); }, answerQuery: function(serviceName, answer) { - // Clear any previous message - this.emit('show-message', null, null); + if (!this._userVerifier.hasPendingMessages) { + this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); + } else { + let signalId = this._userVerifier.connect('no-more-messages', + Lang.bind(this, function() { + this._userVerifier.disconnect(signalId); + this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); + })); + } + }, - this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); + _getIntervalForMessage: function(message) { + // We probably could be smarter here + return message.length * USER_READ_TIME; + }, + + finishMessageQueue: function() { + if (!this.hasPendingMessages) + return; + + this._messageQueue = []; + + this.hasPendingMessages = false; + this.emit('no-more-messages'); + }, + + _queueMessageTimeout: function() { + if (this._messageQueue.length == 0) { + this.finishMessageQueue(); + return; + } + + if (this._messageQueueTimeoutId != 0) + return; + + let message = this._messageQueue.shift(); + this.emit('show-message', message.text, message.iconName); + + this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, + message.interval, + Lang.bind(this, function() { + this._messageQueueTimeoutId = 0; + this._queueMessageTimeout(); + })); + }, + + _queueMessage: function(message, iconName) { + let interval = this._getIntervalForMessage(message); + + this.hasPendingMessages = true; + this._messageQueue.push({ text: message, interval: interval, iconName: iconName }); + this._queueMessageTimeout(); + }, + + _clearMessageQueue: function() { + this.finishMessageQueue(); + + if (this._messageQueueTimeoutId != 0) { + GLib.source_remove(this._messageQueueTimeoutId); + this._messageQueueTimeoutId = 0; + } + this.emit('show-message', null, null); }, _checkForFingerprintReader: function() { @@ -179,7 +246,7 @@ const ShellUserVerifier = new Lang.Class({ logError(error, where); this._hold.release(); - this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning'); + this._queueMessage(_("Authentication error"), 'login-dialog-message-warning'); this._verificationFailed(false); }, @@ -298,7 +365,7 @@ const ShellUserVerifier = new Lang.Class({ // to indicate the user can swipe their finger instead this.emit('show-login-hint', _("(or swipe finger)")); } else if (serviceName == PASSWORD_SERVICE_NAME) { - this.emit('show-message', info, 'login-dialog-message-info'); + this._queueMessage(info, 'login-dialog-message-info'); } }, @@ -307,7 +374,7 @@ const ShellUserVerifier = new Lang.Class({ // users who haven't enrolled their fingerprint. if (serviceName != PASSWORD_SERVICE_NAME) return; - this.emit('show-message', problem, 'login-dialog-message-warning'); + this._queueMessage(problem, 'login-dialog-message-warning'); }, _showRealmLoginHint: function() { @@ -356,6 +423,15 @@ const ShellUserVerifier = new Lang.Class({ this.emit('verification-complete'); }, + _cancelAndReset: function() { + this.cancel(); + this._onReset(); + }, + + _retry: function() { + this.begin(this._userName, new Batch.Hold()); + }, + _verificationFailed: function(retry) { // For Not Listed / enterprise logins, immediately reset // the dialog @@ -367,15 +443,25 @@ const ShellUserVerifier = new Lang.Class({ this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY); if (canRetry) { - this.clear(); - this.begin(this._userName, new Batch.Hold()); + if (!this._userVerifier.hasPendingMessages) { + this._retry(); + } else { + let signalId = this._userVerifier.connect('no-more-messages', + Lang.bind(this, function() { + this._userVerifier.disconnect(signalId); + this._retry(); + })); + } } else { - // Allow some time to see the message, then reset everything - Mainloop.timeout_add(3000, Lang.bind(this, function() { - this.cancel(); - - this._onReset(); - })); + if (!this._userVerifier.hasPendingMessages) { + this._cancelAndReset(); + } else { + let signalId = this._userVerifier.connect('no-more-messages', + Lang.bind(this, function() { + this._userVerifier.disconnect(signalId); + this._cancelAndReset(); + })); + } } this.emit('verification-failed'); diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js index f9227a34c..727df6175 100644 --- a/js/ui/unlockDialog.js +++ b/js/ui/unlockDialog.js @@ -250,12 +250,24 @@ const UnlockDialog = new Lang.Class({ this._userVerifier.answerQuery(query, this._promptEntry.text); }, - _onVerificationComplete: function() { - this._userVerified = true; + _finishUnlock: function() { this._userVerifier.clear(); this.emit('unlocked'); }, + _onVerificationComplete: function() { + this._userVerified = true; + if (!this._userVerifier.hasPendingMessages) { + this._finishUnlock(); + } else { + let signalId = this._userVerifier.connect('no-more-messages', + Lang.bind(this, function() { + this._userVerifier.disconnect(signalId); + this._finishUnlock(); + })); + } + }, + _onReset: function() { if (!this._userVerified) { this._userVerifier.clear();