diff --git a/src/Indicator.vala b/src/Indicator.vala index 8199ecf7..27c848b7 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -54,6 +54,8 @@ public class Sound.Indicator : Wingpanel.Indicator { } construct { + unowned PulseAudioManager pam; + var touchpad_settings = new GLib.Settings ("org.gnome.desktop.peripherals.touchpad"); touchpad_settings.bind ("natural-scroll", this, "natural-scroll-touchpad", SettingsBindFlags.DEFAULT); var mouse_settings = new GLib.Settings ("org.gnome.desktop.peripherals.mouse"); @@ -75,12 +77,10 @@ public class Sound.Indicator : Wingpanel.Indicator { volume_control.notify["volume"].connect (update_tooltip); volume_control.notify["mute"].connect (update_tooltip); - output_device_manager = new Widgets.DeviceManagerWidget () { - is_input_manager = false - }; - input_device_manager = new Widgets.DeviceManagerWidget () { - is_input_manager = true - }; + pam = PulseAudioManager.get_default (); + output_device_manager = new Widgets.DeviceManagerWidget (false); + input_device_manager = new Widgets.DeviceManagerWidget (true); + pam.start (); Notify.init ("wingpanel-indicator-sound"); @@ -126,6 +126,22 @@ public class Sound.Indicator : Wingpanel.Indicator { if (notify_timeout_id > 0) { Source.remove (notify_timeout_id); } + + unowned PulseAudioManager? pam = PulseAudioManager.get_active (); + if (pam != null) + pam.stop (); + + output_device_manager.clear (); + output_device_manager.destroy (); + + input_device_manager.clear (); + input_device_manager.destroy (); + + output_device_manager = null; + input_device_manager = null; + + if (pam != null) + pam.unref (); } private void set_max_volume () { diff --git a/src/Services/PulseAudioManager.vala b/src/Services/PulseAudioManager.vala index dc1268f6..18856612 100644 --- a/src/Services/PulseAudioManager.vala +++ b/src/Services/PulseAudioManager.vala @@ -34,14 +34,19 @@ public class Sound.PulseAudioManager : GLib.Object { if (pam == null) { pam = new PulseAudioManager (); } + return pam; + } + public static unowned PulseAudioManager? get_active () { return pam; } public signal void new_device (Device dev); + public signal void update_device (Device dev); + public signal void disconnected (); private PulseAudio.Context context; - private PulseAudio.GLibMainLoop loop; + private static PulseAudio.GLibMainLoop loop; private bool is_ready = false; private uint reconnect_timer_id = 0U; private Gee.HashMap input_devices; @@ -56,7 +61,9 @@ public class Sound.PulseAudioManager : GLib.Object { } construct { - loop = new PulseAudio.GLibMainLoop (); + if (loop == null) + loop = new PulseAudio.GLibMainLoop (); + input_devices = new Gee.HashMap (); output_devices = new Gee.HashMap (); @@ -67,10 +74,39 @@ public class Sound.PulseAudioManager : GLib.Object { } } + ~PulseAudioManager () { + if (reconnect_timer_id != 0U) { + Source.remove (reconnect_timer_id); + reconnect_timer_id = 0U; + } + pam = null; + } + public void start () { reconnect_to_pulse.begin (); } + public void stop () { + if (reconnect_timer_id != 0U) { + Source.remove (reconnect_timer_id); + reconnect_timer_id = 0U; + } + + foreach (var device in input_devices.values) { + device.unref (); + } + input_devices.clear (); + default_input = null; + + foreach (var device in output_devices.values) { + device.unref (); + } + output_devices.clear (); + default_output = null; + + is_ready = false; + } + public async void set_default_device (Device device) { debug ("\n"); debug ("set_default_device: %s", device.id); @@ -245,14 +281,23 @@ public class Sound.PulseAudioManager : GLib.Object { PulseAudio.Context.SubscriptionMask.CARD); context.get_server_info (server_info_callback); is_ready = true; + if (reconnect_timer_id != 0U) { + Source.remove (reconnect_timer_id); + reconnect_timer_id = 0U; + } break; case PulseAudio.Context.State.FAILED: - case PulseAudio.Context.State.TERMINATED: + disconnected (); + is_ready = false; if (reconnect_timer_id == 0U) { reconnect_timer_id = Timeout.add_seconds (2, reconnect_timeout); } + break; + case PulseAudio.Context.State.TERMINATED: + stop (); + disconnected (); break; default: @@ -563,6 +608,8 @@ public class Sound.PulseAudioManager : GLib.Object { if (is_new) { devices.set (id, device); new_device (device); + } else { + update_device (device); } } diff --git a/src/Widgets/DeviceItem.vala b/src/Widgets/DeviceItem.vala index 8913033e..d0aa79c3 100644 --- a/src/Widgets/DeviceItem.vala +++ b/src/Widgets/DeviceItem.vala @@ -27,13 +27,14 @@ public class DeviceItem : Gtk.ListBoxRow { private Gtk.RadioButton radio_button; public Gtk.ListBoxRow row { get; construct; } + public string device_id { get; construct; } public string display_name { get; construct; } public string icon_name { get; construct; } public bool is_priority { get; set construct; } public bool is_default { get; construct; } - public DeviceItem (string display_name, bool is_default, bool is_priority, string icon_name, Gtk.ListBoxRow? row) { - Object (display_name: display_name, is_default: is_default, is_priority: is_priority, icon_name: icon_name, row: row); + public DeviceItem (string device_id, string display_name, bool is_default, bool is_priority, string icon_name, Gtk.ListBoxRow? row) { + Object (device_id: device_id, display_name: display_name, is_default: is_default, is_priority: is_priority, icon_name: icon_name, row: row); } class construct { diff --git a/src/Widgets/DeviceManagerWidget.vala b/src/Widgets/DeviceManagerWidget.vala index 67caa2f4..9e5e36cf 100644 --- a/src/Widgets/DeviceManagerWidget.vala +++ b/src/Widgets/DeviceManagerWidget.vala @@ -23,16 +23,24 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { private Gtk.ListBox device_list; private Gtk.ScrolledWindow scrolled_box; - public bool is_input_manager; + public bool is_input_manager { get; construct; } private unowned PulseAudioManager pam; + public DeviceManagerWidget (bool is_input_manager) { + Object (is_input_manager: is_input_manager); + } + construct { pam = PulseAudioManager.get_default (); pam.new_device.connect (add_device); - pam.notify["default-output"].connect (default_output_changed); - pam.notify["default-input"].connect (default_input_changed); - pam.start (); + pam.update_device.connect (update_device); + pam.disconnected.connect (disconnected); + if (is_input_manager) { + pam.notify["default-input"].connect (default_changed); + } else { + pam.notify["default-output"].connect (default_changed); + } device_list = new Gtk.ListBox () { activate_on_single_click = true, @@ -52,13 +60,37 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { update_showable (); } + public void clear () { + foreach (Gtk.Widget child in device_list.get_children ()) { + device_list.remove (child); + child.destroy (); + } + } + + private void disconnected () { + clear (); + } + + private void update_device (Device device) { + if (device.input != is_input_manager) { + return; + } + foreach (unowned var child in device_list.get_children ()) { + if (device.id == ((DeviceItem) child).device_id) { + return; + } + } + add_device (device); + } + private void add_device (Device device) { if (device.input != is_input_manager) { return; } Gtk.ListBoxRow? row = device_list.get_row_at_index (0); - var device_item = new DeviceItem (device.display_name, + var device_item = new DeviceItem (device.id, + device.display_name, device.is_default, device.is_priority, device.get_nice_icon (), @@ -74,6 +106,7 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { device_list.remove (device_item); device_list.show_all (); update_showable (); + device_item.destroy (); }); device.defaulted.connect (() => { @@ -82,6 +115,10 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { update_showable (); }); + if (device.is_default) { + device_item.set_default (); + } + update_showable (); } @@ -137,11 +174,10 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { } } - private void default_output_changed () { - pam.default_output.defaulted (); + private void default_changed () { + unowned var output = is_input_manager ? pam.default_input : pam.default_output; + if (output != null) + output.defaulted (); } - private void default_input_changed () { - pam.default_input.defaulted (); - } }