1
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
3
const Gdm = imports.gi.Gdm;
4
const DBus = imports.dbus;
5
const Gio = imports.gi.Gio;
6
const GLib = imports.gi.GLib;
7
const Lang = imports.lang;
8
const Shell = imports.gi.Shell;
9
const St = imports.gi.St;
10
const Tp = imports.gi.TelepathyGLib;
11
const UPowerGlib = imports.gi.UPowerGlib;
13
const GnomeSession = imports.misc.gnomeSession;
14
const Main = imports.ui.main;
15
const PanelMenu = imports.ui.panelMenu;
16
const PopupMenu = imports.ui.popupMenu;
17
const ScreenSaver = imports.misc.screenSaver;
18
const Util = imports.misc.util;
20
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
21
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
22
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
23
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
25
// Adapted from gdm/gui/user-switch-applet/applet.c
27
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
28
// Copyright (C) 2008,2009 Red Hat, Inc.
30
function StatusMenuButton() {
34
StatusMenuButton.prototype = {
35
__proto__: PanelMenu.Button.prototype,
38
PanelMenu.Button.prototype._init.call(this, 0.0);
39
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
40
this.actor.set_child(box);
42
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
44
this._gdm = Gdm.UserManager.ref_default();
45
this._gdm.queue_load();
47
this._user = this._gdm.get_user(GLib.get_user_name());
48
this._presence = new GnomeSession.Presence();
49
this._presenceItems = {};
50
this._session = new GnomeSession.SessionManager();
51
this._haveShutdown = true;
53
this._account_mgr = Tp.AccountManager.dup()
55
this._upClient = new UPowerGlib.Client();
56
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
57
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
59
this._iconBox = new St.Bin();
60
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
62
let textureCache = St.TextureCache.get_default();
63
this._availableIcon = new St.Icon({ icon_name: 'user-available', style_class: 'popup-menu-icon' });
64
this._busyIcon = new St.Icon({ icon_name: 'user-busy', style_class: 'popup-menu-icon' });
65
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible', style_class: 'popup-menu-icon' });
66
this._idleIcon = new St.Icon({ icon_name: 'user-idle', style_class: 'popup-menu-icon' });
68
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
69
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
71
this._name = new St.Label();
72
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
73
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
74
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
76
this._createSubMenu();
77
this._gdm.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUser));
78
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
79
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
80
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
81
Lang.bind(this, this._updateSwitchUser));
82
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
83
Lang.bind(this, this._updateLogout));
85
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
86
Lang.bind(this, this._updateLockScreen));
87
this._updateSwitchUser();
89
this._updateLockScreen();
91
// Whether shutdown is available or not depends on both lockdown
92
// settings (disable-log-out) and Polkit policy - the latter doesn't
93
// notify, so we update the menu item each time the menu opens or
94
// the lockdown setting changes, which should be close enough.
95
this.menu.connect('open-state-changed', Lang.bind(this,
96
function(menu, open) {
98
this._updateHaveShutdown();
100
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
101
Lang.bind(this, this._updateHaveShutdown));
103
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
106
_onDestroy: function() {
107
this._user.disconnect(this._userLoadedId);
108
this._user.disconnect(this._userChangedId);
111
_updateUserName: function() {
112
if (this._user.is_loaded)
113
this._name.set_text(this._user.get_real_name());
115
this._name.set_text("");
118
_updateSessionSeparator: function() {
119
let sessionItemsVisible = this._loginScreenItem.actor.visible ||
120
this._logoutItem.actor.visible ||
121
this._lockScreenItem.actor.visible;
123
let showSessionSeparator = sessionItemsVisible &&
124
this._suspendOrPowerOffItem.actor.visible;
126
let showSettingsSeparator = sessionItemsVisible ||
127
this._suspendOrPowerOffItem.actor.visible;
129
if (showSessionSeparator)
130
this._sessionSeparator.actor.show();
132
this._sessionSeparator.actor.hide();
134
if (showSettingsSeparator)
135
this._settingsSeparator.actor.show();
137
this._settingsSeparator.actor.hide();
140
_updateSwitchUser: function() {
141
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
142
if (allowSwitch && this._gdm.can_switch ())
143
this._loginScreenItem.actor.show();
145
this._loginScreenItem.actor.hide();
146
this._updateSessionSeparator();
149
_updateLogout: function() {
150
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
152
this._logoutItem.actor.show();
154
this._logoutItem.actor.hide();
155
this._updateSessionSeparator();
158
_updateLockScreen: function() {
159
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
161
this._lockScreenItem.actor.show();
163
this._lockScreenItem.actor.hide();
164
this._updateSessionSeparator();
167
_updateHaveShutdown: function() {
168
this._session.CanShutdownRemote(Lang.bind(this,
169
function(result, error) {
171
this._haveShutdown = result;
172
this._updateSuspendOrPowerOff();
177
_updateSuspendOrPowerOff: function() {
178
this._haveSuspend = this._upClient.get_can_suspend();
180
if (!this._suspendOrPowerOffItem)
183
if (!this._haveShutdown && !this._haveSuspend)
184
this._suspendOrPowerOffItem.actor.hide();
186
this._suspendOrPowerOffItem.actor.show();
187
this._updateSessionSeparator();
189
// If we can't suspend show Power Off... instead
190
// and disable the alt key
191
if (!this._haveSuspend) {
192
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
193
} else if (!this._haveShutdown) {
194
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
196
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
200
_updatePresenceIcon: function(presence, status) {
201
if (status == GnomeSession.PresenceStatus.AVAILABLE)
202
this._iconBox.child = this._availableIcon;
203
else if (status == GnomeSession.PresenceStatus.BUSY)
204
this._iconBox.child = this._busyIcon;
205
else if (status == GnomeSession.PresenceStatus.INVISIBLE)
206
this._iconBox.child = this._invisibleIcon;
208
this._iconBox.child = this._idleIcon;
210
for (let itemStatus in this._presenceItems)
211
this._presenceItems[itemStatus].setShowDot(itemStatus == status);
214
_createSubMenu: function() {
217
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
218
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
219
this.menu.addMenuItem(item);
220
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
222
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
223
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
224
this.menu.addMenuItem(item);
225
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
227
item = new PopupMenu.PopupSeparatorMenuItem();
228
this.menu.addMenuItem(item);
230
item = new PopupMenu.PopupMenuItem(_("My Account"));
231
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
232
this.menu.addMenuItem(item);
234
item = new PopupMenu.PopupMenuItem(_("System Settings"));
235
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
236
this.menu.addMenuItem(item);
238
item = new PopupMenu.PopupSeparatorMenuItem();
239
this.menu.addMenuItem(item);
240
this._settingsSeparator = item;
242
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
243
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
244
this.menu.addMenuItem(item);
245
this._lockScreenItem = item;
247
item = new PopupMenu.PopupMenuItem(_("Switch User"));
248
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
249
this.menu.addMenuItem(item);
250
this._loginScreenItem = item;
252
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
253
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
254
this.menu.addMenuItem(item);
255
this._logoutItem = item;
257
item = new PopupMenu.PopupSeparatorMenuItem();
258
this.menu.addMenuItem(item);
259
this._sessionSeparator = item;
261
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
263
this.menu.addMenuItem(item);
264
this._suspendOrPowerOffItem = item;
265
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
266
this._updateSuspendOrPowerOff();
269
_setPresenceStatus: function(item, event, status) {
270
this._presence.setStatus(status);
272
this._setIMStatus(status);
275
_onMyAccountActivate: function() {
276
Main.overview.hide();
277
let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
281
_onPreferencesActivate: function() {
282
Main.overview.hide();
283
let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
287
_onLockScreenActivate: function() {
288
Main.overview.hide();
289
this._screenSaverProxy.LockRemote();
292
_onLoginScreenActivate: function() {
293
Main.overview.hide();
294
// Ensure we only move to GDM after the screensaver has activated; in some
295
// OS configurations, the X server may block event processing on VT switch
296
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
297
this._gdm.goto_login_session();
301
_onQuitSessionActivate: function() {
302
Main.overview.hide();
303
this._session.LogoutRemote(0);
306
_onSuspendOrPowerOffActivate: function() {
307
Main.overview.hide();
309
if (this._haveSuspend &&
310
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
311
// Ensure we only suspend after the screensaver has activated
312
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
313
this._upClient.suspend_sync(null);
316
this._session.ShutdownRemote();
320
_setIMStatus: function(session_status) {
321
let [presence_type, presence_status, msg] = this._account_mgr.get_most_available_presence();
324
// We change the IM presence only if there are connected accounts
325
if (presence_type == Tp.ConnectionPresenceType.UNSET ||
326
presence_type == Tp.ConnectionPresenceType.OFFLINE ||
327
presence_type == Tp.ConnectionPresenceType.UNKNOWN ||
328
presence_type == Tp.ConnectionPresenceType.ERROR)
331
if (session_status == GnomeSession.PresenceStatus.AVAILABLE) {
332
type = Tp.ConnectionPresenceType.AVAILABLE;
333
status = "available";
335
else if (session_status == GnomeSession.PresenceStatus.BUSY) {
336
type = Tp.ConnectionPresenceType.BUSY;
343
this._account_mgr.set_all_requested_presences(type, status, msg);