1
/*********************************************************
2
* Copyright (C) 2010 VMware, Inc. All rights reserved.
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.
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.
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.
17
*********************************************************/
22
* GHI/X11 icon collection code.
30
#include <gio/gdesktopappinfo.h>
34
#include "guest_msg_def.h"
37
#include "ghiX11icon.h"
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.
45
static const size_t MAX_ICON_SIZE = GUESTMSG_MAX_IN_SIZE - 1024;
49
* Local function declarations.
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);
65
*-----------------------------------------------------------------------------
67
* GHIX11IconGetIconsForDesktopFile --
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.
73
* Returns FALSE if any errors were encountered and TRUE otherwise.
76
* iconList.size() may grow.
78
*-----------------------------------------------------------------------------
82
GHIX11IconGetIconsForDesktopFile(const char* desktopFile, // IN
83
std::list<GHIBinaryIconInfo>& iconList) // OUT
85
GDesktopAppInfo* desktopAppInfo = NULL;
89
desktopAppInfo = g_desktop_app_info_new_from_filename(desktopFile);
91
GAppInfo* appInfo = (GAppInfo*)G_APP_INFO(desktopAppInfo);
92
gicon = g_app_info_get_icon(appInfo);
94
success = GetIconsForGIcon(gicon, iconList);
96
g_object_unref(desktopAppInfo);
104
*-----------------------------------------------------------------------------
106
* GHIX11IconGetIconsByName --
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.
113
* Returns FALSE if any errors were encountered and TRUE otherwise.
116
* iconList.size() may grow.
118
*-----------------------------------------------------------------------------
122
GHIX11IconGetIconsByName(const char* iconName, // IN
123
std::list<GHIBinaryIconInfo>& iconList) // OUT
128
gicon = g_icon_new_for_string(iconName, NULL);
130
retval = GetIconsForGIcon(gicon, iconList);
131
g_object_unref(G_OBJECT(gicon));
144
*-----------------------------------------------------------------------------
146
* GetIconsForGIcon --
148
* Given a GLib GIcon, search the default icon theme or filesystem for
152
* Returns FALSE if any errors were encountered and TRUE otherwise.
155
* iconList.size() may grow.
157
*-----------------------------------------------------------------------------
161
GetIconsForGIcon(GIcon* gicon, // IN
162
std::list<GHIBinaryIconInfo>& iconList) // OUT
164
gchar* iconName = NULL;
165
GtkIconTheme* iconTheme;
166
Bool success = FALSE;
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.)
177
iconName = g_icon_to_string(gicon);
178
iconTheme = gtk_icon_theme_get_default();
180
if (G_IS_THEMED_ICON(gicon) && gtk_icon_theme_has_icon(iconTheme, iconName)) {
183
* Sweet - GTK claims that our icon is themed and can give us pixbufs for
184
* it at various sizes.
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.
191
gint* iconSizes = GetIconSizesDescending(iconTheme, iconName);
193
if (iconSizes && *iconSizes != 0) {
196
for (sizeIter = iconSizes; *sizeIter; sizeIter++) {
199
pixbuf = gtk_icon_theme_load_icon(iconTheme, iconName, *sizeIter,
200
(GtkIconLookupFlags)0, NULL);
202
AppendPixbufToArray(pixbuf, iconList);
203
g_object_unref(pixbuf);
206
} else if (iconSizes && *iconSizes == 0) {
207
GtkIconInfo* iconInfo;
209
iconInfo = gtk_icon_theme_lookup_icon(iconTheme, iconName, 0,
210
(GtkIconLookupFlags)0);
212
AppendFileToArray(gtk_icon_info_get_filename(iconInfo), iconList);
213
gtk_icon_info_free(iconInfo);
218
} else if (G_IS_FILE_ICON(gicon)) {
223
fileIcon = G_FILE_ICON(gicon);
224
file = g_file_icon_get_file(fileIcon);
225
path = g_file_get_path(file);
228
AppendFileToArray(path, iconList);
247
*-----------------------------------------------------------------------------
249
* AppendFileToArray --
251
* Load an icon from a file into a pixbuf, then append it to a GPtrArray of
255
* Appends an icon on success, no-op on failure.
260
*-----------------------------------------------------------------------------
264
AppendFileToArray(const gchar* iconPath, // IN
265
std::list<GHIBinaryIconInfo>& iconList) // OUT
269
if ((pixbuf = gdk_pixbuf_new_from_file(iconPath, NULL)) != NULL) {
270
AppendPixbufToArray(pixbuf, iconList, true);
271
g_object_unref(pixbuf);
277
*-----------------------------------------------------------------------------
279
* AppendPixbufToArray --
281
* Appends a pixbuf to a GPtrArray of GHIX11Icons.
284
* Appends an icon on success, no-op on failure.
289
*-----------------------------------------------------------------------------
294
const GdkPixbuf* pixbuf, // IN
295
std::list<GHIBinaryIconInfo>& iconList, // OUT
296
bool scaleHint) // IN: Scale icon to fit GuestMsg
298
GHIBinaryIconInfo ghiIcon;
307
GdkPixbuf *scaledPixbuf = NULL;
311
ASSERT(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB);
312
ASSERT(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8);
314
rowstride = gdk_pixbuf_get_rowstride(pixbuf);
315
n_channels = gdk_pixbuf_get_n_channels(pixbuf);
316
pixels = gdk_pixbuf_get_pixels(pixbuf);
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);
325
&& height * bgraStride > MAX_ICON_SIZE) {
327
pixbuf = scaledPixbuf = ShrinkPixbuf(pixbuf, MAX_ICON_SIZE);
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;
339
b = &ghiIcon.dataBGRA[bgraOffset];
340
p = &pixels[pixbufOffset];
345
b[3] = (n_channels > 3) ? p[3] : 0xFF;
349
iconList.push_back(ghiIcon);
352
g_object_unref(G_OBJECT(scaledPixbuf));
358
*-----------------------------------------------------------------------------
360
* GetIconSizesDescending --
362
* Query an icon theme for an icon's sizes. Return the sizes as a gint
363
* array in descending order.
366
* Pointer to a gint array on success or NULL on failure.
371
*-----------------------------------------------------------------------------
375
DescendingIntCmp(const void* a,
378
gint ia = *(gint* )a;
379
gint ib = *(gint* )b;
381
return (ia < ib) ? 1 : (ia == ib) ? 0 : -1;
385
GetIconSizesDescending(GtkIconTheme* iconTheme, // IN
386
const gchar* iconName) // IN
388
gint* iconSizes = NULL;
392
if (!gtk_icon_theme_has_icon(iconTheme, iconName)) {
396
iconSizes = gtk_icon_theme_get_icon_sizes(iconTheme, iconName);
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.)
404
if (!iconSizes || *iconSizes == 0) {
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.
414
for (iter = iconSizes, nsizes = 0; *iter; iter++, nsizes++);
415
qsort(iconSizes, nsizes, sizeof *iter, DescendingIntCmp);
422
*-----------------------------------------------------------------------------
426
* Scale a pixbuf to fit within transport size constraints.
429
* Pointer to new pixbuf. Caller should free with g_object_unref.
434
*-----------------------------------------------------------------------------
438
ShrinkPixbuf(const GdkPixbuf* pixbuf, // IN
439
size_t maxSize) // IN: Must scale to less than this.
442
volatile double newWidth;
443
volatile double newHeight;
444
volatile double scaleFactor;
446
newWidth = gdk_pixbuf_get_width(pixbuf);
447
newHeight = gdk_pixbuf_get_height(pixbuf);
448
scaleFactor = maxSize / (newWidth * newHeight * 4.0);
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.
455
scaleFactor = MIN(scaleFactor, 0.95);
457
newWidth *= scaleFactor;
458
newHeight *= scaleFactor;
459
newIcon = gdk_pixbuf_scale_simple(pixbuf,
461
(int)ceil(newHeight),