From 7e73bf89ff64e130a26fe31250504f69511ef7e9 Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Sat, 10 Apr 2021 20:03:12 -0400 Subject: [PATCH 1/4] Fix reconnecting to PulseAudio, reattaching to devices --- src/Services/PulseAudioManager.vala | 9 +++++++++ src/Widgets/DeviceManagerWidget.vala | 29 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/Services/PulseAudioManager.vala b/src/Services/PulseAudioManager.vala index f385d228..f322b2b5 100644 --- a/src/Services/PulseAudioManager.vala +++ b/src/Services/PulseAudioManager.vala @@ -39,6 +39,8 @@ public class Sound.PulseAudioManager : GLib.Object { } 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; @@ -245,10 +247,15 @@ 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 (); if (reconnect_timer_id == 0U) { reconnect_timer_id = Timeout.add_seconds (2, reconnect_timeout); } @@ -549,6 +556,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/DeviceManagerWidget.vala b/src/Widgets/DeviceManagerWidget.vala index 318180e8..78c0c746 100644 --- a/src/Widgets/DeviceManagerWidget.vala +++ b/src/Widgets/DeviceManagerWidget.vala @@ -30,6 +30,8 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { construct { pam = PulseAudioManager.get_default (); pam.new_device.connect (add_device); + pam.update_device.connect (update_device); + pam.disconnected.connect (disconnected); pam.notify["default-output"].connect (default_output_changed); pam.notify["default-input"].connect (default_input_changed); pam.start (); @@ -52,6 +54,29 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { update_showable (); } + private void disconnected () { + if (null == device_list) { + return; + } + foreach (unowned var child in device_list.get_children ()) { + device_list.remove (child); + } + } + + private void update_device (Device device) { + if (device.input != is_input_manager) { + return; + } + if (null != device_list) { + foreach (unowned var child in device_list.get_children ()) { + if (device.display_name == ((DeviceItem) child).display_name) { + return; + } + } + } + add_device (device); + } + private void add_device (Device device) { if (device.input != is_input_manager) { return; @@ -80,6 +105,10 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { update_showable (); }); + if (device.is_default) { + device_item.set_default (); + } + update_showable (); } From c1a90dbf67e610c46f88d3eedb7ae2fa9a0e5cb2 Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Thu, 29 Apr 2021 19:39:48 -0400 Subject: [PATCH 2/4] WIP: Fixing various memory leaks, object disposal --- src/Indicator.vala | 28 +++++++++++---- src/Services/PulseAudioManager.vala | 45 +++++++++++++++++++++-- src/Widgets/DeviceItem.vala | 9 +++-- src/Widgets/DeviceManagerWidget.vala | 53 ++++++++++++++++++---------- 4 files changed, 106 insertions(+), 29 deletions(-) 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 f322b2b5..6ff75ce7 100644 --- a/src/Services/PulseAudioManager.vala +++ b/src/Services/PulseAudioManager.vala @@ -34,7 +34,10 @@ public class Sound.PulseAudioManager : GLib.Object { if (pam == null) { pam = new PulseAudioManager (); } + return pam; + } + public static unowned PulseAudioManager? get_active () { return pam; } @@ -43,7 +46,7 @@ public class Sound.PulseAudioManager : GLib.Object { 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; @@ -58,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 (); @@ -69,10 +74,40 @@ public class Sound.PulseAudioManager : GLib.Object { } } + ~PulseAudioManager () { + if (reconnect_timer_id != 0U) { + Source.remove (reconnect_timer_id); + reconnect_timer_id = 0U; + } + pam = null; + warning ("PulseAudio out!"); + } + 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); @@ -254,12 +289,16 @@ public class Sound.PulseAudioManager : GLib.Object { 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: diff --git a/src/Widgets/DeviceItem.vala b/src/Widgets/DeviceItem.vala index bedd6d99..2f255a02 100644 --- a/src/Widgets/DeviceItem.vala +++ b/src/Widgets/DeviceItem.vala @@ -27,19 +27,24 @@ 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; 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 { set_css_name (Gtk.STYLE_CLASS_MENUITEM); } + ~DeviceItem () { + warning ("DeviceItem (%s) out!", device_id); + } + construct { selectable = false; var label = new Gtk.Label (display_name) { diff --git a/src/Widgets/DeviceManagerWidget.vala b/src/Widgets/DeviceManagerWidget.vala index 78c0c746..481cba02 100644 --- a/src/Widgets/DeviceManagerWidget.vala +++ b/src/Widgets/DeviceManagerWidget.vala @@ -23,18 +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.update_device.connect (update_device); pam.disconnected.connect (disconnected); - pam.notify["default-output"].connect (default_output_changed); - pam.notify["default-input"].connect (default_input_changed); - pam.start (); + 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, @@ -54,24 +60,34 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { update_showable (); } - private void disconnected () { - if (null == device_list) { + ~DeviceManagerWidget () { + warning ("DeviceManagerWidget (%s) out!", is_input_manager ? "Input" : "Output"); + } + + public void clear () { + if (device_list == null) { return; } - foreach (unowned var child in device_list.get_children ()) { + 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; } - if (null != device_list) { - foreach (unowned var child in device_list.get_children ()) { - if (device.display_name == ((DeviceItem) child).display_name) { - return; - } + if (device_list == null) { + return; + } + foreach (unowned var child in device_list.get_children ()) { + if (device.id == ((DeviceItem) child).device_id) { + return; } } add_device (device); @@ -83,7 +99,8 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { } 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 (), @@ -98,6 +115,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 (() => { @@ -130,11 +148,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 (); - } } From f7091023e8252397766a2d11f7b981cdee551f12 Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Thu, 29 Apr 2021 19:46:46 -0400 Subject: [PATCH 3/4] Cleanup debug code --- src/Services/PulseAudioManager.vala | 1 - src/Widgets/DeviceItem.vala | 4 ---- src/Widgets/DeviceManagerWidget.vala | 4 ---- 3 files changed, 9 deletions(-) diff --git a/src/Services/PulseAudioManager.vala b/src/Services/PulseAudioManager.vala index 6ff75ce7..1b0a791f 100644 --- a/src/Services/PulseAudioManager.vala +++ b/src/Services/PulseAudioManager.vala @@ -80,7 +80,6 @@ public class Sound.PulseAudioManager : GLib.Object { reconnect_timer_id = 0U; } pam = null; - warning ("PulseAudio out!"); } public void start () { diff --git a/src/Widgets/DeviceItem.vala b/src/Widgets/DeviceItem.vala index 2f255a02..7d390f36 100644 --- a/src/Widgets/DeviceItem.vala +++ b/src/Widgets/DeviceItem.vala @@ -41,10 +41,6 @@ public class DeviceItem : Gtk.ListBoxRow { set_css_name (Gtk.STYLE_CLASS_MENUITEM); } - ~DeviceItem () { - warning ("DeviceItem (%s) out!", device_id); - } - construct { selectable = false; var label = new Gtk.Label (display_name) { diff --git a/src/Widgets/DeviceManagerWidget.vala b/src/Widgets/DeviceManagerWidget.vala index 481cba02..9a1f221c 100644 --- a/src/Widgets/DeviceManagerWidget.vala +++ b/src/Widgets/DeviceManagerWidget.vala @@ -60,10 +60,6 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { update_showable (); } - ~DeviceManagerWidget () { - warning ("DeviceManagerWidget (%s) out!", is_input_manager ? "Input" : "Output"); - } - public void clear () { if (device_list == null) { return; From e9448cd2749e2915a784715122bc71314a53d4b2 Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Thu, 29 Apr 2021 19:55:27 -0400 Subject: [PATCH 4/4] Drop no-longer-needed null device_list check --- src/Widgets/DeviceManagerWidget.vala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Widgets/DeviceManagerWidget.vala b/src/Widgets/DeviceManagerWidget.vala index 9a1f221c..c234c1ac 100644 --- a/src/Widgets/DeviceManagerWidget.vala +++ b/src/Widgets/DeviceManagerWidget.vala @@ -61,9 +61,6 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { } public void clear () { - if (device_list == null) { - return; - } foreach (Gtk.Widget child in device_list.get_children ()) { device_list.remove (child); child.destroy (); @@ -78,9 +75,6 @@ public class Sound.Widgets.DeviceManagerWidget : Gtk.Grid { if (device.input != is_input_manager) { return; } - if (device_list == null) { - return; - } foreach (unowned var child in device_list.get_children ()) { if (device.id == ((DeviceItem) child).device_id) { return;