~ubuntu-branches/ubuntu/maverick/awn-extras-applets/maverick

« back to all changes in this revision

Viewing changes to applets/maintained/related/xutils.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2010-08-29 14:29:52 UTC
  • mto: This revision was merged to the branch mainline in revision 21.
  • Revision ID: james.westby@ubuntu.com-20100829142952-rhvuetyms9bv5uu7
Tags: upstream-0.4.0+bzr1372
ImportĀ upstreamĀ versionĀ 0.4.0+bzr1372

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Xlib utils */
 
2
/* vim: set sw=2 et: */
 
3
 
 
4
/*
 
5
 * Copyright (C) 2001 Havoc Pennington
 
6
 * Copyright (C) 2005-2007 Vincent Untz
 
7
 *
 
8
 * This library is free software; you can redistribute it and/or
 
9
 * modify it under the terms of the GNU Library General Public
 
10
 * License as published by the Free Software Foundation; either
 
11
 * version 2 of the License, or (at your option) any later version.
 
12
 *
 
13
 * This library is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * Library General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU Library General Public
 
19
 * License along with this library; if not, write to the
 
20
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
21
 * Boston, MA 02111-1307, USA.
 
22
 */
 
23
 
 
24
#include <config.h>
 
25
 
 
26
#include <string.h>
 
27
#include <stdio.h>
 
28
 
 
29
#include <X11/Xatom.h>
 
30
#include <gdk/gdkx.h>
 
31
#include <gtk/gtk.h>
 
32
 
 
33
#include <unistd.h>
 
34
 
 
35
#include "xutils.h"
 
36
 
 
37
typedef struct _WnckIconCache WnckIconCache;
 
38
 
 
39
static void
 
40
_wnck_error_trap_push (void)
 
41
{
 
42
  gdk_error_trap_push ();
 
43
}
 
44
 
 
45
static int
 
46
_wnck_error_trap_pop (void)
 
47
{
 
48
  XSync (gdk_display, False);
 
49
  return gdk_error_trap_pop ();
 
50
}
 
51
 
 
52
static char*
 
53
latin1_to_utf8 (const char *latin1)
 
54
{
 
55
  GString *str;
 
56
  const char *p;
 
57
  
 
58
  str = g_string_new (NULL);
 
59
 
 
60
  p = latin1;
 
61
  while (*p)
 
62
    {
 
63
      g_string_append_unichar (str, (gunichar) *p);
 
64
      ++p;
 
65
    }
 
66
 
 
67
  return g_string_free (str, FALSE);
 
68
}
 
69
 
 
70
void
 
71
_wnck_get_wmclass (Window xwindow,
 
72
                   char **res_class,
 
73
                   char **res_name)
 
74
{
 
75
  XClassHint ch;
 
76
  char *retval;
 
77
  
 
78
  _wnck_error_trap_push ();
 
79
 
 
80
  ch.res_name = NULL;
 
81
  ch.res_class = NULL;
 
82
 
 
83
  XGetClassHint (gdk_display, xwindow,
 
84
                 &ch);
 
85
 
 
86
  _wnck_error_trap_pop ();
 
87
  
 
88
  retval = NULL;
 
89
 
 
90
  if (res_class)
 
91
    *res_class = NULL;
 
92
 
 
93
  if (res_name)
 
94
    *res_name = NULL;
 
95
  
 
96
  if (ch.res_name)
 
97
    {
 
98
      if (res_name)
 
99
        *res_name = latin1_to_utf8 (ch.res_name);
 
100
      
 
101
      XFree (ch.res_name);
 
102
    }
 
103
 
 
104
  if (ch.res_class)
 
105
    {
 
106
      if (res_class)
 
107
        *res_class = latin1_to_utf8 (ch.res_class);
 
108
      
 
109
      XFree (ch.res_class);
 
110
    }
 
111
}
 
112
 
 
113
 
 
114
void
 
115
_wnck_get_client_name (Window xwindow, char **client_name)
 
