Merge lp:~donadigo/wingpanel-indicator-session/use-native-accounts-library into lp:~wingpanel-devs/wingpanel-indicator-session/trunk

Proposed by Adam Bieńkowski
Status: Merged
Approved by: Felipe Escoto
Approved revision: 96
Merged at revision: 94
Proposed branch: lp:~donadigo/wingpanel-indicator-session/use-native-accounts-library
Merge into: lp:~wingpanel-devs/wingpanel-indicator-session/trunk
Diff against target: 655 lines (+245/-206)
7 files modified
src/CMakeLists.txt (+0/-1)
src/Indicator.vala (+6/-3)
src/Services/DbusInterfaces.vala (+12/-23)
src/Services/User.vala (+0/-108)
src/Services/UserManager.vala (+144/-50)
src/Widgets/UserBox.vala (+56/-18)
src/Widgets/UserListBox.vala (+27/-3)
To merge this branch: bzr merge lp:~donadigo/wingpanel-indicator-session/use-native-accounts-library
Reviewer Review Type Date Requested Status
Felipe Escoto code/ux Approve
Review via email: mp+302229@code.launchpad.net

Commit message

* Use native accounts-service library
* Close indicator on activating row (logging of, switching accounts)
* Show guest account proper state when logged in

Description of the change

This branch replaces the old DBus implementation with native accounts-service library. In result we don't need a User class, that's provided already by the library.

Other features include:
- Close indicator on activating row (logging of, switching accounts)
- Show guest account proper state when logged in

To post a comment you must log in.
94. By Adam Bieńkowski

Use enums

Revision history for this message
Felipe Escoto (philip.scott) wrote :

Code wise the indicator now looks better than ever! The only problem I have is with the current user being clickable.

review: Needs Fixing (code/ux)
95. By Adam Bieńkowski

Prevent logged in users to be able to click themselves

96. By Adam Bieńkowski

Simpler draw function

