From 55fe8df4842d683b3f7b446e1dbd091bd7a85a4d Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 7 Apr 2026 21:51:52 +0200 Subject: Add widget leak detection --- main/src/ui/util/leak.vala | 48 ++++++++++++++++++++++++++++++++++ main/src/ui/util/size_request_box.vala | 5 ++++ main/src/ui/util/sizing_bin.vala | 4 +++ 3 files changed, 57 insertions(+) create mode 100644 main/src/ui/util/leak.vala (limited to 'main/src/ui/util') 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(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; -- cgit v1.3