xfreerdp: set _NET_WM_ICON to RAIL app icon

Icons on X11 windows are configured using the _NET_WM_ICON property
described in Extended Window Manager Hints. Here we implement converison
from DIB bitmaps used by RAIL to the format expected by _NET_WM_ICON,
and actually set the icon for RAIL app windows.

Both DIB format and _NET_WM_ICON (or rather, Xlib) are weird. Let's
start with RAIL's format. That's the one used in BMP and ICO formats
on Windows. It has some strange properties but thankfully FreeRDP's
freerdp_image_copy() can handle most of them for us. (With an exception
of monochrome and 16-color formats that it does not support. Sorry, but
I'm too lazy to fix them. They are not seem to be used by any real
application either.) The one thing that it can't do is to apply the
alpha transparency bitmask so we have to do it manually. This instantly
reminds us that DIB format has HISTORY: it's vertically flipped and
each must be padded to 4 bytes. Both these quirks having reasonable
(for a certain definition of 'reason') explanations. Such is life.
(Also, 8-bit images require a color palette which we must fill in.)

So okay, now comes _NET_WM_ICON. It is more sane (or rather, easier to
deal with). The bitmap is represented with a tiny [width, height] header
followed by an array of pixels in ARGB format. There is no padding, no
weird color formats. But here's a catch: you can't simply take the
output of freerdp_image_copy() and cast to (unsigned char*) of colors.
We have to allocate an array of C's longs and copy the pixels there,
because that's what Xlib expects (and this is mentioned in the spec).
Simply casting an array of bytes causes crashes on 64-bit systems.
So don't try to cheat or "optimize" and read the docs, kids.

Note that XFlush() call after XChangeProperty(). It's there because it
seems to helps see the icon quicker with Unity on Ubuntu 14.04. I don't
know why. (And Unity does not support _NET_WM_ICON officially. But it
sorta kinda works sometimes.)

Oh, and while we're here, delete some old, unused, and commented out
code that was setting window icons in the past. It's not needed anymore.
This commit is contained in:
ilammy 2018-11-10 14:43:36 +02:00 committed by Armin Novak
parent 8163b6d1ce
commit 9292b2231f
2 changed files with 192 additions and 37 deletions

View File

