gtksettings: Remove display from cache on closing

GTK caches the settings per display in a static `GArray`, keeping a
reference to the `GdkDisplay` as the key.

However, when closing the display, the corresponding entry is not
removed from the cache in `GtkSettings`.

So when reopening again a `GdkDisplay`, if the new address matches one
of the previously closed display, the cache will return the existing
`GtkSettings` from the cache, which still holds a reference to the old
`GdkScreen` which was freed along the `GdkDisplay`.

To avoid the issue, make sure to remove the `GdkDisplay` and
corresponding `GdkSettings` when closing the `GdkDisplay`.

Also, care must be taken not to recreate the `GdkSettings` and re-add
the `GdkDisplay` to the cache once the display is closed, and make sure
callers of `gtk_settings_get_for_display()` can deal with a returned
value being `NULL` if the display is closed.

Fixes: commit 360a3c1690 - "Use a cheaper way to store settings per
display"
This commit is contained in:
Olivier Fourdan 2020-01-23 14:25:29 +01:00
parent c9601f1567
commit b1d55f2c85
3 changed files with 38 additions and 8 deletions

View File

@ -608,9 +608,10 @@ unset_screen (GtkIconTheme *icon_theme)
g_signal_handlers_disconnect_by_func (display,
(gpointer) display_closed,
icon_theme);
g_signal_handlers_disconnect_by_func (settings,
(gpointer) theme_changed,
icon_theme);
if (settings)
g_signal_handlers_disconnect_by_func (settings,
(gpointer) theme_changed,
icon_theme);
priv->screen = NULL;
}

View File

@ -463,10 +463,10 @@ display_closed_cb (GdkDisplay *display,
screen = gdk_display_get_default_screen (display);
settings = gtk_settings_get_for_screen (screen);
g_object_set_data_full (G_OBJECT (settings),
I_("gtk-modules"),
NULL, NULL);
if (settings)
g_object_set_data_full (G_OBJECT (settings),
I_("gtk-modules"),
NULL, NULL);
}

View File

@ -1900,6 +1900,29 @@ settings_init_style (GtkSettings *settings)
settings_update_key_theme (settings);
}
static void
settings_display_closed (GdkDisplay *display,
gboolean is_error,
gpointer data)
{
DisplaySettings *ds;
int i;
if (G_UNLIKELY (display_settings == NULL))
return;
ds = (DisplaySettings *)display_settings->data;
for (i = 0; i < display_settings->len; i++)
{
if (ds[i].display == display)
{
g_clear_object (&ds[i].settings);
display_settings = g_array_remove_index_fast (display_settings, i);
break;
}
}
}
static GtkSettings *
gtk_settings_create_for_display (GdkDisplay *display)
{
@ -1955,7 +1978,9 @@ gtk_settings_create_for_display (GdkDisplay *display)
v.display = display;
v.settings = settings;
g_array_append_val (display_settings, v);
display_settings = g_array_append_val (display_settings, v);
g_signal_connect (display, "closed",
G_CALLBACK (settings_display_closed), NULL);
settings_init_style (settings);
settings_update_xsettings (settings);
@ -1975,6 +2000,10 @@ gtk_settings_get_for_display (GdkDisplay *display)
DisplaySettings *ds;
int i;
/* If the display is closed, we don't want to recreate the settings! */
if G_UNLIKELY (gdk_display_is_closed (display))
return NULL;
if G_UNLIKELY (display_settings == NULL)
display_settings = g_array_new (FALSE, TRUE, sizeof (DisplaySettings));