Revision history for this message
Felipe Escoto (philip.scott) :
review: Approve (code/ux)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2016-02-18 19:58:35 +0000
3+++ src/CMakeLists.txt 2016-08-08 16:32:36 +0000
4@@ -17,7 +17,6 @@
5 Widgets/UserBox.vala
6 Widgets/UserListBox.vala
7 Widgets/EndSessionDialog.vala
8- Services/User.vala
9 Services/UserManager.vala
10 Services/DbusInterfaces.vala
11 ${CMAKE_CURRENT_BINARY_DIR}/config.vala
12
13=== modified file 'src/Indicator.vala'
14--- src/Indicator.vala 2016-07-12 16:33:07 +0000
15+++ src/Indicator.vala 2016-08-08 16:32:36 +0000
16@@ -67,11 +67,10 @@
17 users_separator = new Wingpanel.Widgets.Separator ();
18 manager = new Session.Services.UserManager (users_separator);
19
20- main_grid.add (manager.current_user);
21 main_grid.add (manager.user_grid);
22
23 if (manager.has_guest) {
24- manager.user_grid.add_guest (manager.guest (false));
25+ manager.add_guest (false);
26 }
27
28 main_grid.add (users_separator);
29@@ -118,6 +117,8 @@
30 }
31
32 public void connections () {
33+ manager.close.connect (() => close ());
34+
35 lock_screen.clicked.connect (() => {
36 close ();
37 try {
38@@ -151,7 +152,9 @@
39 });
40 }
41
42- public override void opened () {}
43+ public override void opened () {
44+ manager.update_all ();
45+ }
46
47 public override void closed () {}
48 }
49
50=== modified file 'src/Services/DbusInterfaces.vala'
51--- src/Services/DbusInterfaces.vala 2016-08-05 22:26:34 +0000
52+++ src/Services/DbusInterfaces.vala 2016-08-08 16:32:36 +0000
53@@ -17,12 +17,9 @@
54 * Boston, MA 02111-1307, USA.
55 */
56
57-/* To generate new UserBoxes for each user, and when a new one is added */
58-[DBus (name = "org.freedesktop.Accounts")]
59-interface AccountsInterface : Object {
60- public abstract string[] list_cached_users () throws IOError;
61- public signal void user_added (ObjectPath user_path);
62- public signal void user_deleted (ObjectPath user_path);
63+struct UserInfo {
64+ uint32 uid;
65+ string user_name;
66 }
67
68 /* Power and system control */
69@@ -42,26 +39,18 @@
70 public abstract void reboot (bool interactive) throws IOError;
71 public abstract void power_off (bool interactive) throws IOError;
72
73- public abstract string? get_user (uint32 uuid) throws IOError;
74+ public abstract UserInfo[] list_users () throws IOError;
75+ public abstract ObjectPath? get_user (uint32 uuid) throws IOError;
76+}
77+
78+[DBus (name = "org.freedesktop.login1.User")]
79+interface UserInterface : Object {
80+ public abstract string state { owned get; }
81 }
82
83 [DBus (name = "org.freedesktop.DisplayManager.Seat")]
84 interface SeatInterface : Object {
85- //public abstract void SwitchToGreeter () throws IOError;
86+ public abstract bool has_guest_account { get; }
87 public abstract void switch_to_guest (string session_name) throws IOError;
88 public abstract void switch_to_user (string username, string session_name) throws IOError;
89-}
90-
91-/* for User.vala, to get the user properties */
92-[DBus (name = "org.freedesktop.Accounts.User")]
93-interface UserInterface : Object {
94- public signal void changed ();
95-}
96-
97-[DBus (name = "org.freedesktop.DBus.Properties")]
98-interface PropertiesInterface : Object {
99- public abstract Variant get (string interface, string propname) throws IOError;
100-
101- /* public abstract void Set (string interface, string propname, Variant value) throws IOError; */
102- public signal void properties_changed ();
103-}
104+}
105\ No newline at end of file
106
107=== removed file 'src/Services/User.vala'
108--- src/Services/User.vala 2016-01-15 18:20:25 +0000
109+++ src/Services/User.vala 1970-01-01 00:00:00 +0000
110@@ -1,108 +0,0 @@
111-/*
112- * Copyright (c) 2011-2015 Wingpanel Developers (http://launchpad.net/wingpanel)
113- *
114- * This program is free software; you can redistribute it and/or
115- * modify it under the terms of the GNU General Public
116- * License as published by the Free Software Foundation; either
117- * version 2 of the License, or (at your option) any later version.
118- *
119- * This program is distributed in the hope that it will be useful,
120- * but WITHOUT ANY WARRANTY; without even the implied warranty of
121- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
122- * General Public License for more details.
123- *
124- * You should have received a copy of the GNU General Public
125- * License along with this program; if not, write to the
126- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
127- * Boston, MA 02111-1307, USA.
128- */
129-
130-public class Session.Services.User : Object {
131- private static string ACCOUNTS_INTERFACE = "org.freedesktop.Accounts";
132- private static string USER_INTERFACE = "org.freedesktop.Accounts.User";
133- private static string LOGIN_INTERFACE = "org.freedesktop.login1";
134- private static string MANAGER = "/org/freedesktop/login1";
135-
136- public string user_path { get; private set; }
137- public string real_name { get; private set; }
138- public string user_name { get; private set; }
139- public string icon_file { get; private set; }
140- public uint64 Uid { get; private set; }
141- public bool locked { get; private set; }
142-
143- private UserInterface? user_interface = null;
144- private PropertiesInterface? user_properties = null;
145- private PropertiesInterface? state_properties = null;
146- private SystemInterface? system_interface = null;
147-
148- public signal void properties_updated ();
149-
150- public User (string user_path_) {
151- this.user_path = user_path_;
152-
153- connect_to_bus ();
154- connect_signals ();
155- update_properties ();
156- get_state ();
157-
158- }
159-
160- private bool connect_to_bus () {
161- try {
162- system_interface = Bus.get_proxy_sync (BusType.SYSTEM, LOGIN_INTERFACE, MANAGER, DBusProxyFlags.NONE);
163- user_interface = Bus.get_proxy_sync (BusType.SYSTEM, ACCOUNTS_INTERFACE, user_path, DBusProxyFlags.NONE);
164- user_properties = Bus.get_proxy_sync (BusType.SYSTEM, ACCOUNTS_INTERFACE, user_path, DBusProxyFlags.NONE);
165-
166- update_properties ();
167- string? user_object_path = system_interface.get_user ((uint32)Uid);
168- state_properties = Bus.get_proxy_sync (BusType.SYSTEM, LOGIN_INTERFACE, user_object_path, DBusProxyFlags.NONE);
169-
170- debug ("Connection to user account established. User path: %s", user_object_path);
171-
172- return user_interface != null & user_properties != null;
173- } catch (Error e) {
174- critical ("Connecting to Accounts failed: %s", e.message);
175- return false;
176- }
177- }
178-
179- private void connect_signals () {
180- user_interface.changed.connect (update_properties);
181- user_properties.properties_changed.connect (update_properties);
182- state_properties.properties_changed.connect (update_properties);
183- }
184-
185- public bool get_state () {
186- bool state = false;
187-
188- try {
189- string status = state_properties.get (LOGIN_INTERFACE + ".User", "State").get_string ();
190-
191- if (status == "active" || status == "online") {
192- state = true;
193- }
194- } catch (Error e) {
195- critical ("Could not get users' state: %s", e.message);
196- }
197-
198- return state;
199- }
200-
201- public void update_properties () {
202- try {
203- real_name = user_properties.get (USER_INTERFACE, "RealName").get_string ();
204- user_name = user_properties.get (USER_INTERFACE, "UserName").get_string ();
205- icon_file = user_properties.get (USER_INTERFACE, "IconFile").get_string ();
206- locked = user_properties.get (USER_INTERFACE, "Locked").get_boolean ();
207- Uid = user_properties.get (USER_INTERFACE, "Uid").get_uint64 ();
208-
209- if (real_name == "") {
210- real_name = user_name;
211- }
212-
213- properties_updated ();
214- } catch (Error e) {
215- critical ("Updating device properties failed: %s", e.message);
216- }
217- }
218-}
219
220=== modified file 'src/Services/UserManager.vala'
221--- src/Services/UserManager.vala 2016-05-21 15:56:59 +0000
222+++ src/Services/UserManager.vala 2016-08-08 16:32:36 +0000
223@@ -17,16 +17,93 @@
224 * Boston, MA 02111-1307, USA.
225 */
226
227+public enum UserState {
228+ ACTIVE,
229+ ONLINE,
230+ OFFLINE;
231+
232+ public static UserState to_enum (string state) {
233+ switch (state) {
234+ case "active":
235+ return UserState.ACTIVE;
236+ case "online":
237+ return UserState.ONLINE;
238+ }
239+
240+ return UserState.OFFLINE;
241+ }
242+}
243+
244 public class Session.Services.UserManager : Object {
245+ public signal void close ();
246+
247+ private const string LOGIN_IFACE = "org.freedesktop.login1";
248+ private const string LOGIN_PATH = "/org/freedesktop/login1";
249+
250 private signal void delete_user (ObjectPath user_path);
251- private AccountsInterface accounts_interface;
252- private PropertiesInterface state_properties;
253+ private Act.UserManager manager;
254+ private List<Widgets.Userbox> userbox_list;
255+ private SeatInterface dm_proxy;
256 private Wingpanel.Widgets.Separator users_separator;
257
258 public Session.Widgets.UserListBox user_grid;
259- public Session.Widgets.Userbox current_user;
260
261- public bool has_guest {public get; private set; default = false;}
262+ public bool has_guest { public get; private set; default = false; }
263+
264+ private static SystemInterface? login_proxy;
265+
266+ static construct {
267+ try {
268+ login_proxy = Bus.get_proxy_sync (BusType.SYSTEM, LOGIN_IFACE, LOGIN_PATH, DBusProxyFlags.NONE);
269+ } catch (IOError e) {
270+ stderr.printf ("UserManager error: %s\n", e.message);
271+ }
272+ }
273+
274+ public static UserState get_user_state (uint32 uuid) {
275+ if (login_proxy == null) {
276+ return UserState.OFFLINE;
277+ }
278+
279+ try {
280+ ObjectPath? path = login_proxy.get_user (uuid);
281+ if (path == null) {
282+ return UserState.OFFLINE;
283+ }
284+
285+ UserInterface? user = Bus.get_proxy_sync (BusType.SYSTEM, LOGIN_IFACE, path, DBusProxyFlags.NONE);
286+ if (user == null) {
287+ return UserState.OFFLINE;
288+ }
289+
290+ return UserState.to_enum (user.state);
291+ } catch (IOError e) {
292+ stderr.printf ("Error: %s\n", e.message);
293+ }
294+
295+ return UserState.OFFLINE;
296+ }
297+
298+ public static UserState get_guest_state () {
299+ if (login_proxy == null) {
300+ return UserState.OFFLINE;
301+ }
302+
303+ try {
304+ UserInfo[] users = login_proxy.list_users ();
305+ foreach (UserInfo user in users) {
306+ var state = get_user_state (user.uid);
307+ if (user.user_name.has_prefix ("guest-")
308+ && state == UserState.ACTIVE) {
309+ return UserState.ACTIVE;
310+ }
311+ }
312+ } catch (IOError e) {
313+ stderr.printf ("Error: %s\n", e.message);
314+ }
315+
316+ return UserState.OFFLINE;
317+ }
318
319 public UserManager (Wingpanel.Widgets.Separator users_separator) {
320 this.users_separator = users_separator;
321@@ -37,74 +114,91 @@
322 }
323
324 private void init () {
325+ userbox_list = new List<Widgets.Userbox> ();
326 user_grid = new Session.Widgets.UserListBox ();
327+ user_grid.close.connect (() => close ());
328+
329+ manager = Act.UserManager.get_default ();
330+ connect_signals ();
331+ init_users ();
332
333 try {
334- accounts_interface = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.Accounts", "/org/freedesktop/Accounts", DBusProxyFlags.NONE);
335- state_properties = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.DisplayManager", Environment.get_variable ("XDG_SEAT_PATH"), DBusProxyFlags.NONE);
336- has_guest = state_properties.get ("org.freedesktop.DisplayManager.Seat", "HasGuestAccount").get_boolean ();
337-
338- connect_signals ();
339- init_users ();
340+ dm_proxy = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.DisplayManager", Environment.get_variable ("XDG_SEAT_PATH"), DBusProxyFlags.NONE);
341+ has_guest = dm_proxy.has_guest_account;
342 } catch (IOError e) {
343 stderr.printf ("UserManager error: %s\n", e.message);
344 }
345 }
346
347 private void connect_signals () {
348- accounts_interface.user_added.connect ((user_path) => {
349- var user = new_user (user_path);
350+ manager.user_added.connect (add_user);
351+ manager.user_removed.connect (remove_user);
352+ manager.user_is_logged_in_changed.connect (update_user);
353
354- if (user != null) {
355- user_grid.add (user);
356+ manager.notify["is-loaded"].connect (() => {
357+ if (manager.is_loaded) {
358+ init_users ();
359 }
360- });
361-
362- accounts_interface.user_deleted.connect ((user_path) => {
363- delete_user (user_path);
364- });
365+ });
366 }
367
368 private void init_users () {
369- string current_user = GLib.Environment.get_user_name ();
370-
371- try {
372- var users = accounts_interface.list_cached_users ();
373- foreach (string user_address in users) {
374- var userbox = new_user (user_address);
375-
376- if (userbox.user.user_name == current_user) {
377- this.current_user = userbox;
378- } else {
379- user_grid.add (userbox);
380- }
381- }
382- } catch (IOError e) {
383- stderr.printf ("ERROR: %s\n", e.message);
384+ foreach (Act.User user in manager.list_users ()) {
385+ add_user (user);
386 }
387 }
388
389- private Session.Widgets.Userbox new_user (string user_address) {
390- var user = new Session.Services.User (user_address);
391+ private void add_user (Act.User user) {
392 var userbox = new Session.Widgets.Userbox (user);
393-
394- delete_user.connect ((user_path) => {
395- if (userbox.user.user_path == user_path) {
396- user_grid.remove (userbox);
397+ userbox_list.append (userbox);
398+
399+ user_grid.add (userbox);
400+
401+ users_separator.visible = true;
402+ }
403+
404+ private Widgets.Userbox? get_userbox_from_user (Act.User user) {
405+ foreach (Widgets.Userbox userbox in userbox_list) {
406+ if (userbox.user.get_user_name () == user.get_user_name ()) {
407+ return userbox;
408 }
409- });
410-
411- users_separator.visible = true;
412-
413- return userbox;
414- }
415-
416- public Session.Widgets.Userbox guest (bool logged_in) {
417+ }
418+
419+ return null;
420+ }
421+
422+ private void remove_user (Act.User user) {
423+ var userbox = get_userbox_from_user (user);
424+ if (userbox == null) {
425+ return;
426+ }
427+
428+ userbox_list.remove (userbox);
429+ user_grid.remove (userbox);
430+ }
431+
432+ private void update_user (Act.User user) {
433+ var userbox = get_userbox_from_user (user);
434+ if (userbox == null) {
435+ return;
436+ }
437+
438+ userbox.update_state ();
439+ }
440+
441+ public void update_all () {
442+ foreach (var userbox in userbox_list) {
443+ userbox.update_state ();
444+ }
445+ }
446+
447+ public void add_guest (bool logged_in) {
448 var userbox = new Session.Widgets.Userbox.from_data (_("Guest"), logged_in, true);
449+ userbox_list.append (userbox);
450 userbox.visible = true;
451
452+ user_grid.add_guest (userbox);
453+
454 users_separator.visible = true;
455-
456- return userbox;
457 }
458 }
459
460=== modified file 'src/Widgets/UserBox.vala'
461--- src/Widgets/UserBox.vala 2016-05-21 17:15:08 +0000
462+++ src/Widgets/UserBox.vala 2016-08-08 16:32:36 +0000
463@@ -22,28 +22,30 @@
464 private const string LOGGED_OFF = _("Logged out");
465 private const int ICON_SIZE = 48;
466
467- public Session.Services.User user { public get; private set; }
468+ public Act.User? user { public get; private set; }
469 public bool is_guest = false;
470
471 private Granite.Widgets.Avatar avatar;
472 private Gtk.Label fullname_label;
473 private Gtk.Label status_label;
474
475- public Userbox (Session.Services.User user) {
476+ public Userbox (Act.User user) {
477 this.user = user;
478 build_ui ();
479 connect_signals ();
480- user.update_properties ();
481+ update ();
482+ update_state ();
483 }
484
485 public Userbox.from_data (string fullname, bool logged_in, bool is_guest = false) {
486 this.is_guest = is_guest;
487- build_ui (false);
488+ this.user = null;
489+ build_ui ();
490 fullname_label.label = "<b>" + fullname + "</b>";
491- update_state (logged_in);
492+ update_state ();
493 }
494
495- private void build_ui (bool load_icon = true) {
496+ private void build_ui () {
497 get_style_context ().add_class ("menuitem");
498
499 var grid = new Gtk.Grid ();
500@@ -56,11 +58,12 @@
501 status_label = new Gtk.Label (LOGGED_OFF);
502 status_label.halign = Gtk.Align.START;
503
504- if (load_icon) {
505- avatar = new Granite.Widgets.Avatar.from_file (user.icon_file, ICON_SIZE);
506- } else {
507+ if (is_guest) {
508 avatar = new Granite.Widgets.Avatar.with_default_icon (ICON_SIZE);
509+ } else {
510+ avatar = new Granite.Widgets.Avatar.from_file (user.get_icon_file (), ICON_SIZE);
511 }
512+
513 avatar.margin_end = 6;
514
515 grid.attach (avatar, 0, 0, 3, 3);
516@@ -69,11 +72,34 @@
517 this.add (grid);
518 }
519
520- public void update (string? fullname, string icon) {
521- this.fullname_label.set_label ("<b>" + fullname + "</b>");
522+ // For some reason Act.User.is_logged_in () does not work
523+ public UserState get_user_state () {
524+ if (is_guest) {
525+ return Services.UserManager.get_guest_state ();
526+ }
527+
528+ return Services.UserManager.get_user_state (user.get_uid ());
529+ }
530+
531+ public bool is_logged_in () {
532+ var state = get_user_state ();
533+ return state == UserState.ONLINE || state == UserState.ACTIVE;
534+ }
535+
536+ public void set_can_activate (bool can_activate) {
537+ selectable = can_activate;
538+ activatable = can_activate;
539+ }
540+
541+ private void update () {
542+ if (is_guest) {
543+ return;
544+ }
545+
546+ this.fullname_label.label = "<b>" + user.get_real_name () + "</b>";
547
548 try {
549- var pixbuf = new Gdk.Pixbuf.from_file (icon);
550+ var pixbuf = new Gdk.Pixbuf.from_file (user.get_icon_file ());
551 pixbuf = pixbuf.scale_simple (ICON_SIZE, ICON_SIZE, Gdk.InterpType.BILINEAR);
552 avatar.pixbuf = pixbuf;
553 } catch (Error e) {
554@@ -81,21 +107,33 @@
555 }
556 }
557
558- public void update_state (bool logged_in) {
559- if (logged_in) {
560+ public void update_state () {
561+ var state = get_user_state ();
562+ set_can_activate (state != UserState.ACTIVE);
563+ if (is_logged_in ()) {
564 status_label.label = LOGGED_IN;
565 } else {
566 status_label.label = LOGGED_OFF;
567 }
568+
569+ changed ();
570 }
571
572 private void connect_signals () {
573- user.properties_updated.connect (() => {
574- update (user.real_name, user.icon_file);
575- update_state (user.get_state ());
576+ user.changed.connect (() => {
577+ update ();
578+ update_state ();
579 });
580
581 user.bind_property ("locked", this, "visible", BindingFlags.SYNC_CREATE | BindingFlags.INVERT_BOOLEAN);
582- user.bind_property ("locked", this, "no_show_all", BindingFlags.SYNC_CREATE);
583+ user.bind_property ("locked", this, "no-show-all", BindingFlags.SYNC_CREATE);
584 }
585+
586+ public override bool draw (Cairo.Context ctx) {
587+ if (!get_selectable ()) {
588+ get_style_context ().set_state (Gtk.StateFlags.NORMAL);
589+ }
590+
591+ return base.draw (ctx);
592+ }
593 }
594
595=== modified file 'src/Widgets/UserListBox.vala'
596--- src/Widgets/UserListBox.vala 2016-02-22 17:24:06 +0000
597+++ src/Widgets/UserListBox.vala 2016-08-08 16:32:36 +0000
598@@ -18,6 +18,8 @@
599 */
600
601 public class Session.Widgets.UserListBox : Gtk.ListBox {
602+ public signal void close ();
603+
604 private SeatInterface? seat = null;
605 private string session_path;
606 private bool has_guest;
607@@ -32,6 +34,7 @@
608 stderr.printf ("DisplayManager.Seat error: %s\n", e.message);
609 }
610
611+ this.set_sort_func (sort_func);
612 this.set_activate_on_single_click (true);
613 }
614
615@@ -50,14 +53,35 @@
616 return;
617 }
618
619+ close ();
620 try {
621 if (userbox.is_guest) {
622 seat.switch_to_guest ("");
623 } else {
624- seat.switch_to_user (userbox.user.user_name, session_path);
625- }
626+ seat.switch_to_user (userbox.user.get_user_name (), session_path);
627+ }
628 } catch (IOError e) {
629 stderr.printf ("DisplayManager.Seat error: %s\n", e.message);
630 }
631 }
632- }
633\ No newline at end of file
634+
635+ // We could use here Act.User.collate () but we want to show the logged user first
636+ public int sort_func (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
637+ var userbox1 = (Userbox)row1;
638+ var userbox2 = (Userbox)row2;
639+
640+ if (userbox1.get_user_state () == UserState.ACTIVE) {
641+ return -1;
642+ } else if (userbox2.get_user_state () == UserState.ACTIVE) {
643+ return 1;
644+ }
645+
646+ if (userbox1.is_guest && !userbox2.is_guest) {
647+ return 1;
648+ } else if (!userbox1.is_guest && userbox2.is_guest) {
649+ return -1;
650+ }
651+
652+ return 0;
653+ }
654+}
655\ No newline at end of file

Subscribers

People subscribed via source and target branches