From 96db18a8fe30eafb7bd3da04d18901723482f97b Mon Sep 17 00:00:00 2001 From: raveit65 Date: Dec 12 2021 17:56:35 +0000 Subject: Scale tasklist icons --- diff --git a/libwnck3.spec b/libwnck3.spec index 48455cc..c514b1d 100644 --- a/libwnck3.spec +++ b/libwnck3.spec @@ -3,12 +3,20 @@ Summary: Window Navigator Construction Kit Name: libwnck3 Version: 40.0 -Release: 2%{?dist} +Release: 3%{?dist} URL: http://download.gnome.org/sources/%{source_name}/ Source0: http://download.gnome.org/sources/%{source_name}/40/%{source_name}-%{version}.tar.xz License: LGPLv2+ Patch1: libwnck_0001-Revert-pager-do-not-change-workspace-size-from-size_.patch +# https://gitlab.gnome.org/GNOME/libwnck/-/commit/bd8ab37 +Patch2: libwnck_0001-xutils-move-WnckIconCache-to-its-own-file.patch +# https://gitlab.gnome.org/GNOME/libwnck/-/merge_requests/10 +Patch3: libwnck_0001-Expose-window-scaling-factor.patch +Patch4: libwnck_0002-icons-Use-cairo-surfaces-to-render-icons.patch +Patch5: libwnck_0003-xutils-Change-icons-to-being-cairo-surfaces.patch +Patch6: libwnck_0004-icons-Mark-GdkPixbuf-icons-as-deprecated.patch +Patch7: libwnck_0005-tasklist-Add-surface-loader-function.patch BuildRequires: gcc BuildRequires: meson @@ -72,6 +80,10 @@ developing applications that use %{name}. %changelog +* Sun Dec 12 2021 Wolfgang Ulbrich - 40.0-3 +- use https://gitlab.gnome.org/GNOME/libwnck/-/commit/bd8ab37 +- Scale tasklist icons + * Fri Jun 11 2021 Wolfgang Ulbrich - 40.0-2 - revert https://gitlab.gnome.org/GNOME/libwnck/-/commit/3456b74 - fixes rhbz #1971048 diff --git a/libwnck_0001-Expose-window-scaling-factor.patch b/libwnck_0001-Expose-window-scaling-factor.patch new file mode 100644 index 0000000..75baa59 --- /dev/null +++ b/libwnck_0001-Expose-window-scaling-factor.patch @@ -0,0 +1,58 @@ +From 42c7798ccd4d488a4a927af62f1b8b4e404e9dd4 Mon Sep 17 00:00:00 2001 +From: Victor Kareh +Date: Tue, 6 Aug 2019 09:59:59 -0400 +Subject: [PATCH 1/5] Expose window scaling factor + +--- + libwnck/private.h | 2 ++ + libwnck/util.c | 21 +++++++++++++++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/libwnck/private.h b/libwnck/private.h +index f1a4af2..199d2b4 100644 +--- a/libwnck/private.h ++++ b/libwnck/private.h +@@ -43,6 +43,8 @@ WnckHandle *_wnck_get_handle (void); + + WnckClientType _wnck_get_client_type (void); + ++int _wnck_get_window_scaling_factor (void); ++ + gsize _wnck_get_default_icon_size (void); + gsize _wnck_get_default_mini_icon_size (void); + +diff --git a/libwnck/util.c b/libwnck/util.c +index b3d8750..d51ee05 100644 +--- a/libwnck/util.c ++++ b/libwnck/util.c +@@ -680,6 +680,27 @@ _wnck_get_client_type (void) + return _wnck_handle_get_client_type (_wnck_get_handle ()); + } + ++/** ++ * _wnck_get_window_scaling_factor: ++ * ++ * Retrieves the internal scale factor that maps from window coordinates to the ++ * actual device pixels. On traditional systems this is 1, on high density ++ * outputs, it can be a higher value (typically 2). ++ */ ++int ++_wnck_get_window_scaling_factor (void) ++{ ++ GdkScreen *screen; ++ GValue value = G_VALUE_INIT; ++ ++ g_value_init (&value, G_TYPE_INT); ++ ++ screen = gdk_screen_get_default (); ++ if (gdk_screen_get_setting (screen, "gdk-window-scaling-factor", &value)) ++ return g_value_get_int (&value); ++ return 1; ++} ++ + static gsize default_icon_size = WNCK_DEFAULT_ICON_SIZE; + + /** +-- +2.31.1 + diff --git a/libwnck_0001-xutils-move-WnckIconCache-to-its-own-file.patch b/libwnck_0001-xutils-move-WnckIconCache-to-its-own-file.patch new file mode 100644 index 0000000..2b72c66 --- /dev/null +++ b/libwnck_0001-xutils-move-WnckIconCache-to-its-own-file.patch @@ -0,0 +1,1663 @@ +From bd8ab37c271c065c0bcd09362222ca37f05aedf7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alberts=20Muktup=C4=81vels?= +Date: Tue, 11 May 2021 21:00:01 +0300 +Subject: [PATCH 1/6] xutils: move WnckIconCache to its own file + +--- + libwnck/application.c | 1 + + libwnck/meson.build | 2 + + libwnck/window.c | 1 + + libwnck/wnck-icon-cache-private.h | 51 ++ + libwnck/wnck-icon-cache.c | 756 ++++++++++++++++++++++++++++++ + libwnck/xutils.c | 729 +--------------------------- + libwnck/xutils.h | 22 +- + 7 files changed, 815 insertions(+), 747 deletions(-) + create mode 100644 libwnck/wnck-icon-cache-private.h + create mode 100644 libwnck/wnck-icon-cache.c + +diff --git a/libwnck/application.c b/libwnck/application.c +index 9a03342..8d78209 100644 +--- a/libwnck/application.c ++++ b/libwnck/application.c +@@ -25,6 +25,7 @@ + #include + #include "application.h" + #include "private.h" ++#include "wnck-icon-cache-private.h" + + /** + * SECTION:application +diff --git a/libwnck/meson.build b/libwnck/meson.build +index eef9de5..aa60e83 100644 +--- a/libwnck/meson.build ++++ b/libwnck/meson.build +@@ -59,6 +59,8 @@ sources = [ + 'window.c', + 'wnck-handle-private.h', + 'wnck-handle.c', ++ 'wnck-icon-cache-private.h', ++ 'wnck-icon-cache.c', + 'wnck-image-menu-item-private.h', + 'wnck-image-menu-item.c', + 'workspace.c', +diff --git a/libwnck/window.c b/libwnck/window.c +index 66a4386..25c56a5 100644 +--- a/libwnck/window.c ++++ b/libwnck/window.c +@@ -33,6 +33,7 @@ + #include "xutils.h" + #include "private.h" + #include "wnck-enum-types.h" ++#include "wnck-icon-cache-private.h" + + /** + * SECTION:window +diff --git a/libwnck/wnck-icon-cache-private.h b/libwnck/wnck-icon-cache-private.h +new file mode 100644 +index 0000000..6a3d5ec +--- /dev/null ++++ b/libwnck/wnck-icon-cache-private.h +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (C) 2001 Havoc Pennington ++ * Copyright (C) 2005-2007 Vincent Untz ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, see . ++ */ ++ ++#ifndef WNCK_ICON_CACHE_PRIVATE_H ++#define WNCK_ICON_CACHE_PRIVATE_H ++ ++#include ++#include ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++typedef struct _WnckIconCache WnckIconCache; ++ ++WnckIconCache *_wnck_icon_cache_new (void); ++void _wnck_icon_cache_free (WnckIconCache *icon_cache); ++void _wnck_icon_cache_property_changed (WnckIconCache *icon_cache, ++ Atom atom); ++gboolean _wnck_icon_cache_get_icon_invalidated (WnckIconCache *icon_cache); ++void _wnck_icon_cache_set_want_fallback (WnckIconCache *icon_cache, ++ gboolean setting); ++gboolean _wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache); ++ ++gboolean _wnck_read_icons (WnckScreen *screen, ++ Window xwindow, ++ WnckIconCache *icon_cache, ++ GdkPixbuf **iconp, ++ int ideal_size, ++ GdkPixbuf **mini_iconp, ++ int ideal_mini_size); ++ ++G_END_DECLS ++ ++#endif +diff --git a/libwnck/wnck-icon-cache.c b/libwnck/wnck-icon-cache.c +new file mode 100644 +index 0000000..1749585 +--- /dev/null ++++ b/libwnck/wnck-icon-cache.c +@@ -0,0 +1,756 @@ ++/* ++ * Copyright (C) 2001 Havoc Pennington ++ * Copyright (C) 2005-2007 Vincent Untz ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, see . ++ */ ++ ++#include "config.h" ++#include "wnck-icon-cache-private.h" ++ ++#include ++#if HAVE_CAIRO_XLIB_XRENDER ++#include ++#endif ++ ++#include "private.h" ++#include "xutils.h" ++ ++typedef enum ++{ ++ /* These MUST be in ascending order of preference; ++ * i.e. if we get _NET_WM_ICON and already have ++ * WM_HINTS, we prefer _NET_WM_ICON ++ */ ++ USING_NO_ICON, ++ USING_FALLBACK_ICON, ++ USING_KWM_WIN_ICON, ++ USING_WM_HINTS, ++ USING_NET_WM_ICON ++} IconOrigin; ++ ++struct _WnckIconCache ++{ ++ IconOrigin origin; ++ Pixmap prev_pixmap; ++ Pixmap prev_mask; ++ GdkPixbuf *icon; ++ GdkPixbuf *mini_icon; ++ int ideal_size; ++ int ideal_mini_size; ++ guint want_fallback : 1; ++ /* TRUE if these props have changed */ ++ guint wm_hints_dirty : 1; ++ guint kwm_win_icon_dirty : 1; ++ guint net_wm_icon_dirty : 1; ++}; ++ ++static gboolean ++find_best_size (gulong *data, ++ gulong nitems, ++ int ideal_size, ++ int *width, ++ int *height, ++ gulong **start) ++{ ++ int best_w; ++ int best_h; ++ gulong *best_start; ++ ++ *width = 0; ++ *height = 0; ++ *start = NULL; ++ ++ best_w = 0; ++ best_h = 0; ++ best_start = NULL; ++ ++ while (nitems > 0) ++ { ++ int w, h; ++ gboolean replace; ++ ++ replace = FALSE; ++ ++ if (nitems < 3) ++ return FALSE; /* no space for w, h */ ++ ++ w = data[0]; ++ h = data[1]; ++ ++ if (nitems < ((gulong) (w * h) + 2)) ++ break; /* not enough data */ ++ ++ if (best_start == NULL) ++ { ++ replace = TRUE; ++ } ++ else ++ { ++ /* work with averages */ ++ int best_size = (best_w + best_h) / 2; ++ int this_size = (w + h) / 2; ++ ++ /* larger than desired is always better than smaller */ ++ if (best_size < ideal_size && ++ this_size >= ideal_size) ++ replace = TRUE; ++ /* if we have too small, pick anything bigger */ ++ else if (best_size < ideal_size && ++ this_size > best_size) ++ replace = TRUE; ++ /* if we have too large, pick anything smaller ++ * but still >= the ideal ++ */ ++ else if (best_size > ideal_size && ++ this_size >= ideal_size && ++ this_size < best_size) ++ replace = TRUE; ++ } ++ ++ if (replace) ++ { ++ best_start = data + 2; ++ best_w = w; ++ best_h = h; ++ } ++ ++ data += (w * h) + 2; ++ nitems -= (w * h) + 2; ++ } ++ ++ if (best_start) ++ { ++ *start = best_start; ++ *width = best_w; ++ *height = best_h; ++ return TRUE; ++ } ++ else ++ return FALSE; ++} ++ ++static void ++argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata) ++{ ++ guchar *p; ++ int i; ++ ++ *pixdata = g_new (guchar, len * 4); ++ p = *pixdata; ++ ++ /* One could speed this up a lot. */ ++ i = 0; ++ while (i < len) ++ { ++ guint argb; ++ guint rgba; ++ ++ argb = argb_data[i]; ++ rgba = (argb << 8) | (argb >> 24); ++ ++ *p = rgba >> 24; ++ ++p; ++ *p = (rgba >> 16) & 0xff; ++ ++p; ++ *p = (rgba >> 8) & 0xff; ++ ++p; ++ *p = rgba & 0xff; ++ ++p; ++ ++ ++i; ++ } ++} ++ ++static gboolean ++read_rgb_icon (Screen *screen, ++ Window xwindow, ++ int ideal_size, ++ int ideal_mini_size, ++ int *width, ++ int *height, ++ guchar **pixdata, ++ int *mini_width, ++ int *mini_height, ++ guchar **mini_pixdata) ++{ ++ Display *display; ++ Atom type; ++ int format; ++ gulong nitems; ++ gulong bytes_after; ++ int result, err; ++ gulong *data; ++ gulong *best; ++ int w, h; ++ gulong *best_mini; ++ int mini_w, mini_h; ++ ++ display = DisplayOfScreen (screen); ++ ++ _wnck_error_trap_push (display); ++ type = None; ++ data = NULL; ++ result = XGetWindowProperty (display, ++ xwindow, ++ _wnck_atom_get ("_NET_WM_ICON"), ++ 0, G_MAXLONG, ++ False, XA_CARDINAL, &type, &format, &nitems, ++ &bytes_after, (void*)&data); ++ ++ err = _wnck_error_trap_pop (display); ++ ++ if (err != Success || ++ result != Success) ++ return FALSE; ++ ++ if (type != XA_CARDINAL) ++ { ++ XFree (data); ++ return FALSE; ++ } ++ ++ if (!find_best_size (data, nitems, ideal_size, &w, &h, &best)) ++ { ++ XFree (data); ++ return FALSE; ++ } ++ ++ if (!find_best_size (data, nitems, ++ ideal_mini_size, ++ &mini_w, &mini_h, &best_mini)) ++ { ++ XFree (data); ++ return FALSE; ++ } ++ ++ *width = w; ++ *height = h; ++ ++ *mini_width = mini_w; ++ *mini_height = mini_h; ++ ++ argbdata_to_pixdata (best, w * h, pixdata); ++ argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata); ++ ++ XFree (data); ++ ++ return TRUE; ++} ++ ++static gboolean ++try_pixmap_and_mask (Screen *screen, ++ Pixmap src_pixmap, ++ Pixmap src_mask, ++ GdkPixbuf **iconp, ++ int ideal_size, ++ GdkPixbuf **mini_iconp, ++ int ideal_mini_size) ++{ ++ cairo_surface_t *surface, *mask_surface, *image; ++ GdkDisplay *gdk_display; ++ GdkPixbuf *unscaled; ++ int width, height; ++ cairo_t *cr; ++ ++ if (src_pixmap == None) ++ return FALSE; ++ ++ surface = _wnck_cairo_surface_get_from_pixmap (screen, src_pixmap); ++ ++ if (surface && src_mask != None) ++ mask_surface = _wnck_cairo_surface_get_from_pixmap (screen, src_mask); ++ else ++ mask_surface = NULL; ++ ++ if (surface == NULL) ++ return FALSE; ++ ++ gdk_display = gdk_x11_lookup_xdisplay (XDisplayOfScreen (screen)); ++ g_assert (gdk_display != NULL); ++ ++ gdk_x11_display_error_trap_push (gdk_display); ++ ++ width = cairo_xlib_surface_get_width (surface); ++ height = cairo_xlib_surface_get_height (surface); ++ ++ image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ++ width, height); ++ cr = cairo_create (image); ++ ++ /* Need special code for alpha-only surfaces. We only get those ++ * for bitmaps. And in that case, it's a differentiation between ++ * foreground (white) and background (black). ++ */ ++ if (cairo_surface_get_content (surface) & CAIRO_CONTENT_ALPHA) ++ { ++ cairo_push_group (cr); ++ ++ /* black background */ ++ cairo_set_source_rgb (cr, 0, 0, 0); ++ cairo_paint (cr); ++ /* mask with white foreground */ ++ cairo_set_source_rgb (cr, 1, 1, 1); ++ cairo_mask_surface (cr, surface, 0, 0); ++ ++ cairo_pop_group_to_source (cr); ++ } ++ else ++ cairo_set_source_surface (cr, surface, 0, 0); ++ ++ if (mask_surface) ++ { ++ cairo_mask_surface (cr, mask_surface, 0, 0); ++ cairo_surface_destroy (mask_surface); ++ } ++ else ++ cairo_paint (cr); ++ ++ cairo_surface_destroy (surface); ++ cairo_destroy (cr); ++ ++ if (gdk_x11_display_error_trap_pop (gdk_display) != Success) ++ { ++ cairo_surface_destroy (image); ++ return FALSE; ++ } ++ ++ unscaled = gdk_pixbuf_get_from_surface (image, ++ 0, 0, ++ width, height); ++ ++ cairo_surface_destroy (image); ++ ++ if (unscaled) ++ { ++ *iconp = ++ gdk_pixbuf_scale_simple (unscaled, ++ ideal_size, ++ ideal_size, ++ GDK_INTERP_BILINEAR); ++ *mini_iconp = ++ gdk_pixbuf_scale_simple (unscaled, ++ ideal_mini_size, ++ ideal_mini_size, ++ GDK_INTERP_BILINEAR); ++ ++ g_object_unref (G_OBJECT (unscaled)); ++ return TRUE; ++ } ++ else ++ return FALSE; ++} ++ ++static void ++get_kwm_win_icon (Screen *screen, ++ Window xwindow, ++ Pixmap *pixmap, ++ Pixmap *mask) ++{ ++ Display *display; ++ Atom type; ++ int format; ++ gulong nitems; ++ gulong bytes_after; ++ Pixmap *icons; ++ int err, result; ++ ++ display = DisplayOfScreen (screen); ++ ++ *pixmap = None; ++ *mask = None; ++ ++ _wnck_error_trap_push (display); ++ icons = NULL; ++ result = XGetWindowProperty (display, xwindow, ++ _wnck_atom_get ("KWM_WIN_ICON"), ++ 0, G_MAXLONG, ++ False, ++ _wnck_atom_get ("KWM_WIN_ICON"), ++ &type, &format, &nitems, ++ &bytes_after, (void*)&icons); ++ ++ err = _wnck_error_trap_pop (display); ++ if (err != Success || ++ result != Success) ++ return; ++ ++ if (type != _wnck_atom_get ("KWM_WIN_ICON")) ++ { ++ XFree (icons); ++ return; ++ } ++ ++ *pixmap = icons[0]; ++ *mask = icons[1]; ++ ++ XFree (icons); ++ ++ return; ++} ++ ++static void ++clear_icon_cache (WnckIconCache *icon_cache, ++ gboolean dirty_all) ++{ ++ if (icon_cache->icon) ++ g_object_unref (G_OBJECT (icon_cache->icon)); ++ icon_cache->icon = NULL; ++ ++ if (icon_cache->mini_icon) ++ g_object_unref (G_OBJECT (icon_cache->mini_icon)); ++ icon_cache->mini_icon = NULL; ++ ++ icon_cache->origin = USING_NO_ICON; ++ ++ if (dirty_all) ++ { ++ icon_cache->wm_hints_dirty = TRUE; ++ icon_cache->kwm_win_icon_dirty = TRUE; ++ icon_cache->net_wm_icon_dirty = TRUE; ++ } ++} ++ ++static void ++replace_cache (WnckIconCache *icon_cache, ++ IconOrigin origin, ++ GdkPixbuf *new_icon, ++ GdkPixbuf *new_mini_icon) ++{ ++ clear_icon_cache (icon_cache, FALSE); ++ ++ icon_cache->origin = origin; ++ ++ if (new_icon) ++ g_object_ref (G_OBJECT (new_icon)); ++ ++ icon_cache->icon = new_icon; ++ ++ if (new_mini_icon) ++ g_object_ref (G_OBJECT (new_mini_icon)); ++ ++ icon_cache->mini_icon = new_mini_icon; ++} ++ ++static void ++free_pixels (guchar *pixels, ++ gpointer data) ++{ ++ g_free (pixels); ++} ++ ++static GdkPixbuf* ++scaled_from_pixdata (guchar *pixdata, ++ int w, ++ int h, ++ int new_w, ++ int new_h) ++{ ++ GdkPixbuf *src; ++ GdkPixbuf *dest; ++ ++ src = gdk_pixbuf_new_from_data (pixdata, ++ GDK_COLORSPACE_RGB, ++ TRUE, ++ 8, ++ w, h, w * 4, ++ free_pixels, ++ NULL); ++ ++ if (src == NULL) ++ return NULL; ++ ++ if (w != h) ++ { ++ GdkPixbuf *tmp; ++ int size; ++ ++ size = MAX (w, h); ++ ++ tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size); ++ ++ if (tmp != NULL) ++ { ++ gdk_pixbuf_fill (tmp, 0); ++ gdk_pixbuf_copy_area (src, 0, 0, w, h, ++ tmp, ++ (size - w) / 2, (size - h) / 2); ++ ++ g_object_unref (src); ++ src = tmp; ++ } ++ } ++ ++ if (w != new_w || h != new_h) ++ { ++ dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR); ++ ++ g_object_unref (G_OBJECT (src)); ++ } ++ else ++ { ++ dest = src; ++ } ++ ++ return dest; ++} ++ ++WnckIconCache* ++_wnck_icon_cache_new (void) ++{ ++ WnckIconCache *icon_cache; ++ ++ icon_cache = g_slice_new0 (WnckIconCache); ++ ++ icon_cache->origin = USING_NO_ICON; ++ icon_cache->prev_pixmap = None; ++ icon_cache->icon = NULL; ++ icon_cache->mini_icon = NULL; ++ icon_cache->ideal_size = -1; /* won't be a legit size */ ++ icon_cache->ideal_mini_size = -1; ++ icon_cache->want_fallback = TRUE; ++ icon_cache->wm_hints_dirty = TRUE; ++ icon_cache->kwm_win_icon_dirty = TRUE; ++ icon_cache->net_wm_icon_dirty = TRUE; ++ ++ return icon_cache; ++} ++ ++void ++_wnck_icon_cache_free (WnckIconCache *icon_cache) ++{ ++ clear_icon_cache (icon_cache, FALSE); ++ ++ g_slice_free (WnckIconCache, icon_cache); ++} ++ ++void ++_wnck_icon_cache_property_changed (WnckIconCache *icon_cache, ++ Atom atom) ++{ ++ if (atom == _wnck_atom_get ("_NET_WM_ICON")) ++ icon_cache->net_wm_icon_dirty = TRUE; ++ else if (atom == _wnck_atom_get ("KWM_WIN_ICON")) ++ icon_cache->kwm_win_icon_dirty = TRUE; ++ else if (atom == _wnck_atom_get ("WM_HINTS")) ++ icon_cache->wm_hints_dirty = TRUE; ++} ++ ++gboolean ++_wnck_icon_cache_get_icon_invalidated (WnckIconCache *icon_cache) ++{ ++ if (icon_cache->origin <= USING_KWM_WIN_ICON && ++ icon_cache->kwm_win_icon_dirty) ++ return TRUE; ++ else if (icon_cache->origin <= USING_WM_HINTS && ++ icon_cache->wm_hints_dirty) ++ return TRUE; ++ else if (icon_cache->origin <= USING_NET_WM_ICON && ++ icon_cache->net_wm_icon_dirty) ++ return TRUE; ++ else if (icon_cache->origin < USING_FALLBACK_ICON && ++ icon_cache->want_fallback) ++ return TRUE; ++ else if (icon_cache->origin == USING_NO_ICON) ++ return TRUE; ++ else if (icon_cache->origin == USING_FALLBACK_ICON && ++ !icon_cache->want_fallback) ++ return TRUE; ++ else ++ return FALSE; ++} ++ ++void ++_wnck_icon_cache_set_want_fallback (WnckIconCache *icon_cache, ++ gboolean setting) ++{ ++ icon_cache->want_fallback = setting; ++} ++ ++gboolean ++_wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache) ++{ ++ return icon_cache->origin == USING_FALLBACK_ICON; ++} ++ ++gboolean ++_wnck_read_icons (WnckScreen *screen, ++ Window xwindow, ++ WnckIconCache *icon_cache, ++ GdkPixbuf **iconp, ++ int ideal_size, ++ GdkPixbuf **mini_iconp, ++ int ideal_mini_size) ++{ ++ Screen *xscreen; ++ Display *display; ++ guchar *pixdata; ++ int w, h; ++ guchar *mini_pixdata; ++ int mini_w, mini_h; ++ Pixmap pixmap; ++ Pixmap mask; ++ XWMHints *hints; ++ ++ /* Return value is whether the icon changed */ ++ ++ g_return_val_if_fail (icon_cache != NULL, FALSE); ++ ++ xscreen = _wnck_screen_get_xscreen (screen); ++ display = DisplayOfScreen (xscreen); ++ ++ *iconp = NULL; ++ *mini_iconp = NULL; ++ ++ if (ideal_size != icon_cache->ideal_size || ++ ideal_mini_size != icon_cache->ideal_mini_size) ++ clear_icon_cache (icon_cache, TRUE); ++ ++ icon_cache->ideal_size = ideal_size; ++ icon_cache->ideal_mini_size = ideal_mini_size; ++ ++ if (!_wnck_icon_cache_get_icon_invalidated (icon_cache)) ++ return FALSE; /* we have no new info to use */ ++ ++ pixdata = NULL; ++ ++ /* Our algorithm here assumes that we can't have for example origin ++ * < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE ++ * unless we have tried to read NET_WM_ICON. ++ * ++ * Put another way, if an icon origin is not dirty, then we have ++ * tried to read it at the current size. If it is dirty, then ++ * we haven't done that since the last change. ++ */ ++ ++ if (icon_cache->origin <= USING_NET_WM_ICON && ++ icon_cache->net_wm_icon_dirty) ++ ++ { ++ icon_cache->net_wm_icon_dirty = FALSE; ++ ++ if (read_rgb_icon (xscreen, xwindow, ++ ideal_size, ++ ideal_mini_size, ++ &w, &h, &pixdata, ++ &mini_w, &mini_h, &mini_pixdata)) ++ { ++ *iconp = scaled_from_pixdata (pixdata, w, h, ideal_size, ideal_size); ++ ++ *mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h, ++ ideal_mini_size, ideal_mini_size); ++ ++ replace_cache (icon_cache, USING_NET_WM_ICON, ++ *iconp, *mini_iconp); ++ ++ return TRUE; ++ } ++ } ++ ++ if (icon_cache->origin <= USING_WM_HINTS && ++ icon_cache->wm_hints_dirty) ++ { ++ icon_cache->wm_hints_dirty = FALSE; ++ ++ _wnck_error_trap_push (display); ++ hints = XGetWMHints (display, xwindow); ++ _wnck_error_trap_pop (display); ++ pixmap = None; ++ mask = None; ++ if (hints) ++ { ++ if (hints->flags & IconPixmapHint) ++ pixmap = hints->icon_pixmap; ++ if (hints->flags & IconMaskHint) ++ mask = hints->icon_mask; ++ ++ XFree (hints); ++ hints = NULL; ++ } ++ ++ /* We won't update if pixmap is unchanged; ++ * avoids a get_from_drawable() on every geometry ++ * hints change ++ */ ++ if ((pixmap != icon_cache->prev_pixmap || ++ mask != icon_cache->prev_mask) && ++ pixmap != None) ++ { ++ if (try_pixmap_and_mask (xscreen, pixmap, mask, ++ iconp, ideal_size, ++ mini_iconp, ideal_mini_size)) ++ { ++ icon_cache->prev_pixmap = pixmap; ++ icon_cache->prev_mask = mask; ++ ++ replace_cache (icon_cache, USING_WM_HINTS, ++ *iconp, *mini_iconp); ++ ++ return TRUE; ++ } ++ } ++ } ++ ++ if (icon_cache->origin <= USING_KWM_WIN_ICON && ++ icon_cache->kwm_win_icon_dirty) ++ { ++ icon_cache->kwm_win_icon_dirty = FALSE; ++ ++ get_kwm_win_icon (xscreen, xwindow, &pixmap, &mask); ++ ++ if ((pixmap != icon_cache->prev_pixmap || ++ mask != icon_cache->prev_mask) && ++ pixmap != None) ++ { ++ if (try_pixmap_and_mask (xscreen, pixmap, mask, ++ iconp, ideal_size, ++ mini_iconp, ideal_mini_size)) ++ { ++ icon_cache->prev_pixmap = pixmap; ++ icon_cache->prev_mask = mask; ++ ++ replace_cache (icon_cache, USING_KWM_WIN_ICON, ++ *iconp, *mini_iconp); ++ ++ return TRUE; ++ } ++ } ++ } ++ ++ if (icon_cache->want_fallback && ++ icon_cache->origin < USING_FALLBACK_ICON) ++ { ++ _wnck_get_fallback_icons (iconp, ++ ideal_size, ++ mini_iconp, ++ ideal_mini_size); ++ ++ replace_cache (icon_cache, USING_FALLBACK_ICON, ++ *iconp, *mini_iconp); ++ ++ return TRUE; ++ } ++ ++ if (!icon_cache->want_fallback && ++ icon_cache->origin == USING_FALLBACK_ICON) ++ { ++ /* Get rid of current icon */ ++ clear_icon_cache (icon_cache, FALSE); ++ ++ return TRUE; ++ } ++ ++ /* found nothing new */ ++ return FALSE; ++} +diff --git a/libwnck/xutils.c b/libwnck/xutils.c +index 8c14234..58873dc 100644 +--- a/libwnck/xutils.c ++++ b/libwnck/xutils.c +@@ -1451,206 +1451,7 @@ _wnck_select_input (Screen *screen, + return old_mask; + } + +-static gboolean +-find_best_size (gulong *data, +- gulong nitems, +- int ideal_size, +- int *width, +- int *height, +- gulong **start) +-{ +- int best_w; +- int best_h; +- gulong *best_start; +- +- *width = 0; +- *height = 0; +- *start = NULL; +- +- best_w = 0; +- best_h = 0; +- best_start = NULL; +- +- while (nitems > 0) +- { +- int w, h; +- gboolean replace; +- +- replace = FALSE; +- +- if (nitems < 3) +- return FALSE; /* no space for w, h */ +- +- w = data[0]; +- h = data[1]; +- +- if (nitems < ((gulong) (w * h) + 2)) +- break; /* not enough data */ +- +- if (best_start == NULL) +- { +- replace = TRUE; +- } +- else +- { +- /* work with averages */ +- int best_size = (best_w + best_h) / 2; +- int this_size = (w + h) / 2; +- +- /* larger than desired is always better than smaller */ +- if (best_size < ideal_size && +- this_size >= ideal_size) +- replace = TRUE; +- /* if we have too small, pick anything bigger */ +- else if (best_size < ideal_size && +- this_size > best_size) +- replace = TRUE; +- /* if we have too large, pick anything smaller +- * but still >= the ideal +- */ +- else if (best_size > ideal_size && +- this_size >= ideal_size && +- this_size < best_size) +- replace = TRUE; +- } +- +- if (replace) +- { +- best_start = data + 2; +- best_w = w; +- best_h = h; +- } +- +- data += (w * h) + 2; +- nitems -= (w * h) + 2; +- } +- +- if (best_start) +- { +- *start = best_start; +- *width = best_w; +- *height = best_h; +- return TRUE; +- } +- else +- return FALSE; +-} +- +-static void +-argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata) +-{ +- guchar *p; +- int i; +- +- *pixdata = g_new (guchar, len * 4); +- p = *pixdata; +- +- /* One could speed this up a lot. */ +- i = 0; +- while (i < len) +- { +- guint argb; +- guint rgba; +- +- argb = argb_data[i]; +- rgba = (argb << 8) | (argb >> 24); +- +- *p = rgba >> 24; +- ++p; +- *p = (rgba >> 16) & 0xff; +- ++p; +- *p = (rgba >> 8) & 0xff; +- ++p; +- *p = rgba & 0xff; +- ++p; +- +- ++i; +- } +-} +- +-static gboolean +-read_rgb_icon (Screen *screen, +- Window xwindow, +- int ideal_size, +- int ideal_mini_size, +- int *width, +- int *height, +- guchar **pixdata, +- int *mini_width, +- int *mini_height, +- guchar **mini_pixdata) +-{ +- Display *display; +- Atom type; +- int format; +- gulong nitems; +- gulong bytes_after; +- int result, err; +- gulong *data; +- gulong *best; +- int w, h; +- gulong *best_mini; +- int mini_w, mini_h; +- +- display = DisplayOfScreen (screen); +- +- _wnck_error_trap_push (display); +- type = None; +- data = NULL; +- result = XGetWindowProperty (display, +- xwindow, +- _wnck_atom_get ("_NET_WM_ICON"), +- 0, G_MAXLONG, +- False, XA_CARDINAL, &type, &format, &nitems, +- &bytes_after, (void*)&data); +- +- err = _wnck_error_trap_pop (display); +- +- if (err != Success || +- result != Success) +- return FALSE; +- +- if (type != XA_CARDINAL) +- { +- XFree (data); +- return FALSE; +- } +- +- if (!find_best_size (data, nitems, ideal_size, &w, &h, &best)) +- { +- XFree (data); +- return FALSE; +- } +- +- if (!find_best_size (data, nitems, +- ideal_mini_size, +- &mini_w, &mini_h, &best_mini)) +- { +- XFree (data); +- return FALSE; +- } +- +- *width = w; +- *height = h; +- +- *mini_width = mini_w; +- *mini_height = mini_h; +- +- argbdata_to_pixdata (best, w * h, pixdata); +- argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata); +- +- XFree (data); +- +- return TRUE; +-} +- +-static void +-free_pixels (guchar *pixels, gpointer data) +-{ +- g_free (pixels); +-} +- +-static cairo_surface_t * ++cairo_surface_t * + _wnck_cairo_surface_get_from_pixmap (Screen *screen, + Pixmap xpixmap) + { +@@ -1741,534 +1542,6 @@ _wnck_gdk_pixbuf_get_from_pixmap (Screen *screen, + return retval; + } + +-static gboolean +-try_pixmap_and_mask (Screen *screen, +- Pixmap src_pixmap, +- Pixmap src_mask, +- GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size) +-{ +- cairo_surface_t *surface, *mask_surface, *image; +- GdkDisplay *gdk_display; +- GdkPixbuf *unscaled; +- int width, height; +- cairo_t *cr; +- +- if (src_pixmap == None) +- return FALSE; +- +- surface = _wnck_cairo_surface_get_from_pixmap (screen, src_pixmap); +- +- if (surface && src_mask != None) +- mask_surface = _wnck_cairo_surface_get_from_pixmap (screen, src_mask); +- else +- mask_surface = NULL; +- +- if (surface == NULL) +- return FALSE; +- +- gdk_display = gdk_x11_lookup_xdisplay (XDisplayOfScreen (screen)); +- g_assert (gdk_display != NULL); +- +- gdk_x11_display_error_trap_push (gdk_display); +- +- width = cairo_xlib_surface_get_width (surface); +- height = cairo_xlib_surface_get_height (surface); +- +- image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, +- width, height); +- cr = cairo_create (image); +- +- /* Need special code for alpha-only surfaces. We only get those +- * for bitmaps. And in that case, it's a differentiation between +- * foreground (white) and background (black). +- */ +- if (cairo_surface_get_content (surface) & CAIRO_CONTENT_ALPHA) +- { +- cairo_push_group (cr); +- +- /* black background */ +- cairo_set_source_rgb (cr, 0, 0, 0); +- cairo_paint (cr); +- /* mask with white foreground */ +- cairo_set_source_rgb (cr, 1, 1, 1); +- cairo_mask_surface (cr, surface, 0, 0); +- +- cairo_pop_group_to_source (cr); +- } +- else +- cairo_set_source_surface (cr, surface, 0, 0); +- +- if (mask_surface) +- { +- cairo_mask_surface (cr, mask_surface, 0, 0); +- cairo_surface_destroy (mask_surface); +- } +- else +- cairo_paint (cr); +- +- cairo_surface_destroy (surface); +- cairo_destroy (cr); +- +- if (gdk_x11_display_error_trap_pop (gdk_display) != Success) +- { +- cairo_surface_destroy (image); +- return FALSE; +- } +- +- unscaled = gdk_pixbuf_get_from_surface (image, +- 0, 0, +- width, height); +- +- cairo_surface_destroy (image); +- +- if (unscaled) +- { +- *iconp = +- gdk_pixbuf_scale_simple (unscaled, +- ideal_size, +- ideal_size, +- GDK_INTERP_BILINEAR); +- *mini_iconp = +- gdk_pixbuf_scale_simple (unscaled, +- ideal_mini_size, +- ideal_mini_size, +- GDK_INTERP_BILINEAR); +- +- g_object_unref (G_OBJECT (unscaled)); +- return TRUE; +- } +- else +- return FALSE; +-} +- +-static void +-get_kwm_win_icon (Screen *screen, +- Window xwindow, +- Pixmap *pixmap, +- Pixmap *mask) +-{ +- Display *display; +- Atom type; +- int format; +- gulong nitems; +- gulong bytes_after; +- Pixmap *icons; +- int err, result; +- +- display = DisplayOfScreen (screen); +- +- *pixmap = None; +- *mask = None; +- +- _wnck_error_trap_push (display); +- icons = NULL; +- result = XGetWindowProperty (display, xwindow, +- _wnck_atom_get ("KWM_WIN_ICON"), +- 0, G_MAXLONG, +- False, +- _wnck_atom_get ("KWM_WIN_ICON"), +- &type, &format, &nitems, +- &bytes_after, (void*)&icons); +- +- err = _wnck_error_trap_pop (display); +- if (err != Success || +- result != Success) +- return; +- +- if (type != _wnck_atom_get ("KWM_WIN_ICON")) +- { +- XFree (icons); +- return; +- } +- +- *pixmap = icons[0]; +- *mask = icons[1]; +- +- XFree (icons); +- +- return; +-} +- +-typedef enum +-{ +- /* These MUST be in ascending order of preference; +- * i.e. if we get _NET_WM_ICON and already have +- * WM_HINTS, we prefer _NET_WM_ICON +- */ +- USING_NO_ICON, +- USING_FALLBACK_ICON, +- USING_KWM_WIN_ICON, +- USING_WM_HINTS, +- USING_NET_WM_ICON +-} IconOrigin; +- +-struct _WnckIconCache +-{ +- IconOrigin origin; +- Pixmap prev_pixmap; +- Pixmap prev_mask; +- GdkPixbuf *icon; +- GdkPixbuf *mini_icon; +- int ideal_size; +- int ideal_mini_size; +- guint want_fallback : 1; +- /* TRUE if these props have changed */ +- guint wm_hints_dirty : 1; +- guint kwm_win_icon_dirty : 1; +- guint net_wm_icon_dirty : 1; +-}; +- +-WnckIconCache* +-_wnck_icon_cache_new (void) +-{ +- WnckIconCache *icon_cache; +- +- icon_cache = g_slice_new0 (WnckIconCache); +- +- icon_cache->origin = USING_NO_ICON; +- icon_cache->prev_pixmap = None; +- icon_cache->icon = NULL; +- icon_cache->mini_icon = NULL; +- icon_cache->ideal_size = -1; /* won't be a legit size */ +- icon_cache->ideal_mini_size = -1; +- icon_cache->want_fallback = TRUE; +- icon_cache->wm_hints_dirty = TRUE; +- icon_cache->kwm_win_icon_dirty = TRUE; +- icon_cache->net_wm_icon_dirty = TRUE; +- +- return icon_cache; +-} +- +-static void +-clear_icon_cache (WnckIconCache *icon_cache, +- gboolean dirty_all) +-{ +- if (icon_cache->icon) +- g_object_unref (G_OBJECT (icon_cache->icon)); +- icon_cache->icon = NULL; +- +- if (icon_cache->mini_icon) +- g_object_unref (G_OBJECT (icon_cache->mini_icon)); +- icon_cache->mini_icon = NULL; +- +- icon_cache->origin = USING_NO_ICON; +- +- if (dirty_all) +- { +- icon_cache->wm_hints_dirty = TRUE; +- icon_cache->kwm_win_icon_dirty = TRUE; +- icon_cache->net_wm_icon_dirty = TRUE; +- } +-} +- +-void +-_wnck_icon_cache_free (WnckIconCache *icon_cache) +-{ +- clear_icon_cache (icon_cache, FALSE); +- +- g_slice_free (WnckIconCache, icon_cache); +-} +- +-void +-_wnck_icon_cache_property_changed (WnckIconCache *icon_cache, +- Atom atom) +-{ +- if (atom == _wnck_atom_get ("_NET_WM_ICON")) +- icon_cache->net_wm_icon_dirty = TRUE; +- else if (atom == _wnck_atom_get ("KWM_WIN_ICON")) +- icon_cache->kwm_win_icon_dirty = TRUE; +- else if (atom == _wnck_atom_get ("WM_HINTS")) +- icon_cache->wm_hints_dirty = TRUE; +-} +- +-gboolean +-_wnck_icon_cache_get_icon_invalidated (WnckIconCache *icon_cache) +-{ +- if (icon_cache->origin <= USING_KWM_WIN_ICON && +- icon_cache->kwm_win_icon_dirty) +- return TRUE; +- else if (icon_cache->origin <= USING_WM_HINTS && +- icon_cache->wm_hints_dirty) +- return TRUE; +- else if (icon_cache->origin <= USING_NET_WM_ICON && +- icon_cache->net_wm_icon_dirty) +- return TRUE; +- else if (icon_cache->origin < USING_FALLBACK_ICON && +- icon_cache->want_fallback) +- return TRUE; +- else if (icon_cache->origin == USING_NO_ICON) +- return TRUE; +- else if (icon_cache->origin == USING_FALLBACK_ICON && +- !icon_cache->want_fallback) +- return TRUE; +- else +- return FALSE; +-} +- +-void +-_wnck_icon_cache_set_want_fallback (WnckIconCache *icon_cache, +- gboolean setting) +-{ +- icon_cache->want_fallback = setting; +-} +- +-gboolean +-_wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache) +-{ +- return icon_cache->origin == USING_FALLBACK_ICON; +-} +- +-static void +-replace_cache (WnckIconCache *icon_cache, +- IconOrigin origin, +- GdkPixbuf *new_icon, +- GdkPixbuf *new_mini_icon) +-{ +- clear_icon_cache (icon_cache, FALSE); +- +- icon_cache->origin = origin; +- +- if (new_icon) +- g_object_ref (G_OBJECT (new_icon)); +- +- icon_cache->icon = new_icon; +- +- if (new_mini_icon) +- g_object_ref (G_OBJECT (new_mini_icon)); +- +- icon_cache->mini_icon = new_mini_icon; +-} +- +-static GdkPixbuf* +-scaled_from_pixdata (guchar *pixdata, +- int w, +- int h, +- int new_w, +- int new_h) +-{ +- GdkPixbuf *src; +- GdkPixbuf *dest; +- +- src = gdk_pixbuf_new_from_data (pixdata, +- GDK_COLORSPACE_RGB, +- TRUE, +- 8, +- w, h, w * 4, +- free_pixels, +- NULL); +- +- if (src == NULL) +- return NULL; +- +- if (w != h) +- { +- GdkPixbuf *tmp; +- int size; +- +- size = MAX (w, h); +- +- tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size); +- +- if (tmp != NULL) +- { +- gdk_pixbuf_fill (tmp, 0); +- gdk_pixbuf_copy_area (src, 0, 0, w, h, +- tmp, +- (size - w) / 2, (size - h) / 2); +- +- g_object_unref (src); +- src = tmp; +- } +- } +- +- if (w != new_w || h != new_h) +- { +- dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR); +- +- g_object_unref (G_OBJECT (src)); +- } +- else +- { +- dest = src; +- } +- +- return dest; +-} +- +-gboolean +-_wnck_read_icons (WnckScreen *screen, +- Window xwindow, +- WnckIconCache *icon_cache, +- GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size) +-{ +- Screen *xscreen; +- Display *display; +- guchar *pixdata; +- int w, h; +- guchar *mini_pixdata; +- int mini_w, mini_h; +- Pixmap pixmap; +- Pixmap mask; +- XWMHints *hints; +- +- /* Return value is whether the icon changed */ +- +- g_return_val_if_fail (icon_cache != NULL, FALSE); +- +- xscreen = _wnck_screen_get_xscreen (screen); +- display = DisplayOfScreen (xscreen); +- +- *iconp = NULL; +- *mini_iconp = NULL; +- +- if (ideal_size != icon_cache->ideal_size || +- ideal_mini_size != icon_cache->ideal_mini_size) +- clear_icon_cache (icon_cache, TRUE); +- +- icon_cache->ideal_size = ideal_size; +- icon_cache->ideal_mini_size = ideal_mini_size; +- +- if (!_wnck_icon_cache_get_icon_invalidated (icon_cache)) +- return FALSE; /* we have no new info to use */ +- +- pixdata = NULL; +- +- /* Our algorithm here assumes that we can't have for example origin +- * < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE +- * unless we have tried to read NET_WM_ICON. +- * +- * Put another way, if an icon origin is not dirty, then we have +- * tried to read it at the current size. If it is dirty, then +- * we haven't done that since the last change. +- */ +- +- if (icon_cache->origin <= USING_NET_WM_ICON && +- icon_cache->net_wm_icon_dirty) +- +- { +- icon_cache->net_wm_icon_dirty = FALSE; +- +- if (read_rgb_icon (xscreen, xwindow, +- ideal_size, +- ideal_mini_size, +- &w, &h, &pixdata, +- &mini_w, &mini_h, &mini_pixdata)) +- { +- *iconp = scaled_from_pixdata (pixdata, w, h, ideal_size, ideal_size); +- +- *mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h, +- ideal_mini_size, ideal_mini_size); +- +- replace_cache (icon_cache, USING_NET_WM_ICON, +- *iconp, *mini_iconp); +- +- return TRUE; +- } +- } +- +- if (icon_cache->origin <= USING_WM_HINTS && +- icon_cache->wm_hints_dirty) +- { +- icon_cache->wm_hints_dirty = FALSE; +- +- _wnck_error_trap_push (display); +- hints = XGetWMHints (display, xwindow); +- _wnck_error_trap_pop (display); +- pixmap = None; +- mask = None; +- if (hints) +- { +- if (hints->flags & IconPixmapHint) +- pixmap = hints->icon_pixmap; +- if (hints->flags & IconMaskHint) +- mask = hints->icon_mask; +- +- XFree (hints); +- hints = NULL; +- } +- +- /* We won't update if pixmap is unchanged; +- * avoids a get_from_drawable() on every geometry +- * hints change +- */ +- if ((pixmap != icon_cache->prev_pixmap || +- mask != icon_cache->prev_mask) && +- pixmap != None) +- { +- if (try_pixmap_and_mask (xscreen, pixmap, mask, +- iconp, ideal_size, +- mini_iconp, ideal_mini_size)) +- { +- icon_cache->prev_pixmap = pixmap; +- icon_cache->prev_mask = mask; +- +- replace_cache (icon_cache, USING_WM_HINTS, +- *iconp, *mini_iconp); +- +- return TRUE; +- } +- } +- } +- +- if (icon_cache->origin <= USING_KWM_WIN_ICON && +- icon_cache->kwm_win_icon_dirty) +- { +- icon_cache->kwm_win_icon_dirty = FALSE; +- +- get_kwm_win_icon (xscreen, xwindow, &pixmap, &mask); +- +- if ((pixmap != icon_cache->prev_pixmap || +- mask != icon_cache->prev_mask) && +- pixmap != None) +- { +- if (try_pixmap_and_mask (xscreen, pixmap, mask, +- iconp, ideal_size, +- mini_iconp, ideal_mini_size)) +- { +- icon_cache->prev_pixmap = pixmap; +- icon_cache->prev_mask = mask; +- +- replace_cache (icon_cache, USING_KWM_WIN_ICON, +- *iconp, *mini_iconp); +- +- return TRUE; +- } +- } +- } +- +- if (icon_cache->want_fallback && +- icon_cache->origin < USING_FALLBACK_ICON) +- { +- _wnck_get_fallback_icons (iconp, +- ideal_size, +- mini_iconp, +- ideal_mini_size); +- +- replace_cache (icon_cache, USING_FALLBACK_ICON, +- *iconp, *mini_iconp); +- +- return TRUE; +- } +- +- if (!icon_cache->want_fallback && +- icon_cache->origin == USING_FALLBACK_ICON) +- { +- /* Get rid of current icon */ +- clear_icon_cache (icon_cache, FALSE); +- +- return TRUE; +- } +- +- /* found nothing new */ +- return FALSE; +-} +- + static GdkPixbuf* + default_icon_at_size (int size) + { +diff --git a/libwnck/xutils.h b/libwnck/xutils.h +index e5ef85d..2af255d 100644 +--- a/libwnck/xutils.h ++++ b/libwnck/xutils.h +@@ -159,25 +159,6 @@ void _wnck_keyboard_size (WnckScreen *screen, + void _wnck_toggle_showing_desktop (Screen *screen, + gboolean show); + +-typedef struct _WnckIconCache WnckIconCache; +- +-WnckIconCache *_wnck_icon_cache_new (void); +-void _wnck_icon_cache_free (WnckIconCache *icon_cache); +-void _wnck_icon_cache_property_changed (WnckIconCache *icon_cache, +- Atom atom); +-gboolean _wnck_icon_cache_get_icon_invalidated (WnckIconCache *icon_cache); +-void _wnck_icon_cache_set_want_fallback (WnckIconCache *icon_cache, +- gboolean setting); +-gboolean _wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache); +- +-gboolean _wnck_read_icons (WnckScreen *screen, +- Window xwindow, +- WnckIconCache *icon_cache, +- GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size); +- + void _wnck_get_fallback_icons (GdkPixbuf **iconp, + int ideal_size, + GdkPixbuf **mini_iconp, +@@ -213,6 +194,9 @@ void _wnck_set_desktop_layout (Screen *xscreen, + int rows, + int columns); + ++cairo_surface_t *_wnck_cairo_surface_get_from_pixmap (Screen *screen, ++ Pixmap xpixmap); ++ + GdkPixbuf* _wnck_gdk_pixbuf_get_from_pixmap (Screen *screen, + Pixmap xpixmap); + +-- +2.31.1 + diff --git a/libwnck_0002-icons-Use-cairo-surfaces-to-render-icons.patch b/libwnck_0002-icons-Use-cairo-surfaces-to-render-icons.patch new file mode 100644 index 0000000..35aee44 --- /dev/null +++ b/libwnck_0002-icons-Use-cairo-surfaces-to-render-icons.patch @@ -0,0 +1,1336 @@ +From 421809299f8d4bf20422c8300717eadb0ddc8ada Mon Sep 17 00:00:00 2001 +From: Victor Kareh +Date: Fri, 27 Nov 2020 11:25:08 -0500 +Subject: [PATCH 2/5] icons: Use cairo surfaces to render icons + +This replaces GdkPixbuf manipulation with the cairo_surface equivalents. +As a result, icons can now render sharply in HiDPI displays. +--- + libwnck/application.c | 151 +++++++++++++++++++--- + libwnck/application.h | 3 + + libwnck/class-group.c | 172 ++++++++++++++++++++----- + libwnck/class-group.h | 3 + + libwnck/pager.c | 20 +-- + libwnck/selector.c | 103 +++++++-------- + libwnck/tasklist.c | 169 ++++++++++++------------ + libwnck/util.c | 4 +- + libwnck/window.c | 139 +++++++++++++++++--- + libwnck/window.h | 3 + + libwnck/wnck-image-menu-item-private.h | 3 + + libwnck/wnck-image-menu-item.c | 8 ++ + 12 files changed, 556 insertions(+), 222 deletions(-) + +diff --git a/libwnck/application.c b/libwnck/application.c +index 8d78209..66f1502 100644 +--- a/libwnck/application.c ++++ b/libwnck/application.c +@@ -63,8 +63,8 @@ struct _WnckApplicationPrivate + + WnckWindow *name_window; /* window we are using name of */ + +- GdkPixbuf *icon; +- GdkPixbuf *mini_icon; ++ cairo_surface_t *icon; ++ cairo_surface_t *mini_icon; + + WnckIconCache *icon_cache; + +@@ -171,13 +171,8 @@ wnck_application_finalize (GObject *object) + g_free (application->priv->name); + application->priv->name = NULL; + +- if (application->priv->icon) +- g_object_unref (G_OBJECT (application->priv->icon)); +- application->priv->icon = NULL; +- +- if (application->priv->mini_icon) +- g_object_unref (G_OBJECT (application->priv->mini_icon)); +- application->priv->mini_icon = NULL; ++ g_clear_pointer (&application->priv->icon, cairo_surface_destroy); ++ g_clear_pointer (&application->priv->mini_icon, cairo_surface_destroy); + + _wnck_icon_cache_free (application->priv->icon_cache); + application->priv->icon_cache = NULL; +@@ -349,14 +344,20 @@ get_icons (WnckApplication *app) + app->priv->need_emit_icon_changed = TRUE; + app->priv->icon_from_leader = TRUE; + +- if (app->priv->icon) +- g_object_unref (G_OBJECT (app->priv->icon)); ++ g_clear_pointer (&app->priv->icon, cairo_surface_destroy); ++ g_clear_pointer (&app->priv->mini_icon, cairo_surface_destroy); + +- if (app->priv->mini_icon) +- g_object_unref (G_OBJECT (app->priv->mini_icon)); ++ if (icon) ++ { ++ app->priv->icon = gdk_cairo_surface_create_from_pixbuf (icon, 0, NULL); ++ g_clear_object (&icon); ++ } + +- app->priv->icon = icon; +- app->priv->mini_icon = mini_icon; ++ if (mini_icon) ++ { ++ app->priv->mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon, 0, NULL); ++ g_clear_object (&mini_icon); ++ } + } + + /* FIXME we should really fall back to using the icon +@@ -417,12 +418,39 @@ find_icon_window (WnckApplication *app) + GdkPixbuf* + wnck_application_get_icon (WnckApplication *app) + { ++ static const cairo_user_data_key_t app_icon_pixbuf_key; ++ + g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); + + _wnck_application_load_icons (app); + + if (app->priv->icon) +- return app->priv->icon; ++ { ++ GdkPixbuf *pixbuf; ++ ++ pixbuf = cairo_surface_get_user_data (app->priv->icon, &app_icon_pixbuf_key); ++ ++ if (pixbuf == NULL) ++ { ++ int scaling_factor; ++ ++ pixbuf = gdk_pixbuf_get_from_surface (app->priv->icon, ++ 0, ++ 0, ++ cairo_image_surface_get_width (app->priv->icon), ++ cairo_image_surface_get_height (app->priv->icon)); ++ ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ pixbuf = gdk_pixbuf_scale_simple (pixbuf, ++ gdk_pixbuf_get_width (pixbuf) / scaling_factor, ++ gdk_pixbuf_get_height (pixbuf) / scaling_factor, ++ GDK_INTERP_BILINEAR); ++ ++ cairo_surface_set_user_data (app->priv->icon, &app_icon_pixbuf_key, pixbuf, g_object_unref); ++ } ++ ++ return pixbuf; ++ } + else + { + WnckWindow *w = find_icon_window (app); +@@ -448,12 +476,39 @@ wnck_application_get_icon (WnckApplication *app) + GdkPixbuf* + wnck_application_get_mini_icon (WnckApplication *app) + { ++ static const cairo_user_data_key_t app_mini_icon_pixbuf_key; ++ + g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); + + _wnck_application_load_icons (app); + + if (app->priv->mini_icon) +- return app->priv->mini_icon; ++ { ++ GdkPixbuf *pixbuf; ++ ++ pixbuf = cairo_surface_get_user_data (app->priv->mini_icon, &app_mini_icon_pixbuf_key); ++ ++ if (pixbuf == NULL) ++ { ++ int scaling_factor; ++ ++ pixbuf = gdk_pixbuf_get_from_surface (app->priv->mini_icon, ++ 0, ++ 0, ++ cairo_image_surface_get_width (app->priv->mini_icon), ++ cairo_image_surface_get_height (app->priv->mini_icon)); ++ ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ pixbuf = gdk_pixbuf_scale_simple (pixbuf, ++ gdk_pixbuf_get_width (pixbuf) / scaling_factor, ++ gdk_pixbuf_get_height (pixbuf) / scaling_factor, ++ GDK_INTERP_BILINEAR); ++ ++ cairo_surface_set_user_data (app->priv->mini_icon, &app_mini_icon_pixbuf_key, pixbuf, g_object_unref); ++ } ++ ++ return pixbuf; ++ } + else + { + WnckWindow *w = find_icon_window (app); +@@ -464,6 +519,68 @@ wnck_application_get_mini_icon (WnckApplication *app) + } + } + ++/** ++ * wnck_application_get_icon_surface: ++ * @app: a #WnckApplication. ++ * ++ * Gets the icon-surface to be used for @app. If no icon-surfaceis set for @app, ++ * a suboptimal heuristic is used to find an appropriate icon. If no icon-surface ++ * was found, a fallback icon-surface is used. ++ * ++ * Return value: (transfer full): a reference to the icon-surface for @app. The ++ * caller should unreference the cairo_surface_t once done ++ * with it. ++ **/ ++cairo_surface_t* ++wnck_application_get_icon_surface (WnckApplication *app) ++{ ++ g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); ++ ++ _wnck_application_load_icons (app); ++ ++ if (app->priv->icon) ++ return cairo_surface_reference (app->priv->icon); ++ else ++ { ++ WnckWindow *w = find_icon_window (app); ++ if (w) ++ return wnck_window_get_icon_surface (w); ++ else ++ return NULL; ++ } ++} ++ ++/** ++ * wnck_application_get_mini_icon_surface: ++ * @app: a #WnckApplication. ++ * ++ * Gets the mini-icon-surface to be used for @app. If no mini-icon-surfaceis set ++ * for @app, a suboptimal heuristic is used to find an appropriate icon. If no ++ * mini-icon-surface was found, a fallback mini-icon-surface is used. ++ * ++ * Return value: (transfer full): a reference to the mini-icon-surface for @app. ++ * The caller should unreference the cairo_surface_t once ++ * done with it. ++ **/ ++cairo_surface_t* ++wnck_application_get_mini_icon_surface (WnckApplication *app) ++{ ++ g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); ++ ++ _wnck_application_load_icons (app); ++ ++ if (app->priv->mini_icon) ++ return cairo_surface_reference (app->priv->mini_icon); ++ else ++ { ++ WnckWindow *w = find_icon_window (app); ++ if (w) ++ return wnck_window_get_mini_icon_surface (w); ++ else ++ return NULL; ++ } ++} ++ + /** + * wnck_application_get_icon_is_fallback: + * @app: a #WnckApplication +diff --git a/libwnck/application.h b/libwnck/application.h +index 7966a81..e8893f5 100644 +--- a/libwnck/application.h ++++ b/libwnck/application.h +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + G_BEGIN_DECLS + +@@ -91,6 +92,8 @@ const char* wnck_application_get_icon_name (WnckApplication *app); + int wnck_application_get_pid (WnckApplication *app); + GdkPixbuf* wnck_application_get_icon (WnckApplication *app); + GdkPixbuf* wnck_application_get_mini_icon (WnckApplication *app); ++cairo_surface_t* wnck_application_get_icon_surface (WnckApplication *app); ++cairo_surface_t* wnck_application_get_mini_icon_surface (WnckApplication *app); + gboolean wnck_application_get_icon_is_fallback (WnckApplication *app); + const char* wnck_application_get_startup_id (WnckApplication *app); + +diff --git a/libwnck/class-group.c b/libwnck/class-group.c +index f4afc31..b0d4fee 100644 +--- a/libwnck/class-group.c ++++ b/libwnck/class-group.c +@@ -58,8 +58,8 @@ struct _WnckClassGroupPrivate { + GHashTable *window_icon_handlers; + GHashTable *window_name_handlers; + +- GdkPixbuf *icon; +- GdkPixbuf *mini_icon; ++ cairo_surface_t *icon; ++ cairo_surface_t *mini_icon; + }; + + G_DEFINE_TYPE_WITH_PRIVATE (WnckClassGroup, wnck_class_group, G_TYPE_OBJECT); +@@ -183,17 +183,8 @@ wnck_class_group_finalize (GObject *object) + class_group->priv->window_name_handlers = NULL; + } + +- if (class_group->priv->icon) +- { +- g_object_unref (class_group->priv->icon); +- class_group->priv->icon = NULL; +- } +- +- if (class_group->priv->mini_icon) +- { +- g_object_unref (class_group->priv->mini_icon); +- class_group->priv->mini_icon = NULL; +- } ++ g_clear_pointer (&class_group->priv->icon, cairo_surface_destroy); ++ g_clear_pointer (&class_group->priv->mini_icon, cairo_surface_destroy); + + G_OBJECT_CLASS (wnck_class_group_parent_class)->finalize (object); + } +@@ -381,7 +372,8 @@ set_name (WnckClassGroup *class_group) + + /* Walks the list of applications, trying to get an icon from them */ + static void +-get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon) ++get_icons_from_applications (WnckClassGroup *class_group, ++ cairo_surface_t **icon, cairo_surface_t **mini_icon) + { + GList *l; + +@@ -397,15 +389,15 @@ get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkP + app = wnck_window_get_application (window); + if (app) + { +- *icon = wnck_application_get_icon (app); +- *mini_icon = wnck_application_get_mini_icon (app); ++ *icon = wnck_application_get_icon_surface (app); ++ *mini_icon = wnck_application_get_mini_icon_surface (app); + + if (*icon && *mini_icon) + return; + else + { +- *icon = NULL; +- *mini_icon = NULL; ++ g_clear_pointer (icon, cairo_surface_destroy); ++ g_clear_pointer (mini_icon, cairo_surface_destroy); + } + } + } +@@ -413,7 +405,8 @@ get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkP + + /* Walks the list of windows, trying to get an icon from them */ + static void +-get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon) ++get_icons_from_windows (WnckClassGroup *class_group, ++ cairo_surface_t **icon, cairo_surface_t **mini_icon) + { + GList *l; + +@@ -426,15 +419,15 @@ get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf + + window = WNCK_WINDOW (l->data); + +- *icon = wnck_window_get_icon (window); +- *mini_icon = wnck_window_get_mini_icon (window); ++ *icon = wnck_window_get_icon_surface (window); ++ *mini_icon = wnck_window_get_mini_icon_surface (window); + + if (*icon && *mini_icon) + return; + else + { +- *icon = NULL; +- *mini_icon = NULL; ++ g_clear_pointer (icon, cairo_surface_destroy); ++ g_clear_pointer (mini_icon, cairo_surface_destroy); + } + } + } +@@ -445,7 +438,7 @@ get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf + static void + set_icon (WnckClassGroup *class_group) + { +- GdkPixbuf *icon, *mini_icon; ++ cairo_surface_t *icon, *mini_icon; + gboolean icons_reffed = FALSE; + + get_icons_from_applications (class_group, &icon, &mini_icon); +@@ -455,28 +448,39 @@ set_icon (WnckClassGroup *class_group) + + if (!icon || !mini_icon) + { +- _wnck_get_fallback_icons (&icon, ++ GdkPixbuf *icon_pixbuf, *mini_icon_pixbuf; ++ ++ _wnck_get_fallback_icons (&icon_pixbuf, + _wnck_get_default_icon_size (), +- &mini_icon, ++ &mini_icon_pixbuf, + _wnck_get_default_mini_icon_size ()); ++ if (icon_pixbuf) ++ { ++ icon = gdk_cairo_surface_create_from_pixbuf (icon_pixbuf, 0, NULL); ++ g_clear_object (&icon_pixbuf); ++ } ++ ++ if (mini_icon_pixbuf) ++ { ++ mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon_pixbuf, 0, NULL); ++ g_clear_object (&mini_icon_pixbuf); ++ } ++ + icons_reffed = TRUE; + } + + g_assert (icon && mini_icon); + +- if (class_group->priv->icon) +- g_object_unref (class_group->priv->icon); +- +- if (class_group->priv->mini_icon) +- g_object_unref (class_group->priv->mini_icon); ++ g_clear_pointer (&class_group->priv->icon, cairo_surface_destroy); ++ g_clear_pointer (&class_group->priv->mini_icon, cairo_surface_destroy); + + class_group->priv->icon = icon; + class_group->priv->mini_icon = mini_icon; + + if (!icons_reffed) + { +- g_object_ref (class_group->priv->icon); +- g_object_ref (class_group->priv->mini_icon); ++ cairo_surface_reference (class_group->priv->icon); ++ cairo_surface_reference (class_group->priv->mini_icon); + } + + g_signal_emit (G_OBJECT (class_group), signals[ICON_CHANGED], 0); +@@ -709,9 +713,39 @@ wnck_class_group_get_name (WnckClassGroup *class_group) + GdkPixbuf * + wnck_class_group_get_icon (WnckClassGroup *class_group) + { ++ static const cairo_user_data_key_t class_group_icon_pixbuf_key; ++ + g_return_val_if_fail (class_group != NULL, NULL); + +- return class_group->priv->icon; ++ if (class_group->priv->icon) ++ { ++ GdkPixbuf *pixbuf; ++ ++ pixbuf = cairo_surface_get_user_data (class_group->priv->icon, &class_group_icon_pixbuf_key); ++ ++ if (pixbuf == NULL) ++ { ++ int scaling_factor; ++ ++ pixbuf = gdk_pixbuf_get_from_surface (class_group->priv->icon, ++ 0, ++ 0, ++ cairo_image_surface_get_width (class_group->priv->icon), ++ cairo_image_surface_get_height (class_group->priv->icon)); ++ ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ pixbuf = gdk_pixbuf_scale_simple (pixbuf, ++ gdk_pixbuf_get_width (pixbuf) / scaling_factor, ++ gdk_pixbuf_get_height (pixbuf) / scaling_factor, ++ GDK_INTERP_BILINEAR); ++ ++ cairo_surface_set_user_data (class_group->priv->icon, &class_group_icon_pixbuf_key, pixbuf, g_object_unref); ++ } ++ ++ return pixbuf; ++ } ++ ++ return NULL; + } + + /** +@@ -730,8 +764,76 @@ wnck_class_group_get_icon (WnckClassGroup *class_group) + **/ + GdkPixbuf * + wnck_class_group_get_mini_icon (WnckClassGroup *class_group) ++{ ++ static const cairo_user_data_key_t class_group_mini_icon_pixbuf_key; ++ ++ g_return_val_if_fail (class_group != NULL, NULL); ++ ++ if (class_group->priv->mini_icon) ++ { ++ GdkPixbuf *pixbuf; ++ ++ pixbuf = cairo_surface_get_user_data (class_group->priv->mini_icon, &class_group_mini_icon_pixbuf_key); ++ ++ if (pixbuf == NULL) ++ { ++ int scaling_factor; ++ ++ pixbuf = gdk_pixbuf_get_from_surface (class_group->priv->mini_icon, ++ 0, ++ 0, ++ cairo_image_surface_get_width (class_group->priv->mini_icon), ++ cairo_image_surface_get_height (class_group->priv->mini_icon)); ++ ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ pixbuf = gdk_pixbuf_scale_simple (pixbuf, ++ gdk_pixbuf_get_width (pixbuf) / scaling_factor, ++ gdk_pixbuf_get_height (pixbuf) / scaling_factor, ++ GDK_INTERP_BILINEAR); ++ ++ cairo_surface_set_user_data (class_group->priv->mini_icon, &class_group_mini_icon_pixbuf_key, pixbuf, g_object_unref); ++ } ++ ++ return pixbuf; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * wnck_class_group_get_icon_surface: ++ * @class_group: a #WnckClassGroup. ++ * ++ * Gets the icon-surface to be used for @class_group. Since there is no way to ++ * properly find the icon-surface, the same suboptimal heuristic as the one for ++ * wnck_class_group_get_icon() is used to find it. ++ * ++ * Return value: (transfer full): the icon-surface for @class_group. The caller should ++ * unreference the returned cairo_surface_t once done with it. ++ **/ ++cairo_surface_t * ++wnck_class_group_get_icon_surface (WnckClassGroup *class_group) ++{ ++ g_return_val_if_fail (class_group != NULL, NULL); ++ ++ return cairo_surface_reference (class_group->priv->icon); ++} ++ ++/** ++ * wnck_class_group_get_mini_icon_surface: ++ * @class_group: a #WnckClassGroup. ++ * ++ * Gets the mini-icon-surface to be used for @class_group. Since there is no way to ++ * properly find the mini-icon-surface, the same suboptimal heuristic as the one for ++ * wnck_class_group_get_icon() is used to find it. ++ * ++ * Return value: (transfer full): the mini-icon-surface for @class_group. The caller should ++ * unreference the returned cairo_surface_t once done with it. ++ **/ ++cairo_surface_t * ++wnck_class_group_get_mini_icon_surface (WnckClassGroup *class_group) + { + g_return_val_if_fail (class_group != NULL, NULL); + +- return class_group->priv->mini_icon; ++ return cairo_surface_reference (class_group->priv->mini_icon); + } +diff --git a/libwnck/class-group.h b/libwnck/class-group.h +index ce084c3..5a9e07c 100644 +--- a/libwnck/class-group.h ++++ b/libwnck/class-group.h +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + G_BEGIN_DECLS + +@@ -81,6 +82,8 @@ const char * wnck_class_group_get_name (WnckClassGroup *class_group); + + GdkPixbuf *wnck_class_group_get_icon (WnckClassGroup *class_group); + GdkPixbuf *wnck_class_group_get_mini_icon (WnckClassGroup *class_group); ++cairo_surface_t *wnck_class_group_get_icon_surface (WnckClassGroup *class_group); ++cairo_surface_t *wnck_class_group_get_mini_icon_surface (WnckClassGroup *class_group); + + #ifndef WNCK_DISABLE_DEPRECATED + G_DEPRECATED_FOR(wnck_class_group_get_id) +diff --git a/libwnck/pager.c b/libwnck/pager.c +index c769d62..6a49585 100644 +--- a/libwnck/pager.c ++++ b/libwnck/pager.c +@@ -900,8 +900,9 @@ draw_window (cairo_t *cr, + gboolean translucent) + { + GtkStyleContext *context; +- GdkPixbuf *icon; ++ cairo_surface_t *icon; + int icon_x, icon_y, icon_w, icon_h; ++ int scaling_factor; + gboolean is_active; + GdkRGBA fg; + gdouble translucency; +@@ -931,14 +932,15 @@ draw_window (cairo_t *cr, + cairo_pop_group_to_source (cr); + cairo_paint_with_alpha (cr, translucency); + +- icon = wnck_window_get_icon (win); ++ icon = wnck_window_get_icon_surface (win); + + icon_w = icon_h = 0; ++ scaling_factor = gtk_widget_get_scale_factor (widget); + + if (icon) + { +- icon_w = gdk_pixbuf_get_width (icon); +- icon_h = gdk_pixbuf_get_height (icon); ++ icon_w = cairo_image_surface_get_width (icon) / scaling_factor; ++ icon_h = cairo_image_surface_get_height (icon) / scaling_factor; + + /* If the icon is too big, fall back to mini icon. + * We don't arbitrarily scale the icon, because it's +@@ -947,11 +949,12 @@ draw_window (cairo_t *cr, + if (icon_w > (winrect->width - 2) || + icon_h > (winrect->height - 2)) + { +- icon = wnck_window_get_mini_icon (win); ++ cairo_surface_destroy (icon); ++ icon = wnck_window_get_mini_icon_surface (win); + if (icon) + { +- icon_w = gdk_pixbuf_get_width (icon); +- icon_h = gdk_pixbuf_get_height (icon); ++ icon_w = cairo_image_surface_get_width (icon) / scaling_factor; ++ icon_h = cairo_image_surface_get_height (icon) / scaling_factor; + + /* Give up. */ + if (icon_w > (winrect->width - 2) || +@@ -967,7 +970,7 @@ draw_window (cairo_t *cr, + icon_y = winrect->y + (winrect->height - icon_h) / 2; + + cairo_push_group (cr); +- gtk_render_icon (context, cr, icon, icon_x, icon_y); ++ gtk_render_icon_surface (context, cr, icon, icon_x, icon_y); + cairo_pop_group_to_source (cr); + cairo_paint_with_alpha (cr, translucency); + } +@@ -988,6 +991,7 @@ draw_window (cairo_t *cr, + cairo_stroke (cr); + + gtk_style_context_restore (context); ++ cairo_surface_destroy (icon); + } + + static WnckWindow * +diff --git a/libwnck/selector.c b/libwnck/selector.c +index b975b27..6d85273 100644 +--- a/libwnck/selector.c ++++ b/libwnck/selector.c +@@ -121,103 +121,96 @@ wnck_selector_get_screen (WnckSelector *selector) + return wnck_screen_get (gdk_x11_screen_get_screen_number (screen)); + } + +-static GdkPixbuf * ++static cairo_surface_t * + wnck_selector_get_default_window_icon (void) + { +- static GdkPixbuf *retval = NULL; ++ static cairo_surface_t *retval = NULL; ++ GdkPixbuf *pixbuf; + + if (retval) + return retval; + +- retval = gdk_pixbuf_new_from_resource ("/org/gnome/libwnck/default_icon.png", NULL); ++ pixbuf = gdk_pixbuf_new_from_resource ("/org/gnome/libwnck/default_icon.png", NULL); + +- g_assert (retval); ++ g_assert (pixbuf); ++ ++ retval = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL); ++ ++ g_object_unref (pixbuf); + + return retval; + } + +-static GdkPixbuf * +-wnck_selector_dimm_icon (GdkPixbuf *pixbuf) ++static void ++wnck_selector_dimm_icon (cairo_t *cr, cairo_surface_t *surface) + { +- int x, y, pixel_stride, row_stride; +- guchar *row, *pixels; +- int w, h; +- GdkPixbuf *dimmed; ++ cairo_surface_t *temp; ++ cairo_t *temp_cr; + +- w = gdk_pixbuf_get_width (pixbuf); +- h = gdk_pixbuf_get_height (pixbuf); ++ g_assert (surface != NULL); ++ g_assert (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR); + +- if (gdk_pixbuf_get_has_alpha (pixbuf)) +- dimmed = gdk_pixbuf_copy (pixbuf); +- else +- dimmed = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0); ++ temp = cairo_surface_create_similar (surface, ++ cairo_surface_get_content (surface), ++ cairo_image_surface_get_width (surface), ++ cairo_image_surface_get_height (surface)); + +- pixel_stride = 4; ++ temp_cr = cairo_create (temp); + +- row = gdk_pixbuf_get_pixels (dimmed); +- row_stride = gdk_pixbuf_get_rowstride (dimmed); ++ cairo_set_source_surface (temp_cr, surface, 0, 0); ++ cairo_paint_with_alpha (temp_cr, 0.5); + +- for (y = 0; y < h; y++) +- { +- pixels = row; +- for (x = 0; x < w; x++) +- { +- pixels[3] /= 2; +- pixels += pixel_stride; +- } +- row += row_stride; +- } ++ cairo_set_operator (cr, CAIRO_OPERATOR_IN); ++ cairo_set_source_surface (cr, temp, 0, 0); ++ cairo_paint (cr); + +- return dimmed; ++ cairo_destroy (temp_cr); ++ cairo_surface_destroy (temp); + } + + void + _wnck_selector_set_window_icon (GtkWidget *image, + WnckWindow *window) + { +- GdkPixbuf *pixbuf, *freeme, *freeme2; +- int width, height; ++ cairo_surface_t *orig, *surface; ++ cairo_t *cr; ++ int scaling_factor; + int icon_size = -1; + +- pixbuf = NULL; +- freeme = NULL; +- freeme2 = NULL; ++ orig = NULL; ++ surface = NULL; + + if (window) +- pixbuf = wnck_window_get_mini_icon (window); ++ orig = wnck_window_get_mini_icon_surface (window); + +- if (!pixbuf) +- pixbuf = wnck_selector_get_default_window_icon (); ++ if (!orig) ++ orig = wnck_selector_get_default_window_icon (); + + if (icon_size == -1) + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size); + +- width = gdk_pixbuf_get_width (pixbuf); +- height = gdk_pixbuf_get_height (pixbuf); ++ surface = cairo_surface_create_similar_image (orig, ++ cairo_image_surface_get_format (orig), ++ cairo_image_surface_get_width (orig), ++ cairo_image_surface_get_height (orig)); + +- if (icon_size != -1 && (width > icon_size || height > icon_size)) +- { +- double scale; ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ cairo_surface_set_device_scale (surface, (double)scaling_factor, (double)scaling_factor); + +- scale = ((double) icon_size) / MAX (width, height); ++ cr = cairo_create (surface); + +- pixbuf = gdk_pixbuf_scale_simple (pixbuf, width * scale, +- height * scale, GDK_INTERP_BILINEAR); +- freeme = pixbuf; +- } ++ cairo_set_source_surface (cr, orig, 0, 0); ++ cairo_paint (cr); + + if (window && wnck_window_is_minimized (window)) + { +- pixbuf = wnck_selector_dimm_icon (pixbuf); +- freeme2 = pixbuf; ++ wnck_selector_dimm_icon (cr, surface); + } + +- gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); ++ gtk_image_set_from_surface (GTK_IMAGE (image), surface); + +- if (freeme) +- g_object_unref (freeme); +- if (freeme2) +- g_object_unref (freeme2); ++ cairo_destroy (cr); ++ cairo_surface_destroy (surface); + } + + static void +diff --git a/libwnck/tasklist.c b/libwnck/tasklist.c +index 8791b10..9cb8f0b 100644 +--- a/libwnck/tasklist.c ++++ b/libwnck/tasklist.c +@@ -270,11 +270,11 @@ static WnckTask *wnck_task_new_from_startup_sequence (WnckTasklist *tasklis + #endif + static gboolean wnck_task_get_needs_attention (WnckTask *task); + ++static cairo_surface_t *wnck_task_get_icon (WnckTask *task); + + static char *wnck_task_get_text (WnckTask *task, + gboolean icon_text, + gboolean include_state); +-static GdkPixbuf *wnck_task_get_icon (WnckTask *task); + static gint wnck_task_compare_alphabetically (gconstpointer a, + gconstpointer b); + static gint wnck_task_compare (gconstpointer a, +@@ -611,10 +611,10 @@ wnck_button_new (void) + } + + static void +-wnck_button_set_image_from_pixbuf (WnckButton *self, +- GdkPixbuf *pixbuf) ++wnck_button_set_image_from_surface (WnckButton *self, ++ cairo_surface_t *surface) + { +- gtk_image_set_from_pixbuf (GTK_IMAGE (self->image), pixbuf); ++ gtk_image_set_from_surface (GTK_IMAGE (self->image), surface); + } + + static void +@@ -3338,7 +3338,7 @@ wnck_task_popup_menu (WnckTask *task, + GtkWidget *menu; + WnckTask *win_task; + char *text; +- GdkPixbuf *pixbuf; ++ cairo_surface_t *surface; + GtkWidget *menu_item; + GList *l, *list; + +@@ -3382,15 +3382,15 @@ wnck_task_popup_menu (WnckTask *task, + gtk_widget_set_tooltip_text (menu_item, text); + g_free (text); + +- pixbuf = wnck_task_get_icon (win_task); +- if (pixbuf) ++ surface = wnck_task_get_icon (win_task); ++ if (surface) + { + WnckImageMenuItem *item; + + item = WNCK_IMAGE_MENU_ITEM (menu_item); + +- wnck_image_menu_item_set_image_from_icon_pixbuf (item, pixbuf); +- g_object_unref (pixbuf); ++ wnck_image_menu_item_set_image_from_icon_surface (item, surface); ++ cairo_surface_destroy (surface); + } + + gtk_widget_show (menu_item); +@@ -3576,111 +3576,91 @@ wnck_task_get_text (WnckTask *task, + } + + static void +-wnck_dimm_icon (GdkPixbuf *pixbuf) ++wnck_dimm_icon (cairo_t *cr, cairo_surface_t *surface) + { +- int x, y, pixel_stride, row_stride; +- guchar *row, *pixels; +- int w, h; ++ cairo_surface_t *temp; ++ cairo_t *temp_cr; + +- g_assert (pixbuf != NULL); ++ g_assert (surface != NULL); ++ g_assert (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR); + +- w = gdk_pixbuf_get_width (pixbuf); +- h = gdk_pixbuf_get_height (pixbuf); ++ temp = cairo_surface_create_similar (surface, ++ cairo_surface_get_content (surface), ++ cairo_image_surface_get_width (surface), ++ cairo_image_surface_get_height (surface)); + +- g_assert (gdk_pixbuf_get_has_alpha (pixbuf)); ++ temp_cr = cairo_create (temp); + +- pixel_stride = 4; ++ cairo_set_source_surface (temp_cr, surface, 0, 0); ++ cairo_paint_with_alpha (temp_cr, 0.5); + +- row = gdk_pixbuf_get_pixels (pixbuf); +- row_stride = gdk_pixbuf_get_rowstride (pixbuf); ++ cairo_set_operator (cr, CAIRO_OPERATOR_IN); ++ cairo_set_source_surface (cr, temp, 0, 0); ++ cairo_paint (cr); + +- for (y = 0; y < h; y++) +- { +- pixels = row; +- +- for (x = 0; x < w; x++) +- { +- pixels[3] /= 2; +- +- pixels += pixel_stride; +- } +- +- row += row_stride; +- } ++ cairo_destroy (temp_cr); ++ cairo_surface_destroy (temp); + } + +-static GdkPixbuf * +-wnck_task_scale_icon (GdkPixbuf *orig, gboolean minimized) ++static cairo_surface_t * ++wnck_task_scale_icon (cairo_surface_t *orig, gboolean minimized) + { +- int w, h; +- GdkPixbuf *pixbuf; ++ int scaling_factor; ++ cairo_surface_t *surface; ++ cairo_t *cr; + + if (!orig) + return NULL; + +- w = gdk_pixbuf_get_width (orig); +- h = gdk_pixbuf_get_height (orig); ++ surface = cairo_surface_create_similar_image (orig, ++ cairo_image_surface_get_format (orig), ++ cairo_image_surface_get_width (orig), ++ cairo_image_surface_get_height (orig)); + +- if (h != (int) MINI_ICON_SIZE || +- !gdk_pixbuf_get_has_alpha (orig)) +- { +- double scale; +- +- pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, +- TRUE, +- 8, +- MINI_ICON_SIZE * w / (double) h, +- MINI_ICON_SIZE); +- +- scale = MINI_ICON_SIZE / (double) gdk_pixbuf_get_height (orig); +- +- gdk_pixbuf_scale (orig, +- pixbuf, +- 0, 0, +- gdk_pixbuf_get_width (pixbuf), +- gdk_pixbuf_get_height (pixbuf), +- 0, 0, +- scale, scale, +- GDK_INTERP_HYPER); +- } +- else +- pixbuf = orig; ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ cairo_surface_set_device_scale (surface, (double)scaling_factor, (double)scaling_factor); ++ ++ cr = cairo_create (surface); ++ ++ cairo_set_source_surface (cr, orig, 0, 0); ++ cairo_paint (cr); + + if (minimized) + { +- if (orig == pixbuf) +- pixbuf = gdk_pixbuf_copy (orig); +- +- wnck_dimm_icon (pixbuf); ++ wnck_dimm_icon (cr, surface); + } + +- if (orig == pixbuf) +- g_object_ref (pixbuf); ++ cairo_destroy (cr); + +- return pixbuf; ++ return surface; + } + + +-static GdkPixbuf * ++static cairo_surface_t * + wnck_task_get_icon (WnckTask *task) + { + WnckWindowState state; +- GdkPixbuf *pixbuf; ++ cairo_surface_t *surface; ++ cairo_surface_t *mini_icon; + +- pixbuf = NULL; ++ surface = NULL; + + switch (task->type) + { + case WNCK_TASK_CLASS_GROUP: +- pixbuf = wnck_task_scale_icon (wnck_class_group_get_mini_icon (task->class_group), +- FALSE); ++ mini_icon = wnck_class_group_get_mini_icon_surface (task->class_group); ++ surface = wnck_task_scale_icon (mini_icon, FALSE); ++ ++ cairo_surface_destroy (mini_icon); + break; + + case WNCK_TASK_WINDOW: + state = wnck_window_get_state (task->window); + +- pixbuf = wnck_task_scale_icon (wnck_window_get_mini_icon (task->window), +- state & WNCK_WINDOW_STATE_MINIMIZED); ++ mini_icon = wnck_window_get_mini_icon_surface (task->window); ++ surface = wnck_task_scale_icon (mini_icon, state & WNCK_WINDOW_STATE_MINIMIZED); ++ ++ cairo_surface_destroy (mini_icon); + break; + + case WNCK_TASK_STARTUP_SEQUENCE: +@@ -3701,16 +3681,28 @@ wnck_task_get_icon (WnckTask *task) + + if (loaded != NULL) + { +- pixbuf = wnck_task_scale_icon (loaded, FALSE); ++ cairo_surface_t *temp; ++ ++ temp = gdk_cairo_surface_create_from_pixbuf (loaded, 0, NULL); ++ surface = wnck_task_scale_icon (temp, FALSE); ++ ++ cairo_surface_destroy (temp); + g_object_unref (G_OBJECT (loaded)); + } + } + } + +- if (pixbuf == NULL) ++ if (surface == NULL) + { ++ GdkPixbuf *pixbuf; + _wnck_get_fallback_icons (NULL, 0, + &pixbuf, MINI_ICON_SIZE); ++ ++ if (pixbuf != NULL) ++ { ++ surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL); ++ g_object_unref (pixbuf); ++ } + } + #endif + break; +@@ -3719,7 +3711,7 @@ wnck_task_get_icon (WnckTask *task) + break; + } + +- return pixbuf; ++ return surface; + } + + static gboolean +@@ -3768,12 +3760,13 @@ wnck_task_get_needs_attention (WnckTask *task) + static void + wnck_task_update_visible_state (WnckTask *task) + { +- GdkPixbuf *pixbuf; ++ cairo_surface_t *surface; + char *text; + +- pixbuf = wnck_task_get_icon (task); +- wnck_button_set_image_from_pixbuf (WNCK_BUTTON (task->button), pixbuf); +- g_clear_object (&pixbuf); ++ surface = wnck_task_get_icon (task); ++ wnck_button_set_image_from_surface (WNCK_BUTTON (task->button), surface); ++ if (surface) ++ cairo_surface_destroy (surface); + + text = wnck_task_get_text (task, TRUE, TRUE); + if (text != NULL) +@@ -4244,7 +4237,7 @@ wnck_task_draw (GtkWidget *widget, + static void + wnck_task_create_widgets (WnckTask *task, GtkReliefStyle relief) + { +- GdkPixbuf *pixbuf; ++ cairo_surface_t *surface; + char *text; + static const GtkTargetEntry targets[] = { + { (gchar *) "application/x-wnck-window-id", 0, 0 } +@@ -4271,9 +4264,9 @@ wnck_task_create_widgets (WnckTask *task, GtkReliefStyle relief) + gtk_drag_dest_set (GTK_WIDGET (task->button), 0, + NULL, 0, GDK_ACTION_DEFAULT); + +- pixbuf = wnck_task_get_icon (task); +- wnck_button_set_image_from_pixbuf (WNCK_BUTTON (task->button), pixbuf); +- g_clear_object (&pixbuf); ++ surface = wnck_task_get_icon (task); ++ wnck_button_set_image_from_surface (WNCK_BUTTON (task->button), surface); ++ cairo_surface_destroy (surface); + + text = wnck_task_get_text (task, TRUE, TRUE); + wnck_button_set_text (WNCK_BUTTON (task->button), text); +diff --git a/libwnck/util.c b/libwnck/util.c +index d51ee05..8ccce16 100644 +--- a/libwnck/util.c ++++ b/libwnck/util.c +@@ -721,7 +721,7 @@ wnck_set_default_icon_size (gsize size) + gsize + _wnck_get_default_icon_size (void) + { +- return default_icon_size; ++ return default_icon_size * _wnck_get_window_scaling_factor (); + } + + static gsize default_mini_icon_size = WNCK_DEFAULT_MINI_ICON_SIZE; +@@ -766,7 +766,7 @@ wnck_set_default_mini_icon_size (gsize size) + gsize + _wnck_get_default_mini_icon_size (void) + { +- return default_mini_icon_size; ++ return default_mini_icon_size * _wnck_get_window_scaling_factor (); + } + + /** +diff --git a/libwnck/window.c b/libwnck/window.c +index 25c56a5..bd8ac31 100644 +--- a/libwnck/window.c ++++ b/libwnck/window.c +@@ -89,8 +89,8 @@ struct _WnckWindowPrivate + + WnckWindowType wintype; + +- GdkPixbuf *icon; +- GdkPixbuf *mini_icon; ++ cairo_surface_t *icon; ++ cairo_surface_t *mini_icon; + + WnckIconCache *icon_cache; + +@@ -423,13 +423,8 @@ wnck_window_finalize (GObject *object) + g_free (window->priv->session_id_utf8); + window->priv->session_id_utf8 = NULL; + +- if (window->priv->icon) +- g_object_unref (G_OBJECT (window->priv->icon)); +- window->priv->icon = NULL; +- +- if (window->priv->mini_icon) +- g_object_unref (G_OBJECT (window->priv->mini_icon)); +- window->priv->mini_icon = NULL; ++ g_clear_pointer (&window->priv->icon, cairo_surface_destroy); ++ g_clear_pointer (&window->priv->mini_icon, cairo_surface_destroy); + + _wnck_icon_cache_free (window->priv->icon_cache); + window->priv->icon_cache = NULL; +@@ -2136,14 +2131,20 @@ get_icons (WnckWindow *window) + { + window->priv->need_emit_icon_changed = TRUE; + +- if (window->priv->icon) +- g_object_unref (G_OBJECT (window->priv->icon)); ++ g_clear_pointer (&window->priv->icon, cairo_surface_destroy); ++ g_clear_pointer (&window->priv->mini_icon, cairo_surface_destroy); + +- if (window->priv->mini_icon) +- g_object_unref (G_OBJECT (window->priv->mini_icon)); ++ if (icon) ++ { ++ window->priv->icon = gdk_cairo_surface_create_from_pixbuf (icon, 0, NULL); ++ g_clear_object (&icon); ++ } + +- window->priv->icon = icon; +- window->priv->mini_icon = mini_icon; ++ if (mini_icon) ++ { ++ window->priv->mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon, 0, NULL); ++ g_clear_object (&mini_icon); ++ } + } + + g_assert ((window->priv->icon && window->priv->mini_icon) || +@@ -2177,11 +2178,41 @@ _wnck_window_load_icons (WnckWindow *window) + GdkPixbuf* + wnck_window_get_icon (WnckWindow *window) + { ++ static const cairo_user_data_key_t window_icon_pixbuf_key; ++ + g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); + + _wnck_window_load_icons (window); + +- return window->priv->icon; ++ if (window->priv->icon) ++ { ++ GdkPixbuf *pixbuf; ++ ++ pixbuf = cairo_surface_get_user_data (window->priv->icon, &window_icon_pixbuf_key); ++ ++ if (pixbuf == NULL) ++ { ++ int scaling_factor; ++ ++ pixbuf = gdk_pixbuf_get_from_surface (window->priv->icon, ++ 0, ++ 0, ++ cairo_image_surface_get_width (window->priv->icon), ++ cairo_image_surface_get_height (window->priv->icon)); ++ ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ pixbuf = gdk_pixbuf_scale_simple (pixbuf, ++ gdk_pixbuf_get_width (pixbuf) / scaling_factor, ++ gdk_pixbuf_get_height (pixbuf) / scaling_factor, ++ GDK_INTERP_BILINEAR); ++ ++ cairo_surface_set_user_data (window->priv->icon, &window_icon_pixbuf_key, pixbuf, g_object_unref); ++ } ++ ++ return pixbuf; ++ } ++ ++ return NULL; + } + + /** +@@ -2198,12 +2229,86 @@ wnck_window_get_icon (WnckWindow *window) + **/ + GdkPixbuf* + wnck_window_get_mini_icon (WnckWindow *window) ++{ ++ static const cairo_user_data_key_t window_mini_icon_pixbuf_key; ++ ++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); ++ ++ _wnck_window_load_icons (window); ++ ++ if (window->priv->mini_icon) ++ { ++ GdkPixbuf *pixbuf; ++ ++ pixbuf = cairo_surface_get_user_data (window->priv->mini_icon, &window_mini_icon_pixbuf_key); ++ ++ if (pixbuf == NULL) ++ { ++ int scaling_factor; ++ ++ pixbuf = gdk_pixbuf_get_from_surface (window->priv->mini_icon, ++ 0, ++ 0, ++ cairo_image_surface_get_width (window->priv->mini_icon), ++ cairo_image_surface_get_height (window->priv->mini_icon)); ++ ++ scaling_factor = _wnck_get_window_scaling_factor (); ++ pixbuf = gdk_pixbuf_scale_simple (pixbuf, ++ gdk_pixbuf_get_width (pixbuf) / scaling_factor, ++ gdk_pixbuf_get_height (pixbuf) / scaling_factor, ++ GDK_INTERP_BILINEAR); ++ ++ cairo_surface_set_user_data (window->priv->mini_icon, &window_mini_icon_pixbuf_key, pixbuf, g_object_unref); ++ } ++ ++ return pixbuf; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * wnck_window_get_icon_surface: ++ * @window: a #WnckWindow. ++ * ++ * Gets the icon-surface to be used for @window. If no icon-surface was found, a ++ * fallback icon-surface is used. wnck_window_get_icon_is_fallback() can be used ++ * to tell if the icon-surface is the fallback icon-surface. ++ * ++ * Return value: (transfer full): a reference to the icon-surface for @window. ++ * The caller should unreference the returned cairo_surface_t ++ * once done with it. ++ **/ ++cairo_surface_t* ++wnck_window_get_icon_surface (WnckWindow *window) ++{ ++ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); ++ ++ _wnck_window_load_icons (window); ++ ++ return cairo_surface_reference (window->priv->icon); ++} ++ ++/** ++ * wnck_window_get_mini_icon_surface: ++ * @window: a #WnckWindow. ++ * ++ * Gets the mini-icon-surface to be used for @window. If no mini-icon-surface ++ * was found, a fallback mini-icon-surface is used. wnck_window_get_icon_is_fallback() ++ * can be used to tell if the mini-icon-surface is the fallback mini-icon-surface. ++ * ++ * Return value: (transfer full): a reference to the mini-icon-surface for @window. ++ * The caller should unreference the returned cairo_surface_t ++ * once done with it. ++ **/ ++cairo_surface_t* ++wnck_window_get_mini_icon_surface (WnckWindow *window) + { + g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); + + _wnck_window_load_icons (window); + +- return window->priv->mini_icon; ++ return cairo_surface_reference (window->priv->mini_icon); + } + + /** +diff --git a/libwnck/window.h b/libwnck/window.h +index 72545aa..fb3ce51 100644 +--- a/libwnck/window.h ++++ b/libwnck/window.h +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + G_BEGIN_DECLS + +@@ -381,6 +382,8 @@ gboolean wnck_window_transient_is_most_recently_activated (WnckWindow *window); + + GdkPixbuf* wnck_window_get_icon (WnckWindow *window); + GdkPixbuf* wnck_window_get_mini_icon (WnckWindow *window); ++cairo_surface_t* wnck_window_get_icon_surface (WnckWindow *window); ++cairo_surface_t* wnck_window_get_mini_icon_surface (WnckWindow *window); + + gboolean wnck_window_get_icon_is_fallback (WnckWindow *window); + +diff --git a/libwnck/wnck-image-menu-item-private.h b/libwnck/wnck-image-menu-item-private.h +index 265289d..5c47517 100644 +--- a/libwnck/wnck-image-menu-item-private.h ++++ b/libwnck/wnck-image-menu-item-private.h +@@ -34,6 +34,9 @@ GtkWidget *wnck_image_menu_item_new_with_label (const gchar *l + void wnck_image_menu_item_set_image_from_icon_pixbuf (WnckImageMenuItem *item, + GdkPixbuf *pixbuf); + ++void wnck_image_menu_item_set_image_from_icon_surface (WnckImageMenuItem *item, ++ cairo_surface_t *surface); ++ + void wnck_image_menu_item_set_image_from_window (WnckImageMenuItem *item, + WnckWindow *window); + +diff --git a/libwnck/wnck-image-menu-item.c b/libwnck/wnck-image-menu-item.c +index e8e6d87..7f5efdc 100644 +--- a/libwnck/wnck-image-menu-item.c ++++ b/libwnck/wnck-image-menu-item.c +@@ -219,6 +219,14 @@ wnck_image_menu_item_set_image_from_icon_pixbuf (WnckImageMenuItem *item, + gtk_widget_show (item->image); + } + ++void ++wnck_image_menu_item_set_image_from_icon_surface (WnckImageMenuItem *item, ++ cairo_surface_t *surface) ++{ ++ gtk_image_set_from_surface (GTK_IMAGE (item->image), surface); ++ gtk_widget_show (item->image); ++} ++ + void + wnck_image_menu_item_set_image_from_window (WnckImageMenuItem *item, + WnckWindow *window) +-- +2.31.1 + diff --git a/libwnck_0003-xutils-Change-icons-to-being-cairo-surfaces.patch b/libwnck_0003-xutils-Change-icons-to-being-cairo-surfaces.patch new file mode 100644 index 0000000..70f4874 --- /dev/null +++ b/libwnck_0003-xutils-Change-icons-to-being-cairo-surfaces.patch @@ -0,0 +1,812 @@ +From b5db0c72ffcb702f7c277bf46ce0c6585b079b25 Mon Sep 17 00:00:00 2001 +From: Victor Kareh +Date: Mon, 20 Jan 2020 13:38:59 -0500 +Subject: [PATCH 3/5] xutils: Change icons to being cairo surfaces + +Since all icons are stored internally as cairo surfaces, we should be +returning icons as cairo surfaces from the private functions in xutils. +This simplifies the drawing codepath and makes us able to delete a bunch +of GdkPixbuf manipulation. + +adapted from https://gitlab.gnome.org/GNOME/mutter/commit/af7f51b9 +--- + libwnck/application.c | 22 +-- + libwnck/class-group.c | 17 +- + libwnck/tasklist.c | 9 +- + libwnck/util.c | 4 +- + libwnck/window.c | 22 +-- + libwnck/wnck-icon-cache-private.h | 15 +- + libwnck/wnck-icon-cache.c | 305 +++++++++++++----------------- + libwnck/xutils.c | 42 ++-- + libwnck/xutils.h | 11 +- + 9 files changed, 186 insertions(+), 261 deletions(-) + +diff --git a/libwnck/application.c b/libwnck/application.c +index 66f1502..8d9d034 100644 +--- a/libwnck/application.c ++++ b/libwnck/application.c +@@ -323,15 +323,17 @@ wnck_application_get_pid (WnckApplication *app) + static void + get_icons (WnckApplication *app) + { +- GdkPixbuf *icon; +- GdkPixbuf *mini_icon; ++ cairo_surface_t *icon; ++ cairo_surface_t *mini_icon; + gsize normal_size; + gsize mini_size; ++ int scaling_factor; + + icon = NULL; + mini_icon = NULL; + normal_size = _wnck_get_default_icon_size (); + mini_size = _wnck_get_default_mini_icon_size (); ++ scaling_factor = _wnck_get_window_scaling_factor (); + + if (_wnck_read_icons (app->priv->screen, + app->priv->xwindow, +@@ -339,7 +341,8 @@ get_icons (WnckApplication *app) + &icon, + normal_size, + &mini_icon, +- mini_size)) ++ mini_size, ++ scaling_factor)) + { + app->priv->need_emit_icon_changed = TRUE; + app->priv->icon_from_leader = TRUE; +@@ -347,17 +350,8 @@ get_icons (WnckApplication *app) + g_clear_pointer (&app->priv->icon, cairo_surface_destroy); + g_clear_pointer (&app->priv->mini_icon, cairo_surface_destroy); + +- if (icon) +- { +- app->priv->icon = gdk_cairo_surface_create_from_pixbuf (icon, 0, NULL); +- g_clear_object (&icon); +- } +- +- if (mini_icon) +- { +- app->priv->mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon, 0, NULL); +- g_clear_object (&mini_icon); +- } ++ app->priv->icon = icon; ++ app->priv->mini_icon = mini_icon; + } + + /* FIXME we should really fall back to using the icon +diff --git a/libwnck/class-group.c b/libwnck/class-group.c +index b0d4fee..dbf5e1d 100644 +--- a/libwnck/class-group.c ++++ b/libwnck/class-group.c +@@ -448,23 +448,10 @@ set_icon (WnckClassGroup *class_group) + + if (!icon || !mini_icon) + { +- GdkPixbuf *icon_pixbuf, *mini_icon_pixbuf; +- +- _wnck_get_fallback_icons (&icon_pixbuf, ++ _wnck_get_fallback_icons (&icon, + _wnck_get_default_icon_size (), +- &mini_icon_pixbuf, ++ &mini_icon, + _wnck_get_default_mini_icon_size ()); +- if (icon_pixbuf) +- { +- icon = gdk_cairo_surface_create_from_pixbuf (icon_pixbuf, 0, NULL); +- g_clear_object (&icon_pixbuf); +- } +- +- if (mini_icon_pixbuf) +- { +- mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon_pixbuf, 0, NULL); +- g_clear_object (&mini_icon_pixbuf); +- } + + icons_reffed = TRUE; + } +diff --git a/libwnck/tasklist.c b/libwnck/tasklist.c +index 9cb8f0b..b10ef8c 100644 +--- a/libwnck/tasklist.c ++++ b/libwnck/tasklist.c +@@ -3694,15 +3694,8 @@ wnck_task_get_icon (WnckTask *task) + + if (surface == NULL) + { +- GdkPixbuf *pixbuf; + _wnck_get_fallback_icons (NULL, 0, +- &pixbuf, MINI_ICON_SIZE); +- +- if (pixbuf != NULL) +- { +- surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL); +- g_object_unref (pixbuf); +- } ++ &surface, MINI_ICON_SIZE); + } + #endif + break; +diff --git a/libwnck/util.c b/libwnck/util.c +index 8ccce16..d51ee05 100644 +--- a/libwnck/util.c ++++ b/libwnck/util.c +@@ -721,7 +721,7 @@ wnck_set_default_icon_size (gsize size) + gsize + _wnck_get_default_icon_size (void) + { +- return default_icon_size * _wnck_get_window_scaling_factor (); ++ return default_icon_size; + } + + static gsize default_mini_icon_size = WNCK_DEFAULT_MINI_ICON_SIZE; +@@ -766,7 +766,7 @@ wnck_set_default_mini_icon_size (gsize size) + gsize + _wnck_get_default_mini_icon_size (void) + { +- return default_mini_icon_size * _wnck_get_window_scaling_factor (); ++ return default_mini_icon_size; + } + + /** +diff --git a/libwnck/window.c b/libwnck/window.c +index bd8ac31..92f3c08 100644 +--- a/libwnck/window.c ++++ b/libwnck/window.c +@@ -2111,15 +2111,17 @@ wnck_window_transient_is_most_recently_activated (WnckWindow *window) + static void + get_icons (WnckWindow *window) + { +- GdkPixbuf *icon; +- GdkPixbuf *mini_icon; ++ cairo_surface_t *icon; ++ cairo_surface_t *mini_icon; + gsize normal_size; + gsize mini_size; ++ int scaling_factor; + + icon = NULL; + mini_icon = NULL; + normal_size = _wnck_get_default_icon_size (); + mini_size = _wnck_get_default_mini_icon_size (); ++ scaling_factor = _wnck_get_window_scaling_factor (); + + if (_wnck_read_icons (window->priv->screen, + window->priv->xwindow, +@@ -2127,24 +2129,16 @@ get_icons (WnckWindow *window) + &icon, + normal_size, + &mini_icon, +- mini_size)) ++ mini_size, ++ scaling_factor)) + { + window->priv->need_emit_icon_changed = TRUE; + + g_clear_pointer (&window->priv->icon, cairo_surface_destroy); + g_clear_pointer (&window->priv->mini_icon, cairo_surface_destroy); + +- if (icon) +- { +- window->priv->icon = gdk_cairo_surface_create_from_pixbuf (icon, 0, NULL); +- g_clear_object (&icon); +- } +- +- if (mini_icon) +- { +- window->priv->mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon, 0, NULL); +- g_clear_object (&mini_icon); +- } ++ window->priv->icon = icon; ++ window->priv->mini_icon = mini_icon; + } + + g_assert ((window->priv->icon && window->priv->mini_icon) || +diff --git a/libwnck/wnck-icon-cache-private.h b/libwnck/wnck-icon-cache-private.h +index 6a3d5ec..d3c39e2 100644 +--- a/libwnck/wnck-icon-cache-private.h ++++ b/libwnck/wnck-icon-cache-private.h +@@ -38,13 +38,14 @@ void _wnck_icon_cache_set_want_fallback (WnckIconCache *icon_cache + gboolean setting); + gboolean _wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache); + +-gboolean _wnck_read_icons (WnckScreen *screen, +- Window xwindow, +- WnckIconCache *icon_cache, +- GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size); ++gboolean _wnck_read_icons (WnckScreen *screen, ++ Window xwindow, ++ WnckIconCache *icon_cache, ++ cairo_surface_t **iconp, ++ int ideal_size, ++ cairo_surface_t **mini_iconp, ++ int ideal_mini_size, ++ int scaling_factor); + + G_END_DECLS + +diff --git a/libwnck/wnck-icon-cache.c b/libwnck/wnck-icon-cache.c +index 1749585..38131d8 100644 +--- a/libwnck/wnck-icon-cache.c ++++ b/libwnck/wnck-icon-cache.c +@@ -45,8 +45,8 @@ struct _WnckIconCache + IconOrigin origin; + Pixmap prev_pixmap; + Pixmap prev_mask; +- GdkPixbuf *icon; +- GdkPixbuf *mini_icon; ++ cairo_surface_t *icon; ++ cairo_surface_t *mini_icon; + int ideal_size; + int ideal_mini_size; + guint want_fallback : 1; +@@ -141,49 +141,65 @@ find_best_size (gulong *data, + return FALSE; + } + +-static void +-argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata) ++static cairo_surface_t * ++argbdata_to_surface (gulong *argb_data, ++ int w, ++ int h, ++ int ideal_w, ++ int ideal_h, ++ int scaling_factor) + { +- guchar *p; +- int i; ++ cairo_surface_t *surface, *icon; ++ cairo_t *cr; ++ int y, x, stride; ++ uint32_t *data; + +- *pixdata = g_new (guchar, len * 4); +- p = *pixdata; ++ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); ++ cairo_surface_set_device_scale (surface, (double)scaling_factor, (double)scaling_factor); ++ stride = cairo_image_surface_get_stride (surface) / sizeof (uint32_t); ++ data = (uint32_t *) cairo_image_surface_get_data (surface); + + /* One could speed this up a lot. */ +- i = 0; +- while (i < len) ++ for (y = 0; y < h; y++) + { +- guint argb; +- guint rgba; +- +- argb = argb_data[i]; +- rgba = (argb << 8) | (argb >> 24); +- +- *p = rgba >> 24; +- ++p; +- *p = (rgba >> 16) & 0xff; +- ++p; +- *p = (rgba >> 8) & 0xff; +- ++p; +- *p = rgba & 0xff; +- ++p; +- +- ++i; ++ for (x = 0; x < w; x++) ++ { ++ uint32_t *p = &data[y * stride + x]; ++ gulong *d = &argb_data[y * w + x]; ++ *p = *d; ++ } + } ++ ++ cairo_surface_mark_dirty (surface); ++ ++ icon = cairo_surface_create_similar_image (surface, ++ cairo_image_surface_get_format (surface), ++ ideal_w, ideal_h); ++ ++ cairo_surface_set_device_scale (icon, (double)scaling_factor, (double)scaling_factor); ++ ++ cr = cairo_create (icon); ++ cairo_scale (cr, ideal_w / (double)w, ideal_h / (double)h); ++ cairo_set_source_surface (cr, surface, 0, 0); ++ cairo_paint (cr); ++ ++ cairo_set_operator (cr, CAIRO_OPERATOR_IN); ++ cairo_paint (cr); ++ ++ cairo_destroy (cr); ++ cairo_surface_destroy (surface); ++ ++ return icon; + } + + static gboolean +-read_rgb_icon (Screen *screen, +- Window xwindow, +- int ideal_size, +- int ideal_mini_size, +- int *width, +- int *height, +- guchar **pixdata, +- int *mini_width, +- int *mini_height, +- guchar **mini_pixdata) ++read_rgb_icon (Screen *screen, ++ Window xwindow, ++ int ideal_size, ++ int ideal_mini_size, ++ cairo_surface_t **iconp, ++ cairo_surface_t **mini_iconp, ++ int scaling_factor) + { + Display *display; + Atom type; +@@ -221,7 +237,9 @@ read_rgb_icon (Screen *screen, + return FALSE; + } + +- if (!find_best_size (data, nitems, ideal_size, &w, &h, &best)) ++ if (!find_best_size (data, nitems, ++ ideal_size, ++ &w, &h, &best)) + { + XFree (data); + return FALSE; +@@ -235,14 +253,8 @@ read_rgb_icon (Screen *screen, + return FALSE; + } + +- *width = w; +- *height = h; +- +- *mini_width = mini_w; +- *mini_height = mini_h; +- +- argbdata_to_pixdata (best, w * h, pixdata); +- argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata); ++ *iconp = argbdata_to_surface (best, w, h, ideal_size, ideal_size, scaling_factor); ++ *mini_iconp = argbdata_to_surface (best_mini, mini_w, mini_h, ideal_mini_size, ideal_mini_size, scaling_factor); + + XFree (data); + +@@ -250,27 +262,27 @@ read_rgb_icon (Screen *screen, + } + + static gboolean +-try_pixmap_and_mask (Screen *screen, +- Pixmap src_pixmap, +- Pixmap src_mask, +- GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size) ++try_pixmap_and_mask (Screen *screen, ++ Pixmap src_pixmap, ++ Pixmap src_mask, ++ cairo_surface_t **iconp, ++ int ideal_size, ++ cairo_surface_t **mini_iconp, ++ int ideal_mini_size, ++ int scaling_factor) + { + cairo_surface_t *surface, *mask_surface, *image; + GdkDisplay *gdk_display; +- GdkPixbuf *unscaled; + int width, height; + cairo_t *cr; + + if (src_pixmap == None) + return FALSE; + +- surface = _wnck_cairo_surface_get_from_pixmap (screen, src_pixmap); ++ surface = _wnck_cairo_surface_get_from_pixmap (screen, src_pixmap, scaling_factor); + + if (surface && src_mask != None) +- mask_surface = _wnck_cairo_surface_get_from_pixmap (screen, src_mask); ++ mask_surface = _wnck_cairo_surface_get_from_pixmap (screen, src_mask, scaling_factor); + else + mask_surface = NULL; + +@@ -326,26 +338,41 @@ try_pixmap_and_mask (Screen *screen, + return FALSE; + } + +- unscaled = gdk_pixbuf_get_from_surface (image, +- 0, 0, +- width, height); ++ if (image) ++ { ++ int image_w, image_h; + +- cairo_surface_destroy (image); ++ image_w = cairo_image_surface_get_width (image); ++ image_h = cairo_image_surface_get_height (image); ++ ++ *iconp = cairo_surface_create_similar (image, ++ cairo_surface_get_content (image), ++ ideal_size, ++ ideal_size); ++ ++ cairo_surface_set_device_scale (*iconp, (double)scaling_factor, (double)scaling_factor); ++ ++ cr = cairo_create (*iconp); ++ cairo_scale (cr, ideal_size / (double)image_w, ideal_size / (double)image_h); ++ cairo_set_source_surface (cr, image, 0, 0); ++ cairo_paint (cr); ++ cairo_destroy (cr); ++ ++ *mini_iconp = cairo_surface_create_similar (image, ++ cairo_surface_get_content (image), ++ ideal_mini_size, ++ ideal_mini_size); ++ ++ cairo_surface_set_device_scale (*mini_iconp, (double)scaling_factor, (double)scaling_factor); ++ ++ cr = cairo_create (*mini_iconp); ++ cairo_scale (cr, ideal_mini_size / (double)image_w, ideal_mini_size / (double)image_h); ++ cairo_set_source_surface (cr, image, 0, 0); ++ cairo_paint (cr); ++ cairo_destroy (cr); ++ ++ cairo_surface_destroy (image); + +- if (unscaled) +- { +- *iconp = +- gdk_pixbuf_scale_simple (unscaled, +- ideal_size, +- ideal_size, +- GDK_INTERP_BILINEAR); +- *mini_iconp = +- gdk_pixbuf_scale_simple (unscaled, +- ideal_mini_size, +- ideal_mini_size, +- GDK_INTERP_BILINEAR); +- +- g_object_unref (G_OBJECT (unscaled)); + return TRUE; + } + else +@@ -404,13 +431,8 @@ static void + clear_icon_cache (WnckIconCache *icon_cache, + gboolean dirty_all) + { +- if (icon_cache->icon) +- g_object_unref (G_OBJECT (icon_cache->icon)); +- icon_cache->icon = NULL; +- +- if (icon_cache->mini_icon) +- g_object_unref (G_OBJECT (icon_cache->mini_icon)); +- icon_cache->mini_icon = NULL; ++ g_clear_pointer (&icon_cache->icon, cairo_surface_destroy); ++ g_clear_pointer (&icon_cache->mini_icon, cairo_surface_destroy); + + icon_cache->origin = USING_NO_ICON; + +@@ -423,89 +445,26 @@ clear_icon_cache (WnckIconCache *icon_cache, + } + + static void +-replace_cache (WnckIconCache *icon_cache, +- IconOrigin origin, +- GdkPixbuf *new_icon, +- GdkPixbuf *new_mini_icon) ++replace_cache (WnckIconCache *icon_cache, ++ IconOrigin origin, ++ cairo_surface_t *new_icon, ++ cairo_surface_t *new_mini_icon) + { + clear_icon_cache (icon_cache, FALSE); + + icon_cache->origin = origin; + + if (new_icon) +- g_object_ref (G_OBJECT (new_icon)); ++ cairo_surface_reference (new_icon); + + icon_cache->icon = new_icon; + + if (new_mini_icon) +- g_object_ref (G_OBJECT (new_mini_icon)); ++ cairo_surface_reference (new_mini_icon); + + icon_cache->mini_icon = new_mini_icon; + } + +-static void +-free_pixels (guchar *pixels, +- gpointer data) +-{ +- g_free (pixels); +-} +- +-static GdkPixbuf* +-scaled_from_pixdata (guchar *pixdata, +- int w, +- int h, +- int new_w, +- int new_h) +-{ +- GdkPixbuf *src; +- GdkPixbuf *dest; +- +- src = gdk_pixbuf_new_from_data (pixdata, +- GDK_COLORSPACE_RGB, +- TRUE, +- 8, +- w, h, w * 4, +- free_pixels, +- NULL); +- +- if (src == NULL) +- return NULL; +- +- if (w != h) +- { +- GdkPixbuf *tmp; +- int size; +- +- size = MAX (w, h); +- +- tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size); +- +- if (tmp != NULL) +- { +- gdk_pixbuf_fill (tmp, 0); +- gdk_pixbuf_copy_area (src, 0, 0, w, h, +- tmp, +- (size - w) / 2, (size - h) / 2); +- +- g_object_unref (src); +- src = tmp; +- } +- } +- +- if (w != new_w || h != new_h) +- { +- dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR); +- +- g_object_unref (G_OBJECT (src)); +- } +- else +- { +- dest = src; +- } +- +- return dest; +-} +- + WnckIconCache* + _wnck_icon_cache_new (void) + { +@@ -585,22 +544,17 @@ _wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache) + } + + gboolean +-_wnck_read_icons (WnckScreen *screen, +- Window xwindow, +- WnckIconCache *icon_cache, +- GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size) ++_wnck_read_icons (WnckScreen *screen, ++ Window xwindow, ++ WnckIconCache *icon_cache, ++ cairo_surface_t **iconp, ++ int ideal_size, ++ cairo_surface_t **mini_iconp, ++ int ideal_mini_size, ++ int scaling_factor) + { + Screen *xscreen; + Display *display; +- guchar *pixdata; +- int w, h; +- guchar *mini_pixdata; +- int mini_w, mini_h; +- Pixmap pixmap; +- Pixmap mask; + XWMHints *hints; + + /* Return value is whether the icon changed */ +@@ -613,6 +567,9 @@ _wnck_read_icons (WnckScreen *screen, + *iconp = NULL; + *mini_iconp = NULL; + ++ ideal_size *= scaling_factor; ++ ideal_mini_size *= scaling_factor; ++ + if (ideal_size != icon_cache->ideal_size || + ideal_mini_size != icon_cache->ideal_mini_size) + clear_icon_cache (icon_cache, TRUE); +@@ -623,8 +580,6 @@ _wnck_read_icons (WnckScreen *screen, + if (!_wnck_icon_cache_get_icon_invalidated (icon_cache)) + return FALSE; /* we have no new info to use */ + +- pixdata = NULL; +- + /* Our algorithm here assumes that we can't have for example origin + * < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE + * unless we have tried to read NET_WM_ICON. +@@ -636,21 +591,15 @@ _wnck_read_icons (WnckScreen *screen, + + if (icon_cache->origin <= USING_NET_WM_ICON && + icon_cache->net_wm_icon_dirty) +- + { + icon_cache->net_wm_icon_dirty = FALSE; + + if (read_rgb_icon (xscreen, xwindow, + ideal_size, + ideal_mini_size, +- &w, &h, &pixdata, +- &mini_w, &mini_h, &mini_pixdata)) ++ iconp, mini_iconp, ++ scaling_factor)) + { +- *iconp = scaled_from_pixdata (pixdata, w, h, ideal_size, ideal_size); +- +- *mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h, +- ideal_mini_size, ideal_mini_size); +- + replace_cache (icon_cache, USING_NET_WM_ICON, + *iconp, *mini_iconp); + +@@ -661,6 +610,9 @@ _wnck_read_icons (WnckScreen *screen, + if (icon_cache->origin <= USING_WM_HINTS && + icon_cache->wm_hints_dirty) + { ++ Pixmap pixmap; ++ Pixmap mask; ++ + icon_cache->wm_hints_dirty = FALSE; + + _wnck_error_trap_push (display); +@@ -689,7 +641,8 @@ _wnck_read_icons (WnckScreen *screen, + { + if (try_pixmap_and_mask (xscreen, pixmap, mask, + iconp, ideal_size, +- mini_iconp, ideal_mini_size)) ++ mini_iconp, ideal_mini_size, ++ scaling_factor)) + { + icon_cache->prev_pixmap = pixmap; + icon_cache->prev_mask = mask; +@@ -705,6 +658,9 @@ _wnck_read_icons (WnckScreen *screen, + if (icon_cache->origin <= USING_KWM_WIN_ICON && + icon_cache->kwm_win_icon_dirty) + { ++ Pixmap pixmap; ++ Pixmap mask; ++ + icon_cache->kwm_win_icon_dirty = FALSE; + + get_kwm_win_icon (xscreen, xwindow, &pixmap, &mask); +@@ -715,7 +671,8 @@ _wnck_read_icons (WnckScreen *screen, + { + if (try_pixmap_and_mask (xscreen, pixmap, mask, + iconp, ideal_size, +- mini_iconp, ideal_mini_size)) ++ mini_iconp, ideal_mini_size, ++ scaling_factor)) + { + icon_cache->prev_pixmap = pixmap; + icon_cache->prev_mask = mask; +diff --git a/libwnck/xutils.c b/libwnck/xutils.c +index 58873dc..cd9036a 100644 +--- a/libwnck/xutils.c ++++ b/libwnck/xutils.c +@@ -1453,7 +1453,8 @@ _wnck_select_input (Screen *screen, + + cairo_surface_t * + _wnck_cairo_surface_get_from_pixmap (Screen *screen, +- Pixmap xpixmap) ++ Pixmap xpixmap, ++ int scaling_factor) + { + cairo_surface_t *surface; + Display *display; +@@ -1471,6 +1472,9 @@ _wnck_cairo_surface_get_from_pixmap (Screen *screen, + &x_ret, &y_ret, &w_ret, &h_ret, &bw_ret, &depth_ret)) + goto TRAP_POP; + ++ w_ret *= scaling_factor; ++ h_ret *= scaling_factor; ++ + if (depth_ret == 1) + { + surface = cairo_xlib_surface_create_for_bitmap (display, +@@ -1527,7 +1531,7 @@ _wnck_gdk_pixbuf_get_from_pixmap (Screen *screen, + cairo_surface_t *surface; + GdkPixbuf *retval; + +- surface = _wnck_cairo_surface_get_from_pixmap (screen, xpixmap); ++ surface = _wnck_cairo_surface_get_from_pixmap (screen, xpixmap, 1); + + if (surface == NULL) + return NULL; +@@ -1542,36 +1546,30 @@ _wnck_gdk_pixbuf_get_from_pixmap (Screen *screen, + return retval; + } + +-static GdkPixbuf* ++static cairo_surface_t* + default_icon_at_size (int size) + { +- GdkPixbuf *base; ++ GdkPixbuf *pixbuf; ++ cairo_surface_t *surface; + +- base = gdk_pixbuf_new_from_resource ("/org/gnome/libwnck/default_icon.png", NULL); ++ pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/org/gnome/libwnck/default_icon.png", ++ size, size, ++ TRUE, NULL); + +- g_assert (base); ++ g_assert (pixbuf); + +- if (gdk_pixbuf_get_width (base) == size && +- gdk_pixbuf_get_height (base) == size) +- { +- return base; +- } +- else +- { +- GdkPixbuf *scaled; ++ surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL); + +- scaled = gdk_pixbuf_scale_simple (base, size, size, GDK_INTERP_BILINEAR); +- g_object_unref (G_OBJECT (base)); ++ g_clear_object (&pixbuf); + +- return scaled; +- } ++ return surface; + } + + void +-_wnck_get_fallback_icons (GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size) ++_wnck_get_fallback_icons (cairo_surface_t **iconp, ++ int ideal_size, ++ cairo_surface_t **mini_iconp, ++ int ideal_mini_size) + { + if (iconp) + *iconp = default_icon_at_size (ideal_size); +diff --git a/libwnck/xutils.h b/libwnck/xutils.h +index 2af255d..8146cda 100644 +--- a/libwnck/xutils.h ++++ b/libwnck/xutils.h +@@ -159,10 +159,10 @@ void _wnck_keyboard_size (WnckScreen *screen, + void _wnck_toggle_showing_desktop (Screen *screen, + gboolean show); + +-void _wnck_get_fallback_icons (GdkPixbuf **iconp, +- int ideal_size, +- GdkPixbuf **mini_iconp, +- int ideal_mini_size); ++void _wnck_get_fallback_icons (cairo_surface_t **iconp, ++ int ideal_size, ++ cairo_surface_t **mini_iconp, ++ int ideal_mini_size); + + void _wnck_get_window_geometry (Screen *screen, + Window xwindow, +@@ -195,7 +195,8 @@ void _wnck_set_desktop_layout (Screen *xscreen, + int columns); + + cairo_surface_t *_wnck_cairo_surface_get_from_pixmap (Screen *screen, +- Pixmap xpixmap); ++ Pixmap xpixmap, ++ int scaling_factor); + + GdkPixbuf* _wnck_gdk_pixbuf_get_from_pixmap (Screen *screen, + Pixmap xpixmap); +-- +2.31.1 + diff --git a/libwnck_0004-icons-Mark-GdkPixbuf-icons-as-deprecated.patch b/libwnck_0004-icons-Mark-GdkPixbuf-icons-as-deprecated.patch new file mode 100644 index 0000000..9b227c5 --- /dev/null +++ b/libwnck_0004-icons-Mark-GdkPixbuf-icons-as-deprecated.patch @@ -0,0 +1,189 @@ +From af4bab0581f164a0cfb0396591940b26584281c1 Mon Sep 17 00:00:00 2001 +From: Victor Kareh +Date: Tue, 11 Feb 2020 07:40:47 -0500 +Subject: [PATCH 4/5] icons: Mark GdkPixbuf icons as deprecated + +Since we have migrated icons to render as cairo surfaces we can now mark +GdkPixbuf icons as deprecated without having to break the API. +--- + libwnck/application.c | 8 ++++++++ + libwnck/application.h | 6 ++++++ + libwnck/class-group.c | 4 ++++ + libwnck/class-group.h | 4 ++++ + libwnck/test-wnck.c | 2 +- + libwnck/window.c | 6 ++++++ + libwnck/window.h | 4 ++++ + 7 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/libwnck/application.c b/libwnck/application.c +index 8d9d034..90eee73 100644 +--- a/libwnck/application.c ++++ b/libwnck/application.c +@@ -408,6 +408,8 @@ find_icon_window (WnckApplication *app) + * Return value: (transfer none): the icon for @app. The caller should + * reference the returned GdkPixbuf if it needs to keep + * the icon around. ++ * ++ * Deprecated:41.0: Use wnck_application_get_icon_surface() instead. + **/ + GdkPixbuf* + wnck_application_get_icon (WnckApplication *app) +@@ -447,10 +449,12 @@ wnck_application_get_icon (WnckApplication *app) + } + else + { ++G_GNUC_BEGIN_IGNORE_DEPRECATIONS + WnckWindow *w = find_icon_window (app); + if (w) + return wnck_window_get_icon (w); + else ++G_GNUC_END_IGNORE_DEPRECATIONS + return NULL; + } + } +@@ -466,6 +470,8 @@ wnck_application_get_icon (WnckApplication *app) + * Return value: (transfer none): the mini-icon for @app. The caller should + * reference the returned GdkPixbuf if it needs to keep + * the mini-icon around. ++ * ++ * Deprecated:41.0: Use wnck_application_get_mini_icon_surface() instead. + **/ + GdkPixbuf* + wnck_application_get_mini_icon (WnckApplication *app) +@@ -505,10 +511,12 @@ wnck_application_get_mini_icon (WnckApplication *app) + } + else + { ++G_GNUC_BEGIN_IGNORE_DEPRECATIONS + WnckWindow *w = find_icon_window (app); + if (w) + return wnck_window_get_mini_icon (w); + else ++G_GNUC_END_IGNORE_DEPRECATIONS + return NULL; + } + } +diff --git a/libwnck/application.h b/libwnck/application.h +index e8893f5..fbb2442 100644 +--- a/libwnck/application.h ++++ b/libwnck/application.h +@@ -90,10 +90,16 @@ int wnck_application_get_n_windows (WnckApplication *app); + const char* wnck_application_get_name (WnckApplication *app); + const char* wnck_application_get_icon_name (WnckApplication *app); + int wnck_application_get_pid (WnckApplication *app); ++ ++G_DEPRECATED_FOR(wnck_application_get_icon_surface) + GdkPixbuf* wnck_application_get_icon (WnckApplication *app); ++ ++G_DEPRECATED_FOR(wnck_application_get_mini_icon_surface) + GdkPixbuf* wnck_application_get_mini_icon (WnckApplication *app); ++ + cairo_surface_t* wnck_application_get_icon_surface (WnckApplication *app); + cairo_surface_t* wnck_application_get_mini_icon_surface (WnckApplication *app); ++ + gboolean wnck_application_get_icon_is_fallback (WnckApplication *app); + const char* wnck_application_get_startup_id (WnckApplication *app); + +diff --git a/libwnck/class-group.c b/libwnck/class-group.c +index dbf5e1d..9ec6ea6 100644 +--- a/libwnck/class-group.c ++++ b/libwnck/class-group.c +@@ -696,6 +696,8 @@ wnck_class_group_get_name (WnckClassGroup *class_group) + * the icon around. + * + * Since: 2.2 ++ * ++ * Deprecated:41.0: Use wnck_class_group_get_icon_surface() instead. + **/ + GdkPixbuf * + wnck_class_group_get_icon (WnckClassGroup *class_group) +@@ -748,6 +750,8 @@ wnck_class_group_get_icon (WnckClassGroup *class_group) + * to keep the mini-icon around. + * + * Since: 2.2 ++ * ++ * Deprecated:41.0: Use wnck_class_group_get_mini_icon_surface() instead. + **/ + GdkPixbuf * + wnck_class_group_get_mini_icon (WnckClassGroup *class_group) +diff --git a/libwnck/class-group.h b/libwnck/class-group.h +index 5a9e07c..1370ca6 100644 +--- a/libwnck/class-group.h ++++ b/libwnck/class-group.h +@@ -80,8 +80,12 @@ const char * wnck_class_group_get_id (WnckClassGroup *class_group); + + const char * wnck_class_group_get_name (WnckClassGroup *class_group); + ++G_DEPRECATED_FOR(wnck_class_group_get_icon_surface) + GdkPixbuf *wnck_class_group_get_icon (WnckClassGroup *class_group); ++ ++G_DEPRECATED_FOR(wnck_class_group_get_mini_icon_surface) + GdkPixbuf *wnck_class_group_get_mini_icon (WnckClassGroup *class_group); ++ + cairo_surface_t *wnck_class_group_get_icon_surface (WnckClassGroup *class_group); + cairo_surface_t *wnck_class_group_get_mini_icon_surface (WnckClassGroup *class_group); + +diff --git a/libwnck/test-wnck.c b/libwnck/test-wnck.c +index ffaad59..05ddd7e 100644 +--- a/libwnck/test-wnck.c ++++ b/libwnck/test-wnck.c +@@ -520,7 +520,7 @@ icon_set_func (GtkTreeViewColumn *tree_column, + return; + + g_object_set (GTK_CELL_RENDERER (cell), +- "pixbuf", wnck_window_get_mini_icon (window), ++ "surface", wnck_window_get_mini_icon_surface (window), + NULL); + } + +diff --git a/libwnck/window.c b/libwnck/window.c +index 92f3c08..021dea8 100644 +--- a/libwnck/window.c ++++ b/libwnck/window.c +@@ -21,6 +21,8 @@ + * License along with this library; if not, see . + */ + ++#undef WNCK_DISABLE_DEPRECATED ++ + #include + + #include +@@ -2168,6 +2170,8 @@ _wnck_window_load_icons (WnckWindow *window) + * Return value: (transfer none): the icon for @window. The caller should + * reference the returned GdkPixbuf if it needs to keep + * the icon around. ++ * ++ * Deprecated:41.0: Use wnck_window_get_icon_surface() instead. + **/ + GdkPixbuf* + wnck_window_get_icon (WnckWindow *window) +@@ -2220,6 +2224,8 @@ wnck_window_get_icon (WnckWindow *window) + * Return value: (transfer none): the mini-icon for @window. The caller should + * reference the returned GdkPixbuf if it needs to keep + * the icon around. ++ * ++ * Deprecated:41.0: Use wnck_window_get_mini_icon_surface() instead. + **/ + GdkPixbuf* + wnck_window_get_mini_icon (WnckWindow *window) +diff --git a/libwnck/window.h b/libwnck/window.h +index fb3ce51..831024c 100644 +--- a/libwnck/window.h ++++ b/libwnck/window.h +@@ -380,8 +380,12 @@ void wnck_window_activate_transient (WnckWindow *window, + guint32 timestamp); + gboolean wnck_window_transient_is_most_recently_activated (WnckWindow *window); + ++G_DEPRECATED_FOR(wnck_window_get_icon_surface) + GdkPixbuf* wnck_window_get_icon (WnckWindow *window); ++ ++G_DEPRECATED_FOR(wnck_window_get_mini_icon_surface) + GdkPixbuf* wnck_window_get_mini_icon (WnckWindow *window); ++ + cairo_surface_t* wnck_window_get_icon_surface (WnckWindow *window); + cairo_surface_t* wnck_window_get_mini_icon_surface (WnckWindow *window); + +-- +2.31.1 + diff --git a/libwnck_0005-tasklist-Add-surface-loader-function.patch b/libwnck_0005-tasklist-Add-surface-loader-function.patch new file mode 100644 index 0000000..8ffc4cf --- /dev/null +++ b/libwnck_0005-tasklist-Add-surface-loader-function.patch @@ -0,0 +1,134 @@ +From 4bc5ffe67b88fe47b283cf1b59b3bcd4f18eb105 Mon Sep 17 00:00:00 2001 +From: Victor Kareh +Date: Thu, 3 Jun 2021 14:04:06 -0400 +Subject: [PATCH 5/5] tasklist: Add surface loader function + +Since the tasklist now supports cairo_surface_t icons, we provide +a similar icon loader function that takes surface icons. +--- + libwnck/tasklist.c | 50 +++++++++++++++++++++++++++++++++++++++++++++- + libwnck/tasklist.h | 26 ++++++++++++++++++++++++ + 2 files changed, 75 insertions(+), 1 deletion(-) + +diff --git a/libwnck/tasklist.c b/libwnck/tasklist.c +index b10ef8c..9c921b0 100644 +--- a/libwnck/tasklist.c ++++ b/libwnck/tasklist.c +@@ -228,6 +228,10 @@ struct _WnckTasklistPrivate + void *icon_loader_data; + GDestroyNotify free_icon_loader_data; + ++ WnckLoadSurfaceFunction surface_loader; ++ void *surface_loader_data; ++ GDestroyNotify free_surface_loader_data; ++ + #ifdef HAVE_STARTUP_NOTIFICATION + SnMonitorContext *sn_context; + guint startup_sequence_timeout; +@@ -1077,6 +1081,11 @@ wnck_tasklist_finalize (GObject *object) + tasklist->priv->free_icon_loader_data = NULL; + tasklist->priv->icon_loader_data = NULL; + ++ if (tasklist->priv->free_surface_loader_data != NULL) ++ (* tasklist->priv->free_surface_loader_data) (tasklist->priv->surface_loader_data); ++ tasklist->priv->free_surface_loader_data = NULL; ++ tasklist->priv->surface_loader_data = NULL; ++ + G_OBJECT_CLASS (wnck_tasklist_parent_class)->finalize (object); + } + +@@ -1315,6 +1324,31 @@ wnck_tasklist_set_icon_loader (WnckTasklist *tasklist, + tasklist->priv->free_icon_loader_data = free_data_func; + } + ++/** ++ * wnck_tasklist_set_surface_loader: ++ * @tasklist: a #WnckTasklist ++ * @load_surface_func: icon loader function ++ * @data: data for icon loader function ++ * @free_data_func: function to free the data ++ * ++ * Sets a function to be used for loading cairo surface icons. ++ **/ ++void ++wnck_tasklist_set_surface_loader (WnckTasklist *tasklist, ++ WnckLoadSurfaceFunction load_surface_func, ++ void *data, ++ GDestroyNotify free_data_func) ++{ ++ g_return_if_fail (WNCK_IS_TASKLIST (tasklist)); ++ ++ if (tasklist->priv->free_surface_loader_data != NULL) ++ (* tasklist->priv->free_surface_loader_data) (tasklist->priv->surface_loader_data); ++ ++ tasklist->priv->surface_loader = load_surface_func; ++ tasklist->priv->surface_loader_data = data; ++ tasklist->priv->free_surface_loader_data = free_data_func; ++} ++ + static void + get_layout (GtkOrientation orientation, + int for_size, +@@ -3665,7 +3699,21 @@ wnck_task_get_icon (WnckTask *task) + + case WNCK_TASK_STARTUP_SEQUENCE: + #ifdef HAVE_STARTUP_NOTIFICATION +- if (task->tasklist->priv->icon_loader != NULL) ++ if (task->tasklist->priv->surface_loader != NULL) ++ { ++ const char *icon; ++ ++ icon = sn_startup_sequence_get_icon_name (task->startup_sequence); ++ if (icon != NULL) ++ { ++ surface = (* task->tasklist->priv->surface_loader) (icon, ++ MINI_ICON_SIZE, ++ 0, ++ task->tasklist->priv->surface_loader_data); ++ ++ } ++ } ++ else if (task->tasklist->priv->icon_loader != NULL) + { + const char *icon; + +diff --git a/libwnck/tasklist.h b/libwnck/tasklist.h +index 0659f9d..0af8df5 100644 +--- a/libwnck/tasklist.h ++++ b/libwnck/tasklist.h +@@ -138,6 +138,32 @@ void wnck_tasklist_set_icon_loader (WnckTasklist *tasklist, + void *data, + GDestroyNotify free_data_func); + ++/** ++ * WnckLoadSurfaceFunction: ++ * @icon_name: an icon name as in the Icon field in a .desktop file for the ++ * icon to load. ++ * @size: the desired icon size. ++ * @flags: not defined to do anything yet. ++ * @data: data passed to the function, set when the #WnckLoadSurfaceFunction has ++ * been set for the #WnckTasklist. ++ * ++ * Specifies the type of function passed to wnck_tasklist_set_icon_loader(). ++ * ++ * Returns: it should return a cairo_surface_t of @icon_name ++ * at size @size, or %NULL if no icon for @icon_name at size @size could be ++ * loaded. ++ * ++ */ ++typedef cairo_surface_t* (*WnckLoadSurfaceFunction) (const char *icon_name, ++ int size, ++ unsigned int flags, ++ void *data); ++ ++void wnck_tasklist_set_surface_loader (WnckTasklist *tasklist, ++ WnckLoadSurfaceFunction load_surface_func, ++ void *data, ++ GDestroyNotify free_data_func); ++ + G_END_DECLS + + #endif /* WNCK_TASKLIST_H */ +-- +2.31.1 +