Initial support for SeamlessRDP

git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/branches/seamlessrdp-branch/rdesktop@1088 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
Peter Åstrand 2006-03-10 08:50:43 +00:00
parent fddda415b1
commit b06a70bf51
9 changed files with 780 additions and 69 deletions

View File

@ -25,7 +25,7 @@ LDVNC = @LDVNC@
VNCLINK = @VNCLINK@ VNCLINK = @VNCLINK@
SOUNDOBJ = @SOUNDOBJ@ 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 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 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 \ 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 \ 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 \ 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 cat proto.tail >> proto.h
.PHONY: clean .PHONY: clean

View File

@ -21,7 +21,7 @@
#include "rdesktop.h" #include "rdesktop.h"
#define MAX_CHANNELS 5 #define MAX_CHANNELS 6
#define CHANNEL_CHUNK_LENGTH 1600 #define CHANNEL_CHUNK_LENGTH 1600
#define CHANNEL_FLAG_FIRST 0x01 #define CHANNEL_FLAG_FIRST 0x01
#define CHANNEL_FLAG_LAST 0x02 #define CHANNEL_FLAG_LAST 0x02

View File

@ -412,3 +412,8 @@ enum RDP_INPUT_DEVICE
#define exDiscReasonLicenseErrClientEncryption 0x0108 #define exDiscReasonLicenseErrClientEncryption 0x0108
#define exDiscReasonLicenseCantUpgradeLicense 0x0109 #define exDiscReasonLicenseCantUpgradeLicense 0x0109
#define exDiscReasonLicenseNoRemoteConnections 0x010a #define exDiscReasonLicenseNoRemoteConnections 0x010a
/* SeamlessRDP constants */
#define SEAMLESSRDP_NORMAL 0
#define SEAMLESSRDP_MINIMIZED 1
#define SEAMLESSRDP_MAXIMIZED 2

10
proto.h
View File

