Merge pull request #5012 from akallabeth/rail_app_icons

Rail app icons
This commit is contained in:
Bernhard Miklautz 2018-11-15 08:50:56 +00:00 committed by GitHub
commit d915491717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 379 additions and 66 deletions

View File

@ -62,7 +62,7 @@ BOOL rail_string_to_unicode_string(const char* string, RAIL_UNICODE_STRING* unic
if (!string || strlen(string) < 1)
return TRUE;
length = ConvertToUnicode(CP_UTF8, 0, string, -1, &buffer, 0) * 2;
length = ConvertToUnicode(CP_UTF8, 0, string, -1, &buffer, 0);
if ((length < 0) || ((size_t)length * sizeof(WCHAR) > UINT16_MAX))
{

View File

@ -22,6 +22,7 @@
#endif
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <winpr/wlog.h>
@ -61,6 +62,22 @@ static const char* movetype_names[] =
};
#endif
struct xf_rail_icon
{
long* data;
int length;
};
typedef struct xf_rail_icon xfRailIcon;
struct xf_rail_icon_cache
{
xfRailIcon* entries;
UINT32 numCaches;
UINT32 numCacheEntries;
xfRailIcon scratch;
};
typedef struct xf_rail_icon_cache xfRailIconCache;
void xf_rail_enable_remoteapp_mode(xfContext* xfc)
{
if (!xfc->remote_app)
@ -531,22 +548,332 @@ static BOOL xf_rail_window_delete(rdpContext* context,
return TRUE;
}
static xfRailIconCache* RailIconCache_New(rdpSettings* settings)
{
xfRailIconCache* cache;
cache = calloc(1, sizeof(xfRailIconCache));
if (!cache)
return NULL;
cache->numCaches = settings->RemoteAppNumIconCaches;
cache->numCacheEntries = settings->RemoteAppNumIconCacheEntries;
cache->entries = calloc(cache->numCaches * cache->numCacheEntries,
sizeof(xfRailIcon));
if (!cache->entries)
{
WLog_ERR(TAG, "failed to allocate icon cache %d x %d entries",
cache->numCaches, cache->numCacheEntries);
free(cache);
return NULL;
}
return cache;
}
static void RailIconCache_Free(xfRailIconCache* cache)
{
UINT32 i;
if (cache)
{
for (i = 0; i < cache->numCaches * cache->numCacheEntries; i++)
{
free(cache->entries[i].data);
}
free(cache->scratch.data);
free(cache->entries);
free(cache);
}
}
static xfRailIcon* RailIconCache_Lookup(xfRailIconCache* cache,
UINT8 cacheId, UINT16 cacheEntry)
{
/*
* MS-RDPERP 2.2.1.2.3 Icon Info (TS_ICON_INFO)
*
* CacheId (1 byte):
* If the value is 0xFFFF, the icon SHOULD NOT be cached.
*
* Yes, the spec says "0xFFFF" in the 2018-03-16 revision,
* but the actual protocol field is 1-byte wide.
*/
if (cacheId == 0xFF)
return &cache->scratch;
if (cacheId >= cache->numCaches)
return NULL;
if (cacheEntry >= cache->numCacheEntries)
return NULL;
return &cache->entries[cache->numCacheEntries * cacheId + cacheEntry];
}
/*
* 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] = ReadColor(&iconInfo->colorTable[4 * i], palette->format);
}
}
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(long));
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] = ReadColor(nextPixel, PIXEL_FORMAT_BGRA32);
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)
{
return (xfAppWindow*) HashTable_GetItemValue(xfc->railWindows,
(void*)(UINT_PTR) windowId);
}
static BOOL xf_rail_window_icon(rdpContext* context,
WINDOW_ORDER_INFO* orderInfo, WINDOW_ICON_ORDER* windowIcon)
{
xfContext* xfc = (xfContext*) context;
xfAppWindow* railWindow = (xfAppWindow*) HashTable_GetItemValue(xfc->railWindows,
(void*)(UINT_PTR) orderInfo->windowId);
xfAppWindow* railWindow;
xfRailIcon* icon;
BOOL replaceIcon;
railWindow = xf_rail_get_window_by_id(xfc, orderInfo->windowId);
if (!railWindow)
return FALSE;
return TRUE;
icon = RailIconCache_Lookup(xfc->railIconCache,
windowIcon->iconInfo->cacheId,
windowIcon->iconInfo->cacheEntry);
if (!icon)
{
WLog_WARN(TAG, "failed to get icon from cache %02X:%04X",
windowIcon->iconInfo->cacheId,
windowIcon->iconInfo->cacheEntry);
return FALSE;
}
if (!convert_rail_icon(windowIcon->iconInfo, icon))
{
WLog_WARN(TAG, "failed to convert icon for window %08X", orderInfo->windowId);
return FALSE;
}
replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
return TRUE;
}
static BOOL xf_rail_window_cached_icon(rdpContext* context,
WINDOW_ORDER_INFO* orderInfo, WINDOW_CACHED_ICON_ORDER* windowCachedIcon)
{
xfContext* xfc = (xfContext*) context;
xfAppWindow* railWindow;
xfRailIcon* icon;
BOOL replaceIcon;
railWindow = xf_rail_get_window_by_id(xfc, orderInfo->windowId);
if (!railWindow)
return TRUE;
icon = RailIconCache_Lookup(xfc->railIconCache,
windowCachedIcon->cachedIcon.cacheId,
windowCachedIcon->cachedIcon.cacheEntry);
if (!icon)
{
WLog_WARN(TAG, "failed to get icon from cache %02X:%04X",
windowCachedIcon->cachedIcon.cacheId,
windowCachedIcon->cachedIcon.cacheEntry);
return FALSE;
}
replaceIcon = !!(orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW);
xf_rail_set_window_icon(xfc, railWindow, icon, replaceIcon);
return TRUE;
}
@ -911,6 +1238,14 @@ int xf_rail_init(xfContext* xfc, RailClientContext* rail)
return 0;
xfc->railWindows->valueFree = rail_window_free;
xfc->railIconCache = RailIconCache_New(xfc->context.settings);
if (!xfc->railIconCache)
{
HashTable_Free(xfc->railWindows);
return 0;
}
return 1;
}
@ -928,5 +1263,11 @@ int xf_rail_uninit(xfContext* xfc, RailClientContext* rail)
xfc->railWindows = NULL;
}
if (xfc->railIconCache)
{
RailIconCache_Free(xfc->railIconCache);
xfc->railIconCache = NULL;
}
return 1;
}

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)
{

View File

@ -83,6 +83,7 @@ typedef struct xf_glyph xfGlyph;
typedef struct xf_clipboard xfClipboard;
typedef struct _xfDispContext xfDispContext;
typedef struct _xfVideoContext xfVideoContext;
typedef struct xf_rail_icon_cache xfRailIconCache;
/* Value of the first logical button number in X11 which must be */
/* subtracted to go from a button number in X11 to an index into */
@ -225,6 +226,7 @@ struct xf_context
RailClientContext* rail;
wHashTable* railWindows;
xfRailIconCache* railIconCache;
BOOL xkbAvailable;
BOOL xrenderAvailable;

View File

@ -66,7 +66,8 @@ BOOL rail_read_unicode_string(wStream* s, RAIL_UNICODE_STRING* unicode_string)
return TRUE;
}
BOOL update_read_icon_info(wStream* s, ICON_INFO* iconInfo)
/* See [MS-RDPERP] 2.2.1.2.3 Icon Info (TS_ICON_INFO) */
static BOOL update_read_icon_info(wStream* s, ICON_INFO* iconInfo)
{
BYTE* newBitMask;
@ -86,17 +87,21 @@ BOOL update_read_icon_info(wStream* s, ICON_INFO* iconInfo)
Stream_Read_UINT16(s, iconInfo->width); /* width (2 bytes) */
Stream_Read_UINT16(s, iconInfo->height); /* height (2 bytes) */
/* cbColorTable is only present when bpp is 1, 2 or 4 */
if (iconInfo->bpp == 1 || iconInfo->bpp == 2 || iconInfo->bpp == 4)
/* cbColorTable is only present when bpp is 1, 4 or 8 */
switch (iconInfo->bpp)
{
if (Stream_GetRemainingLength(s) < 2)
return FALSE;
case 1:
case 4:
case 8:
if (Stream_GetRemainingLength(s) < 2)
return FALSE;
Stream_Read_UINT16(s, iconInfo->cbColorTable); /* cbColorTable (2 bytes) */
}
else
{
iconInfo->cbColorTable = 0;
Stream_Read_UINT16(s, iconInfo->cbColorTable); /* cbColorTable (2 bytes) */
break;
default:
iconInfo->cbColorTable = 0;
break;
}
if (Stream_GetRemainingLength(s) < 4)
@ -170,7 +175,7 @@ BOOL update_read_icon_info(wStream* s, ICON_INFO* iconInfo)
return TRUE;
}
BOOL update_read_cached_icon_info(wStream* s, CACHED_ICON_INFO* cachedIconInfo)
static BOOL update_read_cached_icon_info(wStream* s, CACHED_ICON_INFO* cachedIconInfo)
{
if (Stream_GetRemainingLength(s) < 3)
return FALSE;
@ -180,7 +185,7 @@ BOOL update_read_cached_icon_info(wStream* s, CACHED_ICON_INFO* cachedIconInfo)
return TRUE;
}
BOOL update_read_notify_icon_infotip(wStream* s, NOTIFY_ICON_INFOTIP* notifyIconInfoTip)
static BOOL update_read_notify_icon_infotip(wStream* s, NOTIFY_ICON_INFOTIP* notifyIconInfoTip)
{
if (Stream_GetRemainingLength(s) < 8)
return FALSE;
@ -191,8 +196,8 @@ BOOL update_read_notify_icon_infotip(wStream* s, NOTIFY_ICON_INFOTIP* notifyIcon
rail_read_unicode_string(s, &notifyIconInfoTip->title); /* title */
}
BOOL update_read_window_state_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
WINDOW_STATE_ORDER* windowState)
static BOOL update_read_window_state_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
WINDOW_STATE_ORDER* windowState)
{
int i;
int size;
@ -376,8 +381,8 @@ BOOL update_read_window_state_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
return TRUE;
}
BOOL update_read_window_icon_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
WINDOW_ICON_ORDER* window_icon)
static BOOL update_read_window_icon_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
WINDOW_ICON_ORDER* window_icon)
{
update_free_window_icon_info(window_icon->iconInfo);
window_icon->iconInfo = (ICON_INFO*) calloc(1, sizeof(ICON_INFO));
@ -388,19 +393,20 @@ BOOL update_read_window_icon_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
return update_read_icon_info(s, window_icon->iconInfo); /* iconInfo (ICON_INFO) */
}
BOOL update_read_window_cached_icon_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
static BOOL update_read_window_cached_icon_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
WINDOW_CACHED_ICON_ORDER* window_cached_icon)
{
return update_read_cached_icon_info(s,
&window_cached_icon->cachedIcon); /* cachedIcon (CACHED_ICON_INFO) */
}
void update_read_window_delete_order(wStream* s, WINDOW_ORDER_INFO* orderInfo)
static void update_read_window_delete_order(wStream* s, WINDOW_ORDER_INFO* orderInfo)
{
/* window deletion event */
}
BOOL update_recv_window_info_order(rdpUpdate* update, wStream* s, WINDOW_ORDER_INFO* orderInfo)
static BOOL update_recv_window_info_order(rdpUpdate* update, wStream* s,
WINDOW_ORDER_INFO* orderInfo)
{
rdpContext* context = update->context;
rdpWindowUpdate* window = update->window;
@ -453,7 +459,7 @@ BOOL update_recv_window_info_order(rdpUpdate* update, wStream* s, WINDOW_ORDER_I
return result;
}
BOOL update_read_notification_icon_state_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
static BOOL update_read_notification_icon_state_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
NOTIFY_ICON_STATE_ORDER* notify_icon_state)
{
if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_VERSION)
@ -501,12 +507,12 @@ BOOL update_read_notification_icon_state_order(wStream* s, WINDOW_ORDER_INFO* or
return TRUE;
}
void update_read_notification_icon_delete_order(wStream* s, WINDOW_ORDER_INFO* orderInfo)
static void update_read_notification_icon_delete_order(wStream* s, WINDOW_ORDER_INFO* orderInfo)
{
/* notification icon deletion event */
}
BOOL update_recv_notification_icon_info_order(rdpUpdate* update, wStream* s,
static BOOL update_recv_notification_icon_info_order(rdpUpdate* update, wStream* s,
WINDOW_ORDER_INFO* orderInfo)
{
rdpContext* context = update->context;
@ -545,7 +551,7 @@ BOOL update_recv_notification_icon_info_order(rdpUpdate* update, wStream* s,
return result;
}
BOOL update_read_desktop_actively_monitored_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
static BOOL update_read_desktop_actively_monitored_order(wStream* s, WINDOW_ORDER_INFO* orderInfo,
MONITORED_DESKTOP_ORDER* monitored_desktop)
{
int i;
@ -596,12 +602,13 @@ BOOL update_read_desktop_actively_monitored_order(wStream* s, WINDOW_ORDER_INFO*
return TRUE;
}
void update_read_desktop_non_monitored_order(wStream* s, WINDOW_ORDER_INFO* orderInfo)
static void update_read_desktop_non_monitored_order(wStream* s, WINDOW_ORDER_INFO* orderInfo)
{
/* non-monitored desktop notification event */
}
BOOL update_recv_desktop_info_order(rdpUpdate* update, wStream* s, WINDOW_ORDER_INFO* orderInfo)
static BOOL update_recv_desktop_info_order(rdpUpdate* update, wStream* s,
WINDOW_ORDER_INFO* orderInfo)
{
rdpContext* context = update->context;
rdpWindowUpdate* window = update->window;