1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
/* Marco window icons */
6
* Copyright (C) 2002 Havoc Pennington
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public License as
10
* published by the Free Software Foundation; either version 2 of the
11
* License, or (at your option) any later version.
13
* This program is distributed in the hope that it will be useful, but
14
* WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
25
#include "iconcache.h"
29
#include <X11/Xatom.h>
31
/* The icon-reading code is also in libwnck, please sync bugfixes */
34
get_fallback_icons (MetaScreen *screen,
38
GdkPixbuf **mini_iconp,
40
int ideal_mini_height)
42
/* we don't scale, should be fixed if we ever un-hardcode the icon
45
*iconp = meta_ui_get_default_window_icon (screen->ui);
46
*mini_iconp = meta_ui_get_default_mini_icon (screen->ui);
50
find_largest_sizes (gulong *data,
63
return FALSE; /* no space for w, h */
68
if (nitems < ((gulong)(w * h) + 2))
69
return FALSE; /* not enough data */
71
*width = MAX (w, *width);
72
*height = MAX (h, *height);
75
nitems -= (w * h) + 2;
82
find_best_size (gulong *data,
93
int max_width, max_height;
99
if (!find_largest_sizes (data, nitems, &max_width, &max_height))
103
ideal_width = max_width;
104
if (ideal_height < 0)
105
ideal_height = max_height;
119
return FALSE; /* no space for w, h */
124
if (nitems < ((gulong)(w * h) + 2))
125
break; /* not enough data */
127
if (best_start == NULL)
133
/* work with averages */
134
const int ideal_size = (ideal_width + ideal_height) / 2;
135
int best_size = (best_w + best_h) / 2;
136
int this_size = (w + h) / 2;
138
/* larger than desired is always better than smaller */
139
if (best_size < ideal_size &&
140
this_size >= ideal_size)
142
/* if we have too small, pick anything bigger */
143
else if (best_size < ideal_size &&
144
this_size > best_size)
146
/* if we have too large, pick anything smaller
147
* but still >= the ideal
149
else if (best_size > ideal_size &&
150
this_size >= ideal_size &&
151
this_size < best_size)
157
best_start = data + 2;
163
nitems -= (w * h) + 2;
178
argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata)
183
*pixdata = g_new (guchar, len * 4);
186
/* One could speed this up a lot. */
194
rgba = (argb << 8) | (argb >> 24);
198
*p = (rgba >> 16) & 0xff;
200
*p = (rgba >> 8) & 0xff;
210
read_rgb_icon (MetaDisplay *display,
214
int ideal_mini_width,
215
int ideal_mini_height,
221
guchar **mini_pixdata)
233
gulong *data_as_long;
235
meta_error_trap_push_with_return (display);
238
result = XGetWindowProperty (display->xdisplay,
240
display->atom__NET_WM_ICON,
242
False, XA_CARDINAL, &type, &format, &nitems,
243
&bytes_after, &data);
244
err = meta_error_trap_pop_with_return (display, TRUE);
246
if (err != Success ||
250
if (type != XA_CARDINAL)
256
data_as_long = (gulong *)data;
258
if (!find_best_size (data_as_long, nitems,
259
ideal_width, ideal_height,
266
if (!find_best_size (data_as_long, nitems,
267
ideal_mini_width, ideal_mini_height,
268
&mini_w, &mini_h, &best_mini))
277
*mini_width = mini_w;
278
*mini_height = mini_h;
280
argbdata_to_pixdata (best, w * h, pixdata);
281
argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata);
289
free_pixels (guchar *pixels, gpointer data)
295
get_pixmap_geometry (MetaDisplay *display,
302
int x_ignored, y_ignored;
304
guint border_width_ignored;
314
XGetGeometry (display->xdisplay,
315
pixmap, &root_ignored, &x_ignored, &y_ignored,
316
&width, &height, &border_width_ignored, &depth);
327
apply_mask (GdkPixbuf *pixbuf,
332
GdkPixbuf *with_alpha;
338
w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf));
339
h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf));
341
with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
343
dest = gdk_pixbuf_get_pixels (with_alpha);
344
src = gdk_pixbuf_get_pixels (mask);
346
dest_stride = gdk_pixbuf_get_rowstride (with_alpha);
347
src_stride = gdk_pixbuf_get_rowstride (mask);
355
guchar *s = src + i * src_stride + j * 3;
356
guchar *d = dest + i * dest_stride + j * 4;
358
/* s[0] == s[1] == s[2], they are 255 if the bit was set, 0
362
d[3] = 0; /* transparent */
364
d[3] = 255; /* opaque */
376
try_pixmap_and_mask (MetaDisplay *display,
382
GdkPixbuf **mini_iconp,
383
int ideal_mini_width,
384
int ideal_mini_height)
386
GdkPixbuf *unscaled = NULL;
387
GdkPixbuf *mask = NULL;
390
if (src_pixmap == None)
393
meta_error_trap_push (display);
395
get_pixmap_geometry (display, src_pixmap, &w, &h, NULL);
397
unscaled = meta_gdk_pixbuf_get_from_pixmap (NULL,
402
if (unscaled && src_mask != None)
404
get_pixmap_geometry (display, src_mask, &w, &h, NULL);
405
mask = meta_gdk_pixbuf_get_from_pixmap (NULL,
411
meta_error_trap_pop (display, FALSE);
417
masked = apply_mask (unscaled, mask);
418
g_object_unref (G_OBJECT (unscaled));
421
g_object_unref (G_OBJECT (mask));
428
gdk_pixbuf_scale_simple (unscaled,
429
ideal_width > 0 ? ideal_width :
430
gdk_pixbuf_get_width (unscaled),
431
ideal_height > 0 ? ideal_height :
432
gdk_pixbuf_get_height (unscaled),
433
GDK_INTERP_BILINEAR);
435
gdk_pixbuf_scale_simple (unscaled,
436
ideal_mini_width > 0 ? ideal_mini_width :
437
gdk_pixbuf_get_width (unscaled),
438
ideal_mini_height > 0 ? ideal_mini_height :
439
gdk_pixbuf_get_height (unscaled),
440
GDK_INTERP_BILINEAR);
442
g_object_unref (G_OBJECT (unscaled));
444
if (*iconp && *mini_iconp)
449
g_object_unref (G_OBJECT (*iconp));
451
g_object_unref (G_OBJECT (*mini_iconp));
460
get_kwm_win_icon (MetaDisplay *display,
476
meta_error_trap_push_with_return (display);
478
result = XGetWindowProperty (display->xdisplay, xwindow,
479
display->atom__KWM_WIN_ICON,
482
display->atom__KWM_WIN_ICON,
483
&type, &format, &nitems,
484
&bytes_after, &data);
485
icons = (Pixmap *)data;
487
err = meta_error_trap_pop_with_return (display, TRUE);
488
if (err != Success ||
492
if (type != display->atom__KWM_WIN_ICON)
507
meta_icon_cache_init (MetaIconCache *icon_cache)
509
g_return_if_fail (icon_cache != NULL);
511
icon_cache->origin = USING_NO_ICON;
512
icon_cache->prev_pixmap = None;
513
icon_cache->prev_mask = None;
515
icon_cache->icon = NULL;
516
icon_cache->mini_icon = NULL;
517
icon_cache->ideal_width = -1; /* won't be a legit width */
518
icon_cache->ideal_height = -1;
519
icon_cache->ideal_mini_width = -1;
520
icon_cache->ideal_mini_height = -1;
522
icon_cache->want_fallback = TRUE;
523
icon_cache->wm_hints_dirty = TRUE;
524
icon_cache->kwm_win_icon_dirty = TRUE;
525
icon_cache->net_wm_icon_dirty = TRUE;
529
clear_icon_cache (MetaIconCache *icon_cache,
533
if (icon_cache->icon)
534
g_object_unref (G_OBJECT (icon_cache->icon));
535
icon_cache->icon = NULL;
537
if (icon_cache->mini_icon)
538
g_object_unref (G_OBJECT (icon_cache->mini_icon));
539
icon_cache->mini_icon = NULL;
542
icon_cache->origin = USING_NO_ICON;
546
icon_cache->wm_hints_dirty = TRUE;
547
icon_cache->kwm_win_icon_dirty = TRUE;
548
icon_cache->net_wm_icon_dirty = TRUE;
553
meta_icon_cache_free (MetaIconCache *icon_cache)
555
clear_icon_cache (icon_cache, FALSE);
559
meta_icon_cache_property_changed (MetaIconCache *icon_cache,
560
MetaDisplay *display,
563
if (atom == display->atom__NET_WM_ICON)
564
icon_cache->net_wm_icon_dirty = TRUE;
565
else if (atom == display->atom__KWM_WIN_ICON)
566
icon_cache->kwm_win_icon_dirty = TRUE;
567
else if (atom == XA_WM_HINTS)
568
icon_cache->wm_hints_dirty = TRUE;
572
meta_icon_cache_get_icon_invalidated (MetaIconCache *icon_cache)
574
if (icon_cache->origin <= USING_KWM_WIN_ICON &&
575
icon_cache->kwm_win_icon_dirty)
577
else if (icon_cache->origin <= USING_WM_HINTS &&
578
icon_cache->wm_hints_dirty)
580
else if (icon_cache->origin <= USING_NET_WM_ICON &&
581
icon_cache->net_wm_icon_dirty)
583
else if (icon_cache->origin < USING_FALLBACK_ICON &&
584
icon_cache->want_fallback)
586
else if (icon_cache->origin == USING_NO_ICON)
588
else if (icon_cache->origin == USING_FALLBACK_ICON &&
589
!icon_cache->want_fallback)
596
replace_cache (MetaIconCache *icon_cache,
599
GdkPixbuf *new_mini_icon)
601
clear_icon_cache (icon_cache, FALSE);
603
icon_cache->origin = origin;
607
g_object_ref (G_OBJECT (new_icon));
609
icon_cache->icon = new_icon;
612
g_object_ref (G_OBJECT (new_mini_icon));
614
icon_cache->mini_icon = new_mini_icon;
619
scaled_from_pixdata (guchar *pixdata,
628
src = gdk_pixbuf_new_from_data (pixdata,
646
tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size);
650
gdk_pixbuf_fill (tmp, 0);
651
gdk_pixbuf_copy_area (src, 0, 0, w, h,
653
(size - w) / 2, (size - h) / 2);
655
g_object_unref (src);
660
if (w != new_w || h != new_h)
662
dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR);
664
g_object_unref (G_OBJECT (src));
675
meta_read_icons (MetaScreen *screen,
677
MetaIconCache *icon_cache,
678
Pixmap wm_hints_pixmap,
679
Pixmap wm_hints_mask,
683
GdkPixbuf **mini_iconp,
684
int ideal_mini_width,
685
int ideal_mini_height)
689
guchar *mini_pixdata;
694
/* Return value is whether the icon changed */
696
g_return_val_if_fail (icon_cache != NULL, FALSE);
702
if (ideal_width != icon_cache->ideal_width ||
703
ideal_height != icon_cache->ideal_height ||
704
ideal_mini_width != icon_cache->ideal_mini_width ||
705
ideal_mini_height != icon_cache->ideal_mini_height)
706
clear_icon_cache (icon_cache, TRUE);
708
icon_cache->ideal_width = ideal_width;
709
icon_cache->ideal_height = ideal_height;
710
icon_cache->ideal_mini_width = ideal_mini_width;
711
icon_cache->ideal_mini_height = ideal_mini_height;
714
if (!meta_icon_cache_get_icon_invalidated (icon_cache))
715
return FALSE; /* we have no new info to use */
719
/* Our algorithm here assumes that we can't have for example origin
720
* < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE
721
* unless we have tried to read NET_WM_ICON.
723
* Put another way, if an icon origin is not dirty, then we have
724
* tried to read it at the current size. If it is dirty, then
725
* we haven't done that since the last change.
728
if (icon_cache->origin <= USING_NET_WM_ICON &&
729
icon_cache->net_wm_icon_dirty)
732
icon_cache->net_wm_icon_dirty = FALSE;
734
if (read_rgb_icon (screen->display, xwindow,
735
ideal_width, ideal_height,
736
ideal_mini_width, ideal_mini_height,
738
&mini_w, &mini_h, &mini_pixdata))
740
*iconp = scaled_from_pixdata (pixdata, w, h,
741
ideal_width, ideal_height);
743
*mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h,
744
ideal_mini_width, ideal_mini_height);
746
if (*iconp && *mini_iconp)
748
replace_cache (icon_cache, USING_NET_WM_ICON,
749
*iconp, *mini_iconp);
756
g_object_unref (G_OBJECT (*iconp));
758
g_object_unref (G_OBJECT (*mini_iconp));
763
if (icon_cache->origin <= USING_WM_HINTS &&
764
icon_cache->wm_hints_dirty)
766
icon_cache->wm_hints_dirty = FALSE;
768
pixmap = wm_hints_pixmap;
769
mask = wm_hints_mask;
771
/* We won't update if pixmap is unchanged;
772
* avoids a get_from_drawable() on every geometry
775
if ((pixmap != icon_cache->prev_pixmap ||
776
mask != icon_cache->prev_mask) &&
779
if (try_pixmap_and_mask (screen->display,
781
iconp, ideal_width, ideal_height,
782
mini_iconp, ideal_mini_width, ideal_mini_height))
784
icon_cache->prev_pixmap = pixmap;
785
icon_cache->prev_mask = mask;
787
replace_cache (icon_cache, USING_WM_HINTS,
788
*iconp, *mini_iconp);
795
if (icon_cache->origin <= USING_KWM_WIN_ICON &&
796
icon_cache->kwm_win_icon_dirty)
798
icon_cache->kwm_win_icon_dirty = FALSE;
800
get_kwm_win_icon (screen->display, xwindow, &pixmap, &mask);
802
if ((pixmap != icon_cache->prev_pixmap ||
803
mask != icon_cache->prev_mask) &&
806
if (try_pixmap_and_mask (screen->display, pixmap, mask,
807
iconp, ideal_width, ideal_height,
808
mini_iconp, ideal_mini_width, ideal_mini_height))
810
icon_cache->prev_pixmap = pixmap;
811
icon_cache->prev_mask = mask;
813
replace_cache (icon_cache, USING_KWM_WIN_ICON,
814
*iconp, *mini_iconp);
821
if (icon_cache->want_fallback &&
822
icon_cache->origin < USING_FALLBACK_ICON)
824
get_fallback_icons (screen,
832
replace_cache (icon_cache, USING_FALLBACK_ICON,
833
*iconp, *mini_iconp);
838
if (!icon_cache->want_fallback &&
839
icon_cache->origin == USING_FALLBACK_ICON)
841
/* Get rid of current icon */
842
clear_icon_cache (icon_cache, FALSE);
847
/* found nothing new */