116
{
 
117
  XTextProperty text_prop;
 
118
  Status status;
 
119
  
 
120
  _wnck_error_trap_push ();
 
121
 
 
122
        status = XGetWMClientMachine(gdk_display, xwindow, &text_prop);
 
123
 
 
124
  _wnck_error_trap_pop ();
 
125
  
 
126
  if (!status)
 
127
  {
 
128
    *client_name = NULL;
 
129
    if (text_prop.value)
 
130
    {
 
131
      XFree (text_prop.value);
 
132
    }
 
133
    return;
 
134
  }
 
135
  if (text_prop.value)
 
136
  {
 
137
    *client_name = latin1_to_utf8 (text_prop.value);       
 
138
    XFree (text_prop.value);
 
139
  }
 
140
}
 
141
 
 
142
/*
 
143
 * WNCK_GET_ICON_AT_SIZE
 
144
 */
 
145
static GHashTable *atom_hash = NULL;
 
146
static GHashTable *reverse_atom_hash = NULL;
 
147
 
 
148
static Atom
 
149
_wnck_atom_get (const char *atom_name)
 
150
{
 
151
  Atom retval;
 
152
 
 
153
  g_return_val_if_fail (atom_name != NULL, None);
 
154
 
 
155
  if (!atom_hash)
 
156
  {
 
157
    atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
 
158
    reverse_atom_hash = g_hash_table_new (NULL, NULL);
 
159
  }
 
160
 
 
161
  retval = GPOINTER_TO_UINT (g_hash_table_lookup (atom_hash, atom_name));
 
162
  if (!retval)
 
163
  {
 
164
    retval = XInternAtom (gdk_display, atom_name, FALSE);
 
165
 
 
166
    if (retval != None)
 
167
    {
 
168
      char *name_copy;
 
169
 
 
170
      name_copy = g_strdup (atom_name);
 
171
 
 
172
      g_hash_table_insert (atom_hash, name_copy, GUINT_TO_POINTER (retval));
 
173
      g_hash_table_insert (reverse_atom_hash,
 
174
                           GUINT_TO_POINTER (retval), name_copy);
 
175
    }
 
176
  }
 
177
 
 
178
  return retval;
 
179
}
 
180
 
 
181
/* The icon-reading code is copied
 
182
 * from metacity, please sync bugfixes
 
183
 */
 
184
static gboolean
 
185
find_largest_sizes (gulong * data, gulong nitems, int *width, int *height)
 
186
{
 
187
  *width = 0;
 
188
  *height = 0;
 
189
 
 
190
  while (nitems > 0)
 
191
  {
 
192
    int w, h;
 
193
    gboolean replace;
 
194
 
 
195
    replace = FALSE;
 
196
 
 
197
    if (nitems < 3)
 
198
      return FALSE;             /* no space for w, h */
 
199
 
 
200
    w = data[0];
 
201
    h = data[1];
 
202
 
 
203
    if (nitems < ((w * h) + 2))
 
204
      return FALSE;             /* not enough data */
 
205
 
 
206
    *width = MAX (w, *width);
 
207
    *height = MAX (h, *height);
 
208
 
 
209
    data += (w * h) + 2;
 
210
    nitems -= (w * h) + 2;
 
211
  }
 
212
 
 
213
  return TRUE;
 
214
}
 
215
 
 
216
static gboolean
 
217
find_best_size (gulong * data,
 
218
                gulong nitems,
 
219
                int ideal_width,
 
220
                int ideal_height, int *width, int *height, gulong ** start)
 
