Merge lp:~mterry/indicator-sound/greeter-volume into lp:indicator-sound/14.04

Proposed by Michael Terry
Status: Merged
Approved by: Ted Gould
Approved revision: 430
Merged at revision: 431
Proposed branch: lp:~mterry/indicator-sound/greeter-volume
Merge into: lp:indicator-sound/14.04
Diff against target: 229 lines (+166/-11)
1 file modified
src/volume-control.vala (+166/-11)
To merge this branch: bzr merge lp:~mterry/indicator-sound/greeter-volume
Reviewer Review Type Date Requested Status
Ted Gould (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+209159@code.launchpad.net

Commit message

Sync volume and mute settings with AccountsService.

Description of the change

Sync volume and mute settings with AccountsService.

On startup, the volume/mute is grabbed from AS. If the user changes either, AS is informed. If AS changes, we take note.

If the greeter is available, we look at whichever user is selected rather than the current user (i.e. greeter vs user session handling).

This needs https://code.launchpad.net/~mterry/gsettings-ubuntu-touch-schemas/volume/+merge/209158 to land first / with it.

Should fix bug 840777.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
427. By Michael Terry

Remove touch namespace for sound settings

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :

I think we should change a few things here.

* I think that a better way to detect if we're on the greeter or not is to check which user we're running as. Just checking to see if a name is owned could get messed up pretty easily. It also means we don't have a race with dbus getting the name owner ever. We know our username at start.

* I'd like to see the accounts service support use the accounts service lib. It seems like a better solution.

* I'd like to see the greeter interface as an object. Vala makes this pretty easy, and it's much better than calling the dbus connection directly.

For the last two I think the easiest way to do them might be to base your branch on:

https://code.launchpad.net/~ted/indicator-sound/greeter-control/+merge/209338

Which sets those up already.

review: Needs Fixing
428. By Michael Terry

Merge from trunk

429. By Michael Terry

Check XDG_SESSION_CLASS to determine if we're in greeter; use native vala dbus interface for greeter list

430. By Michael Terry

Fix bug where we weren't asking for volume settings on startup

Revision history for this message
Michael Terry (mterry) wrote :

As discussed on IRC, I don't want to check username because the greeter user is a sysadmin-configurable option. But I can check an environment variable that LightDM sets that can tell us if we are in greeter. That's done.

I made the greeter interface an object.

But on using accounts service lib or a vala interface object for AccountsService, I'm going to give soft pushback. The lib isn't async nor can vala interface objects expose async property getter/setters. And since this is in UI code (like, this code is run when the user moves the volume control), I don't think making sync calls is appropriate.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :

The actual variables would still have to be accessed through a DBusProxy, the part that the account service API would give you is the object path to the user object.

Revision history for this message
Michael Terry (mterry) wrote :

Yeah, but getting (A) the main Manager proxy and (B) asking it for the object path are both sync operations in the library API.

And once you have the object path, Vala doesn't let you perform async get/set with DBus object properties. (We could define a DBus interface for the org.freedesktop.DBus.Properties interface and use Get()/Set() directly, but that's not buying us much.)

So all in all, I agree it would be prettier code, but we'd be giving up too much.

