--- sourceforge.net/trunk/rdesktop/xwin.c 2006/04/28 07:55:36 1232 +++ sourceforge.net/trunk/rdesktop/xwin.c 2007/06/18 12:00:34 1413 @@ -1,7 +1,8 @@ /* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. User interface services - X Window System - Copyright (C) Matthew Chapman 1999-2005 + 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 @@ -20,6 +21,7 @@ #include #include +#include #include #include #include @@ -28,18 +30,15 @@ #include "rdesktop.h" #include "xproto.h" -/* We can't include Xproto.h because of conflicting defines for BOOL */ -#define X_ConfigureWindow 12 - extern int g_width; extern int g_height; extern int g_xpos; extern int g_ypos; extern int g_pos; -extern BOOL g_sendmotion; -extern BOOL g_fullscreen; -extern BOOL g_grab_keyboard; -extern BOOL g_hide_decorations; +extern RD_BOOL g_sendmotion; +extern RD_BOOL g_fullscreen; +extern RD_BOOL g_grab_keyboard; +extern RD_BOOL g_hide_decorations; extern char g_title[]; /* Color depth of the RDP session. As of RDP 5.1, it may be 8, 15, 16 or 24. */ @@ -71,23 +70,27 @@ unsigned int desktop; struct timeval *position_timer; - BOOL outstanding_position; + RD_BOOL outstanding_position; unsigned int outpos_serial; 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; static unsigned long g_seamless_focused = 0; -static BOOL g_seamless_started = False; /* Server end is up and running */ -static BOOL g_seamless_active = False; /* We are currently in seamless mode */ -static BOOL g_seamless_hidden = False; /* Desktop is hidden on server */ -extern BOOL g_seamless_rdp; +static RD_BOOL g_seamless_started = False; /* Server end is up and running */ +static RD_BOOL g_seamless_active = False; /* We are currently in seamless mode */ +static RD_BOOL g_seamless_hidden = False; /* Desktop is hidden on server */ +extern RD_BOOL g_seamless_rdp; extern uint32 g_embed_wnd; -BOOL g_enable_compose = False; -BOOL g_Unobscured; /* used for screenblt */ +RD_BOOL g_enable_compose = False; +RD_BOOL g_Unobscured; /* used for screenblt */ static GC g_gc = NULL; static GC g_create_bitmap_gc = NULL; static GC g_create_glyph_gc = NULL; @@ -104,20 +107,23 @@ static XIM g_IM; static XIC g_IC; static XModifierKeymap *g_mod_map; +/* Maps logical (xmodmap -pp) pointing device buttons (0-based) back + to physical (1-based) indices. */ +static unsigned char g_pointer_log_to_phys_map[16]; static Cursor g_current_cursor; -static HCURSOR g_null_cursor = NULL; +static RD_HCURSOR g_null_cursor = NULL; static Atom g_protocol_atom, g_kill_atom; extern Atom g_net_wm_state_atom; extern Atom g_net_wm_desktop_atom; -static BOOL g_focused; -static BOOL g_mouse_in_wnd; +static RD_BOOL g_focused; +static RD_BOOL g_mouse_in_wnd; /* Indicates that: 1) visual has 15, 16 or 24 depth and the same color channel masks as its RDP equivalent (implies X server is LE), 2) host is LE This will trigger an optimization whose real value is questionable. */ -static BOOL g_compatible_arch; +static RD_BOOL g_compatible_arch; /* Indicates whether RDP's bitmaps and our XImages have the same binary format. If so, we can avoid an expensive translation. Note that this can be true when g_compatible_arch is false, @@ -128,28 +134,26 @@ ('host' is the machine running rdesktop; the host simply memcpy's so its endianess doesn't matter) */ -static BOOL g_no_translate_image = False; +static RD_BOOL g_no_translate_image = False; /* endianness */ -static BOOL g_host_be; -static BOOL g_xserver_be; +static RD_BOOL g_host_be; +static RD_BOOL g_xserver_be; static int g_red_shift_r, g_blue_shift_r, g_green_shift_r; static int g_red_shift_l, g_blue_shift_l, g_green_shift_l; /* software backing store */ -extern BOOL g_ownbackstore; +extern RD_BOOL g_ownbackstore; static Pixmap g_backstore = 0; /* Moving in single app mode */ -static BOOL g_moving_wnd; +static RD_BOOL g_moving_wnd; static int g_move_x_offset = 0; static int g_move_y_offset = 0; -static BOOL g_using_full_workarea = False; +static RD_BOOL g_using_full_workarea = False; #ifdef WITH_RDPSND -extern int g_dsp_fd; -extern BOOL g_dsp_busy; -extern BOOL g_rdpsnd; +extern RD_BOOL g_rdpsnd; #endif /* MWM decorations */ @@ -242,8 +246,7 @@ break; \ case 1: /* Filled */ \ XFillArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \ - ON_ALL_SEAMLESS_WINDOWS(XCopyArea, (g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc, \ - x, y, cx, cy, x-sw->xoffset, y-sw->yoffset)); \ + ON_ALL_SEAMLESS_WINDOWS(XFillArc, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \ if (g_ownbackstore) \ XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \ break; \ @@ -251,7 +254,7 @@ } /* colour maps */ -extern BOOL g_owncolmap; +extern RD_BOOL g_owncolmap; static Colormap g_xcolmap; static uint32 *g_colmap = NULL; @@ -475,7 +478,7 @@ static seamless_group * -sw_find_group(unsigned long id, BOOL dont_create) +sw_find_group(unsigned long id, RD_BOOL dont_create) { seamless_window *sw; seamless_group *sg; @@ -1265,7 +1268,23 @@ return out; } -BOOL +static void +xwin_refresh_pointer_map(void) +{ + unsigned char phys_to_log_map[sizeof(g_pointer_log_to_phys_map)]; + int i, pointer_buttons; + + pointer_buttons = XGetPointerMapping(g_display, phys_to_log_map, sizeof(phys_to_log_map)); + for (i = 0; i < pointer_buttons; ++i) + { + /* This might produce multiple logical buttons mapping + to a single physical one, but hey, that's + life... */ + g_pointer_log_to_phys_map[phys_to_log_map[i] - 1] = i + 1; + } +} + +RD_BOOL get_key_state(unsigned int state, uint32 keysym) { int modifierpos, key, keysymMask = 0; @@ -1313,8 +1332,8 @@ return weight; } -static BOOL -select_visual() +static RD_BOOL +select_visual(int screen_num) { XPixmapFormatValues *pfm; int pixmap_formats_count, visuals_count; @@ -1340,7 +1359,10 @@ /* Search for best TrueColor visual */ template.class = TrueColor; - vmatches = XGetVisualInfo(g_display, VisualClassMask, &template, &visuals_count); + template.screen = screen_num; + vmatches = + XGetVisualInfo(g_display, VisualClassMask | VisualScreenMask, &template, + &visuals_count); g_visual = NULL; g_no_translate_image = False; g_compatible_arch = False; @@ -1349,7 +1371,7 @@ for (i = 0; i < visuals_count; ++i) { XVisualInfo *visual_info = &vmatches[i]; - BOOL can_translate_to_bpp = False; + RD_BOOL can_translate_to_bpp = False; int j; /* Try to find a no-translation visual that'll @@ -1521,7 +1543,7 @@ return g_old_error_handler(dpy, eev); } -BOOL +RD_BOOL ui_init(void) { int screen_num; @@ -1535,7 +1557,7 @@ { uint16 endianess_test = 1; - g_host_be = !(BOOL) (*(uint8 *) (&endianess_test)); + g_host_be = !(RD_BOOL) (*(uint8 *) (&endianess_test)); } g_old_error_handler = XSetErrorHandler(error_handler); @@ -1545,7 +1567,7 @@ g_screen = ScreenOfDisplay(g_display, screen_num); g_depth = DefaultDepthOfScreen(g_screen); - if (!select_visual()) + if (!select_visual(screen_num)) return False; if (g_no_translate_image) @@ -1616,6 +1638,7 @@ g_width = (g_width + 3) & ~3; g_mod_map = XGetModifierMapping(g_display); + xwin_refresh_pointer_map(); xkeymap_init(); @@ -1687,7 +1710,7 @@ *input_mask |= LeaveWindowMask; } -BOOL +RD_BOOL ui_create_window(void) { uint8 null_pointer_mask[1] = { 0x80 }; @@ -1735,6 +1758,7 @@ } XStoreName(g_display, g_wnd, g_title); + ewmh_set_wm_name(g_wnd, g_title); if (g_hide_decorations) mwm_hide_decorations(g_wnd); @@ -1874,10 +1898,16 @@ } static void -handle_button_event(XEvent xevent, BOOL down) +handle_button_event(XEvent xevent, RD_BOOL down) { uint16 button, flags = 0; g_last_gesturetime = xevent.xbutton.time; + /* Reverse the pointer button mapping, e.g. in the case of + "left-handed mouse mode"; the RDP session expects to + receive physical buttons (true in mstsc as well) and + logical button behavior depends on the remote desktop's own + mouse settings */ + xevent.xbutton.button = g_pointer_log_to_phys_map[xevent.xbutton.button - 1]; button = xkeymap_translate_button(xevent.xbutton.button); if (button == 0) return; @@ -2165,6 +2195,12 @@ XFreeModifiermap(g_mod_map); g_mod_map = XGetModifierMapping(g_display); } + + if (xevent.xmapping.request == MappingPointer) + { + xwin_refresh_pointer_map(); + } + break; /* clipboard stuff */ @@ -2248,7 +2284,7 @@ int n; fd_set rfds, wfds; struct timeval tv; - BOOL s_timeout = False; + RD_BOOL s_timeout = False; while (True) { @@ -2266,18 +2302,14 @@ FD_SET(rdp_socket, &rfds); FD_SET(g_x_socket, &rfds); -#ifdef WITH_RDPSND - /* FIXME: there should be an API for registering fds */ - if (g_dsp_busy) - { - FD_SET(g_dsp_fd, &wfds); - n = (g_dsp_fd > n) ? g_dsp_fd : n; - } -#endif /* default timeout */ tv.tv_sec = 60; tv.tv_usec = 0; +#ifdef WITH_RDPSND + rdpsnd_add_fds(&n, &rfds, &wfds, &tv); +#endif + /* add redirection handles */ rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout); seamless_select_timeout(&tv); @@ -2290,21 +2322,25 @@ error("select: %s\n", strerror(errno)); case 0: +#ifdef WITH_RDPSND + rdpsnd_check_fds(&rfds, &wfds); +#endif + /* Abort serial read calls */ if (s_timeout) - rdpdr_check_fds(&rfds, &wfds, (BOOL) True); + rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True); continue; } - rdpdr_check_fds(&rfds, &wfds, (BOOL) False); +#ifdef WITH_RDPSND + rdpsnd_check_fds(&rfds, &wfds); +#endif + + rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False); if (FD_ISSET(rdp_socket, &rfds)) return 1; -#ifdef WITH_RDPSND - if (g_dsp_busy && FD_ISSET(g_dsp_fd, &wfds)) - wave_out_play(); -#endif } } @@ -2314,7 +2350,7 @@ XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y); } -HBITMAP +RD_HBITMAP ui_create_bitmap(int width, int height, uint8 * data) { XImage *image; @@ -2344,7 +2380,7 @@ XFree(image); if (tdata != data) xfree(tdata); - return (HBITMAP) bitmap; + return (RD_HBITMAP) bitmap; } void @@ -2392,12 +2428,12 @@ } void -ui_destroy_bitmap(HBITMAP bmp) +ui_destroy_bitmap(RD_HBITMAP bmp) { XFreePixmap(g_display, (Pixmap) bmp); } -HGLYPH +RD_HGLYPH ui_create_glyph(int width, int height, uint8 * data) { XImage *image; @@ -2419,20 +2455,20 @@ XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height); XFree(image); - return (HGLYPH) bitmap; + return (RD_HGLYPH) bitmap; } void -ui_destroy_glyph(HGLYPH glyph) +ui_destroy_glyph(RD_HGLYPH glyph) { XFreePixmap(g_display, (Pixmap) glyph); } -HCURSOR +RD_HCURSOR ui_create_cursor(unsigned int x, unsigned int y, int width, int height, uint8 * andmask, uint8 * xormask) { - HGLYPH maskglyph, cursorglyph; + RD_HGLYPH maskglyph, cursorglyph; XColor bg, fg; Cursor xcursor; uint8 *cursor, *pcursor; @@ -2496,11 +2532,11 @@ ui_destroy_glyph(cursorglyph); xfree(mask); xfree(cursor); - return (HCURSOR) xcursor; + return (RD_HCURSOR) xcursor; } void -ui_set_cursor(HCURSOR cursor) +ui_set_cursor(RD_HCURSOR cursor) { g_current_cursor = (Cursor) cursor; XDefineCursor(g_display, g_wnd, g_current_cursor); @@ -2508,7 +2544,7 @@ } void -ui_destroy_cursor(HCURSOR cursor) +ui_destroy_cursor(RD_HCURSOR cursor) { XFreeCursor(g_display, (Cursor) cursor); } @@ -2526,7 +2562,7 @@ (xc)->flags = DoRed | DoGreen | DoBlue; -HCOLOURMAP +RD_HCOLOURMAP ui_create_colourmap(COLOURMAP * colours) { COLOURENTRY *entry; @@ -2622,12 +2658,12 @@ XStoreColors(g_display, map, xcolours, ncolours); xfree(xcolours); - return (HCOLOURMAP) map; + return (RD_HCOLOURMAP) map; } } void -ui_destroy_colourmap(HCOLOURMAP map) +ui_destroy_colourmap(RD_HCOLOURMAP map) { if (!g_owncolmap) xfree(map); @@ -2636,7 +2672,7 @@ } void -ui_set_colourmap(HCOLOURMAP map) +ui_set_colourmap(RD_HCOLOURMAP map) { if (!g_owncolmap) { @@ -2724,7 +2760,7 @@ FILL_RECTANGLE_BACKSTORE(x, y, cx, cy); XSetFillStyle(g_display, g_gc, FillSolid); XSetTSOrigin(g_display, g_gc, 0, 0); - ui_destroy_glyph((HGLYPH) fill); + ui_destroy_glyph((RD_HGLYPH) fill); break; case 3: /* Pattern */ @@ -2739,7 +2775,7 @@ FILL_RECTANGLE_BACKSTORE(x, y, cx, cy); XSetFillStyle(g_display, g_gc, FillSolid); XSetTSOrigin(g_display, g_gc, 0, 0); - ui_destroy_glyph((HGLYPH) fill); + ui_destroy_glyph((RD_HGLYPH) fill); break; default: @@ -2782,7 +2818,7 @@ void ui_memblt(uint8 opcode, /* dest */ int x, int y, int cx, int cy, - /* src */ HBITMAP src, int srcx, int srcy) + /* src */ RD_HBITMAP src, int srcx, int srcy) { SET_FUNCTION(opcode); XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y); @@ -2797,7 +2833,7 @@ void ui_triblt(uint8 opcode, /* dest */ int x, int y, int cx, int cy, - /* src */ HBITMAP src, int srcx, int srcy, + /* src */ RD_HBITMAP src, int srcx, int srcy, /* brush */ BRUSH * brush, int bgcolour, int fgcolour) { /* This is potentially difficult to do in general. Until someone @@ -2855,7 +2891,7 @@ void ui_polygon(uint8 opcode, /* mode */ uint8 fillmode, - /* dest */ POINT * point, int npoints, + /* dest */ RD_POINT * point, int npoints, /* brush */ BRUSH * brush, int bgcolour, int fgcolour) { uint8 style, i, ipattern[8]; @@ -2898,7 +2934,7 @@ FILL_POLYGON((XPoint *) point, npoints); XSetFillStyle(g_display, g_gc, FillSolid); XSetTSOrigin(g_display, g_gc, 0, 0); - ui_destroy_glyph((HGLYPH) fill); + ui_destroy_glyph((RD_HGLYPH) fill); break; case 3: /* Pattern */ @@ -2913,7 +2949,7 @@ FILL_POLYGON((XPoint *) point, npoints); XSetFillStyle(g_display, g_gc, FillSolid); XSetTSOrigin(g_display, g_gc, 0, 0); - ui_destroy_glyph((HGLYPH) fill); + ui_destroy_glyph((RD_HGLYPH) fill); break; default: @@ -2925,7 +2961,7 @@ void ui_polyline(uint8 opcode, - /* dest */ POINT * points, int npoints, + /* dest */ RD_POINT * points, int npoints, /* pen */ PEN * pen) { /* TODO: set join style */ @@ -2976,7 +3012,7 @@ DRAW_ELLIPSE(x, y, cx, cy, fillmode); XSetFillStyle(g_display, g_gc, FillSolid); XSetTSOrigin(g_display, g_gc, 0, 0); - ui_destroy_glyph((HGLYPH) fill); + ui_destroy_glyph((RD_HGLYPH) fill); break; case 3: /* Pattern */ @@ -2991,7 +3027,7 @@ DRAW_ELLIPSE(x, y, cx, cy, fillmode); XSetFillStyle(g_display, g_gc, FillSolid); XSetTSOrigin(g_display, g_gc, 0, 0); - ui_destroy_glyph((HGLYPH) fill); + ui_destroy_glyph((RD_HGLYPH) fill); break; default: @@ -3005,7 +3041,7 @@ void ui_draw_glyph(int mixmode, /* dest */ int x, int y, int cx, int cy, - /* src */ HGLYPH glyph, int srcx, int srcy, + /* src */ RD_HGLYPH glyph, int srcx, int srcy, int bgcolour, int fgcolour) { SET_FOREGROUND(fgcolour); @@ -3190,12 +3226,14 @@ if (g_ownbackstore) { image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap); + exit_if_null(image); } else { pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth); XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0); image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap); + exit_if_null(image); XFreePixmap(g_display, pix); } @@ -3217,7 +3255,7 @@ return; image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0, - (char *) data, cx, cy, BitmapPad(g_display), cx * g_bpp / 8); + (char *) data, cx, cy, g_bpp, 0); if (g_ownbackstore) { @@ -3251,7 +3289,7 @@ void -ui_seamless_begin(BOOL hidden) +ui_seamless_begin(RD_BOOL hidden) { if (!g_seamless_rdp) return; @@ -3419,15 +3457,13 @@ 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)); @@ -3494,6 +3530,93 @@ } +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) {