221
{
 
222
  int best_w;
 
223
  int best_h;
 
224
  gulong *best_start;
 
225
  int max_width, max_height;
 
226
 
 
227
  *width = 0;
 
228
  *height = 0;
 
229
  *start = NULL;
 
230
 
 
231
  if (!find_largest_sizes (data, nitems, &max_width, &max_height))
 
232
    return FALSE;
 
233
 
 
234
  if (ideal_width < 0)
 
235
    ideal_width = max_width;
 
236
  if (ideal_height < 0)
 
237
    ideal_height = max_height;
 
238
 
 
239
  best_w = 0;
 
240
  best_h = 0;
 
241
  best_start = NULL;
 
242
 
 
243
  while (nitems > 0)
 
244
  {
 
245
    int w, h;
 
246
    gboolean replace;
 
247
 
 
248
    replace = FALSE;
 
249
 
 
250
    if (nitems < 3)
 
251
      return FALSE;             /* no space for w, h */
 
252
 
 
253
    w = data[0];
 
254
    h = data[1];
 
255
 
 
256
    if (nitems < ((w * h) + 2))
 
257
      break;                    /* not enough data */
 
258
 
 
259
    if (best_start == NULL)
 
260
    {
 
261
      replace = TRUE;
 
262
    }
 
263
    else
 
264
    {
 
265
      /* work with averages */
 
266
      const int ideal_size = (ideal_width + ideal_height) / 2;
 
267
      int best_size = (best_w + best_h) / 2;
 
268
      int this_size = (w + h) / 2;
 
269
 
 
270
      /* larger than desired is always better than smaller */
 
271
      if (best_size < ideal_size && this_size >= ideal_size)
 
272
              replace = TRUE;
 
273
      /* if we have too small, pick anything bigger */
 
274
      else if (best_size < ideal_size && this_size > best_size)
 
275
              replace = TRUE;
 
276
      /* if we have too large, pick anything smaller
 
277
       * but still >= the ideal
 
278
       */
 
279
      else if (best_size > ideal_size &&
 
280
               this_size >= ideal_size && this_size < best_size)
 
281
              replace = TRUE;
 
282
    }
 
283
 
 
284
    if (replace)
 
285
    {
 
286
      best_start = data + 2;
 
287
      best_w = w;
 
288
      best_h = h;
 
289
    }
 
290
 
 
291
    data += (w * h) + 2;
 
292
    nitems -= (w * h) + 2;
 
293
  }
 
294
 
 
295
  if (best_start)
 
296
  {
 
297
    *start = best_start;
 
298
    *width = best_w;
 
299
    *height = best_h;
 
300
    return TRUE;
 
301
  }
 
302
  else
 
303
    return FALSE;
 
304
}
 
305
 
 
306
static void
 
307
argbdata_to_pixdata (gulong * argb_data, int len, guchar ** pixdata)
 
308
{
 
309
  guchar *p;
 
310
  int i;
 
311
 
 
312
  *pixdata = g_new (guchar, len * 4);
 
313
  p = *pixdata;
 
314
 
 
315
  /* One could speed this up a lot. */
 
316
  i = 0;
 
317
  while (i < len)
 
318
  {
 
319
    guint argb;
 
320
    guint rgba;
 
321
 
 
322
    argb = argb_data[i];
 
323
    rgba = (argb << 8) | (argb >> 24);
 
324
 
 
325
    *p = rgba >> 24;
 
326
    ++p;
 
327
    *p = (rgba >> 16) & 0xff;
 
328
    ++p;
 
329
    *p = (rgba >> 8) & 0xff;
 
330
    ++p;
 
331
    *p = rgba & 0xff;
 
332
    ++p;
 
333
 
 
334
    ++i;
 
335
  }
 
336
}
 
337
 
 
338
static gboolean
 
339
read_rgb_icon (Window xwindow,
 
340
               int ideal_width,
 
341
               int ideal_height,
 
342
               int ideal_mini_width,
 
343
               int ideal_mini_height,
 
344
               int *width,
 
345
               int *height,
 
346
               guchar ** pixdata,
 
347
               int *mini_width, int *mini_height, guchar ** mini_pixdata)
 
348
{
 
349
  Atom type;
 
350
  int format;
 
351
  gulong nitems;
 
352
  gulong bytes_after;
 
353
  int result, err;
 
354
  gulong *data;
 
355
  gulong *best;
 
356
  int w, h;
 
357
  gulong *best_mini;
 
358
  int mini_w, mini_h;
 
359
 
 
360
  _wnck_error_trap_push ();
 
361
  type = None;
 
362
  data = NULL;
 
363
  result = XGetWindowProperty (gdk_display,
 
364
                               xwindow,
 
365
                               _wnck_atom_get ("_NET_WM_ICON"),
 
366
                               0, G_MAXLONG,
 
367
                               False, XA_CARDINAL, &type, &format, &nitems,
 
368
                               &bytes_after, (void *) &data);
 
369
 
 
370
  err = _wnck_error_trap_pop ();
 
371
 
 
372
  if (err != Success || result != Success)
 
373
    return FALSE;
 
374
 
 
375
  if (type != XA_CARDINAL)
 
376
  {
 
377
    XFree (data);
 
378
    return FALSE;
 
379
  }
 
380
 
 
381
  if (!find_best_size (data, nitems,
 
382
                       ideal_width, ideal_height, &w, &h, &best))
 
383
  {
 
384
    XFree (data);
 
385
    return FALSE;
 
386
  }
 
387
 
 
388
  if (!find_best_size (data, nitems,
 
389
                       ideal_mini_width, ideal_mini_height,
 
390
                       &mini_w, &mini_h, &best_mini))
 
391
  {
 
392
    XFree (data);
 
393
    return FALSE;
 
394
  }
 
395
 
 
396
  *width = w;
 
397
  *height = h;
 
398
 
 
399
  *mini_width = mini_w;
 
400
  *mini_height = mini_h;
 
401
 
 
402
  argbdata_to_pixdata (best, w * h, pixdata);
 
403
  argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata);
 