@ -22,6 +22,7 @@
#endif
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <winpr/wlog.h>
@ -610,15 +611,206 @@ static xfRailIcon* RailIconCache_Lookup(xfRailIconCache* cache,
return &cache->entries[cache->numCacheEntries * cacheId + cacheEntry];
}
static inline UINT32 read_color_quad(const BYTE* pixels)
{
return (((UINT32) pixels[0]) << 24)
| (((UINT32) pixels[1]) << 16)
| (((UINT32) pixels[2]) << 8)
| (((UINT32) pixels[3]) << 0);
}
/*
* DIB color palettes are arrays of RGBQUAD structs with colors in BGRX format.
* They are used only by 1, 2, 4, and 8-bit bitmaps.
*/
static void fill_gdi_palette_for_icon(ICON_INFO* iconInfo, gdiPalette *palette)
{
UINT32 i;
palette->format = PIXEL_FORMAT_BGRX32;
ZeroMemory(palette->palette, sizeof(palette->palette));
if (!iconInfo->cbColorTable)
return;
if ((iconInfo->cbColorTable % 4 != 0) || (iconInfo->cbColorTable / 4 > 256))
{
WLog_WARN(TAG, "weird palette size: %u", iconInfo->cbColorTable);
return;
}
for (i = 0; i < iconInfo->cbColorTable / 4; i++)
{
palette->palette[i] = read_color_quad(&iconInfo->colorTable[4 * i]);
}
}
static BOOL convert_icon_color_to_argb(ICON_INFO* iconInfo, BYTE* argbPixels)
{
DWORD format;
gdiPalette palette;
/*
* Color formats used by icons are DIB bitmap formats (2-bit format
* is not used by MS-RDPERP). Note that 16-bit is RGB555, not RGB565,
* and that 32-bit format uses BGRA order.
*/
switch (iconInfo->bpp)
{
case 1:
case 4:
/*
* These formats are not supported by freerdp_image_copy().
* PIXEL_FORMAT_MONO and PIXEL_FORMAT_A4 are *not* correct
* color formats for this. Please fix freerdp_image_copy()
* if you came here to fix a broken icon of some weird app
* that still uses 1 or 4bpp format in the 21st century.
*/
WLog_WARN(TAG, "1bpp and 4bpp icons are not supported");
return FALSE;
case 8:
format = PIXEL_FORMAT_RGB8;
break;
case 16:
format = PIXEL_FORMAT_RGB15;
break;
case 24:
format = PIXEL_FORMAT_RGB24;
break;
case 32:
format = PIXEL_FORMAT_BGRA32;
break;
default:
WLog_WARN(TAG, "invalid icon bpp: %d", iconInfo->bpp);
return FALSE;
}
fill_gdi_palette_for_icon(iconInfo, &palette);
return freerdp_image_copy(
argbPixels,
PIXEL_FORMAT_ARGB32,
0, 0, 0,
iconInfo->width,
iconInfo->height,
iconInfo->bitsColor,
format,
0, 0, 0,
&palette,
FREERDP_FLIP_VERTICAL
);
}
static inline UINT32 div_ceil(UINT32 a, UINT32 b)
{
return (a + (b - 1)) / b;
}
static inline UINT32 round_up(UINT32 a, UINT32 b)
{
return b * div_ceil(a, b);
}
static void apply_icon_alpha_mask(ICON_INFO* iconInfo, BYTE* argbPixels)
{
BYTE nextBit;
BYTE* maskByte;
UINT32 x, y;
UINT32 stride;
if (!iconInfo->cbBitsMask)
return;
/*
* Each byte encodes 8 adjacent pixels (with LSB padding as needed).
* And due to hysterical raisins, stride of DIB bitmaps must be
* a multiple of 4 bytes.
*/
stride = round_up(div_ceil(iconInfo->width, 8), 4);
for (y = 0; y < iconInfo->height; y++)
{
/* ɐᴉlɐɹʇsn∀ uᴉ ǝɹ,ǝʍ ʇɐɥʇ ʇǝƃɹoɟ ʇ,uop */
maskByte = &iconInfo->bitsMask[stride * (iconInfo->height - 1 - y)];
nextBit = 0x80;
for (x = 0; x < iconInfo->width; x++)
{
BYTE alpha = (*maskByte & nextBit) ? 0x00 : 0xFF;
argbPixels[4 * (x + y * iconInfo->width)] &= alpha;
nextBit >>= 1;
if (!nextBit)
{
nextBit = 0x80;
maskByte++;
}
}
}
}
/*
* _NET_WM_ICON format is defined as "array of CARDINAL" values which for
* Xlib must be represented with an array of C's "long" values. Note that
* "long" != "INT32" on 64-bit systems. Therefore we can't simply cast
* the bitmap data as (unsigned char*), we have to copy all the pixels.
*
* The first two values are width and height followed by actual color data
* in ARGB format (e.g., 0xFFFF0000L is opaque red), pixels are in normal,
* left-to-right top-down order.
*/
static BOOL convert_rail_icon(ICON_INFO* iconInfo, xfRailIcon *railIcon)
{
BYTE* argbPixels;
BYTE* nextPixel;
long* pixels;
int i;
int nelements;
argbPixels = calloc(iconInfo->width * iconInfo->height, 4);
if (!argbPixels)
goto error;
if (!convert_icon_color_to_argb(iconInfo, argbPixels))
goto error;
apply_icon_alpha_mask(iconInfo, argbPixels);
nelements = 2 + iconInfo->width * iconInfo->height;
pixels = realloc(railIcon->data, nelements * sizeof(*pixels));
if (!pixels)
goto error;
railIcon->data = pixels;
railIcon->length = nelements;
pixels[0] = iconInfo->width;
pixels[1] = iconInfo->height;
nextPixel = argbPixels;
for (i = 2; i < nelements; i++)
{
pixels[i] = read_color_quad(nextPixel);
nextPixel += 4;
}
free(argbPixels);
return TRUE;
error:
free(argbPixels);
return FALSE;
}
static void xf_rail_set_window_icon(xfContext* xfc,
xfAppWindow* railWindow, xfRailIcon *icon,
BOOL replace)
{
XChangeProperty(xfc->display, railWindow->handle, xfc->_NET_WM_ICON,
XA_CARDINAL, 32, replace ? PropModeReplace : PropModeAppend,
(unsigned char*) icon->data, icon->length);
XFlush(xfc->display);
}
static xfAppWindow* xf_rail_get_window_by_id(xfContext* xfc, UINT32 windowId)

View File

@ -996,43 +996,6 @@ void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state)
XFlush(xfc->display);
}
#if 0
void xf_SetWindowIcon(xfContext* xfc, xfAppWindow* appWindow, rdpIcon* icon)
{
int x, y;
int pixels;
int propsize;
long* propdata;
long* dstp;
UINT32* srcp;
if (!icon->big)
return;
pixels = icon->entry->width * icon->entry->height;
propsize = 2 + pixels;
propdata = malloc(propsize * sizeof(long));
propdata[0] = icon->entry->width;
propdata[1] = icon->entry->height;
dstp = &(propdata[2]);
srcp = (UINT32*) icon->extra;
for (y = 0; y < icon->entry->height; y++)
{
for (x = 0; x < icon->entry->width; x++)
{
*dstp++ = *srcp++;
}
}
XChangeProperty(xfc->display, appWindow->handle, xfc->_NET_WM_ICON, XA_CARDINAL,
32,
PropModeReplace, (BYTE*) propdata, propsize);
XFlush(xfc->display);
free(propdata);
}
#endif
void xf_SetWindowRects(xfContext* xfc, xfAppWindow* appWindow,
RECTANGLE_16* rects, int nrects)
{