aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin W <git@larma.de>2026-04-07 21:51:52 +0200
committerMarvin W <git@larma.de>2026-04-08 16:59:43 +0200
commit55fe8df4842d683b3f7b446e1dbd091bd7a85a4d (patch)
treee24fe3254d761f2cc02760b8d73e52f93941acb9
parentaeb8032753dd004db68a0ee144d4f51b4fe3aa0a (diff)
Add widget leak detection
-rw-r--r--main/meson.build1
-rw-r--r--main/src/ui/call_window/audio_settings_popover.vala4
-rw-r--r--main/src/ui/call_window/call_bottom_bar.vala4
-rw-r--r--main/src/ui/call_window/call_connection_details_window.vala8
-rw-r--r--main/src/ui/call_window/call_window.vala2
-rw-r--r--main/src/ui/call_window/participant_widget.vala4
-rw-r--r--main/src/ui/call_window/video_settings_popover.vala4
-rw-r--r--main/src/ui/conversation_content_view/call_widget.vala1
-rw-r--r--main/src/ui/conversation_content_view/file_default_widget.vala4
-rw-r--r--main/src/ui/conversation_content_view/file_image_widget.vala1
-rw-r--r--main/src/ui/conversation_content_view/file_transmission_progress.vala2
-rw-r--r--main/src/ui/conversation_content_view/file_widget.vala1
-rw-r--r--main/src/ui/conversation_content_view/message_widget.vala2
-rw-r--r--main/src/ui/conversation_content_view/quote_widget.vala1
-rw-r--r--main/src/ui/conversation_content_view/reactions_widget.vala6
-rw-r--r--main/src/ui/conversation_content_view/unread_indicator_populator.vala1
-rw-r--r--main/src/ui/conversation_selector/conversation_selector.vala2
-rw-r--r--main/src/ui/conversation_selector/conversation_selector_row.vala1
-rw-r--r--main/src/ui/file_send_overlay.vala4
-rw-r--r--main/src/ui/occupant_menu/list.vala4
-rw-r--r--main/src/ui/occupant_menu/view.vala4
-rw-r--r--main/src/ui/util/leak.vala48
-rw-r--r--main/src/ui/util/size_request_box.vala5
-rw-r--r--main/src/ui/util/sizing_bin.vala4
-rw-r--r--main/src/ui/widgets/avatar_picture.vala2
-rw-r--r--main/src/ui/widgets/date_separator.vala1
-rw-r--r--main/src/ui/widgets/fixed_ratio_picture.vala1
-rw-r--r--main/src/ui/widgets/natural_size_increase.vala1
-rw-r--r--main/src/windows/conversation_details.vala2
29 files changed, 125 insertions, 0 deletions
diff --git a/main/meson.build b/main/meson.build
index 3f5701bc..b91ddcd9 100644
--- a/main/meson.build
+++ b/main/meson.build
@@ -79,6 +79,7 @@ sources = files(
'src/ui/util/data_forms.vala',
'src/ui/util/file_metadata_providers.vala',
'src/ui/util/helper.vala',
+ 'src/ui/util/leak.vala',
'src/ui/util/preference_group.vala',
'src/ui/util/size_request_box.vala',
'src/ui/util/sizing_bin.vala',
diff --git a/main/src/ui/call_window/audio_settings_popover.vala b/main/src/ui/call_window/audio_settings_popover.vala
index f66c5eee..b00d94c9 100644
--- a/main/src/ui/call_window/audio_settings_popover.vala
+++ b/main/src/ui/call_window/audio_settings_popover.vala
@@ -13,6 +13,10 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
private HashMap<ListBoxRow, Plugins.MediaDevice> row_microphone_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
private HashMap<ListBoxRow, Plugins.MediaDevice> row_speaker_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
+ construct {
+ check_widget_leak(this);
+ }
+
public AudioSettingsPopover() {
Box box = new Box(Orientation.VERTICAL, 15);
box.append(create_microphone_box());
diff --git a/main/src/ui/call_window/call_bottom_bar.vala b/main/src/ui/call_window/call_bottom_bar.vala
index 515fdb38..dcb869c7 100644
--- a/main/src/ui/call_window/call_bottom_bar.vala
+++ b/main/src/ui/call_window/call_bottom_bar.vala
@@ -26,6 +26,10 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
private Label label = new Label("") { halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true };
private Stack stack = new Stack();
+ construct {
+ check_widget_leak(this);
+ }
+
public CallBottomBar() {
Object(orientation:Orientation.HORIZONTAL, spacing:0);
diff --git a/main/src/ui/call_window/call_connection_details_window.vala b/main/src/ui/call_window/call_connection_details_window.vala
index 9e9e1b3a..578d95ed 100644
--- a/main/src/ui/call_window/call_connection_details_window.vala
+++ b/main/src/ui/call_window/call_connection_details_window.vala
@@ -10,6 +10,10 @@ namespace Dino.Ui {
private CallContentDetails audio_details = new CallContentDetails("Audio");
private CallContentDetails video_details = new CallContentDetails("Video");
+ construct {
+ check_widget_leak(this);
+ }
+
public CallConnectionDetailsWindow() {
box.append(audio_details);
box.append(video_details);
@@ -52,6 +56,10 @@ namespace Dino.Ui {
private PeerContentInfo? prev_info = null;
private int row_at = 0;
+ construct {
+ check_widget_leak(this);
+ }
+
public CallContentDetails(string headline) {
attach(new Label("<b>%s</b>".printf(headline)) { use_markup=true, xalign=0 }, 0, row_at++, 1, 1);
attach(rtp_title, 0, row_at, 1, 1);
diff --git a/main/src/ui/call_window/call_window.vala b/main/src/ui/call_window/call_window.vala
index 54e290a7..4a804755 100644
--- a/main/src/ui/call_window/call_window.vala
+++ b/main/src/ui/call_window/call_window.vala
@@ -52,6 +52,8 @@ namespace Dino.Ui {
overlay.get_child_position.connect(on_get_child_position);
set_child(overlay);
+
+ check_widget_leak(this);
}
public CallWindow() {
diff --git a/main/src/ui/call_window/participant_widget.vala b/main/src/ui/call_window/participant_widget.vala
index 045040df..fbcd9eab 100644
--- a/main/src/ui/call_window/participant_widget.vala
+++ b/main/src/ui/call_window/participant_widget.vala
@@ -34,6 +34,10 @@ namespace Dino.Ui {
install_action("menu.debuginfo", null, (widget, action_name) => { ((ParticipantWidget) widget).debug_information_clicked(); });
}
+ construct {
+ check_widget_leak(this);
+ }
+
public ParticipantWidget(string participant_name) {
encryption_button_controller = new CallEncryptionButtonController(encryption_button);
diff --git a/main/src/ui/call_window/video_settings_popover.vala b/main/src/ui/call_window/video_settings_popover.vala
index 58967bd2..26728d0c 100644
--- a/main/src/ui/call_window/video_settings_popover.vala
+++ b/main/src/ui/call_window/video_settings_popover.vala
@@ -10,6 +10,10 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
private HashMap<ListBoxRow, Plugins.MediaDevice> row_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
+ construct {
+ check_widget_leak(this);
+ }
+
public VideoSettingsPopover() {
Box box = new Box(Orientation.VERTICAL, 15);
box.append(create_camera_box());
diff --git a/main/src/ui/conversation_content_view/call_widget.vala b/main/src/ui/conversation_content_view/call_widget.vala
index 3d6b250b..9e8a5dac 100644
--- a/main/src/ui/conversation_content_view/call_widget.vala
+++ b/main/src/ui/conversation_content_view/call_widget.vala
@@ -50,6 +50,7 @@ namespace Dino.Ui {
construct {
margin_top = 4;
size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH;
+ check_widget_leak(this);
}
/** @param call_state Null if it's an old call and we can't interact with it anymore */
diff --git a/main/src/ui/conversation_content_view/file_default_widget.vala b/main/src/ui/conversation_content_view/file_default_widget.vala
index b731c9df..f211e3fe 100644
--- a/main/src/ui/conversation_content_view/file_default_widget.vala
+++ b/main/src/ui/conversation_content_view/file_default_widget.vala
@@ -20,6 +20,10 @@ public class FileDefaultWidget : Box {
private FileTransfer.State state;
+ construct {
+ check_widget_leak(this);
+ }
+
public void init_updating_file_info() {
EventControllerMotion this_motion_events = new EventControllerMotion();
this.add_controller(this_motion_events);
diff --git a/main/src/ui/conversation_content_view/file_image_widget.vala b/main/src/ui/conversation_content_view/file_image_widget.vala
index 2015d9dc..e936ca25 100644
--- a/main/src/ui/conversation_content_view/file_image_widget.vala
+++ b/main/src/ui/conversation_content_view/file_image_widget.vala
@@ -28,6 +28,7 @@ public class FileImageWidget : Widget {
construct {
layout_manager = new BinLayout();
+ check_widget_leak(this);
}
public FileImageWidget(int MAX_WIDTH=600, int MAX_HEIGHT=300) {
diff --git a/main/src/ui/conversation_content_view/file_transmission_progress.vala b/main/src/ui/conversation_content_view/file_transmission_progress.vala
index 92833ea9..e3f33448 100644
--- a/main/src/ui/conversation_content_view/file_transmission_progress.vala
+++ b/main/src/ui/conversation_content_view/file_transmission_progress.vala
@@ -41,6 +41,8 @@ namespace Dino.Ui {
on_state_changed();
setup_animation();
+
+ check_widget_leak(this);
}
private void setup_animation() {
diff --git a/main/src/ui/conversation_content_view/file_widget.vala b/main/src/ui/conversation_content_view/file_widget.vala
index 45972e1c..ecc2d3e9 100644
--- a/main/src/ui/conversation_content_view/file_widget.vala
+++ b/main/src/ui/conversation_content_view/file_widget.vala
@@ -72,6 +72,7 @@ public class FileWidget : SizeRequestBin {
construct {
margin_top = 4;
size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH;
+ check_widget_leak(this);
}
public FileWidget(FileTransfer file_transfer) {
diff --git a/main/src/ui/conversation_content_view/message_widget.vala b/main/src/ui/conversation_content_view/message_widget.vala
index cead6332..7d441657 100644
--- a/main/src/ui/conversation_content_view/message_widget.vala
+++ b/main/src/ui/conversation_content_view/message_widget.vala
@@ -351,6 +351,8 @@ public class MessageItemEditMode : Box {
send_button.clicked.connect(() => send());
chat_text_view.cancel_input.connect(() => cancelled());
chat_text_view.send_text.connect(() => send());
+
+ check_widget_leak(this);
}
private void on_text_view_changed() {
diff --git a/main/src/ui/conversation_content_view/quote_widget.vala b/main/src/ui/conversation_content_view/quote_widget.vala
index aedce687..ce0011e1 100644
--- a/main/src/ui/conversation_content_view/quote_widget.vala
+++ b/main/src/ui/conversation_content_view/quote_widget.vala
@@ -83,6 +83,7 @@ namespace Dino.Ui.Quote {
});
Widget outer = builder.get_object("outer") as Widget;
+ check_widget_leak(outer);
GestureClick gesture_click_controller = new GestureClick();
outer.add_controller(gesture_click_controller);
diff --git a/main/src/ui/conversation_content_view/reactions_widget.vala b/main/src/ui/conversation_content_view/reactions_widget.vala
index fe465676..04d71256 100644
--- a/main/src/ui/conversation_content_view/reactions_widget.vala
+++ b/main/src/ui/conversation_content_view/reactions_widget.vala
@@ -120,12 +120,17 @@ public class ReactionsWidget : Grid {
private HashMap<string, Button> reaction_buttons = new HashMap<string, Button>();
private MenuButton add_button;
+ construct {
+ check_widget_leak(this);
+ }
+
public ReactionsWidget() {
this.row_spacing = this.column_spacing = 5;
this.margin_top = 2;
this.add_css_class("reaction-grid");
add_button = new MenuButton() { tooltip_text= _("Add reaction") };
+ check_widget_leak(add_button);
add_button.add_css_class("pill");
Util.menu_button_set_icon_with_size(add_button, "dino-emoticon-add-symbolic", 14);
@@ -141,6 +146,7 @@ public class ReactionsWidget : Grid {
Label reaction_label = new Label("<span size='small'>" + reaction + "</span>") { use_markup=true };
Label count_label = new Label("") { use_markup=true };
Button button = new Button();
+ check_widget_leak(button);
button.add_css_class("pill");
Box reaction_box = new Box(Orientation.HORIZONTAL, 4) { halign=Align.CENTER };
reaction_box.append(reaction_label);
diff --git a/main/src/ui/conversation_content_view/unread_indicator_populator.vala b/main/src/ui/conversation_content_view/unread_indicator_populator.vala
index 79aa7576..f52408ba 100644
--- a/main/src/ui/conversation_content_view/unread_indicator_populator.vala
+++ b/main/src/ui/conversation_content_view/unread_indicator_populator.vala
@@ -68,6 +68,7 @@ namespace Dino.Ui.ConversationSummary {
public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
Box box = new Box(Orientation.HORIZONTAL, 10) { hexpand=true };
+ check_widget_leak(box);
box.get_style_context().add_class("dino-unread-line");
Separator sep = new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true };
diff --git a/main/src/ui/conversation_selector/conversation_selector.vala b/main/src/ui/conversation_selector/conversation_selector.vala
index 3b3caf5a..5d88c540 100644
--- a/main/src/ui/conversation_selector/conversation_selector.vala
+++ b/main/src/ui/conversation_selector/conversation_selector.vala
@@ -48,6 +48,8 @@ public class ConversationSelector : Widget {
});
list_box.row_activated.connect(row_activated);
+
+ check_widget_leak(this);
}
public void row_activated(ListBoxRow r) {
diff --git a/main/src/ui/conversation_selector/conversation_selector_row.vala b/main/src/ui/conversation_selector/conversation_selector_row.vala
index 566406f9..38132c5b 100644
--- a/main/src/ui/conversation_selector/conversation_selector_row.vala
+++ b/main/src/ui/conversation_selector/conversation_selector_row.vala
@@ -33,6 +33,7 @@ public class ConversationSelectorRow : ListBoxRow {
construct {
name_label.attributes = new AttrList();
+ check_widget_leak(this);
}
public ConversationSelectorRow(StreamInteractor stream_interactor, Conversation conversation) {
diff --git a/main/src/ui/file_send_overlay.vala b/main/src/ui/file_send_overlay.vala
index 0067e09d..907cea00 100644
--- a/main/src/ui/file_send_overlay.vala
+++ b/main/src/ui/file_send_overlay.vala
@@ -18,6 +18,10 @@ public class FileSendOverlay : Adw.Dialog {
private File file;
private bool can_send = true;
+ construct {
+ check_widget_leak(this);
+ }
+
public FileSendOverlay(File file, FileInfo file_info) {
this.file = file;
load_file_widget.begin(file, file_info);
diff --git a/main/src/ui/occupant_menu/list.vala b/main/src/ui/occupant_menu/list.vala
index ce5a1981..6f303ab2 100644
--- a/main/src/ui/occupant_menu/list.vala
+++ b/main/src/ui/occupant_menu/list.vala
@@ -20,6 +20,10 @@ public class List : Box {
private HashMap<Jid, Widget> rows = new HashMap<Jid, Widget>(Jid.hash_func, Jid.equals_func);
public HashMap<Widget, ListRow> row_wrappers = new HashMap<Widget, ListRow>();
+ construct {
+ check_widget_leak(this);
+ }
+
public List(StreamInteractor stream_interactor, Conversation conversation) {
this.stream_interactor = stream_interactor;
list_box.set_header_func(header);
diff --git a/main/src/ui/occupant_menu/view.vala b/main/src/ui/occupant_menu/view.vala
index 08e1e9ed..4196e452 100644
--- a/main/src/ui/occupant_menu/view.vala
+++ b/main/src/ui/occupant_menu/view.vala
@@ -18,6 +18,10 @@ public class View : Popover {
private Jid? selected_jid;
+ construct {
+ check_widget_leak(this);
+ }
+
public View(StreamInteractor stream_interactor, Conversation conversation) {
this.stream_interactor = stream_interactor;
this.conversation = conversation;
diff --git a/main/src/ui/util/leak.vala b/main/src/ui/util/leak.vala
new file mode 100644
index 00000000..07eb1e46
--- /dev/null
+++ b/main/src/ui/util/leak.vala
@@ -0,0 +1,48 @@
+namespace Dino {
+ private const string CHECK_WIDGET_LEAK_ID = "dino_check_widget_leak_id";
+ private const string CHECK_WIDGET_LEAK_NAME = "dino_check_widget_leak_name";
+ private const string CHECK_WIDGET_LEAK_ACTIVE = "dino_check_widget_leak_active";
+ private static void check_widget_leak_timeout_reached(Gtk.Widget widget) {
+ string name = widget.get_data(CHECK_WIDGET_LEAK_NAME) ?? widget.get_type().name();
+ warning("%s likely leaked", name);
+ widget.set_data(CHECK_WIDGET_LEAK_ID, 0);
+ }
+ private static void check_widget_leak_disposed(Object widget) {
+ uint timeout_id = widget.get_data(CHECK_WIDGET_LEAK_ID);
+ if (timeout_id != 0) Source.remove(timeout_id);
+ widget.set_data(CHECK_WIDGET_LEAK_ID, 0);
+ }
+ private static void check_widget_leak_unrealized(Gtk.Widget widget) {
+ widget.set_data(CHECK_WIDGET_LEAK_ID, WeakTimeout.add_seconds_once(1, widget, check_widget_leak_timeout_reached));
+ widget.weak_ref(check_widget_leak_disposed);
+ }
+ private static void check_widget_leak_realized(Gtk.Widget widget) {
+ uint timeout_id = widget.get_data(CHECK_WIDGET_LEAK_ID);
+ if (timeout_id != 0) Source.remove(timeout_id);
+ widget.set_data(CHECK_WIDGET_LEAK_ID, 0);
+ string name = widget.get_type().name();
+ Gtk.Widget parent = widget.parent;
+ while (parent != null) {
+ name = parent.get_type().name() + " > " + name;
+ if (name.has_prefix("Dino")) break;
+ parent = parent.parent;
+ }
+ widget.set_data(CHECK_WIDGET_LEAK_NAME, name);
+ }
+ public static Gtk.Widget check_widget_leak(Gtk.Widget widget) {
+ if (is_leak_check_enabled() && !widget.get_data<bool>(CHECK_WIDGET_LEAK_ACTIVE)) {
+ widget.set_data(CHECK_WIDGET_LEAK_ACTIVE, true);
+ widget.set_data(CHECK_WIDGET_LEAK_ID, 0);
+ widget.realize.connect(check_widget_leak_realized);
+ widget.unrealize.connect(check_widget_leak_unrealized);
+ if (widget.get_realized()) {
+ check_widget_leak_realized(widget);
+ }
+ }
+ return widget;
+ }
+
+ public static bool is_leak_check_enabled() {
+ return Environment.get_variable("DINO_CHECK_LEAK") == "1";
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/util/size_request_box.vala b/main/src/ui/util/size_request_box.vala
index 98f1a20a..f479e14e 100644
--- a/main/src/ui/util/size_request_box.vala
+++ b/main/src/ui/util/size_request_box.vala
@@ -2,6 +2,10 @@ using Gtk;
namespace Dino.Ui {
public class SizeRequestBox : Box {
+ construct {
+ check_widget_leak(this);
+ }
+
public SizeRequestMode size_request_mode { get; set; default = SizeRequestMode.CONSTANT_SIZE; }
public override Gtk.SizeRequestMode get_request_mode() {
@@ -14,6 +18,7 @@ public class SizeRequestBin : Widget {
construct {
this.layout_manager = new BinLayout();
+ check_widget_leak(this);
}
public override void compute_expand_internal(out bool hexpand, out bool vexpand) {
diff --git a/main/src/ui/util/sizing_bin.vala b/main/src/ui/util/sizing_bin.vala
index 7401ddbf..0f5b9035 100644
--- a/main/src/ui/util/sizing_bin.vala
+++ b/main/src/ui/util/sizing_bin.vala
@@ -9,6 +9,10 @@ public class SizingBin : Widget {
public int target_height { get; set; default = -1; }
public int max_height { get; set; default = -1; }
+ construct {
+ check_widget_leak(this);
+ }
+
public override void compute_expand_internal(out bool hexpand, out bool vexpand) {
hexpand = false;
vexpand = false;
diff --git a/main/src/ui/widgets/avatar_picture.vala b/main/src/ui/widgets/avatar_picture.vala
index ef1462ae..6765364c 100644
--- a/main/src/ui/widgets/avatar_picture.vala
+++ b/main/src/ui/widgets/avatar_picture.vala
@@ -345,6 +345,7 @@ public class Dino.Ui.AvatarPicture : Gtk.Widget {
add_css_class("avatar");
notify["radius-percent"].connect(queue_draw);
notify["model"].connect(on_model_changed);
+ check_widget_leak(this);
}
private void on_model_changed() {
@@ -461,6 +462,7 @@ public class Dino.Ui.AvatarPicture : Gtk.Widget {
#endif
picture.insert_after(this, label);
this.notify["model"].connect(on_model_changed);
+ check_widget_leak(this);
}
private void on_model_changed() {
diff --git a/main/src/ui/widgets/date_separator.vala b/main/src/ui/widgets/date_separator.vala
index 589bc9a6..716e7fec 100644
--- a/main/src/ui/widgets/date_separator.vala
+++ b/main/src/ui/widgets/date_separator.vala
@@ -87,6 +87,7 @@ public class Dino.Ui.DateSeparator : Gtk.Widget {
clamp.insert_after(this, null);
notify["model"].connect(on_model_changed);
+ check_widget_leak(this);
}
private void on_model_changed() {
diff --git a/main/src/ui/widgets/fixed_ratio_picture.vala b/main/src/ui/widgets/fixed_ratio_picture.vala
index db0e0afe..6b04ea61 100644
--- a/main/src/ui/widgets/fixed_ratio_picture.vala
+++ b/main/src/ui/widgets/fixed_ratio_picture.vala
@@ -18,6 +18,7 @@ class Dino.Ui.FixedRatioPicture : Gtk.Widget {
add_css_class("fixed-ratio");
inner.insert_after(this, null);
this.notify.connect(queue_resize);
+ check_widget_leak(this);
}
private void measure_target_size(out int width, out int height) {
diff --git a/main/src/ui/widgets/natural_size_increase.vala b/main/src/ui/widgets/natural_size_increase.vala
index 3aad486a..d786e2b5 100644
--- a/main/src/ui/widgets/natural_size_increase.vala
+++ b/main/src/ui/widgets/natural_size_increase.vala
@@ -6,6 +6,7 @@ public class Dino.Ui.NaturalSizeIncrease : Gtk.Widget {
construct {
this.notify.connect(queue_resize);
+ check_widget_leak(this);
}
public override void compute_expand_internal(out bool hexpand, out bool vexpand) {
diff --git a/main/src/windows/conversation_details.vala b/main/src/windows/conversation_details.vala
index a8d0e430..e1f3061f 100644
--- a/main/src/windows/conversation_details.vala
+++ b/main/src/windows/conversation_details.vala
@@ -86,6 +86,8 @@ namespace Dino.Ui.ConversationDetails {
notification_button_menu_content.can_shrink = true;
update_blocked_button();
+
+ check_widget_leak(this);
}
private void update_pinned_button() {