404
 
 
405
  XFree (data);
 
406
 
 
407
  return TRUE;
 
408
}
 
409
 
 
410
static void
 
411
free_pixels (guchar * pixels, gpointer data)
 
412
{
 
413
  g_free (pixels);
 
414
}
 
415
 
 
416
static void
 
417
get_pixmap_geometry (Pixmap pixmap, int *w, int *h, int *d)
 
418
{
 
419
  Window root_ignored;
 
420
  int x_ignored, y_ignored;
 
421
  guint width, height;
 
422
  guint border_width_ignored;
 
423
  guint depth;
 
424
 
 
425
  if (w)
 
426
    *w = 1;
 
427
  if (h)
 
428
    *h = 1;
 
429
  if (d)
 
430
    *d = 1;
 
431
 
 
432
  XGetGeometry (gdk_display,
 
433
                pixmap, &root_ignored, &x_ignored, &y_ignored,
 
434
                &width, &height, &border_width_ignored, &depth);
 
435
 
 
436
  if (w)
 
437
    *w = width;
 
438
  if (h)
 
439
    *h = height;
 
440
  if (d)
 
441
    *d = depth;
 
442
}
 
443
 
 
444
static GdkPixbuf *
 
445
apply_mask (GdkPixbuf * pixbuf, GdkPixbuf * mask)
 
446
{
 
447
  int w, h;
 
448
  int i, j;
 
449
  GdkPixbuf *with_alpha;
 
450
  guchar *src;
 
451
  guchar *dest;
 
452
  int src_stride;
 
453
  int dest_stride;
 
454
 
 
455
  w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf));
 
456
  h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf));
 
457
 
 
458
  with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
 
459
 
 
460
  dest = gdk_pixbuf_get_pixels (with_alpha);
 
461
  src = gdk_pixbuf_get_pixels (mask);
 
462
 
 
463
  dest_stride = gdk_pixbuf_get_rowstride (with_alpha);
 
464
  src_stride = gdk_pixbuf_get_rowstride (mask);
 
465
 
 
466
  i = 0;
 
467
  while (i < h)
 
468
  {
 
469
    j = 0;
 
470
    while (j < w)
 
471
    {
 
472
      guchar *s = src + i * src_stride + j * 3;
 
473
      guchar *d = dest + i * dest_stride + j * 4;
 
474
 
 
475
      /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0
 
476
       * otherwise
 
477
       */
 
478
      if (s[0] == 0)
 
479
              d[3] = 0;         /* transparent */
 
480
      else
 
481
              d[3] = 255;               /* opaque */
 
482
      ++j;
 
483
    }
 
484
 
 
485
    ++i;
 
486
  }
 
487
 
 
488
  return with_alpha;
 
489
}
 
490
 
 
491
static GdkColormap *
 
492
get_cmap (GdkPixmap * pixmap)
 
493
{
 
494
  GdkColormap *cmap;
 
495
 
 
496
  cmap = gdk_drawable_get_colormap (pixmap);
 
497
  if (cmap)
 
498
    g_object_ref (G_OBJECT (cmap));
 
499
 
 
500
  if (cmap == NULL)
 
501
  {
 
502
    if (gdk_drawable_get_depth (pixmap) == 1)
 
503
    {
 
504
      /* try null cmap */
 
505
      cmap = NULL;
 
506
    }
 
507
    else
 
508
    {
 
509
      /* Try system cmap */
 
510
      GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (pixmap));
 
511
      cmap = gdk_screen_get_system_colormap (screen);
 
512
      g_object_ref (G_OBJECT (cmap));
 
513
    }
 
514
  }
 
