diff --git a/Makefile.in b/Makefile.in index 2960469..17d5522 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,7 +25,7 @@ LDVNC = @LDVNC@ VNCLINK = @VNCLINK@ SOUNDOBJ = @SOUNDOBJ@ -RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o +RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o @@ -82,7 +82,7 @@ proto: bitmap.c cache.c channels.c cliprdr.c disk.c mppc.c ewmhints.c \ iso.c licence.c mcs.c orders.c parallel.c printer.c printercache.c \ pstcache.c rdesktop.c rdp5.c rdp.c rdpdr.c rdpsnd.c rdpsnd_oss.c \ - secure.c serial.c tcp.c xclip.c xkeymap.c xwin.c lspci.c >> proto.h + secure.c serial.c tcp.c xclip.c xkeymap.c xwin.c lspci.c seamless.c >> proto.h cat proto.tail >> proto.h .PHONY: clean diff --git a/channels.c b/channels.c index 0cf83b9..71dc960 100644 --- a/channels.c +++ b/channels.c @@ -21,7 +21,7 @@ #include "rdesktop.h" -#define MAX_CHANNELS 5 +#define MAX_CHANNELS 6 #define CHANNEL_CHUNK_LENGTH 1600 #define CHANNEL_FLAG_FIRST 0x01 #define CHANNEL_FLAG_LAST 0x02 diff --git a/constants.h b/constants.h index cdeb443..e4d5aa1 100644 --- a/constants.h +++ b/constants.h @@ -412,3 +412,8 @@ enum RDP_INPUT_DEVICE #define exDiscReasonLicenseErrClientEncryption 0x0108 #define exDiscReasonLicenseCantUpgradeLicense 0x0109 #define exDiscReasonLicenseNoRemoteConnections 0x010a + +/* SeamlessRDP constants */ +#define SEAMLESSRDP_NORMAL 0 +#define SEAMLESSRDP_MINIMIZED 1 +#define SEAMLESSRDP_MAXIMIZED 2 diff --git a/proto.h b/proto.h index 4a1666a..e1e11c1 100644 --- a/proto.h +++ b/proto.h @@ -271,8 +271,18 @@ void ui_desktop_save(uint32 offset, int x, int y, int cx, int cy); void ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy); void ui_begin_update(void); void ui_end_update(void); +void ui_seamless_toggle(void); +void ui_seamless_create_window(unsigned long id, unsigned long flags); +void ui_seamless_destroy_window(unsigned long id, unsigned long flags); +void ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, + unsigned long flags); +void ui_seamless_settitle(unsigned long id, const char *title); +void ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags); /* lspci.c */ BOOL lspci_init(void); +/* seamless.c */ +BOOL seamless_init(void); +void seamless_send_sync(void); /* *INDENT-OFF* */ #ifdef __cplusplus diff --git a/rdesktop.c b/rdesktop.c index c75d27a..77965ee 100644 --- a/rdesktop.c +++ b/rdesktop.c @@ -89,6 +89,7 @@ BOOL g_numlock_sync = False; BOOL lspci_enabled = False; BOOL g_owncolmap = False; BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */ +BOOL g_seamless_rdp = False; uint32 g_embed_wnd; uint32 g_rdp5_performanceflags = RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS; @@ -146,6 +147,7 @@ usage(char *program) #ifdef HAVE_ICONV fprintf(stderr, " -L: local codepage\n"); #endif + fprintf(stderr, " -A: enable SeamlessRDP mode\n"); fprintf(stderr, " -B: use BackingStore of X-server (if available)\n"); fprintf(stderr, " -e: disable encryption (French TS)\n"); fprintf(stderr, " -E: disable encryption from client to server\n"); @@ -390,6 +392,7 @@ main(int argc, char *argv[]) int c; char *locale = NULL; int username_option = 0; + BOOL geometry_option = False; int run_count = 0; /* Session Directory support */ BOOL continue_connect = True; /* Session Directory support */ @@ -416,7 +419,7 @@ main(int argc, char *argv[]) #endif while ((c = getopt(argc, argv, - VNCOPT "u:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1) + VNCOPT "Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1) { switch (c) { @@ -434,6 +437,10 @@ main(int argc, char *argv[]) break; #endif + case 'A': + g_seamless_rdp = True; + break; + case 'u': STRNCPY(g_username, optarg, sizeof(g_username)); username_option = 1; @@ -484,6 +491,7 @@ main(int argc, char *argv[]) break; case 'g': + geometry_option = True; g_fullscreen = False; if (!strcmp(optarg, "workarea")) { @@ -732,6 +740,43 @@ main(int argc, char *argv[]) STRNCPY(server, argv[optind], sizeof(server)); parse_server_and_port(server); + if (g_seamless_rdp) + { + if (g_win_button_size) + { + error("You cannot use -S and -A at the same time\n"); + return 1; + } + g_rdp5_performanceflags &= ~RDP5_NO_FULLWINDOWDRAG; + if (geometry_option) + { + error("You cannot use -g and -A at the same time\n"); + return 1; + } + if (g_fullscreen) + { + error("You cannot use -f and -A at the same time\n"); + return 1; + } + if (g_hide_decorations) + { + error("You cannot use -D and -A at the same time\n"); + return 1; + } + if (g_embed_wnd) + { + error("You cannot use -X and -A at the same time\n"); + return 1; + } + if (!g_use_rdp5) + { + error("You cannot use -4 and -A at the same time\n"); + return 1; + } + g_width = -100; + g_grab_keyboard = False; + } + if (!username_option) { pw = getpwuid(getuid()); diff --git a/seamless.c b/seamless.c new file mode 100644 index 0000000..0b68fd3 --- /dev/null +++ b/seamless.c @@ -0,0 +1,252 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + Seamless Windows support + Copyright (C) Peter Astrand 2005-2006 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "rdesktop.h" + +/* #define WITH_DEBUG_SEAMLESS */ + +#ifdef WITH_DEBUG_SEAMLESS +#define DEBUG_SEAMLESS(args) printf args; +#else +#define DEBUG_SEAMLESS(args) +#endif + +extern BOOL g_seamless_rdp; +static VCHANNEL *seamless_channel; + +static char * +seamless_get_token(char **s) +{ + char *comma, *head; + head = *s; + + if (!head) + return NULL; + + comma = strchr(head, ','); + if (comma) + { + *comma = '\0'; + *s = comma + 1; + } + else + { + *s = NULL; + } + + return head; +} + + +static BOOL +seamless_process_line(const char *line, void *data) +{ + char *p, *l; + char *tok1, *tok2, *tok3, *tok4, *tok5, *tok6, *tok7, *tok8; + unsigned long id, flags; + char *endptr; + + l = xstrdup(line); + p = l; + + DEBUG_SEAMLESS(("seamlessrdp line:%s\n", p)); + + if (!g_seamless_rdp) + return True; + + tok1 = seamless_get_token(&p); + tok2 = seamless_get_token(&p); + tok3 = seamless_get_token(&p); + tok4 = seamless_get_token(&p); + tok5 = seamless_get_token(&p); + tok6 = seamless_get_token(&p); + tok7 = seamless_get_token(&p); + tok8 = seamless_get_token(&p); + + if (!strcmp("CREATE1", tok1)) + { + if (!tok3) + return False; + + id = strtol(tok2, &endptr, 16); + if (*endptr) + return False; + + flags = strtol(tok3, &endptr, 16); + if (*endptr) + return False; + + ui_seamless_create_window(id, flags); + } + else if (!strcmp("DESTROY1", tok1)) + { + if (!tok3) + return False; + + id = strtol(tok2, &endptr, 16); + if (*endptr) + return False; + + flags = strtol(tok3, &endptr, 16); + if (*endptr) + return False; + + ui_seamless_destroy_window(id, flags); + + } + else if (!strcmp("SETICON1", tok1)) + { + unimpl("SeamlessRDP SETICON1\n"); + } + else if (!strcmp("POSITION1", tok1)) + { + int x, y, width, height; + + if (!tok7) + return False; + + id = strtol(tok2, &endptr, 16); + if (*endptr) + return False; + + x = strtol(tok3, &endptr, 10); + if (*endptr) + return False; + y = strtol(tok4, &endptr, 10); + if (*endptr) + return False; + + width = strtol(tok5, &endptr, 10); + if (*endptr) + return False; + height = strtol(tok6, &endptr, 10); + if (*endptr) + return False; + + flags = strtol(tok7, &endptr, 16); + if (*endptr) + return False; + + ui_seamless_move_window(id, x, y, width, height, flags); + } + else if (!strcmp("ZCHANGE1", tok1)) + { + unimpl("SeamlessRDP ZCHANGE1\n"); + } + else if (!strcmp("SETSTATE1", tok1)) + { + unsigned int state; + + if (!tok5) + return False; + + id = strtol(tok2, &endptr, 16); + if (*endptr) + return False; + + state = strtol(tok4, &endptr, 16); // XXX + if (*endptr) + return False; + + flags = strtol(tok5, &endptr, 16); + if (*endptr) + return False; + + /* FIXME */ + ui_seamless_settitle(id, tok3); + ui_seamless_setstate(id, state, flags); + } + else if (!strcmp("DEBUG1", tok1)) + { + printf("SeamlessRDP:%s\n", line); + } + + xfree(l); + return True; +} + + +static BOOL +seamless_line_handler(const char *line, void *data) +{ + if (!seamless_process_line(line, data)) + { + warning("SeamlessRDP: Invalid request:%s\n", line); + } + return True; +} + + +static void +seamless_process(STREAM s) +{ + unsigned int pkglen; + static char *rest = NULL; + char *buf; + + pkglen = s->end - s->p; + /* str_handle_lines requires null terminated strings */ + buf = xmalloc(pkglen + 1); + STRNCPY(buf, (char *) s->p, pkglen + 1); +#if 0 + printf("seamless recv:\n"); + hexdump(s->p, pkglen); +#endif + + str_handle_lines(buf, &rest, seamless_line_handler, NULL); + + xfree(buf); +} + + +BOOL +seamless_init(void) +{ + seamless_channel = + channel_register("seamrdp", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP, + seamless_process); + return (seamless_channel != NULL); +} + + +static void +seamless_send(const char *output) +{ + STREAM s; + size_t len; + + len = strlen(output); + s = channel_init(seamless_channel, len); + out_uint8p(s, output, len) s_mark_end(s); + +#if 0 + printf("seamless send:\n"); + hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8); +#endif + + channel_send(s, seamless_channel); +} + + +void +seamless_send_sync() +{ + seamless_send("SYNC\n"); +} diff --git a/seamless.h b/seamless.h new file mode 100644 index 0000000..9660c26 --- /dev/null +++ b/seamless.h @@ -0,0 +1,19 @@ +/* + rdesktop: A Remote Desktop Protocol client. + Seamless Windows support + Copyright (C) Peter Astrand 2005-2006 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ diff --git a/xkeymap.c b/xkeymap.c index 0022ff4..997d03a 100644 --- a/xkeymap.c +++ b/xkeymap.c @@ -589,6 +589,11 @@ handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pres /* Inhibit */ return True; break; + case XK_Overlay1_Enable: + /* Toggle SeamlessRDP */ + if (pressed) + ui_seamless_toggle(); + break; } return False; diff --git a/xwin.c b/xwin.c index 1b62e9b..f093471 100644 --- a/xwin.c +++ b/xwin.c @@ -48,12 +48,26 @@ Time g_last_gesturetime; static int g_x_socket; static Screen *g_screen; Window g_wnd; + +/* SeamlessRDP support */ +typedef struct _seamless_window +{ + Window wnd; + unsigned long id; + int xoffset, yoffset; + int width, height; + struct _seamless_window *next; +} seamless_window; +static seamless_window *g_seamless_windows = NULL; +extern BOOL g_seamless_rdp; + extern uint32 g_embed_wnd; BOOL g_enable_compose = False; 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; +static XRectangle g_clip_rectangle; static Visual *g_visual; /* Color depth of the X11 visual of our window (e.g. 24 for True Color R8G8B visual). This may be 32 for R8G8B8 visuals, and then the rest of the bits are undefined @@ -133,10 +147,45 @@ typedef struct } PixelColour; +#define ON_ALL_SEAMLESS_WINDOWS(func, args) \ + do { \ + seamless_window *sw; \ + XRectangle rect; \ + for (sw = g_seamless_windows; sw; sw = sw->next) { \ + rect.x = g_clip_rectangle.x - sw->xoffset; \ + rect.y = g_clip_rectangle.y - sw->yoffset; \ + rect.width = g_clip_rectangle.width; \ + rect.height = g_clip_rectangle.height; \ + XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded); \ + func args; \ + } \ + XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded); \ + } while (0) + +static void +seamless_XFillPolygon(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset) +{ + points[0].x -= xoffset; + points[0].y -= yoffset; + XFillPolygon(g_display, d, g_gc, points, npoints, Complex, CoordModePrevious); + points[0].x += xoffset; + points[0].y += yoffset; +} + +static void +seamless_XDrawLines(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset) +{ + points[0].x -= xoffset; + points[0].y -= yoffset; + XDrawLines(g_display, d, g_gc, points, npoints, CoordModePrevious); + points[0].x += xoffset; + points[0].y += yoffset; +} #define FILL_RECTANGLE(x,y,cx,cy)\ { \ XFillRectangle(g_display, g_wnd, g_gc, x, y, cx, cy); \ + ON_ALL_SEAMLESS_WINDOWS(XFillRectangle, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy)); \ if (g_ownbackstore) \ XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \ } @@ -151,6 +200,7 @@ PixelColour; XFillPolygon(g_display, g_wnd, g_gc, p, np, Complex, CoordModePrevious); \ if (g_ownbackstore) \ XFillPolygon(g_display, g_backstore, g_gc, p, np, Complex, CoordModePrevious); \ + ON_ALL_SEAMLESS_WINDOWS(seamless_XFillPolygon, (sw->wnd, p, np, sw->xoffset, sw->yoffset)); \ } #define DRAW_ELLIPSE(x,y,cx,cy,m)\ @@ -159,11 +209,14 @@ PixelColour; { \ case 0: /* Outline */ \ XDrawArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \ + ON_ALL_SEAMLESS_WINDOWS(XDrawArc, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \ if (g_ownbackstore) \ XDrawArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \ break; \ case 1: /* Filled */ \ - XFillArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \ + 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)); \ if (g_ownbackstore) \ XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \ break; \ @@ -201,8 +254,52 @@ static int rop2_map[] = { #define SET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, rop2_map[rop2]); } #define RESET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); } +static seamless_window * +seamless_get_window_by_id(unsigned long id) +{ + seamless_window *sw; + for (sw = g_seamless_windows; sw; sw = sw->next) + { + if (sw->id == id) + return sw; + } + return NULL; +} + + +static seamless_window * +seamless_get_window_by_wnd(Window wnd) +{ + seamless_window *sw; + for (sw = g_seamless_windows; sw; sw = sw->next) + { + if (sw->wnd == wnd) + return sw; + } + return NULL; +} + + static void -mwm_hide_decorations(void) +seamless_remove_window(seamless_window * win) +{ + seamless_window *sw, **prevnext = &g_seamless_windows; + for (sw = g_seamless_windows; sw; sw = sw->next) + { + if (sw == win) + { + *prevnext = sw->next; + xfree(sw); + return; + } + prevnext = &sw->next; + } + return; +} + + +static void +mwm_hide_decorations(Window wnd) { PropMotifWmHints motif_hints; Atom hintsatom; @@ -219,8 +316,9 @@ mwm_hide_decorations(void) return; } - XChangeProperty(g_display, g_wnd, hintsatom, hintsatom, 32, PropModeReplace, + XChangeProperty(g_display, wnd, hintsatom, hintsatom, 32, PropModeReplace, (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS); + } #define SPLITCOLOUR15(colour, rv) \ @@ -1298,6 +1396,8 @@ ui_init(void) g_IM = XOpenIM(g_display, NULL, NULL, NULL); xclip_init(); + if (g_seamless_rdp) + seamless_init(); DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", g_server_depth, g_bpp, g_depth)); @@ -1323,6 +1423,34 @@ ui_deinit(void) g_display = NULL; } + +static void +get_window_attribs(XSetWindowAttributes * attribs) +{ + attribs->background_pixel = BlackPixelOfScreen(g_screen); + attribs->background_pixel = WhitePixelOfScreen(g_screen); + attribs->border_pixel = WhitePixelOfScreen(g_screen); + attribs->backing_store = g_ownbackstore ? NotUseful : Always; + attribs->override_redirect = g_fullscreen; + attribs->colormap = g_xcolmap; +} + +static void +get_input_mask(long *input_mask) +{ + *input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | + VisibilityChangeMask | FocusChangeMask | StructureNotifyMask; + + if (g_sendmotion) + *input_mask |= PointerMotionMask; + if (g_ownbackstore) + *input_mask |= ExposureMask; + if (g_fullscreen || g_grab_keyboard) + *input_mask |= EnterWindowMask; + if (g_grab_keyboard) + *input_mask |= LeaveWindowMask; +} + BOOL ui_create_window(void) { @@ -1345,11 +1473,7 @@ ui_create_window(void) if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4))) g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height; - attribs.background_pixel = BlackPixelOfScreen(g_screen); - attribs.border_pixel = WhitePixelOfScreen(g_screen); - attribs.backing_store = g_ownbackstore ? NotUseful : Always; - attribs.override_redirect = g_fullscreen; - attribs.colormap = g_xcolmap; + get_window_attribs(&attribs); g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth, wndheight, 0, g_depth, InputOutput, g_visual, @@ -1357,7 +1481,10 @@ ui_create_window(void) CWBorderPixel, &attribs); if (g_gc == NULL) + { g_gc = XCreateGC(g_display, g_wnd, 0, NULL); + ui_reset_clip(); + } if (g_create_bitmap_gc == NULL) g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL); @@ -1374,7 +1501,7 @@ ui_create_window(void) XStoreName(g_display, g_wnd, g_title); if (g_hide_decorations) - mwm_hide_decorations(); + mwm_hide_decorations(g_wnd); classhints = XAllocClassHint(); if (classhints != NULL) @@ -1401,17 +1528,7 @@ ui_create_window(void) XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0); } - input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | - VisibilityChangeMask | FocusChangeMask | StructureNotifyMask; - - if (g_sendmotion) - input_mask |= PointerMotionMask; - if (g_ownbackstore) - input_mask |= ExposureMask; - if (g_fullscreen || g_grab_keyboard) - input_mask |= EnterWindowMask; - if (g_grab_keyboard) - input_mask |= LeaveWindowMask; + get_input_mask(&input_mask); if (g_IM != NULL) { @@ -1424,15 +1541,17 @@ ui_create_window(void) } XSelectInput(g_display, g_wnd, input_mask); - XMapWindow(g_display, g_wnd); - - /* wait for VisibilityNotify */ - do + if (!g_seamless_rdp) { - XMaskEvent(g_display, VisibilityChangeMask, &xevent); + XMapWindow(g_display, g_wnd); + /* wait for VisibilityNotify */ + do + { + XMaskEvent(g_display, VisibilityChangeMask, &xevent); + } + while (xevent.type != VisibilityNotify); + g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured; } - while (xevent.type != VisibilityNotify); - g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured; g_focused = False; g_mouse_in_wnd = False; @@ -1496,6 +1615,10 @@ xwin_toggle_fullscreen(void) { Pixmap contents = 0; + if (g_seamless_rdp) + /* Turn off SeamlessRDP mode */ + ui_seamless_toggle(); + if (!g_ownbackstore) { /* need to save contents of window */ @@ -1584,8 +1707,17 @@ handle_button_event(XEvent xevent, BOOL down) } } - rdp_send_input(time(NULL), RDP_INPUT_MOUSE, - flags | button, xevent.xbutton.x, xevent.xbutton.y); + if (xevent.xmotion.window == g_wnd) + { + rdp_send_input(time(NULL), RDP_INPUT_MOUSE, + flags | button, xevent.xbutton.x, xevent.xbutton.y); + } + else + { + /* SeamlessRDP */ + rdp_send_input(time(NULL), RDP_INPUT_MOUSE, + flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root); + } } /* Process events in Xlib queue @@ -1613,7 +1745,10 @@ xwin_process_events(void) switch (xevent.type) { case VisibilityNotify: - g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured; + if (xevent.xvisibility.window == g_wnd) + g_Unobscured = + xevent.xvisibility.state == VisibilityUnobscured; + break; case ClientMessage: /* the window manager told us to quit */ @@ -1693,8 +1828,19 @@ xwin_process_events(void) if (g_fullscreen && !g_focused) XSetInputFocus(g_display, g_wnd, RevertToPointerRoot, CurrentTime); - rdp_send_input(time(NULL), RDP_INPUT_MOUSE, - MOUSE_FLAG_MOVE, xevent.xmotion.x, xevent.xmotion.y); + + if (xevent.xmotion.window == g_wnd) + { + rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE, + xevent.xmotion.x, xevent.xmotion.y); + } + else + { + /* SeamlessRDP */ + rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE, + xevent.xmotion.x_root, + xevent.xmotion.y_root); + } break; case FocusIn: @@ -1737,11 +1883,28 @@ xwin_process_events(void) break; case Expose: - XCopyArea(g_display, g_backstore, g_wnd, g_gc, - xevent.xexpose.x, xevent.xexpose.y, - xevent.xexpose.width, - xevent.xexpose.height, - xevent.xexpose.x, xevent.xexpose.y); + if (xevent.xexpose.window == g_wnd) + { + XCopyArea(g_display, g_backstore, xevent.xexpose.window, + g_gc, + xevent.xexpose.x, xevent.xexpose.y, + xevent.xexpose.width, xevent.xexpose.height, + xevent.xexpose.x, xevent.xexpose.y); + } + else + { + seamless_window *sw; + sw = seamless_get_window_by_wnd(xevent.xexpose.window); + if (sw) + XCopyArea(g_display, g_backstore, + xevent.xexpose.window, g_gc, + xevent.xexpose.x + sw->xoffset, + xevent.xexpose.y + sw->yoffset, + xevent.xexpose.width, + xevent.xexpose.height, xevent.xexpose.x, + xevent.xexpose.y); + } + break; case MappingNotify: @@ -1772,10 +1935,12 @@ xwin_process_events(void) xclip_handle_PropertyNotify(&xevent.xproperty); break; case MapNotify: - rdp_send_client_window_status(1); + if (!g_seamless_rdp) + rdp_send_client_window_status(1); break; case UnmapNotify: - rdp_send_client_window_status(0); + if (!g_seamless_rdp) + rdp_send_client_window_status(0); break; } } @@ -1912,10 +2077,16 @@ ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * dat { XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy); XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y); + ON_ALL_SEAMLESS_WINDOWS(XCopyArea, + (g_display, g_backstore, sw->wnd, g_gc, x, y, cx, cy, + x - sw->xoffset, y - sw->yoffset)); } else { XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy); + ON_ALL_SEAMLESS_WINDOWS(XCopyArea, + (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy, + x - sw->xoffset, y - sw->yoffset)); } XFree(image); @@ -2036,6 +2207,7 @@ ui_set_cursor(HCURSOR cursor) { g_current_cursor = (Cursor) cursor; XDefineCursor(g_display, g_wnd, g_current_cursor); + ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor)); } void @@ -2177,31 +2349,30 @@ ui_set_colourmap(HCOLOURMAP map) g_colmap = (uint32 *) map; } else + { XSetWindowColormap(g_display, g_wnd, (Colormap) map); + ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map)); + } } void ui_set_clip(int x, int y, int cx, int cy) { - XRectangle rect; - - rect.x = x; - rect.y = y; - rect.width = cx; - rect.height = cy; - XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded); + g_clip_rectangle.x = x; + g_clip_rectangle.y = y; + g_clip_rectangle.width = cx; + g_clip_rectangle.height = cy; + XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded); } void ui_reset_clip(void) { - XRectangle rect; - - rect.x = 0; - rect.y = 0; - rect.width = g_width; - rect.height = g_height; - XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded); + g_clip_rectangle.x = 0; + g_clip_rectangle.y = 0; + g_clip_rectangle.width = g_width; + g_clip_rectangle.height = g_height; + XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded); } void @@ -2282,6 +2453,9 @@ ui_patblt(uint8 opcode, if (g_ownbackstore) XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y); + 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)); } void @@ -2292,23 +2466,19 @@ ui_screenblt(uint8 opcode, SET_FUNCTION(opcode); if (g_ownbackstore) { - if (g_Unobscured) - { - XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y); - XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, - y); - } - else - { - XCopyArea(g_display, g_backstore, g_wnd, g_gc, srcx, srcy, cx, cy, x, y); - XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, - y); - } + XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore, + g_wnd, g_gc, srcx, srcy, cx, cy, x, y); + XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y); } else { XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y); } + + 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)); + RESET_FUNCTION(opcode); } @@ -2319,6 +2489,9 @@ ui_memblt(uint8 opcode, { SET_FUNCTION(opcode); XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y); + ON_ALL_SEAMLESS_WINDOWS(XCopyArea, + (g_display, (Pixmap) src, sw->wnd, g_gc, + srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset)); if (g_ownbackstore) XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y); RESET_FUNCTION(opcode); @@ -2365,6 +2538,9 @@ ui_line(uint8 opcode, SET_FUNCTION(opcode); SET_FOREGROUND(pen->colour); XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy); + ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc, + startx - sw->xoffset, starty - sw->yoffset, + endx - sw->xoffset, endy - sw->yoffset)); if (g_ownbackstore) XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy); RESET_FUNCTION(opcode); @@ -2462,6 +2638,10 @@ ui_polyline(uint8 opcode, if (g_ownbackstore) XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints, CoordModePrevious); + + ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines, + (sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset)); + RESET_FUNCTION(opcode); } @@ -2682,11 +2862,25 @@ ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y, if (g_ownbackstore) { if (boxcx > 1) + { XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx, boxy, boxcx, boxcy, boxx, boxy); + ON_ALL_SEAMLESS_WINDOWS(XCopyArea, + (g_display, g_backstore, sw->wnd, g_gc, + boxx, boxy, + boxcx, boxcy, + boxx - sw->xoffset, boxy - sw->yoffset)); + } else + { XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx, clipy, clipcx, clipcy, clipx, clipy); + ON_ALL_SEAMLESS_WINDOWS(XCopyArea, + (g_display, g_backstore, sw->wnd, g_gc, + clipx, clipy, + clipcx, clipcy, clipx - sw->xoffset, + clipy - sw->yoffset)); + } } } @@ -2732,10 +2926,16 @@ ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy) { XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy); XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y); + ON_ALL_SEAMLESS_WINDOWS(XCopyArea, + (g_display, g_backstore, sw->wnd, g_gc, + x, y, cx, cy, x - sw->xoffset, y - sw->yoffset)); } else { XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy); + ON_ALL_SEAMLESS_WINDOWS(XCopyArea, + (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy, + x - sw->xoffset, y - sw->yoffset)); } XFree(image); @@ -2751,3 +2951,178 @@ void ui_end_update(void) { } + +void +ui_seamless_toggle() +{ + if (g_seamless_rdp) + { + /* Deactivate */ + while (g_seamless_windows) + { + XDestroyWindow(g_display, g_seamless_windows->wnd); + seamless_remove_window(g_seamless_windows); + } + XMapWindow(g_display, g_wnd); + } + else + { + /* Activate */ + if (g_win_button_size) + { + error("SeamlessRDP mode cannot be activated when using single application mode\n"); + return; + } + if (!g_using_full_workarea) + { + error("SeamlessRDP mode requires a session that covers the whole screen"); + return; + } + + XUnmapWindow(g_display, g_wnd); + seamless_send_sync(); + } + + g_seamless_rdp = !g_seamless_rdp; +} + +void +ui_seamless_create_window(unsigned long id, unsigned long flags) +{ + Window wnd; + XSetWindowAttributes attribs; + XClassHint *classhints; + long input_mask; + seamless_window *sw; + + get_window_attribs(&attribs); + + attribs.override_redirect = False; + + /* FIXME: Do not assume that -1, -1 is outside screen Consider + wait with showing the window until STATE and others have + been recieved. */ + wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, 0, + InputOutput, g_visual, + CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap | + CWBorderPixel, &attribs); + + XStoreName(g_display, wnd, "rdesktop-seamless"); + + mwm_hide_decorations(wnd); + + classhints = XAllocClassHint(); + if (classhints != NULL) + { + classhints->res_name = classhints->res_class = "rdesktop"; + XSetClassHint(g_display, wnd, classhints); + XFree(classhints); + } + + /* FIXME: Support for Input Context:s */ + + get_input_mask(&input_mask); + + XSelectInput(g_display, wnd, input_mask); + + XMapWindow(g_display, wnd); + + /* handle the WM_DELETE_WINDOW protocol. FIXME: When killing a + seamless window, we could try to close the window on the + serverside, instead of terminating rdesktop */ + XSetWMProtocols(g_display, wnd, &g_kill_atom, 1); + + sw = malloc(sizeof(seamless_window)); + sw->wnd = wnd; + sw->id = id; + sw->xoffset = 0; + sw->yoffset = 0; + sw->width = 0; + sw->height = 0; + sw->next = g_seamless_windows; + g_seamless_windows = sw; +} + + +void +ui_seamless_destroy_window(unsigned long id, unsigned long flags) +{ + seamless_window *sw; + sw = seamless_get_window_by_id(id); + + if (!sw) + { + warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id); + return; + } + + XDestroyWindow(g_display, sw->wnd); + seamless_remove_window(sw); +} + + +void +ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags) +{ + seamless_window *sw; + + sw = seamless_get_window_by_id(id); + + if (!sw) + { + warning("ui_seamless_move_window: No information for window 0x%lx\n", id); + return; + } + + if (!width || !height) + /* X11 windows must be at least 1x1 */ + return; + + /* About MAX and MIN: Windows allows moving a window outside + the desktop. This happens, for example, when maximizing an + application. In this case, the position is set to something + like -4,-4,1288,1032. Many WMs does not allow windows + outside the desktop, however. Therefore, clip the window + ourselves. */ + sw->xoffset = MAX(0, x); + sw->yoffset = MAX(0, y); + sw->width = MIN(MIN(width, width + x), g_width - sw->xoffset); + sw->height = MIN(MIN(height, height + y), g_height - sw->yoffset); + + /* FIXME: Perhaps use ewmh_net_moveresize_window instead */ + XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height); +} + + +void +ui_seamless_settitle(unsigned long id, const char *title) +{ + seamless_window *sw; + + sw = seamless_get_window_by_id(id); + + XStoreName(g_display, sw->wnd, title); +} + + +void +ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags) +{ + seamless_window *sw; + + sw = seamless_get_window_by_id(id); + + switch (state) + { + case SEAMLESSRDP_NORMAL: + case SEAMLESSRDP_MAXIMIZED: + /* FIXME */ + break; + case SEAMLESSRDP_MINIMIZED: + XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display)); + break; + default: + warning("SeamlessRDP: Invalid state %d\n", state); + break; + } +}