1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
3
const AccountsService = imports.gi.AccountsService;
4
const Clutter = imports.gi.Clutter;
5
const Gdm = imports.gi.Gdm;
6
const Gio = imports.gi.Gio;
7
const GLib = imports.gi.GLib;
8
const Gtk = imports.gi.Gtk;
9
const Lang = imports.lang;
10
const Signals = imports.signals;
11
const Shell = imports.gi.Shell;
12
const St = imports.gi.St;
14
const Main = imports.ui.main;
15
const ModalDialog = imports.ui.modalDialog;
16
const ShellEntry = imports.ui.shellEntry;
17
const Tweener = imports.ui.tweener;
18
const UserMenu = imports.ui.userMenu;
20
const Batch = imports.gdm.batch;
21
const GdmUtil = imports.gdm.util;
23
// The timeout before going back automatically to the lock screen (in seconds)
24
const IDLE_TIMEOUT = 2 * 60;
26
function versionCompare(required, reference) {
27
required = required.split('.');
28
reference = reference.split('.');
30
for (let i = 0; i < required.length; i++) {
31
if (required[i] != reference[i])
32
return required[i] < reference[i];
38
function isSupported() {
40
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
41
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
42
'/org/gnome/DisplayManager/Manager',
43
'org.freedesktop.DBus.Properties',
45
Gio.DBusCallFlags.NONE,
48
let version = result.deep_unpack()[0].deep_unpack();
49
return versionCompare('3.5.91', version);
55
// A widget showing the user avatar and name
56
const UserWidget = new Lang.Class({
59
_init: function(user) {
62
this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
65
this._avatar = new UserMenu.UserAvatarWidget(user);
66
this.actor.add(this._avatar.actor,
67
{ x_fill: true, y_fill: true });
69
this._label = new St.Label({ style_class: 'login-dialog-username' });
70
this.actor.add(this._label,
74
y_align: St.Align.MIDDLE });
76
this._userLoadedId = this._user.connect('notify::is-loaded',
77
Lang.bind(this, this._updateUser));
78
this._userChangedId = this._user.connect('changed',
79
Lang.bind(this, this._updateUser));
80
if (this._user.is_loaded)
85
if (this._userLoadedId != 0) {
86
this._user.disconnect(this._userLoadedId);
87
this._userLoadedId = 0;
90
if (this._userChangedId != 0) {
91
this._user.disconnect(this._userChangedId);
92
this._userChangedId = 0;
98
_updateUser: function() {
99
if (this._user.is_loaded)
100
this._label.text = this._user.get_real_name();
102
this._label.text = '';
104
this._avatar.update();
108
const UnlockDialog = new Lang.Class({
109
Name: 'UnlockDialog',
110
Extends: ModalDialog.ModalDialog,
112
_init: function(parentActor) {
113
this.parent({ shellReactive: true,
114
styleClass: 'login-dialog',
115
parentActor: parentActor
118
this._userManager = AccountsService.UserManager.get_default();
119
this._userName = GLib.get_user_name();
120
this._user = this._userManager.get_user(this._userName);
122
this._failCounter = 0;
123
this._firstQuestion = true;
125
this._greeterClient = new Gdm.Client();
126
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
128
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
129
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
130
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
131
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
132
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
134
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
135
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
137
this._userWidget = new UserWidget(this._user);
138
this.contentLayout.add_actor(this._userWidget.actor);
140
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
143
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
144
this._promptLayout.add(this._promptLabel,
145
{ x_align: St.Align.START });
147
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
149
this._promptEntry.clutter_text.set_password_char('\u25cf');
150
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
151
this.setInitialKeyFocus(this._promptEntry);
152
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
154
this._promptLayout.add(this._promptEntry,
158
this.contentLayout.add_actor(this._promptLayout);
160
this._promptMessage = new St.Label({ visible: false });
161
this.contentLayout.add(this._promptMessage, { x_fill: true });
163
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' });
164
this._promptLoginHint.hide();
165
this.contentLayout.add_actor(this._promptLoginHint);
167
this.allowCancel = false;
168
let cancelButton = { label: _("Cancel"),
169
action: Lang.bind(this, this._escape),
170
key: Clutter.KEY_Escape };
171
this._okButton = { label: _("Unlock"),
172
action: Lang.bind(this, this._doUnlock),
174
this.setButtons([cancelButton, this._okButton]);
176
this._updateSensitivity(true);
178
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
179
style_class: 'login-dialog-not-listed-label' });
180
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
182
child: otherUserLabel,
184
x_align: St.Align.START,
186
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
187
this.dialogLayout.add(this._otherUserButton,
188
{ x_align: St.Align.START,
191
let batch = new Batch.Hold();
192
this._userVerifier.begin(this._userName, batch);
194
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
199
this._idleMonitor = Shell.IdleMonitor.get();
200
// this dialog is only created after user activity (curtain drag or
201
// escape key press), so the timeout will fire after IDLE_TIMEOUT seconds of inactivity
202
this._idleWatchId = this._idleMonitor.add_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
205
_updateSensitivity: function(sensitive) {
206
this._promptEntry.reactive = sensitive;
207
this._promptEntry.clutter_text.editable = sensitive;
208
this._okButton.button.reactive = sensitive;
209
this._okButton.button.can_focus = sensitive;
212
_showMessage: function(userVerifier, message, styleClass) {
214
this._promptMessage.text = message;
215
this._promptMessage.styleClass = styleClass;
216
GdmUtil.fadeInActor(this._promptMessage);
218
GdmUtil.fadeOutActor(this._promptMessage);
222
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
223
if (this._firstQuestion && this._firstQuestionAnswer) {
224
this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer);
225
this._firstQuestionAnswer = null;
226
this._firstQuestion = false;
230
this._promptLabel.text = question;
232
if (!this._firstQuestion)
233
this._promptEntry.text = '';
235
this._firstQuestion = false;
237
this._promptEntry.clutter_text.set_password_char(passwordChar);
238
this._promptEntry.menu.isPassword = passwordChar != '';
240
this._currentQuery = serviceName;
241
this._updateSensitivity(true);
244
_showLoginHint: function(verifier, message) {
245
this._promptLoginHint.set_text(message)
246
GdmUtil.fadeInActor(this._promptLoginHint);
249
_hideLoginHint: function() {
250
GdmUtil.fadeOutActor(this._promptLoginHint);
253
_doUnlock: function() {
254
if (this._firstQuestion) {
255
// we haven't received a query yet, so stash the answer
256
// and make ourself non-reactive
257
// the actual reply to GDM will be sent as soon as asked
258
this._firstQuestionAnswer = this._promptEntry.text;
259
this._updateSensitivity(false);
263
if (!this._currentQuery)
266
let query = this._currentQuery;
267
this._currentQuery = null;
269
this._updateSensitivity(false);
271
this._userVerifier.answerQuery(query, this._promptEntry.text);
274
_onVerificationComplete: function() {
275
this._userVerifier.clear();
276
this.emit('unlocked');
279
_onReset: function() {
283
_onVerificationFailed: function() {
284
this._currentQuery = null;
285
this._firstQuestion = true;
287
this._promptEntry.text = '';
288
this._promptEntry.clutter_text.set_password_char('\u25cf');
289
this._promptEntry.menu.isPassword = true;
291
this._updateSensitivity(false);
294
_escape: function() {
295
if (this.allowCancel) {
296
this._userVerifier.cancel();
301
_otherUserClicked: function(button, event) {
302
Gdm.goto_login_session_sync(null);
304
this._userVerifier.cancel();
308
destroy: function() {
309
this._userVerifier.clear();
311
if (this._idleWatchId) {
312
this._idleMonitor.remove_watch(this._idleWatchId);
313
this._idleWatchId = 0;
320
this._userVerifier.cancel(null);