515
 
 
516
  /* Be sure we aren't going to blow up due to visual mismatch */
 
517
  if (cmap &&
 
518
      (gdk_colormap_get_visual (cmap)->depth !=
 
519
       gdk_drawable_get_depth (pixmap)))
 
520
    cmap = NULL;
 
521
 
 
522
  return cmap;
 
523
}
 
524
 
 
525
static GdkPixbuf *
 
526
_wnck_gdk_pixbuf_get_from_pixmap (GdkPixbuf * dest,
 
527
                                  Pixmap xpixmap,
 
528
                                  int src_x,
 
529
                                  int src_y,
 
530
                                  int dest_x,
 
531
                                  int dest_y, int width, int height)
 
532
{
 
533
  GdkDrawable *drawable;
 
534
  GdkPixbuf *retval;
 
535
  GdkColormap *cmap;
 
536
 
 
537
  retval = NULL;
 
538
  cmap = NULL;
 
539
 
 
540
  drawable = gdk_xid_table_lookup (xpixmap);
 
541
 
 
542
  if (drawable)
 
543
    g_object_ref (G_OBJECT (drawable));
 
544
  else
 
545
    drawable = gdk_pixmap_foreign_new (xpixmap);
 
546
 
 
547
  if (drawable)
 
548
  {
 
549
    cmap = get_cmap (drawable);
 
550
 
 
551
    /* GDK is supposed to do this but doesn't in GTK 2.0.2,
 
552
     * fixed in 2.0.3
 
553
     */
 
554
    if (width < 0)
 
555
      gdk_drawable_get_size (drawable, &width, NULL);
 
556
    if (height < 0)
 
557
      gdk_drawable_get_size (drawable, NULL, &height);
 
558
 
 
559
    retval = gdk_pixbuf_get_from_drawable (dest,
 
560
                                           drawable,
 
561
                                           cmap,
 
562
                                           src_x, src_y,
 
563
                                           dest_x, dest_y, width, height);
 
564
  }
 
565
 
 
566
  if (cmap)
 
567
    g_object_unref (G_OBJECT (cmap));
 
568
  if (drawable)
 
569
    g_object_unref (G_OBJECT (drawable));
 
570
 
 
571
  return retval;
 
572
}
 
573
 
 
574
static gboolean
 
575
try_pixmap_and_mask (Pixmap src_pixmap,
 
576
                     Pixmap src_mask,
 
577
                     GdkPixbuf ** iconp,
 
578
                     int ideal_width,
 
579
                     int ideal_height,
 
580
                     GdkPixbuf ** mini_iconp,
 
581
                     int ideal_mini_width, int ideal_mini_height)
 
582
{
 
583
  GdkPixbuf *unscaled = NULL;
 
584
  GdkPixbuf *mask = NULL;
 
585
  int w, h;
 
586
 
 
587
  if (src_pixmap == None)
 
588
    return FALSE;
 
589
 
 
590
  _wnck_error_trap_push ();
 
591
 
 
592
  get_pixmap_geometry (src_pixmap, &w, &h, NULL);
 
593
 
 
594
  unscaled = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
 
595
                                               src_pixmap, 0, 0, 0, 0, w, h);
 
596
 
 
597
  if (unscaled && src_mask != None)
 
598
  {
 
599
    get_pixmap_geometry (src_mask, &w, &h, NULL);
 
600
    mask = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
 
601
                                             src_mask, 0, 0, 0, 0, w, h);
 
602
  }
 
603
 
 
604
  _wnck_error_trap_pop ();
 
605
 
 
606
  if (mask)
 
607
  {
 
608
    GdkPixbuf *masked;
 
609
 
 
610
    masked = apply_mask (unscaled, mask);
 
611
    g_object_unref (G_OBJECT (unscaled));
 
612
    unscaled = masked;
 
613
 
 
614
    g_object_unref (G_OBJECT (mask));
 
615
    mask = NULL;
 
616
  }
 
617
 
 
618
  if (unscaled)
 
