~ubuntu-branches/ubuntu/quantal/open-vm-tools/quantal-201210021442

« back to all changes in this revision

Viewing changes to services/plugins/unity/ghIntegration/icon.cc

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-03-31 14:20:05 UTC
  • mfrom: (1.4.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110331142005-3n9red91p7ogkweo
Tags: 2011.03.28-387002-0ubuntu1
* Merge latest upstream git tag.  This has the unlocked_ioctl change
  needed to fix dkms build failures (LP: #727342)
* Changes in debian/rules:
  - work around a bug in toolbox/Makefile, where install-exec-hook is
    not happening.  This needs to get fixed the right way.
  - don't install 'vmware-user' which seems to no longer exist
  - move /etc/xdg into open-vm-toolbox (which should be done using .install)
* debian/open-vm-tools.init: add 'modprobe [-r] vmblock'. (LP: #332323)
* debian/rules and debian/open-vm-toolbox.lintian-overrides:
  - Make vmware-user-suid-wrapper suid-root (LP: #332323)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*********************************************************
 
2
 * Copyright (C) 2010 VMware, Inc. All rights reserved.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify it
 
5
 * under the terms of the GNU Lesser General Public License as published
 
6
 * by the Free Software Foundation version 2.1 and no later version.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
10
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 
11
 * License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 
15
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 
16
 *
 
17
 *********************************************************/
 
18
 
 
19
/*
 
20
 * icon.c --
 
21
 *
 
22
 *      GHI/X11 icon collection code.
 
23
 */
 
24
 
 
25
 
 
26
#include <math.h>
 
27
 
 
28
#include <gtk/gtk.h>
 
29
#include <glib.h>
 
30
#include <gio/gdesktopappinfo.h>
 
31
 
 
32
extern "C" {
 
33
#include "vmware.h"
 
34
#include "guest_msg_def.h"
 
35
}
 
36
 
 
37
#include "ghiX11icon.h"
 
38
 
 
39
 
 
40
/*
 
41
 * GHI/X11 still pumps icons over GuestMsg, which limits us to 64K.  Until we
 
42
 * switch transports, we'll have to scale down icons to fit within limits.
 
43
 */
 
44
 
 
45
static const size_t MAX_ICON_SIZE = GUESTMSG_MAX_IN_SIZE - 1024;
 
46
 
 
47
 
 
48
/*
 
49
 * Local function declarations.
 
50
 */
 
51
 
 
52
static void  AppendFileToArray(const gchar* iconPath,
 
53
                               std::list<GHIBinaryIconInfo>& iconList);
 
54
static void  AppendPixbufToArray(const GdkPixbuf* pixbuf,
 
55
                                 std::list<GHIBinaryIconInfo>& iconList,
 
56
                                 bool scaleHint = false);
 
57
static gint* GetIconSizesDescending(GtkIconTheme *iconTheme,
 
58
                                    const gchar* iconName);
 
59
static Bool  GetIconsForGIcon(GIcon* gicon,
 
60
                              std::list<GHIBinaryIconInfo>& iconList);
 
61
static GdkPixbuf* ShrinkPixbuf(const GdkPixbuf* pixbuf, size_t maxSize);
 
62
 
 
63
 
 
64
/*
 
65
 *-----------------------------------------------------------------------------
 
66
 *
 
67
 * GHIX11IconGetIconsForDesktopFile --
 
68
 *
 
69
 *      Given an application's .desktop file, look up and return the app's icons
 
70
 *      as BGRA data.  Icons are sorted in descending order by size.
 
71
 *
 
72
 * Results:
 
73
 *      Returns FALSE if any errors were encountered and TRUE otherwise.
 
74
 *
 
75
 * Side effects:
 
76
 *      iconList.size() may grow.
 
77
 *
 
78
 *-----------------------------------------------------------------------------
 
79
 */
 
80
 
 
81
Bool
 
82
GHIX11IconGetIconsForDesktopFile(const char* desktopFile,                // IN
 
83
                                 std::list<GHIBinaryIconInfo>& iconList) // OUT
 
84
{
 
85
   GDesktopAppInfo* desktopAppInfo = NULL;
 
86
   GIcon* gicon = NULL;
 
87
   Bool success = FALSE;
 
88
 
 
89
   desktopAppInfo = g_desktop_app_info_new_from_filename(desktopFile);
 
90
   if (desktopAppInfo) {
 
91
      GAppInfo* appInfo = (GAppInfo*)G_APP_INFO(desktopAppInfo);
 
92
      gicon = g_app_info_get_icon(appInfo);
 
93
      if (gicon) {
 
94
         success = GetIconsForGIcon(gicon, iconList);
 
95
      }
 
96
      g_object_unref(desktopAppInfo);
 
97
   }
 
98
 
 
99
   return success;
 
100
}
 
101
 
 
102
 
 
103
/*
 
104
 *-----------------------------------------------------------------------------
 
105
 *
 
106
 * GHIX11IconGetIconsByName --
 
107
 *
 
108
 *      Try to find icons identified by a string.  The string may refer to a
 
109
 *      generic name leading to searching an icon theme, or it may be an
 
110
 *      absolute path to an icon file.
 
111
 *
 
112
 * Results:
 
113
 *      Returns FALSE if any errors were encountered and TRUE otherwise.
 
114
 *
 
115
 * Side effects:
 
116
 *      iconList.size() may grow.
 
117
 *
 
118
 *-----------------------------------------------------------------------------
 
119
 */
 
120
 
 
121
Bool
 
122
GHIX11IconGetIconsByName(const char* iconName,                          // IN
 
123
                         std::list<GHIBinaryIconInfo>& iconList)        // OUT
 
124
{
 
125
   GIcon *gicon;
 
126
   Bool retval = FALSE;
 
127
 
 
128
   gicon = g_icon_new_for_string(iconName, NULL);
 
129
   if (gicon) {
 
130
      retval = GetIconsForGIcon(gicon, iconList);
 
131
      g_object_unref(G_OBJECT(gicon));
 
132
   }
 
133
 
 
134
   return retval;
 
135
}
 
136
 
 
137
 
 
138
/*
 
139
 * Local functions
 
140
 */
 
141
 
 
142
 
 
143
/*
 
144
 *-----------------------------------------------------------------------------
 
145
 *
 
146
 * GetIconsForGIcon --
 
147
 *
 
148
 *      Given a GLib GIcon, search the default icon theme or filesystem for
 
149
 *      icons.
 
150
 *
 
151
 * Results:
 
152
 *      Returns FALSE if any errors were encountered and TRUE otherwise.
 
153
 *
 
154
 * Side effects:
 
155
 *      iconList.size() may grow.
 
156
 *
 
157
 *-----------------------------------------------------------------------------
 
158
 */
 
159
 
 
160
static Bool
 
161
GetIconsForGIcon(GIcon* gicon,                                  // IN
 
162
                 std::list<GHIBinaryIconInfo>& iconList)        // OUT
 
163
{
 
164
   gchar* iconName = NULL;
 
165
   GtkIconTheme* iconTheme;
 
166
   Bool success = FALSE;
 
167
 
 
168
   ASSERT(gicon);
 
169
 
 
170
   /*
 
171
    * We can handle two icon types, themed and file.  A themed icon is
 
172
    * provided by and varies by icon theme whereas a file icon is stored
 
173
    * in a single file.  (There's a special case where GIO thinks we have
 
174
    * a themed icon, but it turns out to be a file icon.  More on that later.)
 
175
    */
 
176
 
 
177
   iconName = g_icon_to_string(gicon);
 
178
   iconTheme = gtk_icon_theme_get_default();
 
179
 
 
180
   if (G_IS_THEMED_ICON(gicon) && gtk_icon_theme_has_icon(iconTheme, iconName)) {
 
181
 
 
182
      /*
 
183
       * Sweet - GTK claims that our icon is themed and can give us pixbufs for
 
184
       * it at various sizes.
 
185
       *
 
186
       * Remember what I said about GIO thinking we have a themed icon that might
 
187
       * not be?  If an icon doesn't have a size, then it's one of those such
 
188
       * icons, and we have to fall back to loading directly from a file ourselves.
 
189
       */
 
190
 
 
191
      gint* iconSizes = GetIconSizesDescending(iconTheme, iconName);
 
192
 
 
193
      if (iconSizes && *iconSizes != 0) {
 
194
         gint* sizeIter;
 
195
 
 
196
         for (sizeIter = iconSizes; *sizeIter; sizeIter++) {
 
197
            GdkPixbuf* pixbuf;
 
198
 
 
199
            pixbuf = gtk_icon_theme_load_icon(iconTheme, iconName, *sizeIter,
 
200
                                              (GtkIconLookupFlags)0, NULL);
 
201
            if (pixbuf) {
 
202
               AppendPixbufToArray(pixbuf, iconList);
 
203
               g_object_unref(pixbuf);
 
204
            }
 
205
         }
 
206
      } else if (iconSizes && *iconSizes == 0) {
 
207
         GtkIconInfo* iconInfo;
 
208
 
 
209
         iconInfo = gtk_icon_theme_lookup_icon(iconTheme, iconName, 0,
 
210
                                               (GtkIconLookupFlags)0);
 
211
         if (iconInfo) {
 
212
            AppendFileToArray(gtk_icon_info_get_filename(iconInfo), iconList);
 
213
            gtk_icon_info_free(iconInfo);
 
214
         }
 
215
      }
 
216
 
 
217
      g_free(iconSizes);
 
218
   } else if (G_IS_FILE_ICON(gicon)) {
 
219
      GFileIcon* fileIcon;
 
220
      GFile* file;
 
221
      char* path;
 
222
 
 
223
      fileIcon = G_FILE_ICON(gicon);
 
224
      file = g_file_icon_get_file(fileIcon);
 
225
      path = g_file_get_path(file);
 
226
      ASSERT(path);
 
227
 
 
228
      AppendFileToArray(path, iconList);
 
229
 
 
230
      g_free(path);
 
231
   } else {
 
232
      /* Give up. */
 
233
      goto out;
 
234
   }
 
235
 
 
236
   success = TRUE;
 
237
 
 
238
out:
 
239
   if (iconName) {
 
240
      g_free(iconName);
 
241
   }
 
242
   return success;
 
243
}
 
244
 
 
245
 
 
246
/*
 
247
 *-----------------------------------------------------------------------------
 
248
 *
 
249
 * AppendFileToArray --
 
250
 *
 
251
 *      Load an icon from a file into a pixbuf, then append it to a GPtrArray of
 
252
 *      GHIX11Icons.
 
253
 *
 
254
 * Results:
 
255
 *      Appends an icon on success, no-op on failure.
 
256
 *
 
257
 * Side effects:
 
258
 *      None.
 
259
 *
 
260
 *-----------------------------------------------------------------------------
 
261
 */
 
262
 
 
263
static void
 
264
AppendFileToArray(const gchar* iconPath,                  // IN
 
265
                  std::list<GHIBinaryIconInfo>& iconList) // OUT
 
266
{
 
267
   GdkPixbuf* pixbuf;
 
268
 
 
269
   if ((pixbuf = gdk_pixbuf_new_from_file(iconPath, NULL)) != NULL) {
 
270
      AppendPixbufToArray(pixbuf, iconList, true);
 
271
      g_object_unref(pixbuf);
 
272
   }
 
273
}
 
274
 
 
275
 
 
276
/*
 
277
 *-----------------------------------------------------------------------------
 
278
 *
 
279
 * AppendPixbufToArray --
 
280
 *
 
281
 *      Appends a pixbuf to a GPtrArray of GHIX11Icons.
 
282
 *
 
283
 * Results:
 
284
 *      Appends an icon on success, no-op on failure.
 
285
 *
 
286
 * Side effects:
 
287
 *      None.
 
288
 *
 
289
 *-----------------------------------------------------------------------------
 
290
 */
 
291
 
 
292
static void
 
293
AppendPixbufToArray(
 
294
   const GdkPixbuf* pixbuf,                // IN
 
295
   std::list<GHIBinaryIconInfo>& iconList, // OUT
 
296
   bool scaleHint)                         // IN: Scale icon to fit GuestMsg
 
297
{
 
298
   GHIBinaryIconInfo ghiIcon;
 
299
   guchar* pixels;
 
300
   guint width;
 
301
   guint height;
 
302
   guint x, y;
 
303
   guint rowstride;
 
304
   guint n_channels;
 
305
   guint bgraStride;
 
306
   bool scaled = false;
 
307
   GdkPixbuf *scaledPixbuf = NULL;
 
308
 
 
309
rescaled:
 
310
   ASSERT(pixbuf);
 
311
   ASSERT(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB);
 
312
   ASSERT(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8);
 
313
 
 
314
   rowstride = gdk_pixbuf_get_rowstride(pixbuf);
 
315
   n_channels = gdk_pixbuf_get_n_channels(pixbuf);
 
316
   pixels = gdk_pixbuf_get_pixels(pixbuf);
 
317
 
 
318
   ghiIcon.width = width = gdk_pixbuf_get_width(pixbuf);
 
319
   ghiIcon.height = height = gdk_pixbuf_get_height(pixbuf);
 
320
   bgraStride = width * 4;
 
321
   ghiIcon.dataBGRA.resize(height * bgraStride);
 
322
 
 
323
   if (   !scaled
 
324
       && scaleHint
 
325
       && height * bgraStride > MAX_ICON_SIZE) {
 
326
      scaled = true;
 
327
      pixbuf = scaledPixbuf = ShrinkPixbuf(pixbuf, MAX_ICON_SIZE);
 
328
      goto rescaled;
 
329
   }
 
330
 
 
331
   /* GetBinaryInfo icons are bottom-to-top. */
 
332
   for (y = 0; y < height; y++) {
 
333
      for (x = 0; x < width; x++) {
 
334
         guchar* b; // Pointer to BGRA data in ghiIcon.
 
335
         guchar* p; // Pointer to RGBA data in GdkPixbuf
 
336
         gint bgraOffset = y * bgraStride + x * 4;
 
337
         gint pixbufOffset = (height - y - 1) * rowstride + x * n_channels;
 
338
 
 
339
         b = &ghiIcon.dataBGRA[bgraOffset];
 
340
         p = &pixels[pixbufOffset];
 
341
 
 
342
         b[0] = p[2];
 
343
         b[1] = p[1];
 
344
         b[2] = p[0];
 
345
         b[3] = (n_channels > 3) ? p[3] : 0xFF;
 
346
      }
 
347
   }
 
348
 
 
349
   iconList.push_back(ghiIcon);
 
350
 
 
351
   if (scaledPixbuf) {
 
352
      g_object_unref(G_OBJECT(scaledPixbuf));
 
353
   }
 
354
}
 
355
 
 
356
 
 
357
/*
 
358
 *-----------------------------------------------------------------------------
 
359
 *
 
360
 * GetIconSizesDescending --
 
361
 *
 
362
 *      Query an icon theme for an icon's sizes.  Return the sizes as a gint
 
363
 *      array in descending order.
 
364
 *
 
365
 * Results:
 
366
 *      Pointer to a gint array on success or NULL on failure.
 
367
 *
 
368
 * Side effects:
 
369
 *      None.
 
370
 *
 
371
 *-----------------------------------------------------------------------------
 
372
 */
 
373
 
 
374
static int
 
375
DescendingIntCmp(const void* a,
 
376
                 const void* b)
 
377
{
 
378
   gint ia = *(gint* )a;
 
379
   gint ib = *(gint* )b;
 
380
 
 
381
   return (ia < ib) ? 1 : (ia == ib) ? 0 : -1;
 
382
}
 
383
 
 
384
static gint*
 
385
GetIconSizesDescending(GtkIconTheme* iconTheme, // IN
 
386
                       const gchar* iconName)   // IN
 
387
{
 
388
   gint* iconSizes = NULL;
 
389
   gint* iter;
 
390
   size_t nsizes;
 
391
 
 
392
   if (!gtk_icon_theme_has_icon(iconTheme, iconName)) {
 
393
      return NULL;
 
394
   }
 
395
 
 
396
   iconSizes = gtk_icon_theme_get_icon_sizes(iconTheme, iconName);
 
397
   ASSERT(iconSizes);
 
398
 
 
399
   /*
 
400
    * iconSizes is a 0-terminated array. If there are no sizes or the
 
401
    * array has only 1 element, this function has no remaining work to do.
 
402
    * (No point sorting a 1 element array.)
 
403
    */
 
404
   if (!iconSizes || *iconSizes == 0) {
 
405
      return iconSizes;
 
406
   }
 
407
 
 
408
   /*
 
409
    * Sort the array in descending order.  First we have to determine its
 
410
    * size, then we'll just pass it off to qsort.  Note that we don't consider
 
411
    * the final, terminating element when sorting, because the icon array may
 
412
    * contain a -1 to signify a scalable icon.
 
413
    */
 
414
   for (iter = iconSizes, nsizes = 0; *iter; iter++, nsizes++);
 
415
   qsort(iconSizes, nsizes, sizeof *iter, DescendingIntCmp);
 
416
 
 
417
   return iconSizes;
 
418
}
 
419
 
 
420
 
 
421
/*
 
422
 *-----------------------------------------------------------------------------
 
423
 *
 
424
 * ShrinkPixbuf --
 
425
 *
 
426
 *      Scale a pixbuf to fit within transport size constraints.
 
427
 *
 
428
 * Results:
 
429
 *      Pointer to new pixbuf.  Caller should free with g_object_unref.
 
430
 *
 
431
 * Side effects:
 
432
 *      None.
 
433
 *
 
434
 *-----------------------------------------------------------------------------
 
435
 */
 
436
 
 
437
GdkPixbuf*
 
438
ShrinkPixbuf(const GdkPixbuf* pixbuf, // IN
 
439
             size_t maxSize)          // IN: Must scale to less than this.
 
440
{
 
441
   GdkPixbuf *newIcon;
 
442
   volatile double newWidth;
 
443
   volatile double newHeight;
 
444
   volatile double scaleFactor;
 
445
 
 
446
   newWidth = gdk_pixbuf_get_width(pixbuf);
 
447
   newHeight = gdk_pixbuf_get_height(pixbuf);
 
448
   scaleFactor = maxSize / (newWidth * newHeight * 4.0);
 
449
   /*
 
450
    * Ensures that we remove at least a little bit of data from the icon.
 
451
    * Otherwise we can get things like scalefactors of '0.999385' which result
 
452
    * in an image of exactly the same size. A scaleFactor of 0.95 will remove at
 
453
    * least one row or column from any icon large enough to go past the limit.
 
454
    */
 
455
   scaleFactor = MIN(scaleFactor, 0.95);
 
456
 
 
457
   newWidth *= scaleFactor;
 
458
   newHeight *= scaleFactor;
 
459
   newIcon = gdk_pixbuf_scale_simple(pixbuf,
 
460
                                     (int)ceil(newWidth),
 
461
                                     (int)ceil(newHeight),
 
462
                                     GDK_INTERP_HYPER);
 
463
   return newIcon;
 
464
}