Revision history for this message
Ted Gould (ted) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/volume-control.vala'
2--- src/volume-control.vala 2013-11-15 20:55:45 +0000
3+++ src/volume-control.vala 2014-03-14 14:49:38 +0000
4@@ -1,4 +1,5 @@
5 /*
6+ * -*- Mode:Vala; indent-tabs-mode:t; tab-width:4; encoding:utf8 -*-
7 * Copyright 2013 Canonical Ltd.
8 *
9 * This program is free software; you can redistribute it and/or modify
10@@ -22,6 +23,13 @@
11 [CCode(cname="pa_cvolume_set", cheader_filename = "pulse/volume.h")]
12 extern unowned PulseAudio.CVolume? vol_set (PulseAudio.CVolume? cv, uint channels, PulseAudio.Volume v);
13
14+[DBus (name="com.canonical.UnityGreeter.List")]
15+interface GreeterListInterface : Object
16+{
17+ public abstract async string get_active_entry () throws IOError;
18+ public signal void entry_selected (string entry_name);
19+}
20+
21 public class VolumeControl : Object
22 {
23 /* this is static to ensure it being freed after @context (loop does not have ref counting) */
24@@ -34,6 +42,11 @@
25 private double _volume = 0.0;
26 private double _mic_volume = 0.0;
27
28+ private DBusProxy _user_proxy;
29+ private GreeterListInterface _greeter_proxy;
30+ private Cancellable _mute_cancellable;
31+ private Cancellable _volume_cancellable;
32+
33 public signal void volume_changed (double v);
34 public signal void mic_volume_changed (double v);
35
36@@ -48,6 +61,10 @@
37 if (loop == null)
38 loop = new PulseAudio.GLibMainLoop ();
39
40+ _mute_cancellable = new Cancellable ();
41+ _volume_cancellable = new Cancellable ();
42+ setup_accountsservice.begin ();
43+
44 this.reconnect_to_pulse ();
45 }
46
47@@ -212,14 +229,25 @@
48 }
49
50 /* Mute operations */
51+ bool set_mute_internal (bool mute)
52+ {
53+ return_val_if_fail (context.get_state () == Context.State.READY, false);
54+
55+ if (_mute != mute) {
56+ if (mute)
57+ context.get_sink_info_list (sink_info_list_callback_set_mute);
58+ else
59+ context.get_sink_info_list (sink_info_list_callback_unset_mute);
60+ return true;
61+ } else {
62+ return false;
63+ }
64+ }
65+
66 public void set_mute (bool mute)
67 {
68- return_if_fail (context.get_state () == Context.State.READY);
69-
70- if (mute)
71- context.get_sink_info_list (sink_info_list_callback_set_mute);
72- else
73- context.get_sink_info_list (sink_info_list_callback_unset_mute);
74+ if (set_mute_internal (mute))
75+ sync_mute_to_accountsservice.begin (mute);
76 }
77
78 public void toggle_mute ()
79@@ -274,13 +302,23 @@
80 context.get_sink_info_by_name (i.default_sink_name, sink_info_set_volume_cb);
81 }
82
83+ bool set_volume_internal (double volume)
84+ {
85+ return_val_if_fail (context.get_state () == Context.State.READY, false);
86+
87+ if (_volume != volume) {
88+ _volume = volume;
89+ context.get_server_info (server_info_cb_for_set_volume);
90+ return true;
91+ } else {
92+ return false;
93+ }
94+ }
95+
96 public void set_volume (double volume)
97 {
98- return_if_fail (context.get_state () == Context.State.READY);
99-
100- _volume = volume;
101-
102- context.get_server_info (server_info_cb_for_set_volume);
103+ if (set_volume_internal (volume))
104+ sync_volume_to_accountsservice.begin (volume);
105 }
106
107 void set_mic_volume_success_cb (Context c, int success)
108@@ -315,4 +353,121 @@
109 {
110 return _mic_volume;
111 }
112+
113+ /* AccountsService operations */
114+ private void accountsservice_props_changed_cb (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties)
115+ {
116+ Variant volume_variant = changed_properties.lookup_value ("Volume", new VariantType ("d"));
117+ if (volume_variant != null) {
118+ var volume = volume_variant.get_double ();
119+ if (volume >= 0)
120+ set_volume_internal (volume);
121+ }
122+
123+ Variant mute_variant = changed_properties.lookup_value ("Muted", new VariantType ("b"));
124+ if (mute_variant != null) {
125+ var mute = mute_variant.get_boolean ();
126+ set_mute_internal (mute);
127+ }
128+ }
129+
130+ private async void setup_user_proxy (string? username_in = null)
131+ {
132+ var username = username_in;
133+ _user_proxy = null;
134+
135+ // Look up currently selected greeter user, if asked
136+ if (username == null) {
137+ try {
138+ username = yield _greeter_proxy.get_active_entry ();
139+ if (username == "" || username == null)
140+ return;
141+ } catch (GLib.Error e) {
142+ warning ("unable to find Accounts path for user %s: %s", username, e.message);
143+ return;
144+ }
145+ }
146+
147+ // Get master AccountsService object
148+ DBusProxy accounts_proxy;
149+ try {
150+ accounts_proxy = yield DBusProxy.create_for_bus (BusType.SYSTEM, DBusProxyFlags.DO_NOT_LOAD_PROPERTIES | DBusProxyFlags.DO_NOT_CONNECT_SIGNALS, null, "org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts");
151+ } catch (GLib.Error e) {
152+ warning ("unable to get greeter proxy: %s", e.message);
153+ return;
154+ }
155+
156+ // Find user's AccountsService object
157+ try {
158+ var user_path_variant = yield accounts_proxy.call ("FindUserByName", new Variant ("(s)", username), DBusCallFlags.NONE, -1);
159+ string user_path;
160+ user_path_variant.get ("(o)", out user_path);
161+ _user_proxy = yield DBusProxy.create_for_bus (BusType.SYSTEM, DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, "org.freedesktop.Accounts", user_path, "com.ubuntu.AccountsService.Sound");
162+ } catch (GLib.Error e) {
163+ warning ("unable to find Accounts path for user %s: %s", username, e.message);
164+ return;
165+ }
166+
167+ // Get current values and listen for changes
168+ _user_proxy.g_properties_changed.connect (accountsservice_props_changed_cb);
169+ var props_variant = yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "GetAll", new Variant ("(s)", _user_proxy.get_interface_name ()), null, DBusCallFlags.NONE, -1);
170+ Variant props;
171+ props_variant.get ("(@a{sv})", out props);
172+ accountsservice_props_changed_cb(_user_proxy, props, null);
173+ }
174+
175+ private void greeter_user_changed (string username)
176+ {
177+ setup_user_proxy.begin (username);
178+ }
179+
180+ private async void setup_accountsservice ()
181+ {
182+ if (Environment.get_variable ("XDG_SESSION_CLASS") == "greeter") {
183+ try {
184+ _greeter_proxy = yield Bus.get_proxy (BusType.SESSION, "com.canonical.UnityGreeter", "/list");
185+ } catch (GLib.Error e) {
186+ warning ("unable to get greeter proxy: %s", e.message);
187+ return;
188+ }
189+ _greeter_proxy.entry_selected.connect (greeter_user_changed);
190+ yield setup_user_proxy ();
191+ } else {
192+ // We are in a user session. We just need our own proxy
193+ var username = Environment.get_variable ("USER");
194+ if (username != "" && username != null) {
195+ yield setup_user_proxy (username);
196+ }
197+ }
198+ }
199+
200+ private async void sync_mute_to_accountsservice (bool mute)
201+ {
202+ if (_user_proxy == null)
203+ return;
204+
205+ _mute_cancellable.cancel ();
206+ _mute_cancellable.reset ();
207+
208+ try {
209+ yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "Set", new Variant ("(ssv)", _user_proxy.get_interface_name (), "Muted", new Variant ("b", mute)), null, DBusCallFlags.NONE, -1, _mute_cancellable);
210+ } catch (GLib.Error e) {
211+ warning ("unable to sync mute to AccountsService: %s", e.message);
212+ }
213+ }
214+
215+ private async void sync_volume_to_accountsservice (double volume)
216+ {
217+ if (_user_proxy == null)
218+ return;
219+
220+ _volume_cancellable.cancel ();
221+ _volume_cancellable.reset ();
222+
223+ try {
224+ yield _user_proxy.get_connection ().call (_user_proxy.get_name (), _user_proxy.get_object_path (), "org.freedesktop.DBus.Properties", "Set", new Variant ("(ssv)", _user_proxy.get_interface_name (), "Volume", new Variant ("d", volume)), null, DBusCallFlags.NONE, -1, _volume_cancellable);
225+ } catch (GLib.Error e) {
226+ warning ("unable to sync volume to AccountsService: %s", e.message);
227+ }
228+ }
229 }

Subscribers

People subscribed via source and target branches