619
  {
 
620
    *iconp =
 
621
      gdk_pixbuf_scale_simple (unscaled,
 
622
                               ideal_width > 0 ? ideal_width :
 
623
                               gdk_pixbuf_get_width (unscaled),
 
624
                               ideal_height > 0 ? ideal_height :
 
625
                               gdk_pixbuf_get_height (unscaled),
 
626
                               GDK_INTERP_BILINEAR);
 
627
    *mini_iconp =
 
628
      gdk_pixbuf_scale_simple (unscaled,
 
629
                               ideal_mini_width > 0 ? ideal_mini_width :
 
630
                               gdk_pixbuf_get_width (unscaled),
 
631
                               ideal_mini_height > 0 ? ideal_mini_height :
 
632
                               gdk_pixbuf_get_height (unscaled),
 
633
                               GDK_INTERP_BILINEAR);
 
634
 
 
635
    g_object_unref (G_OBJECT (unscaled));
 
636
    return TRUE;
 
637
  }
 
638
  else
 
639
    return FALSE;
 
640
}
 
641
 
 
642
static void
 
643
get_kwm_win_icon (Window xwindow, Pixmap * pixmap, Pixmap * mask)
 
644
{
 
645
  Atom type;
 
646
  int format;
 
647
  gulong nitems;
 
648
  gulong bytes_after;
 
649
  Pixmap *icons;
 
650
  int err, result;
 
651
 
 
652
  *pixmap = None;
 
653
  *mask = None;
 
654
 
 
655
  _wnck_error_trap_push ();
 
656
  icons = NULL;
 
657
  result = XGetWindowProperty (gdk_display, xwindow,
 
658
                               _wnck_atom_get ("KWM_WIN_ICON"),
 
659
                               0, G_MAXLONG,
 
660
                               False,
 
661
                               _wnck_atom_get ("KWM_WIN_ICON"),
 
662
                               &type, &format, &nitems,
 
663
                               &bytes_after, (void *) &icons);
 
664
 
 
665
  err = _wnck_error_trap_pop ();
 
666
  if (err != Success || result != Success)
 
667
    return;
 
668
 
 
669
  if (type != _wnck_atom_get ("KWM_WIN_ICON"))
 
670
  {
 
671
    XFree (icons);
 
672
    return;
 
673
  }
 
674
 
 
675
  *pixmap = icons[0];
 
676
  *mask = icons[1];
 
677
 
 
678
  XFree (icons);
 
679
 
 
680
  return;
 
681
}
 
682
 
 
683
typedef enum
 
684
{
 
685
  /* These MUST be in ascending order of preference;
 
686
   * i.e. if we get _NET_WM_ICON and already have
 
687
   * WM_HINTS, we prefer _NET_WM_ICON
 
688
   */
 
689
  USING_NO_ICON,
 
690
  USING_FALLBACK_ICON,
 
691
  USING_KWM_WIN_ICON,
 
692
  USING_WM_HINTS,
 
693
  USING_NET_WM_ICON
 
694
} IconOrigin;
 
695
 
 
696
struct _WnckIconCache
 
697
{
 
698
  IconOrigin origin;
 
699
  Pixmap prev_pixmap;
 
700
  Pixmap prev_mask;
 
701
  GdkPixbuf *icon;
 
702
  GdkPixbuf *mini_icon;
 
703
  int ideal_width;
 
704
  int ideal_height;
 
705
  int ideal_mini_width;
 
706
  int ideal_mini_height;
 
707
  guint want_fallback:1;
 
708
  /* TRUE if these props have changed */
 
709
  guint wm_hints_dirty:1;
 
710
  guint kwm_win_icon_dirty:1;
 
711
  guint net_wm_icon_dirty:1;
 
712
};
 
713
 
 
714
 
 
715
static GdkPixbuf *
 
716
scaled_from_pixdata (guchar * pixdata, int w, int h, int new_w, int new_h)
 
717
{
 
718
  GdkPixbuf *src;
 
719
  GdkPixbuf *dest;
 
720
 
 
721
  src = gdk_pixbuf_new_from_data (pixdata,
 
722
                                  GDK_COLORSPACE_RGB,
 
723
                                  TRUE, 8, w, h, w * 4, free_pixels, NULL);
 
724
 
 
725
  if (src == NULL)
 
726
    return NULL;
 
727
 
 
728
  if (w != h)
 
729
  {
 
730
    GdkPixbuf *tmp;
 
731
    int size;
 
732
 
 
733
    size = MAX (w, h);
 
734
 
 
735
    tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size);
 
736
 
 
737
    if (tmp != NULL)
 
738
    {
 
739
      gdk_pixbuf_fill (tmp, 0);
 
740
      gdk_pixbuf_copy_area (src, 0, 0, w, h,
 
741
                            tmp, (size - w) / 2, (size - h) / 2);
 
742
 
 
743
      if (src)
 
744
        g_object_unref (src);
 
745
      src = tmp;
 
746
    }
 
747
  }
 
