From 81a75745cb5e9a436729ea0bd088d05fec795066 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Mon, 18 Jun 2007 12:00:34 +0000 Subject: [PATCH] Implement support for icons in SeamlessRDP. git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/trunk/rdesktop@1412 423420c4-83ab-492f-b58f-81f9feb106b5 --- doc/seamlessrdp-channel.txt | 45 +++++++++++++- ewmhints.c | 114 +++++++++++++++++++++++++++++++++++- proto.h | 8 +-- seamless.c | 64 +++++++++++++++++++- xproto.h | 2 + xwin.c | 100 +++++++++++++++++++++++++++++-- 6 files changed, 317 insertions(+), 16 deletions(-) diff --git a/doc/seamlessrdp-channel.txt b/doc/seamlessrdp-channel.txt index 894eb08..143ab15 100644 --- a/doc/seamlessrdp-channel.txt +++ b/doc/seamlessrdp-channel.txt @@ -5,8 +5,6 @@ TODO * Commands for changing z order and focus. -* Command for transferring icon. - * Think about protocol version management * Try to assure that messages aren't repeated or are sent for hidden windows. @@ -237,6 +235,49 @@ This message is sent some time after a HIDE, indicating that the windowed view has been restored. If the client has dropped all information about windows then it can send a SYNC to re-enumerate them. +SETICON +------- + +Sets an icon for a window. + +Syntax: + SETICON,,,,,,, + +This message is sent when a window is initially created and at any time when +the application modifies its icon. + +A window can have multiple icons, but only one of a given format and size. A +SETICON received for an already existing format and size is expected to over- +write that icon. + +Since icons can potentially be very large, it can easily overflow the line +limitation in the protocol. To handle this, multiple SETICON will be issued +with an ever increasing chunk number. + +The initial chunk is 0 (zero) and all chunks must be sent in order. Multiple +SETICON sets for the same window may not interleave. SETICON sets for +different windows may interleave though. + +Formats: + RGBA : Four bytes of data per pixel, representing red, green, blue and + alpha, in that order. + +Data is the raw icon data written in hex (e.g. 3fab32...). Chunks must be +divided on a whole byte boundary. Case is not specified. + +DELICON +------- + +Removes an icon for a window. + +Syntax: + DELICON,,,,, + +Removes the icon of a window matching the given format and size, previously +set with SETICON. + +This command may not be interleaved with a SETICON set. + Client to Server Operations =========================== diff --git a/ewmhints.c b/ewmhints.c index 5376514..15254d3 100644 --- a/ewmhints.c +++ b/ewmhints.c @@ -4,7 +4,8 @@ Support functions for Extended Window Manager Hints, http://www.freedesktop.org/wiki/Standards_2fwm_2dspec - Copyright (C) Peter Astrand 2005 + Copyright 2005 Peter Astrand for Cendio AB + Copyright 2007 Pierre Ossman for Cendio AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,7 +35,8 @@ extern Display *g_display; static Atom g_net_wm_state_maximized_vert_atom, g_net_wm_state_maximized_horz_atom, g_net_wm_state_hidden_atom, g_net_wm_name_atom, g_utf8_string_atom, - g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom, g_net_wm_state_modal_atom; + g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom, + g_net_wm_state_modal_atom, g_net_wm_icon_atom; Atom g_net_wm_state_atom, g_net_wm_desktop_atom; @@ -187,6 +189,7 @@ ewmh_init() g_net_wm_state_atom = XInternAtom(g_display, "_NET_WM_STATE", False); g_net_wm_desktop_atom = XInternAtom(g_display, "_NET_WM_DESKTOP", False); g_net_wm_name_atom = XInternAtom(g_display, "_NET_WM_NAME", False); + g_net_wm_icon_atom = XInternAtom(g_display, "_NET_WM_ICON", False); g_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False); } @@ -423,6 +426,113 @@ ewmh_set_window_modal(Window wnd) return 0; } +void +ewmh_set_icon(Window wnd, int width, int height, const char *rgba_data) +{ + unsigned long nitems, i; + unsigned char *props; + uint32 *cur_set, *new_set; + uint32 *icon; + + cur_set = NULL; + new_set = NULL; + + if (get_property_value(wnd, "_NET_WM_ICON", 10000, &nitems, &props, 1) >= 0) + { + cur_set = (uint32 *) props; + + for (i = 0; i < nitems;) + { + if (cur_set[i] == width && cur_set[i + 1] == height) + break; + + i += 2 + cur_set[i] * cur_set[i + 1]; + } + + if (i != nitems) + icon = cur_set + i; + else + { + new_set = xmalloc((nitems + width * height + 2) * 4); + memcpy(new_set, cur_set, nitems * 4); + icon = new_set + nitems; + nitems += width * height + 2; + } + } + else + { + new_set = xmalloc((width * height + 2) * 4); + icon = new_set; + nitems = width * height + 2; + } + + icon[0] = width; + icon[1] = height; + + /* Convert RGBA -> ARGB */ + for (i = 0; i < width * height; i++) + { + icon[i + 2] = + rgba_data[i * 4 + 3] << 24 | + ((rgba_data[i * 4 + 0] << 16) & 0x00FF0000) | + ((rgba_data[i * 4 + 1] << 8) & 0x0000FF00) | + ((rgba_data[i * 4 + 2] << 0) & 0x000000FF); + } + + XChangeProperty(g_display, wnd, g_net_wm_icon_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) (new_set ? new_set : cur_set), nitems); + + if (cur_set) + XFree(cur_set); + if (new_set) + xfree(new_set); +} + +void +ewmh_del_icon(Window wnd, int width, int height) +{ + unsigned long nitems, i, icon_size; + unsigned char *props; + uint32 *cur_set, *new_set; + + cur_set = NULL; + new_set = NULL; + + if (get_property_value(wnd, "_NET_WM_ICON", 10000, &nitems, &props, 1) < 0) + return; + + cur_set = (uint32 *) props; + + for (i = 0; i < nitems;) + { + if (cur_set[i] == width && cur_set[i + 1] == height) + break; + + i += 2 + cur_set[i] * cur_set[i + 1]; + } + + if (i == nitems) + goto out; + + icon_size = width * height + 2; + new_set = xmalloc((nitems - icon_size) * 4); + + if (i != 0) + memcpy(new_set, cur_set, i * 4); + if (i != nitems - icon_size) + memcpy(new_set + i * 4, cur_set + i * 4 + icon_size, nitems - icon_size); + + nitems -= icon_size; + + XChangeProperty(g_display, wnd, g_net_wm_icon_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) new_set, nitems); + + xfree(new_set); + + out: + XFree(cur_set); +} + #endif /* MAKE_PROTO */ diff --git a/proto.h b/proto.h index 500463a..e20b726 100644 --- a/proto.h +++ b/proto.h @@ -288,6 +288,9 @@ void ui_seamless_create_window(unsigned long id, unsigned long group, unsigned l unsigned long flags); void ui_seamless_destroy_window(unsigned long id, unsigned long flags); void ui_seamless_destroy_group(unsigned long id, unsigned long flags); +void ui_seamless_seticon(unsigned long id, const char *format, int width, int height, int chunk, + const char *data, int chunk_len); +void ui_seamless_delicon(unsigned long id, const char *format, int width, int height); void ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags); void ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags); @@ -307,13 +310,8 @@ void seamless_select_timeout(struct timeval *tv); unsigned int seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags); unsigned int seamless_send_focus(unsigned long id, unsigned long flags); /* scard.c */ -void scardSetInfo(uint32 device, uint32 id, uint32 bytes_out); -int scard_enum_devices(uint32 * id, char *optarg); void scard_lock(int lock); void scard_unlock(int lock); -STREAM scard_tcp_init(void); -void scard_tcp_connect(void); -void scard_tcp_reset_state(void); /* *INDENT-OFF* */ #ifdef __cplusplus diff --git a/seamless.c b/seamless.c index 65864c2..3dddf16 100644 --- a/seamless.c +++ b/seamless.c @@ -1,7 +1,8 @@ /* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. Seamless Windows support - Copyright (C) Peter Astrand 2005-2007 + Copyright 2005-2007 Peter Astrand for Cendio AB + Copyright 2007 Pierre Ossman for Cendio AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +32,7 @@ extern RD_BOOL g_seamless_rdp; static VCHANNEL *seamless_channel; static unsigned int seamless_serial; +static char icon_buf[1024]; static char * seamless_get_token(char **s) @@ -135,7 +137,65 @@ seamless_process_line(const char *line, void *data) } else if (!strcmp("SETICON", tok1)) { - unimpl("SeamlessRDP SETICON1\n"); + int chunk, width, height, len; + char byte[3]; + + if (!tok8) + return False; + + id = strtoul(tok3, &endptr, 0); + if (*endptr) + return False; + + chunk = strtoul(tok4, &endptr, 0); + if (*endptr) + return False; + + width = strtoul(tok6, &endptr, 0); + if (*endptr) + return False; + + height = strtoul(tok7, &endptr, 0); + if (*endptr) + return False; + + byte[2] = '\0'; + len = 0; + while (*tok8 != '\0') + { + byte[0] = *tok8; + tok8++; + if (*tok8 == '\0') + return False; + byte[1] = *tok8; + tok8++; + + icon_buf[len] = strtol(byte, NULL, 16); + len++; + } + + ui_seamless_seticon(id, tok5, width, height, chunk, icon_buf, len); + } + else if (!strcmp("DELICON", tok1)) + { + int width, height; + + if (!tok6) + return False; + + id = strtoul(tok3, &endptr, 0); + if (*endptr) + return False; + + width = strtoul(tok5, &endptr, 0); + if (*endptr) + return False; + + height = strtoul(tok6, &endptr, 0); + if (*endptr) + return False; + + ui_seamless_delicon(id, tok4, width, height); } else if (!strcmp("POSITION", tok1)) { diff --git a/xproto.h b/xproto.h index d3748a1..15be664 100644 --- a/xproto.h +++ b/xproto.h @@ -9,3 +9,5 @@ int ewmh_get_window_desktop(Window wnd); void ewmh_set_wm_name(Window wnd, const char *title); int ewmh_set_window_popup(Window wnd); int ewmh_set_window_modal(Window wnd); +void ewmh_set_icon(Window wnd, int width, int height, const char *rgba_data); +void ewmh_del_icon(Window wnd, int width, int height); diff --git a/xwin.c b/xwin.c index 71b2c8e..377ca94 100644 --- a/xwin.c +++ b/xwin.c @@ -2,6 +2,7 @@ rdesktop: A Remote Desktop Protocol client. User interface services - X Window System Copyright (C) Matthew Chapman 1999-2007 + Copyright 2007 Pierre Ossman for Cendio AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -74,6 +75,10 @@ typedef struct _seamless_window int outpos_xoffset, outpos_yoffset; int outpos_width, outpos_height; + unsigned int icon_size; + unsigned int icon_offset; + char icon_buffer[32 * 32 * 4]; + struct _seamless_window *next; } seamless_window; static seamless_window *g_seamless_windows = NULL; @@ -3452,15 +3457,13 @@ ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long p XSetWMProtocols(g_display, wnd, &g_kill_atom, 1); sw = xmalloc(sizeof(seamless_window)); + + memset(sw, 0, sizeof(seamless_window)); + sw->wnd = wnd; sw->id = id; - sw->behind = 0; sw->group = sw_find_group(group, False); sw->group->refcnt++; - sw->xoffset = 0; - sw->yoffset = 0; - sw->width = 0; - sw->height = 0; sw->state = SEAMLESSRDP_NOTYETMAPPED; sw->desktop = 0; sw->position_timer = xmalloc(sizeof(struct timeval)); @@ -3527,6 +3530,93 @@ ui_seamless_destroy_group(unsigned long id, unsigned long flags) } +void +ui_seamless_seticon(unsigned long id, const char *format, int width, int height, int chunk, + const char *data, int chunk_len) +{ + seamless_window *sw; + + if (!g_seamless_active) + return; + + sw = sw_get_window_by_id(id); + if (!sw) + { + warning("ui_seamless_seticon: No information for window 0x%lx\n", id); + return; + } + + if (chunk == 0) + { + if (sw->icon_size) + warning("ui_seamless_seticon: New icon started before previous completed\n"); + + if (strcmp(format, "RGBA") != 0) + { + warning("ui_seamless_seticon: Uknown icon format \"%s\"\n", format); + return; + } + + sw->icon_size = width * height * 4; + if (sw->icon_size > 32 * 32 * 4) + { + warning("ui_seamless_seticon: Icon too large (%d bytes)\n", sw->icon_size); + sw->icon_size = 0; + return; + } + + sw->icon_offset = 0; + } + else + { + if (!sw->icon_size) + return; + } + + if (chunk_len > (sw->icon_size - sw->icon_offset)) + { + warning("ui_seamless_seticon: Too large chunk received (%d bytes > %d bytes)\n", + chunk_len, sw->icon_size - sw->icon_offset); + sw->icon_size = 0; + return; + } + + memcpy(sw->icon_buffer + sw->icon_offset, data, chunk_len); + sw->icon_offset += chunk_len; + + if (sw->icon_offset == sw->icon_size) + { + ewmh_set_icon(sw->wnd, width, height, sw->icon_buffer); + sw->icon_size = 0; + } +} + + +void +ui_seamless_delicon(unsigned long id, const char *format, int width, int height) +{ + seamless_window *sw; + + if (!g_seamless_active) + return; + + sw = sw_get_window_by_id(id); + if (!sw) + { + warning("ui_seamless_seticon: No information for window 0x%lx\n", id); + return; + } + + if (strcmp(format, "RGBA") != 0) + { + warning("ui_seamless_seticon: Uknown icon format \"%s\"\n", format); + return; + } + + ewmh_del_icon(sw->wnd, width, height); +} + + void ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags) {