[gnome-shell] Add support for gdm greeter session
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] Add support for gdm greeter session
- Date: Mon, 29 Aug 2011 18:15:00 +0000 (UTC)
commit 9f1da20161c05c3832c470b959693fefe63d4677
Author: Ray Strode <rstrode redhat com>
Date: Tue Aug 23 22:12:57 2011 -0400
Add support for gdm greeter session
This commit adds GDM session support.
It provides a user list that talks to GDM,
handles authentication via PAM, etc.
It doesn't currently support fingerprint readers
and smartcards.
https://bugzilla.gnome.org/show_bug.cgi?id=657082
data/Makefile.am | 1 +
data/theme/gdm.css | 161 +++++++
js/Makefile.am | 1 +
js/gdm/loginDialog.js | 1262 +++++++++++++++++++++++++++++++++++++++++++++++++
js/ui/main.js | 22 +-
js/ui/panel.js | 26 +-
6 files changed, 1467 insertions(+), 6 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 69fb40d..eaec30b 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -29,6 +29,7 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \
+ theme/gdm.css \
theme/gnome-shell.css \
theme/panel-border.svg \
theme/panel-button-border.svg \
diff --git a/data/theme/gdm.css b/data/theme/gdm.css
new file mode 100644
index 0000000..ea58fc9
--- /dev/null
+++ b/data/theme/gdm.css
@@ -0,0 +1,161 @@
+/* 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-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-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-prompt-layout {
+ padding-bottom: 64px;
+}
+.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;
+}
+
+.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/js/Makefile.am b/js/Makefile.am
index 745fa27..f0282f2 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -3,6 +3,7 @@ jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/batch.js \
+ gdm/loginDialog.js \
misc/config.js \
misc/docInfo.js \
misc/fileUtils.js \
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
new file mode 100644
index 0000000..36799e3
--- /dev/null
+++ b/js/gdm/loginDialog.js
@@ -0,0 +1,1262 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright 2011 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+const AccountsService = imports.gi.AccountsService;
+const Clutter = imports.gi.Clutter;
+const CtrlAltTab = imports.ui.ctrlAltTab;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Mainloop = imports.mainloop;
+const Lang = imports.lang;
+const Pango = imports.gi.Pango;
+const Signals = imports.signals;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+const GdmGreeter = imports.gi.GdmGreeter;
+
+const Batch = imports.misc.batch;
+const Lightbox = imports.ui.lightbox;
+const Main = imports.ui.main;
+const ModalDialog = imports.ui.modalDialog;
+const Tweener = imports.ui.tweener;
+
+const _FADE_ANIMATION_TIME = 0.16;
+const _RESIZE_ANIMATION_TIME = 0.25;
+const _SCROLL_ANIMATION_TIME = 2.0;
+const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
+
+let _loginDialog = null;
+
+function _fadeInActor(actor) {
+ let hold = new Batch.Hold();
+
+ if (actor.opacity == 255 && actor.visible)
+ return null;
+
+ 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() {
+ actor.set_height(-1);
+ hold.release();
+ },
+ onCompleteScope: this
+ });
+ return hold;
+}
+
+function _fadeOutActor(actor) {
+ let hold = new Batch.Hold();
+
+ if (!actor.visible) {
+ actor.opacity = 0;
+ return null;
+ }
+
+ if (actor.opacity == 0) {
+ actor.hide();
+ return null;
+ }
+
+ Tweener.addTween(actor,
+ { opacity: 0,
+ height: 0,
+ time: _FADE_ANIMATION_TIME,
+ transition: 'easeOutQuad',
+ onComplete: function() {
+ actor.hide();
+ actor.set_height(-1);
+ hold.release();
+ },
+ onCompleteScope: this
+ });
+ return hold;
+}
+
+function _smoothlyResizeActor(actor, width, height) {
+ let finalWidth;
+ let finalHeight;
+
+ if (width < 0)
+ finalWidth = actor.width;
+ else
+ finalWidth = width;
+
+ if (height < 0)
+ finalHeight = actor.height;
+ else
+ finalHeight = height;
+
+ actor.set_size(actor.width, actor.height);
+
+ if (actor.width == finalWidth && actor.height == finalHeight)
+ return null;
+
+ let hold = new Batch.Hold();
+
+ Tweener.addTween(actor,
+ { width: finalWidth,
+ height: finalHeight,
+ time: _RESIZE_ANIMATION_TIME,
+ transition: 'easeOutQuad',
+ onComplete: Lang.bind(this, function() {
+ hold.release();
+ })
+ });
+ return hold;
+}
+
+function UserListItem(user, reason) {
+ this._init(user, reason);
+}
+
+UserListItem.prototype = {
+ _init: function(user) {
+ this.user = user;
+ this._userChangedId = this.user.connect('changed',
+ Lang.bind(this, this._onUserChanged));
+
+ this._verticalBox = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-vertical-layout',
+ vertical: true });
+
+ this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
+ can_focus: true,
+ child: this._verticalBox,
+ reactive: true,
+ x_align: St.Align.START,
+ x_fill: true });
+ let layout = new St.BoxLayout({ vertical: false });
+
+ this._verticalBox.add(layout,
+ { y_fill: true,
+ x_fill: true,
+ expand: true });
+
+ this._focusBin = new St.Bin({ style_class: 'login-dialog-user-list-item-focus-bin' });
+ this._verticalBox.add(this._focusBin,
+ { x_fill: false,
+ x_align: St.Align.MIDDLE,
+ y_fill: false,
+ expand: true });
+
+ this._iconBin = new St.Bin();
+ layout.add(this._iconBin);
+ let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
+ vertical: true });
+ layout.add(textLayout,
+ { y_fill: false,
+ y_align: St.Align.MIDDLE,
+ expand: true });
+
+ this._nameLabel = new St.Label({ text: this.user.get_real_name(),
+ style_class: 'login-dialog-user-list-item-name' });
+ textLayout.add(this._nameLabel);
+
+ this._updateIcon();
+
+ this.actor.connect('clicked', Lang.bind(this, this._onClicked));
+ },
+
+ _onUserChanged: function() {
+ this._nameLabel.set_text(this.user.get_real_name());
+ this._updateIcon();
+ },
+
+ _setIconFromFile: function(iconFile, styleClass) {
+ if (styleClass)
+ this._iconBin.set_style_class_name(styleClass);
+ this._iconBin.set_style(null);
+
+ this._iconBin.child = null;
+ if (iconFile) {
+ this._iconBin.show();
+ // We use background-image instead of, say, St.TextureCache
+ // so the theme writers can add a rounded frame around the image
+ // and so theme writers can pick the icon size.
+ this._iconBin.set_style('background-image: url("' + iconFile + '");');
+ } else {
+ this._iconBin.hide();
+ }
+ },
+
+ _setIconFromName: function(iconName, styleClass) {
+ if (styleClass)
+ this._iconBin.set_style_class_name(styleClass);
+ this._iconBin.set_style(null);
+
+ if (iconName != null) {
+ let icon = new St.Icon();
+ icon.set_icon_name(iconName)
+
+ this._iconBin.child = icon;
+ this._iconBin.show();
+ } else {
+ this._iconBin.child = null;
+ this._iconBin.hide();
+ }
+ },
+
+ _updateIcon: function() {
+ let iconFileName = this.user.get_icon_file();
+ let gicon = null;
+
+ if (GLib.file_test(iconFileName, GLib.FileTest.EXISTS))
+ this._setIconFromFile(iconFileName, 'login-dialog-user-list-item-icon');
+ else
+ this._setIconFromName('avatar-default', 'login-dialog-user-list-item-icon');
+ },
+
+ _onClicked: function() {
+ this.emit('activate');
+ },
+
+ fadeOutName: function() {
+ return _fadeOutActor(this._nameLabel);
+ },
+
+ fadeInName: function() {
+ return _fadeInActor(this._nameLabel);
+ },
+
+ showFocusAnimation: function(time) {
+ let hold = new Batch.Hold();
+
+ let box = this._verticalBox.get_allocation_box();
+
+ Tweener.removeTweens(this._focusBin);
+ this._focusBin.width = 0;
+ Tweener.addTween(this._focusBin,
+ { width: box.x2 - box.x1,
+ time: time,
+ transition: 'linear',
+ onComplete: function() {
+ hold.release();
+ },
+ onCompleteScope: this
+ });
+ return hold;
+ }
+
+};
+Signals.addSignalMethods(UserListItem.prototype);
+
+function UserList() {
+ this._init.apply(this, arguments);
+}
+
+UserList.prototype = {
+ _init: function() {
+ this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view'});
+ this.actor.set_policy(Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC);
+
+ this._box = new St.BoxLayout({ vertical: true,
+ style_class: 'login-dialog-user-list' });
+
+ this.actor.add_actor(this._box,
+ { x_fill: true,
+ y_fill: true,
+ x_align: St.Align.START,
+ y_align: St.Align.MIDDLE });
+ this._items = {};
+ },
+
+ _showItem: function(item) {
+ let tasks = [function() {
+ return _fadeInActor(item.actor);
+ },
+
+ function() {
+ return item.fadeInName();
+ }];
+
+ let batch = new Batch.ConsecutiveBatch(this, tasks);
+ return batch.run();
+ },
+
+ _onItemActivated: function(activatedItem) {
+ this.emit('activate', activatedItem);
+ },
+
+ giveUpWhitespace: function() {
+ let container = this.actor.get_parent();
+
+ container.child_set(this.actor, { expand: false });
+ },
+
+ takeOverWhitespace: function() {
+ let container = this.actor.get_parent();
+
+ container.child_set(this.actor, { expand: true });
+ },
+
+ pinInPlace: function() {
+ this._box.set_size(this._box.width, this._box.height);
+ },
+
+ shrinkToNaturalHeight: function() {
+ let oldWidth = this._box.width;
+ let oldHeight = this._box.height;
+ this._box.set_size(-1, -1);
+ let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
+ this._box.set_size(oldWidth, oldHeight);
+
+ let batch = new Batch.ConsecutiveBatch(this,
+ [function() {
+ return _smoothlyResizeActor(this._box, -1, naturalHeight);
+ },
+
+ function() {
+ this._box.set_size(-1, -1);
+ }
+ ]);
+
+ return batch.run();
+ },
+
+ hideItemsExcept: function(exception) {
+ let tasks = [];
+
+ for (let userName in this._items) {
+ let item = this._items[userName];
+
+ item.actor.can_focus = false;
+ item._focusBin.width = 0;
+ if (item != exception)
+ tasks.push(function() {
+ return _fadeOutActor(item.actor);
+ });
+ }
+
+ let batch = new Batch.ConsecutiveBatch(this,
+ [function() {
+ return _fadeOutActor(this.actor.vscroll);
+ },
+
+ new Batch.ConcurrentBatch(this, tasks)
+ ]);
+
+ return batch.run();
+ },
+
+ hideItems: function() {
+ return this.hideItemsExcept(null);
+ },
+
+ _getExpandedHeight: function() {
+ let hiddenActors = [];
+ for (let userName in this._items) {
+ let item = this._items[userName];
+ if (!item.actor.visible) {
+ item.actor.show();
+ hiddenActors.push(item.actor);
+ }
+ }
+
+ if (!this._box.visible) {
+ this._box.show();
+ hiddenActors.push(this._box);
+ }
+
+ this._box.set_size(-1, -1);
+ let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
+
+ for (let i = 0; i < hiddenActors.length; i++) {
+ let actor = hiddenActors[i];
+ actor.hide();
+ }
+
+ return naturalHeight;
+ },
+
+ showItems: function() {
+ let tasks = [];
+
+ for (let userName in this._items) {
+ let item = this._items[userName];
+ item.actor.can_focus = true;
+ tasks.push(function() {
+ return this._showItem(item);
+ });
+ }
+
+ let batch = new Batch.ConsecutiveBatch(this,
+ [function() {
+ this.takeOverWhitespace();
+ },
+
+ function() {
+ let fullHeight = this._getExpandedHeight();
+ return _smoothlyResizeActor(this._box, -1, fullHeight);
+ },
+
+ new Batch.ConcurrentBatch(this, tasks),
+
+ function() {
+ this.actor.set_size(-1, -1);
+ },
+
+ function() {
+ return _fadeInActor(this.actor.vscroll);
+ }]);
+ return batch.run();
+ },
+
+ scrollToItem: function(item) {
+ let box = item.actor.get_allocation_box();
+
+ let adjustment = this.actor.get_vscroll_bar().get_adjustment();
+
+ let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
+ Tweener.removeTweens(adjustment);
+ Tweener.addTween (adjustment,
+ { value: value,
+ time: _SCROLL_ANIMATION_TIME,
+ transition: 'linear' });
+ },
+
+ jumpToItem: function(item) {
+ let box = item.actor.get_allocation_box();
+
+ let adjustment = this.actor.get_vscroll_bar().get_adjustment();
+
+ let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
+
+ adjustment.set_value(value);
+ },
+
+ getItemFromUserName: function(userName) {
+ let item = this._items[userName];
+
+ if (!item)
+ return null;
+
+ return item;
+ },
+
+ addUser: function(user) {
+ if (!user.is_loaded)
+ return;
+
+ if (user.is_system_account())
+ return;
+
+ let userName = user.get_user_name();
+
+ if (!userName)
+ return;
+
+ this.removeUser(user);
+
+ let item = new UserListItem(user);
+ this._box.add(item.actor, { x_fill: true });
+
+ this._items[userName] = item;
+
+ item.connect('activate',
+ Lang.bind(this, this._onItemActivated));
+
+ // Try to keep the focused item front-and-center
+ item.actor.connect('key-focus-in',
+ Lang.bind(this,
+ function() {
+ this.scrollToItem(item);
+ item.showFocusAnimation(0);
+ }));
+
+ this.emit('item-added', item);
+ },
+
+ removeUser: function(user) {
+ if (!user.is_loaded)
+ return;
+
+ let userName = user.get_user_name();
+
+ if (!userName)
+ return;
+
+ let item = this._items[userName];
+
+ if (!item)
+ return;
+
+ item.actor.destroy();
+ delete this._items[userName];
+ }
+};
+Signals.addSignalMethods(UserList.prototype);
+
+function SessionListItem(id, name) {
+ this._init(id, name);
+}
+
+SessionListItem.prototype = {
+ _init: function(id, name) {
+ this.id = id;
+
+ this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
+ can_focus: true,
+ reactive: true,
+ x_fill: true,
+ x_align: St.Align.START });
+
+ this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
+
+ this.actor.add_actor(this._box,
+ { expand: true,
+ x_fill: true,
+ y_fill: true });
+ this.actor.connect('clicked', Lang.bind(this, this._onClicked));
+
+ this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
+ this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
+ this._box.add_actor(this._dot);
+ this.setShowDot(false);
+
+ let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
+ text: name });
+
+ this._box.add_actor(label,
+ { expand: true,
+ x_fill: true,
+ y_fill: true });
+ },
+
+ setShowDot: function(show) {
+ if (show)
+ this._dot.opacity = 255;
+ else
+ this._dot.opacity = 0;
+ },
+
+ _onRepaintDot: function(area) {
+ let cr = area.get_context();
+ let [width, height] = area.get_surface_size();
+ let color = area.get_theme_node().get_foreground_color();
+
+ cr.setSourceRGBA (color.red / 255,
+ color.green / 255,
+ color.blue / 255,
+ color.alpha / 255);
+ cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
+ cr.fill();
+ },
+
+ _onClicked: function() {
+ this.emit('activate');
+ }
+};
+Signals.addSignalMethods(SessionListItem.prototype);
+
+function SessionList() {
+ this._init();
+}
+
+SessionList.prototype = {
+ _init: function() {
+ this.actor = new St.BoxLayout({ style_class: 'login-dialog-session-list',
+ vertical: true});
+
+ this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
+ can_focus: true,
+ x_fill: true,
+ y_fill: true });
+ let box = new St.BoxLayout();
+ this._button.add_actor(box,
+ { x_fill: true,
+ y_fill: true,
+ expand: true });
+
+ this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
+ text: '\u25B8' });
+ box.add_actor(this._triangle);
+
+ let label = new St.Label({ style_class: 'login-dialog-session-list-label',
+ text: _("Session...") });
+ box.add_actor(label,
+ { x_fill: true,
+ y_fill: true,
+ expand: true });
+
+ this._button.connect('clicked',
+ Lang.bind(this, this._onClicked));
+ this.actor.add_actor(this._button,
+ { x_fill: true,
+ y_fill: true,
+ expand: true });
+ this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
+ this._scrollView.set_policy(Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC);
+ this.actor.add_actor(this._scrollView,
+ { x_fill: true,
+ y_fill: true,
+ expand: true });
+ this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
+ vertical: true });
+ this._scrollView.add_actor(this._itemList,
+ { x_fill: true,
+ y_fill: true,
+ expand: true });
+ this._scrollView.hide();
+ this.isOpen = false;
+ this._populate();
+ },
+
+ open: function() {
+ if (this.isOpen)
+ return;
+
+ this._button.add_style_pseudo_class('open');
+ this._scrollView.show();
+ this._triangle.set_text('\u25BE');
+
+ this.isOpen = true;
+ },
+
+ close: function() {
+ if (!this.isOpen)
+ return;
+
+ this._button.remove_style_pseudo_class('open');
+ this._scrollView.hide();
+ this._triangle.set_text('\u25B8');
+
+ this.isOpen = false;
+ },
+
+ _onClicked: function() {
+ if (!this.isOpen)
+ this.open();
+ else
+ this.close();
+ },
+
+ setActiveSession: function(sessionId) {
+ if (sessionId == this._activeSessionId)
+ return;
+
+ if (this._activeSessionId)
+ this._items[this._activeSessionId].setShowDot(false);
+
+ this._items[sessionId].setShowDot(true);
+ this._activeSessionId = sessionId;
+
+ this.emit('session-activated', this._activeSessionId);
+ },
+
+ _populate: function() {
+ this._itemList.destroy_children();
+ this._activeSessionId = null;
+ this._items = {};
+
+ let ids = GdmGreeter.get_session_ids();
+ ids.sort();
+
+ if (ids.length <= 1)
+ this.actor.hide();
+ else
+ this.actor.show();
+
+ for (let i = 0; i < ids.length; i++) {
+ let [sessionName, sessionDescription] = GdmGreeter.get_session_name_and_description(ids[i]);
+
+ let item = new SessionListItem(ids[i], sessionName);
+ this._itemList.add_actor(item.actor,
+ { x_align: St.Align.START,
+ y_align: St.Align.START,
+ x_fill: true,
+ y_fill: true });
+ this._items[ids[i]] = item;
+
+ if (!this._activeSessionId)
+ this.setActiveSession(ids[i]);
+
+ item.connect('activate',
+ Lang.bind(this, function() {
+ this.setActiveSession(item.id);
+ }));
+ }
+ }
+};
+Signals.addSignalMethods(SessionList.prototype);
+
+function LoginDialog() {
+ if (_loginDialog == null) {
+ this._init();
+ _loginDialog = this;
+ }
+
+ return _loginDialog;
+}
+
+LoginDialog.prototype = {
+ __proto__: ModalDialog.ModalDialog.prototype,
+
+ _init: function() {
+ ModalDialog.ModalDialog.prototype._init.call(this, { shellReactive: true,
+ styleClass: 'login-dialog' });
+ this.connect('destroy',
+ Lang.bind(this, this._onDestroy));
+ this.connect('opened',
+ Lang.bind(this, this._onOpened));
+
+ this._userManager = AccountsService.UserManager.get_default()
+ this._greeterClient = new GdmGreeter.Client();
+
+ this._greeterClient.open_connection();
+
+ this._greeterClient.call_start_conversation('gdm-password');
+
+ this._greeterClient.connect('reset',
+ Lang.bind(this, this._onReset));
+ this._greeterClient.connect('default-session-changed',
+ Lang.bind(this, this._onDefaultSessionChanged));
+ 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('timed-login-requested',
+ Lang.bind(this, this._onTimedLoginRequested));
+ this._greeterClient.connect('authentication-failed',
+ Lang.bind(this, this._onAuthenticationFailed));
+ this._greeterClient.connect('conversation-stopped',
+ Lang.bind(this, this._onConversationStopped));
+
+ this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
+ text: _("Sign In") });
+
+ this.contentLayout.add(this._titleLabel,
+ { y_fill: false,
+ y_align: St.Align.START });
+
+ let mainContentBox = new St.BoxLayout({ vertical: false });
+ this.contentLayout.add(mainContentBox,
+ { expand: true,
+ x_fill: true,
+ y_fill: false });
+
+ this._userList = new UserList();
+ mainContentBox.add(this._userList.actor,
+ { expand: true,
+ x_fill: true,
+ y_fill: true });
+
+ this.setInitialKeyFocus(this._userList.actor);
+
+ this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
+ vertical: true });
+ mainContentBox.add(this._promptBox,
+ { expand: true,
+ x_fill: true,
+ y_fill: true,
+ x_align: St.Align.START });
+ this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
+
+ this._mainContentBox = mainContentBox;
+
+ this._promptBox.add(this._promptLabel,
+ { expand: true,
+ 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._promptBox.add(this._promptEntry,
+ { expand: true,
+ x_fill: true,
+ y_fill: false,
+ x_align: St.Align.START });
+
+ this._sessionList = new SessionList();
+ this._sessionList.connect('session-activated',
+ Lang.bind(this, function(list, sessionId) {
+ this._greeterClient.call_select_session (sessionId);
+ }));
+
+ this._promptBox.add(this._sessionList.actor,
+ { expand: true,
+ x_fill: true,
+ y_fill: true,
+ x_align: St.Align.START,
+ y_align: St.Align.START});
+ this._promptBox.hide();
+
+ let notListedLabel = new St.Label({ text: _("Not listed?"),
+ style_class: 'login-dialog-not-listed-label' });
+ this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
+ can_focus: true,
+ child: notListedLabel,
+ reactive: true,
+ x_align: St.Align.START,
+ x_fill: true });
+
+ this._notListedButton.connect('clicked', Lang.bind(this, this._onNotListedClicked));
+
+ this.contentLayout.add(this._notListedButton,
+ { expand: false,
+ x_align: St.Align.START,
+ x_fill: true });
+
+ if (!this._userManager.is_loaded)
+ this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
+ Lang.bind(this, function() {
+ if (this._userManager.is_loaded) {
+ this._loadUserList();
+ this._userManager.disconnect(this._userManagerLoadedId);
+ this._userManagerLoadedId = 0;
+ }
+ }));
+ else
+ this._loadUserList();
+
+ this._userList.connect('activate',
+ Lang.bind(this, function(userList, item) {
+ this._onUserListActivated(item);
+ }));
+
+ },
+
+ _onReset: function(client, serviceName) {
+ this._greeterClient.call_start_conversation('gdm-password');
+
+ let tasks = [this._hidePrompt,
+
+ new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
+ this._fadeInNotListedButton]),
+
+ function() {
+ this._sessionList.close();
+ this._userList.actor.show();
+ this._userList.actor.opacity = 255;
+ return this._userList.showItems();
+ },
+
+ function() {
+ this._userList.actor.reactive = true;
+ this._userList.actor.grab_key_focus();
+ }];
+
+ this._user = null;
+
+ let batch = new Batch.ConsecutiveBatch(this, tasks);
+ batch.run();
+ },
+
+ _onDefaultSessionChanged: function(client, sessionId) {
+ this._sessionList.setActiveSession(sessionId);
+ },
+
+ _onInfo: function(client, serviceName, info) {
+ Main.notifyError(info);
+ },
+
+ _onProblem: function(client, serviceName, problem) {
+ Main.notifyError(problem);
+ },
+
+ _onCancel: function(client) {
+ this._greeterClient.call_cancel();
+ },
+
+ _fadeInPrompt: function() {
+ let tasks = [function() {
+ return _fadeInActor(this._promptLabel);
+ },
+
+ function() {
+ return _fadeInActor(this._promptEntry);
+ },
+
+ function() {
+ return _fadeInActor(this._promptBox);
+ },
+
+ function() {
+ if (this._user && this._user.is_logged_in())
+ return null;
+
+ return _fadeInActor(this._sessionList.actor);
+ },
+
+ function() {
+ this._promptEntry.grab_key_focus();
+ }];
+
+ this._sessionList.actor.hide();
+ let batch = new Batch.ConcurrentBatch(this, tasks);
+ return batch.run();
+ },
+
+ _showPrompt: function() {
+ let hold = new Batch.Hold();
+
+ let buttons = [{ action: Lang.bind(this, this._onCancel),
+ label: _("Cancel"),
+ key: Clutter.Escape },
+ { action: Lang.bind(this, function() {
+ hold.release();
+ }),
+ label: _("Sign In") }];
+
+ this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
+ Lang.bind(this, function() {
+ hold.release();
+ }));
+ hold.connect('release', Lang.bind(this, function() {
+ this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
+ this._promptEntryActivateCallbackId = null;
+ }));
+
+ let tasks = [function() {
+ return this._fadeInPrompt();
+ },
+
+ function() {
+ this.setButtons(buttons);
+ },
+
+ hold];
+
+ let batch = new Batch.ConcurrentBatch(this, tasks);
+
+ return batch.run();
+ },
+
+ _hidePrompt: function() {
+ if (this._promptEntryActivateCallbackId) {
+ this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
+ this._promptEntryActivateCallbackId = null;
+ }
+
+ this.setButtons([]);
+
+ let tasks = [function() {
+ return _fadeOutActor(this._promptBox);
+ },
+
+ function() {
+ this._promptEntry.set_text('');
+ }];
+
+ let batch = new Batch.ConsecutiveBatch(this, tasks);
+
+ return batch.run();
+ },
+
+ _askQuestion: function(serviceName, question) {
+ this._promptLabel.set_text(question);
+
+ let tasks = [this._showPrompt,
+
+ function() {
+ let _text = this._promptEntry.get_text();
+ this._promptEntry.set_text('');
+ this._greeterClient.call_answer_query(serviceName, _text);
+ }];
+
+ let batch = new Batch.ConsecutiveBatch(this, tasks);
+ return batch.run();
+ },
+ _onInfoQuery: function(client, serviceName, question) {
+ this._promptEntry.set_text('');
+ this._promptEntry.clutter_text.set_password_char('');
+ this._askQuestion(serviceName, question);
+ },
+
+ _onSecretInfoQuery: function(client, serviceName, secretQuestion) {
+ this._promptEntry.set_text('');
+ this._promptEntry.clutter_text.set_password_char('\u25cf');
+ this._askQuestion(serviceName, secretQuestion);
+ },
+
+ _onSessionOpened: function(client, serviceName) {
+ this._greeterClient.call_start_session_when_ready(serviceName, true);
+ },
+
+ _waitForItemForUser: function(userName) {
+ let item = this._userList.getItemFromUserName(userName);
+
+ if (item)
+ return null;
+
+ let hold = new Batch.Hold();
+ let signalId = this._userList.connect('item-added',
+ Lang.bind(this, function() {
+ let item = this._userList.getItemFromUserName(userName);
+
+ if (item)
+ hold.release();
+ }));
+
+ hold.connect('release', Lang.bind(this, function() {
+ this._userList.disconnect(signalId);
+ }));
+
+ return hold;
+ },
+
+ _showTimedLoginAnimation: function() {
+ this._timedLoginItem.actor.grab_key_focus();
+ return this._timedLoginItem.showFocusAnimation(this._timedLoginAnimationTime);
+ },
+
+ _blockTimedLoginUntilIdle: function() {
+ // This blocks timed login from starting until a few
+ // seconds after the user stops interacting with the
+ // login screen.
+ //
+ // We skip this step if the timed login delay is very
+ // short.
+ if ((this._timedLoginDelay - _TIMED_LOGIN_IDLE_THRESHOLD) <= 0)
+ return null;
+
+ let hold = new Batch.Hold();
+
+ this._timedLoginIdleTimeOutId = Mainloop.timeout_add_seconds(_TIMED_LOGIN_IDLE_THRESHOLD,
+ function() {
+ this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
+ hold.release();
+ });
+ return hold;
+ },
+
+ _startTimedLogin: function(userName, delay) {
+ this._timedLoginItem = null;
+ this._timedLoginDelay = delay;
+ this._timedLoginAnimationTime = delay;
+
+ let tasks = [function() {
+ return this._waitForItemForUser(userName);
+ },
+
+ function() {
+ this._timedLoginItem = this._userList.getItemFromUserName(userName);
+ },
+
+ function() {
+ // If we're just starting out, start on the right
+ // item.
+ if (!this.is_loaded) {
+ this._userList.jumpToItem(this._timedLoginItem);
+ this._timedLoginItem.showFocusAnimation(0);
+ }
+ },
+
+ this._blockTimedLoginUntilIdle,
+
+ function() {
+ this._userList.scrollToItem(this._timedLoginItem);
+ },
+
+ this._showTimedLoginAnimation,
+
+ function() {
+ this._timedLoginBatch = null;
+ this._greeterClient.call_begin_auto_login(userName);
+ }];
+
+ this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks);
+
+ return this._timedLoginBatch.run();
+ },
+
+ _resetTimedLogin: function() {
+ if (this._timedLoginBatch) {
+ this._timedLoginBatch.cancel();
+ this._timedLoginBatch = null;
+ }
+
+ let userName = this._timedLoginItem.user.get_user_name();
+
+ if (userName)
+ this._startTimedLogin(userName, this._timedLoginDelay);
+ },
+
+ _onTimedLoginRequested: function(client, userName, seconds) {
+ this._startTimedLogin(userName, seconds);
+
+ global.stage.connect('captured-event',
+ Lang.bind(this, function(actor, event) {
+ if (this._timedLoginDelay == undefined)
+ return false;
+
+ if (event.type() == Clutter.EventType.KEY_PRESS ||
+ event.type() == Clutter.EventType.BUTTON_PRESS) {
+ if (this._timedLoginBatch) {
+ this._timedLoginBatch.cancel();
+ this._timedLoginBatch = null;
+ }
+ } else if (event.type() == Clutter.EventType.KEY_RELEASE ||
+ event.type() == Clutter.EventType.BUTTON_RELEASE) {
+ this._resetTimedLogin();
+ }
+
+ return false;
+ }));
+ },
+
+ _onAuthenticationFailed: function(client) {
+ this._greeterClient.call_cancel();
+ },
+
+ _onConversationStopped: function(client, serviceName) {
+ this._greeterClient.call_cancel();
+ },
+
+ _onNotListedClicked: function(user) {
+ let tasks = [function() {
+ return this._userList.hideItems();
+ },
+
+ function() {
+ return this._userList.giveUpWhitespace();
+ },
+
+ function() {
+ this._userList.actor.hide();
+ },
+
+ new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
+ this._fadeOutNotListedButton]),
+
+ function() {
+ this._greeterClient.call_begin_verification('gdm-password');
+ }];
+
+ let batch = new Batch.ConsecutiveBatch(this, tasks);
+ batch.run();
+ },
+
+ _fadeInTitleLabel: function() {
+ return _fadeInActor(this._titleLabel);
+ },
+
+ _fadeOutTitleLabel: function() {
+ return _fadeOutActor(this._titleLabel);
+ },
+
+ _fadeInNotListedButton: function() {
+ return _fadeInActor(this._notListedButton);
+ },
+
+ _fadeOutNotListedButton: function() {
+ return _fadeOutActor(this._notListedButton);
+ },
+
+ _onUserListActivated: function(activatedItem) {
+ let tasks = [function() {
+ this._userList.actor.reactive = false;
+ return this._userList.pinInPlace();
+ },
+
+ function() {
+ return this._userList.hideItemsExcept(activatedItem);
+ },
+
+ function() {
+ return this._userList.giveUpWhitespace();
+ },
+
+ function() {
+ return activatedItem.fadeOutName();
+ },
+
+ new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
+ this._fadeOutNotListedButton]),
+
+ function() {
+ return this._userList.shrinkToNaturalHeight();
+ },
+
+ function() {
+ let userName = activatedItem.user.get_user_name();
+ this._greeterClient.call_begin_verification_for_user('gdm-password',
+ userName);
+ }];
+
+ this._user = activatedItem.user;
+
+ let batch = new Batch.ConsecutiveBatch(this, tasks);
+ batch.run();
+ },
+
+ _onDestroy: function() {
+ if (this._userManagerLoadedId) {
+ this._userManager.disconnect(this._userManagerLoadedId);
+ this._userManagerLoadedId = 0;
+ }
+ },
+
+ _loadUserList: function() {
+ let users = this._userManager.list_users();
+
+ for (let i = 0; i < users.length; i++) {
+ this._userList.addUser(users[i]);
+ }
+
+ this._userManager.connect('user-added',
+ Lang.bind(this, function(userManager, user) {
+ this._userList.addUser(user);
+ }));
+
+ this._userManager.connect('user-removed',
+ Lang.bind(this, function(userManager, user) {
+ this._userList.removeUser(user);
+ }));
+
+ // emitted in idle so caller doesn't have to explicitly check if
+ // it's loaded immediately after construction
+ // (since there's no way the caller could be listening for
+ // 'loaded' yet)
+ Mainloop.idle_add(Lang.bind(this, function() {
+ this.emit('loaded');
+ this.is_loaded = true;
+ }));
+ },
+
+ _onOpened: function() {
+ Main.ctrlAltTabManager.addGroup(this._mainContentBox,
+ _("Login Window"),
+ 'dialog-password',
+ { sortGroup: CtrlAltTab.SortGroup.MIDDLE });
+
+ },
+
+ close: function() {
+ ModalDialog.ModalDialog.prototype.close.call(this);
+
+ Main.ctrlAltTabManager.removeGroup(this._group);
+ }
+};
diff --git a/js/ui/main.js b/js/ui/main.js
index 3fd21c8..2b50de4 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -71,6 +71,7 @@ let _errorLogStack = [];
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
+let _gdmCssStylesheet = null;
let background = null;
@@ -86,6 +87,17 @@ function _createUserSession() {
networkAgent = new NetworkAgent.NetworkAgent();
}
+function _createGDMSession() {
+ // We do this this here instead of at the top to prevent GDM
+ // related code from getting loaded in normal user sessions
+ const LoginDialog = imports.gdm.loginDialog;
+
+ let loginDialog = new LoginDialog.LoginDialog();
+ loginDialog.connect('loaded', function() {
+ loginDialog.open();
+ });
+}
+
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
@@ -175,6 +187,7 @@ function start() {
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
+ _gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme();
// Set up stage hierarchy to group all UI actors under one container.
@@ -197,7 +210,11 @@ function start() {
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
- _createUserSession();
+
+ if (global.session_type == Shell.SessionType.USER)
+ _createUserSession();
+ else if (global.session_type == Shell.SessionType.GDM)
+ _createGDMSession();
panel.startStatusArea();
@@ -434,6 +451,9 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
+ if (global.session_type == Shell.SessionType.GDM)
+ theme.load_stylesheet(_gdmCssStylesheet);
+
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
diff --git a/js/ui/panel.js b/js/ui/panel.js
index e3803d8..ff181c6 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -45,6 +45,14 @@ try {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
+const GDM_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery'];
+const GDM_TRAY_ICON_SHELL_IMPLEMENTATION = {
+ 'a11y': imports.ui.status.accessibility.ATIndicator,
+ 'volume': imports.ui.status.volume.Indicator,
+ 'battery': imports.ui.status.power.Indicator,
+ 'keyboard': imports.ui.status.keyboard.XKBIndicator
+};
+
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@@ -876,6 +884,14 @@ Panel.prototype = {
this.actor.remove_style_class_name('in-overview');
}));
+ if (global.session_type == Shell.SessionType.GDM) {
+ this._tray_icon_order = GDM_TRAY_ICON_ORDER;
+ this._tray_icon_shell_implementation = GDM_TRAY_ICON_SHELL_IMPLEMENTATION;
+ } else {
+ this._tray_icon_order = STANDARD_TRAY_ICON_ORDER;
+ this._tray_icon_shell_implementation = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION;
+ }
+
this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
@@ -1026,9 +1042,9 @@ Panel.prototype = {
},
startStatusArea: function() {
- for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
- let role = STANDARD_TRAY_ICON_ORDER[i];
- let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
+ for (let i = 0; i < this._tray_icon_order.length; i++) {
+ let role = this._tray_icon_order[i];
+ let constructor = this._tray_icon_shell_implementation[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
continue;
@@ -1069,13 +1085,13 @@ Panel.prototype = {
_onTrayIconAdded: function(o, icon, role) {
icon.height = PANEL_ICON_SIZE;
- if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
+ if (this._tray_icon_shell_implementation[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
// Figure out the index in our well-known order for this icon
- let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
+ let position = this._tray_icon_order.indexOf(role);
icon._rolePosition = position;
let children = this._trayBox.get_children();
let i;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]