748
 
 
749
  if (w != new_w || h != new_h)
 
750
  {
 
751
    dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR);
 
752
 
 
753
    g_object_unref (G_OBJECT (src));
 
754
  }
 
755
  else
 
756
  {
 
757
    dest = src;
 
758
  }
 
759
 
 
760
  return dest;
 
761
}
 
762
 
 
763
static gboolean
 
764
_wnck_read_icons_ (Window xwindow,
 
765
                   GtkWidget * icon_cache,
 
766
                   GdkPixbuf ** iconp,
 
767
                   int ideal_width,
 
768
                   int ideal_height,
 
769
                   GdkPixbuf ** mini_iconp,
 
770
                   int ideal_mini_width, int ideal_mini_height)
 
771
{
 
772
  guchar *pixdata;
 
773
  int w, h;
 
774
  guchar *mini_pixdata;
 
775
  int mini_w, mini_h;
 
776
  Pixmap pixmap;
 
777
  Pixmap mask;
 
778
  XWMHints *hints;
 
779
 
 
780
  *iconp = NULL;
 
781
  *mini_iconp = NULL;
 
782
  pixdata = NULL;
 
783
  if (read_rgb_icon (xwindow,
 
784
                     ideal_width, ideal_height,
 
785
                     ideal_mini_width, ideal_mini_height,
 
786
                     &w, &h, &pixdata, &mini_w, &mini_h, &mini_pixdata))
 
787
  {
 
788
    *iconp = scaled_from_pixdata (pixdata, w, h, ideal_width, ideal_height);
 
789
 
 
790
    *mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h,
 
791
                                       ideal_mini_width, ideal_mini_height);
 
792
 
 
793
    return TRUE;
 
794
  }
 
795
 
 
796
 
 
797
  _wnck_error_trap_push ();
 
798
  hints = XGetWMHints (gdk_display, xwindow);
 
799
  _wnck_error_trap_pop ();
 
800
  pixmap = None;
 
801
  mask = None;
 
802
  if (hints)
 
803
  {
 
804
    if (hints->flags & IconPixmapHint)
 
805
      pixmap = hints->icon_pixmap;
 
806
    if (hints->flags & IconMaskHint)
 
807
      mask = hints->icon_mask;
 
808
 
 
809
    XFree (hints);
 
810
    hints = NULL;
 
811
  }
 
812
 
 
813
 
 
814
  if (try_pixmap_and_mask (pixmap, mask,
 
815
                           iconp, ideal_width, ideal_height,
 
816
                           mini_iconp, ideal_mini_width, ideal_mini_height))
 
817
  {
 
818
    return TRUE;
 
819
  }
 
820
 
 
821
  get_kwm_win_icon (xwindow, &pixmap, &mask);
 
822
 
 
823
  if (try_pixmap_and_mask (pixmap, mask,
 
824
                           iconp, ideal_width, ideal_height,
 
825
                           mini_iconp, ideal_mini_width, ideal_mini_height))
 
826
  {
 
827
    return TRUE;
 
828
  }
 
829
  return FALSE;
 
830
}
 
831
 
 
832
 
 
833
GdkPixbuf *
 
834
_wnck_get_icon_at_size (WnckWindow *window,
 
835
                        gint        width,
 
836
                        gint        height)
 
837
{
 
838
  GdkPixbuf *icon, *icon_scaled, *mini_icon;
 
839
  gboolean res;
 
840
 
 
841
  icon = NULL;
 
842
  icon_scaled = NULL;
 
843
  mini_icon = NULL;
 
844
 
 
845
  res = _wnck_read_icons_ (wnck_window_get_xid (window),
 
846
                           NULL,
 
847
                           &icon, width, width,
 
848
                           &mini_icon, 24, 24);
 
849
 
 
850
  if (res)
 
851
  {
 
852
    if (mini_icon)
 
853
      g_object_unref (mini_icon);
 
854
 
 
855
    if (icon)
 
856
      return icon;
 
857
  }
 
858
 
 
859
  icon = wnck_window_get_icon (window);
 
860
  icon_scaled = gdk_pixbuf_scale_simple (icon, width, height, 
 
861
                                         GDK_INTERP_BILINEAR);
 
862
 
 
863
  return icon_scaled;
 
864
}
 
