Memory leak in context menus
As we can see in nautilus_files_view_pop_up_selection_context_menu()
(the same is true for nautilus_files_view_pop_up_background_context_menu()
):
gtk_menu = g_object_ref_sink (gtk_menu_new_from_model (G_MENU_MODEL (priv->selection_menu)));
gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), NULL);
there is not GtkMenuDetachFunc
supplied in gtk_menu_attach_to_widget()
call, which means the menus attached will be never destroyed.
Confirmed with a simple test patching:
--- a/nautilus-canvas-view.c 2018-12-16 08:55:04.954975394 +1000
+++ b/nautilus-canvas-view.c 2018-10-17 11:48:55.556995622 +1000
@@ -1180,6 +1180,10 @@
nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (canvas_view),
event);
+ {
+ GList *menus = gtk_menu_get_for_attach_widget ((GtkWidget *)canvas_view);
+ g_print ("n-menus: %d\n", g_list_length (menus));
+ }
}
static void
which gives
$ /bld/tmp/nautilus-3.30.2/src/build/src/nautilus
n-menus: 1
n-menus: 2
n-menus: 3
n-menus: 4
n-menus: 5
n-menus: 8
that is every context click to either element or background adds more and more menus to the view. The gap from 5 to 8 in log is because there was a couple background context clicks.
UPD
with a current approach something like this could resolve the issue,
--- a/nautilus-files-view.c 2018-10-17 11:48:55.570329147 +1000
+++ b/nautilus-files-view.c 2018-12-16 09:54:59.924353707 +1000
@@ -8105,6 +8105,13 @@
return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reveal_for_selection_context_menu (view);
}
+static void
+_menu_fina (GtkWidget *attach_widget,
+ GtkMenu *menu)
+{
+ gtk_widget_destroy ((GtkWidget *)menu);
+}
+
/**
* nautilus_files_view_pop_up_selection_context_menu
*
@@ -8130,7 +8137,8 @@
update_context_menus_if_pending (view);
gtk_menu = g_object_ref_sink (gtk_menu_new_from_model (G_MENU_MODEL (priv->selection_menu)));
- gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), NULL);
+ g_signal_connect (gtk_menu, "hide", G_CALLBACK (gtk_menu_detach), NULL);
+ gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), _menu_fina);
if (event != NULL)
{
gtk_menu_popup_at_pointer (GTK_MENU (gtk_menu), event);
@@ -8176,7 +8184,8 @@
update_context_menus_if_pending (view);
gtk_menu = g_object_ref_sink (gtk_menu_new_from_model (G_MENU_MODEL (priv->background_menu)));
- gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), NULL);
+ g_signal_connect (gtk_menu, "hide", G_CALLBACK (gtk_menu_detach), NULL);
+ gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), _menu_fina);
if (event != NULL)
{
gtk_menu_popup_at_pointer (GTK_MENU (gtk_menu), event);
BUT, is it really required to have the menu rebuilt on every context click, or can reuse the same one?
UPD2
with a patch above the actions are not starting, likely something that they are required in is removed all together with menu detach. The patch below behaves better:
--- a/nautilus-files-view.c 2018-10-17 11:48:55.570329147 +1000
+++ b/nautilus-files-view.c 2018-12-16 09:54:59.924353707 +1000
@@ -8105,6 +8105,20 @@
return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reveal_for_selection_context_menu (view);
}
+static gboolean
+_immediate_detach (gpointer menu)
+{
+ gtk_menu_detach (menu);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+_delayed_detach (GtkMenu *menu)
+{
+ /* allow a few time to action for start */
+ g_timeout_add (1500, (GSourceFunc)_immediate_detach, menu);
+}
+
/**
* nautilus_files_view_pop_up_selection_context_menu
*
@@ -8130,6 +8144,7 @@
update_context_menus_if_pending (view);
gtk_menu = g_object_ref_sink (gtk_menu_new_from_model (G_MENU_MODEL (priv->selection_menu)));
+ g_signal_connect (gtk_menu, "hide", G_CALLBACK (_delayed_detach), NULL);
gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), NULL);
if (event != NULL)
{
@@ -8176,6 +8191,7 @@
update_context_menus_if_pending (view);
gtk_menu = g_object_ref_sink (gtk_menu_new_from_model (G_MENU_MODEL (priv->background_menu)));
+ g_signal_connect (gtk_menu, "hide", G_CALLBACK (_delayed_detach), NULL);
gtk_menu_attach_to_widget (GTK_MENU (gtk_menu), GTK_WIDGET (view), NULL);
if (event != NULL)
{