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: 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); -- cgit v1.2.1