865
 
 
866
/*
 
867
 * XUTILS_GET_NAMED_ICON
 
868
 *
 
869
 * Probably shouldn't be in here, but I'm not feeling like creating another
 
870
 * source header and file for one function. Sue me. Well don't actually.
 
871
 */
 
872
 
 
873
static char *
 
874
strip_extension (const char *file)
 
875
{
 
876
  char *stripped, *p;
 
877
 
 
878
  stripped = g_strdup (file);
 
879
 
 
880
  p = strrchr (stripped, '.');
 
881
  if (p &&
 
882
      (!strcmp (p, ".png") ||
 
883
       !strcmp (p, ".svg") ||
 
884
       !strcmp (p, ".xpm")))
 
885
          *p = 0;
 
886
 
 
887
  return stripped;
 
888
}
 
889
 
 
890
/* Gets the pixbuf from a desktop file's icon name. Based on the same function
 
891
 * from matchbox-desktop
 
892
 */
 
893
static GdkPixbuf *
 
894
get_icon (const gchar *name, guint size)
 
895
{
 
896
  static GtkIconTheme *theme = NULL;
 
897
  GdkPixbuf *pixbuf = NULL;
 
898
  GError *error = NULL;
 
899
  gchar *stripped = NULL;
 
900
 
 
901
  gint width, height;
 
902
 
 
903
  if (theme == NULL)
 
904
    theme = gtk_icon_theme_get_default ();
 
905
 
 
906
  if (name == NULL)
 
907
  {
 
908
    pixbuf = gtk_icon_theme_load_icon (theme, "application-x-executable",
 
909
                                       size, 0, NULL);
 
910
    return pixbuf;
 
911
  }
 
912
 
 
913
  if (g_path_is_absolute (name))
 
914
  {
 
915
    if (g_file_test (name, G_FILE_TEST_EXISTS))
 
916
    {
 
917
      pixbuf = gdk_pixbuf_new_from_file_at_scale (name, size, size, 
 
918
                                                  TRUE, &error);
 
919
      if (error)
 
920
      {
 
921
        /*g_warning ("Error loading icon: %s\n", error->message);*/
 
922
        g_error_free (error);
 
923
        error = NULL;
 
924
     }
 
925
      return pixbuf;
 
926
    } 
 
927
  }
 
928
 
 
929
  stripped = strip_extension (name);
 
930
  
 
931
  pixbuf = gtk_icon_theme_load_icon (theme,
 
932
                                     stripped,
 
933
                                     size,
 
934
                                     GTK_ICON_LOOKUP_FORCE_SVG, &error);
 
935
  if (error)
 
936
  {   
 
937
    /*g_warning ("Error loading icon: %s\n", error->message);*/
 
938
    g_error_free (error);
 
939
    error = NULL;
 
940
  }
 
941
  
 
942
  /* Always try and send back something */
 
943
  if (pixbuf == NULL)
 
944
    pixbuf = gtk_icon_theme_load_icon (theme, "stock_folder",
 
945
                                       size, 0, NULL);
 
946
  
 
947
  width = gdk_pixbuf_get_width (pixbuf);
 
948
  height = gdk_pixbuf_get_height (pixbuf);
 
949
 
 
950
  if (width != size || height != size)
 
951
  {
 
952
    GdkPixbuf *temp = pixbuf;
 
953
    pixbuf = gdk_pixbuf_scale_simple (temp, 
 
954
                                      size,
 
955
                                      size,
 
956
                                      GDK_INTERP_HYPER);
 
957
    g_object_unref (temp);
 
958
  }
 
959
 
 
960
  g_free (stripped);
 
961
 
 
962
 return pixbuf;
 
963
}
 
964
 
 
965
GdkPixbuf *
 
966
xutils_get_named_icon (const gchar *icon_name, 
 
967
                       gint         width,
 
968
                       gint         height)
 
969
{
 
970
  return get_icon (icon_name, width);
 
971
}