@ -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_desktop_restore(uint32 offset, int x, int y, int cx, int cy);
void ui_begin_update(void); void ui_begin_update(void);
void ui_end_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 */ /* lspci.c */
BOOL lspci_init(void); BOOL lspci_init(void);
/* seamless.c */
BOOL seamless_init(void);
void seamless_send_sync(void);
/* *INDENT-OFF* */ /* *INDENT-OFF* */
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -89,6 +89,7 @@ BOOL g_numlock_sync = False;
BOOL lspci_enabled = False; BOOL lspci_enabled = False;
BOOL g_owncolmap = False; BOOL g_owncolmap = False;
BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */ BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */
BOOL g_seamless_rdp = False;
uint32 g_embed_wnd; uint32 g_embed_wnd;
uint32 g_rdp5_performanceflags = uint32 g_rdp5_performanceflags =
RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS; RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS;
@ -146,6 +147,7 @@ usage(char *program)
#ifdef HAVE_ICONV #ifdef HAVE_ICONV
fprintf(stderr, " -L: local codepage\n"); fprintf(stderr, " -L: local codepage\n");
#endif #endif
fprintf(stderr, " -A: enable SeamlessRDP mode\n");
fprintf(stderr, " -B: use BackingStore of X-server (if available)\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 (French TS)\n");
fprintf(stderr, " -E: disable encryption from client to server\n"); fprintf(stderr, " -E: disable encryption from client to server\n");
@ -390,6 +392,7 @@ main(int argc, char *argv[])
int c; int c;
char *locale = NULL; char *locale = NULL;
int username_option = 0; int username_option = 0;
BOOL geometry_option = False;
int run_count = 0; /* Session Directory support */ int run_count = 0; /* Session Directory support */
BOOL continue_connect = True; /* Session Directory support */ BOOL continue_connect = True; /* Session Directory support */
@ -416,7 +419,7 @@ main(int argc, char *argv[])
#endif #endif
while ((c = getopt(argc, argv, 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) switch (c)
{ {
@ -434,6 +437,10 @@ main(int argc, char *argv[])
break; break;
#endif #endif
case 'A':
g_seamless_rdp = True;
break;
case 'u': case 'u':
STRNCPY(g_username, optarg, sizeof(g_username)); STRNCPY(g_username, optarg, sizeof(g_username));
username_option = 1; username_option = 1;
@ -484,6 +491,7 @@ main(int argc, char *argv[])
break; break;
case 'g': case 'g':
geometry_option = True;
g_fullscreen = False; g_fullscreen = False;
if (!strcmp(optarg, "workarea")) if (!strcmp(optarg, "workarea"))
{ {
@ -732,6 +740,43 @@ main(int argc, char *argv[])
STRNCPY(server, argv[optind], sizeof(server)); STRNCPY(server, argv[optind], sizeof(server));
parse_server_and_port(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) if (!username_option)
{ {
pw = getpwuid(getuid()); pw = getpwuid(getuid());

252
seamless.c Normal file
View File

@ -0,0 +1,252 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Seamless Windows support
Copyright (C) Peter Astrand <astrand@cendio.se> 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");
}

19
seamless.h Normal file
View File

@ -0,0 +1,19 @@
/*
rdesktop: A Remote Desktop Protocol client.
Seamless Windows support
Copyright (C) Peter Astrand <astrand@cendio.se> 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.
*/

View File

@ -589,6 +589,11 @@ handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pres
/* Inhibit */ /* Inhibit */
return True; return True;
break; break;
case XK_Overlay1_Enable:
/* Toggle SeamlessRDP */
if (pressed)
ui_seamless_toggle();
break;
} }
return False; return False;

481
xwin.c
View File

@ -48,12 +48,26 @@ Time g_last_gesturetime;
static int g_x_socket; static int g_x_socket;
static Screen *g_screen; static Screen *g_screen;
Window g_wnd; 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; extern uint32 g_embed_wnd;
BOOL g_enable_compose = False; BOOL g_enable_compose = False;
BOOL g_Unobscured; /* used for screenblt */ BOOL g_Unobscured; /* used for screenblt */
static GC g_gc = NULL; static GC g_gc = NULL;
static GC g_create_bitmap_gc = NULL; static GC g_create_bitmap_gc = NULL;
static GC g_create_glyph_gc = NULL; static GC g_create_glyph_gc = NULL;
static XRectangle g_clip_rectangle;
static Visual *g_visual; static Visual *g_visual;
/* Color depth of the X11 visual of our window (e.g. 24 for True Color R8G8B 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 This may be 32 for R8G8B8 visuals, and then the rest of the bits are undefined
@ -133,10 +147,45 @@ typedef struct
} }
PixelColour; 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)\ #define FILL_RECTANGLE(x,y,cx,cy)\
{ \ { \
XFillRectangle(g_display, g_wnd, g_gc, 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) \ if (g_ownbackstore) \
XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \ 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); \ XFillPolygon(g_display, g_wnd, g_gc, p, np, Complex, CoordModePrevious); \
if (g_ownbackstore) \ if (g_ownbackstore) \
XFillPolygon(g_display, g_backstore, g_gc, p, np, Complex, CoordModePrevious); \ 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)\ #define DRAW_ELLIPSE(x,y,cx,cy,m)\
@ -159,11 +209,14 @@ PixelColour;
{ \ { \
case 0: /* Outline */ \ case 0: /* Outline */ \
XDrawArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \ 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) \ if (g_ownbackstore) \
XDrawArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \ XDrawArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
break; \ break; \
case 1: /* Filled */ \ 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) \ if (g_ownbackstore) \
XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \ XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
break; \ 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 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); } #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 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; PropMotifWmHints motif_hints;
Atom hintsatom; Atom hintsatom;
@ -219,8 +316,9 @@ mwm_hide_decorations(void)
return; 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); (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
} }
#define SPLITCOLOUR15(colour, rv) \ #define SPLITCOLOUR15(colour, rv) \
@ -1298,6 +1396,8 @@ ui_init(void)
g_IM = XOpenIM(g_display, NULL, NULL, NULL); g_IM = XOpenIM(g_display, NULL, NULL, NULL);
xclip_init(); 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)); 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; 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 BOOL
ui_create_window(void) ui_create_window(void)
{ {
@ -1345,11 +1473,7 @@ ui_create_window(void)
if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4))) if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4)))
g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height; g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height;
attribs.background_pixel = BlackPixelOfScreen(g_screen); get_window_attribs(&attribs);
attribs.border_pixel = WhitePixelOfScreen(g_screen);
attribs.backing_store = g_ownbackstore ? NotUseful : Always;
attribs.override_redirect = g_fullscreen;
attribs.colormap = g_xcolmap;
g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth, g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
wndheight, 0, g_depth, InputOutput, g_visual, wndheight, 0, g_depth, InputOutput, g_visual,
@ -1357,7 +1481,10 @@ ui_create_window(void)
CWBorderPixel, &attribs); CWBorderPixel, &attribs);
if (g_gc == NULL) if (g_gc == NULL)
{
g_gc = XCreateGC(g_display, g_wnd, 0, NULL); g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
ui_reset_clip();
}
if (g_create_bitmap_gc == NULL) if (g_create_bitmap_gc == NULL)
g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, 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); XStoreName(g_display, g_wnd, g_title);
if (g_hide_decorations) if (g_hide_decorations)
mwm_hide_decorations(); mwm_hide_decorations(g_wnd);
classhints = XAllocClassHint(); classhints = XAllocClassHint();
if (classhints != NULL) if (classhints != NULL)
@ -1401,17 +1528,7 @@ ui_create_window(void)
XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0); XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);
} }
input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | get_input_mask(&input_mask);
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;
if (g_IM != NULL) if (g_IM != NULL)
{ {
@ -1424,8 +1541,9 @@ ui_create_window(void)
} }
XSelectInput(g_display, g_wnd, input_mask); XSelectInput(g_display, g_wnd, input_mask);
if (!g_seamless_rdp)
{
XMapWindow(g_display, g_wnd); XMapWindow(g_display, g_wnd);
/* wait for VisibilityNotify */ /* wait for VisibilityNotify */
do do
{ {
@ -1433,6 +1551,7 @@ ui_create_window(void)
} }
while (xevent.type != VisibilityNotify); while (xevent.type != VisibilityNotify);
g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured; g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
}
g_focused = False; g_focused = False;
g_mouse_in_wnd = False; g_mouse_in_wnd = False;
@ -1496,6 +1615,10 @@ xwin_toggle_fullscreen(void)
{ {
Pixmap contents = 0; Pixmap contents = 0;
if (g_seamless_rdp)
/* Turn off SeamlessRDP mode */
ui_seamless_toggle();
if (!g_ownbackstore) if (!g_ownbackstore)
{ {
/* need to save contents of window */ /* need to save contents of window */
@ -1584,8 +1707,17 @@ handle_button_event(XEvent xevent, BOOL down)
} }
} }
if (xevent.xmotion.window == g_wnd)
{
rdp_send_input(time(NULL), RDP_INPUT_MOUSE, rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
flags | button, xevent.xbutton.x, xevent.xbutton.y); 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 /* Process events in Xlib queue
@ -1613,7 +1745,10 @@ xwin_process_events(void)
switch (xevent.type) switch (xevent.type)
{ {
case VisibilityNotify: case VisibilityNotify:
g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured; if (xevent.xvisibility.window == g_wnd)
g_Unobscured =
xevent.xvisibility.state == VisibilityUnobscured;
break; break;
case ClientMessage: case ClientMessage:
/* the window manager told us to quit */ /* the window manager told us to quit */
@ -1693,8 +1828,19 @@ xwin_process_events(void)
if (g_fullscreen && !g_focused) if (g_fullscreen && !g_focused)
XSetInputFocus(g_display, g_wnd, RevertToPointerRoot, XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
CurrentTime); 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; break;
case FocusIn: case FocusIn:
@ -1737,11 +1883,28 @@ xwin_process_events(void)
break; break;
case Expose: case Expose:
XCopyArea(g_display, g_backstore, g_wnd, g_gc, if (xevent.xexpose.window == g_wnd)
{
XCopyArea(g_display, g_backstore, xevent.xexpose.window,
g_gc,
xevent.xexpose.x, xevent.xexpose.y, xevent.xexpose.x, xevent.xexpose.y,
xevent.xexpose.width, xevent.xexpose.width, xevent.xexpose.height,
xevent.xexpose.height,
xevent.xexpose.x, xevent.xexpose.y); 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; break;
case MappingNotify: case MappingNotify:
@ -1772,9 +1935,11 @@ xwin_process_events(void)
xclip_handle_PropertyNotify(&xevent.xproperty); xclip_handle_PropertyNotify(&xevent.xproperty);
break; break;
case MapNotify: case MapNotify:
if (!g_seamless_rdp)
rdp_send_client_window_status(1); rdp_send_client_window_status(1);
break; break;
case UnmapNotify: case UnmapNotify:
if (!g_seamless_rdp)
rdp_send_client_window_status(0); rdp_send_client_window_status(0);
break; 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); 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); 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 else
{ {
XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy); 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); XFree(image);
@ -2036,6 +2207,7 @@ ui_set_cursor(HCURSOR cursor)
{ {
g_current_cursor = (Cursor) cursor; g_current_cursor = (Cursor) cursor;
XDefineCursor(g_display, g_wnd, g_current_cursor); XDefineCursor(g_display, g_wnd, g_current_cursor);
ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor));
} }
void void
@ -2177,31 +2349,30 @@ ui_set_colourmap(HCOLOURMAP map)
g_colmap = (uint32 *) map; g_colmap = (uint32 *) map;
} }
else else
{
XSetWindowColormap(g_display, g_wnd, (Colormap) map); XSetWindowColormap(g_display, g_wnd, (Colormap) map);
ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
}
} }
void void
ui_set_clip(int x, int y, int cx, int cy) ui_set_clip(int x, int y, int cx, int cy)
{ {
XRectangle rect; g_clip_rectangle.x = x;
g_clip_rectangle.y = y;
rect.x = x; g_clip_rectangle.width = cx;
rect.y = y; g_clip_rectangle.height = cy;
rect.width = cx; XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
rect.height = cy;
XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);
} }
void void
ui_reset_clip(void) ui_reset_clip(void)
{ {
XRectangle rect; g_clip_rectangle.x = 0;
g_clip_rectangle.y = 0;
rect.x = 0; g_clip_rectangle.width = g_width;
rect.y = 0; g_clip_rectangle.height = g_height;
rect.width = g_width; XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
rect.height = g_height;
XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);
} }
void void
@ -2282,6 +2453,9 @@ ui_patblt(uint8 opcode,
if (g_ownbackstore) if (g_ownbackstore)
XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y); 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 void
@ -2292,23 +2466,19 @@ ui_screenblt(uint8 opcode,
SET_FUNCTION(opcode); SET_FUNCTION(opcode);
if (g_ownbackstore) if (g_ownbackstore)
{ {
if (g_Unobscured) XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
{ g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
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);
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);
}
} }
else else
{ {
XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y); 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); RESET_FUNCTION(opcode);
} }
@ -2319,6 +2489,9 @@ ui_memblt(uint8 opcode,
{ {
SET_FUNCTION(opcode); SET_FUNCTION(opcode);
XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y); 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) if (g_ownbackstore)
XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y); XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
RESET_FUNCTION(opcode); RESET_FUNCTION(opcode);
@ -2365,6 +2538,9 @@ ui_line(uint8 opcode,
SET_FUNCTION(opcode); SET_FUNCTION(opcode);
SET_FOREGROUND(pen->colour); SET_FOREGROUND(pen->colour);
XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy); 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) if (g_ownbackstore)
XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy); XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
RESET_FUNCTION(opcode); RESET_FUNCTION(opcode);
@ -2462,6 +2638,10 @@ ui_polyline(uint8 opcode,
if (g_ownbackstore) if (g_ownbackstore)
XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints, XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
CoordModePrevious); CoordModePrevious);
ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
(sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
RESET_FUNCTION(opcode); 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 (g_ownbackstore)
{ {
if (boxcx > 1) if (boxcx > 1)
{
XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx, XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
boxy, boxcx, boxcy, boxx, boxy); 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 else
{
XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx, XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
clipy, clipcx, clipcy, clipx, clipy); 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); 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); 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 else
{ {
XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy); 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); XFree(image);
@ -2751,3 +2951,178 @@ void
ui_end_update(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;
}
}