CVS: rdesktop seamless.c,NONE,1.1.2.1 seamless.h,NONE,1.1.2.1 Makefile.in,1.13,1.13.2.1 channels.c,1.15,1.15.2.1 constants.h,1.45,1.45.2.1 proto.h,1.87,1.87.2.1 rdesktop.c,1.144,1.144.2.1 xkeymap.c,1.88,1.88.2.1 xwin.c,1.205,1.205.2.1

Peter Åstrand <[email protected]>
Newsgroups gmane.network.rdesktop.cvs
Message-ID <[email protected]>
Update of /cvsroot/rdesktop/rdesktop
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22293

Modified Files:
      Tag: seamlessrdp-branch
	Makefile.in channels.c constants.h proto.h rdesktop.c 
	xkeymap.c xwin.c 
Added Files:
      Tag: seamlessrdp-branch
	seamless.c seamless.h 
Log Message:
Initial support for SeamlessRDP

--- NEW FILE: seamless.c ---
/* -*- c-basic-offset: 8 -*-
   rdesktop: A Remote Desktop Protocol client.
   Seamless Windows support
   Copyright (C) Peter Astrand <[email protected]> 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");
}

--- NEW FILE: seamless.h ---
/*
   rdesktop: A Remote Desktop Protocol client.
   Seamless Windows support
   Copyright (C) Peter Astrand <[email protected]> 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.
*/

Index: Makefile.in
===================================================================
RCS file: /cvsroot/rdesktop/rdesktop/Makefile.in,v
retrieving revision 1.13
retrieving revision 1.13.2.1
diff -C2 -d -r1.13 -r1.13.2.1
*** Makefile.in	2 Mar 2006 15:22:25 -0000	1.13
--- Makefile.in	10 Mar 2006 08:50:43 -0000	1.13.2.1
***************
*** 26,30 ****
  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
  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
--- 26,30 ----
  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 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
***************
*** 83,87 ****
  	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
  	cat proto.tail >> proto.h
  
--- 83,87 ----
  	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 seamless.c >> proto.h
  	cat proto.tail >> proto.h
  

Index: channels.c
===================================================================
RCS file: /cvsroot/rdesktop/rdesktop/channels.c,v
retrieving revision 1.15
retrieving revision 1.15.2.1
diff -C2 -d -r1.15 -r1.15.2.1
*** channels.c	2 Mar 2006 15:22:25 -0000	1.15
--- channels.c	10 Mar 2006 08:50:43 -0000	1.15.2.1
***************
*** 22,26 ****
  #include "rdesktop.h"
  
! #define MAX_CHANNELS			5
  #define CHANNEL_CHUNK_LENGTH		1600
  #define CHANNEL_FLAG_FIRST		0x01
--- 22,26 ----
  #include "rdesktop.h"
  
! #define MAX_CHANNELS			6
  #define CHANNEL_CHUNK_LENGTH		1600
  #define CHANNEL_FLAG_FIRST		0x01

Index: constants.h
===================================================================
RCS file: /cvsroot/rdesktop/rdesktop/constants.h,v
retrieving revision 1.45
retrieving revision 1.45.2.1
diff -C2 -d -r1.45 -r1.45.2.1
*** constants.h	8 Aug 2005 19:15:53 -0000	1.45
--- constants.h	10 Mar 2006 08:50:43 -0000	1.45.2.1
***************
*** 413,414 ****
--- 413,419 ----
  #define exDiscReasonLicenseCantUpgradeLicense		0x0109
  #define exDiscReasonLicenseNoRemoteConnections		0x010a
+ 
+ /* SeamlessRDP constants */
+ #define SEAMLESSRDP_NORMAL 0
+ #define SEAMLESSRDP_MINIMIZED 1
+ #define SEAMLESSRDP_MAXIMIZED 2

Index: proto.h
===================================================================
RCS file: /cvsroot/rdesktop/rdesktop/proto.h,v
retrieving revision 1.87
retrieving revision 1.87.2.1
diff -C2 -d -r1.87 -r1.87.2.1
*** proto.h	2 Mar 2006 15:22:25 -0000	1.87
--- proto.h	10 Mar 2006 08:50:43 -0000	1.87.2.1
***************
*** 272,277 ****
--- 272,287 ----
  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* */

Index: rdesktop.c
===================================================================
RCS file: /cvsroot/rdesktop/rdesktop/rdesktop.c,v
retrieving revision 1.144
retrieving revision 1.144.2.1
diff -C2 -d -r1.144 -r1.144.2.1
*** rdesktop.c	2 Mar 2006 15:22:25 -0000	1.144
--- rdesktop.c	10 Mar 2006 08:50:43 -0000	1.144.2.1
***************
*** 90,93 ****
--- 90,94 ----
  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 =
***************
*** 147,150 ****
--- 148,152 ----
  	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");
***************
*** 391,394 ****
--- 393,397 ----
  	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 */
***************
*** 417,421 ****
  
  	while ((c = getopt(argc, argv,
! 			   VNCOPT "u:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
  	{
  		switch (c)
--- 420,424 ----
  
  	while ((c = getopt(argc, argv,
! 			   VNCOPT "Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
  	{
  		switch (c)
***************
*** 435,438 ****
--- 438,445 ----
  #endif
  
+ 			case 'A':
+ 				g_seamless_rdp = True;
+ 				break;
+ 
  			case 'u':
  				STRNCPY(g_username, optarg, sizeof(g_username));
***************
*** 485,488 ****
--- 492,496 ----
  
  			case 'g':
+ 				geometry_option = True;
  				g_fullscreen = False;
  				if (!strcmp(optarg, "workarea"))
***************
*** 733,736 ****
--- 741,781 ----
  	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)
  	{

Index: xkeymap.c
===================================================================
RCS file: /cvsroot/rdesktop/rdesktop/xkeymap.c,v
retrieving revision 1.88
retrieving revision 1.88.2.1
diff -C2 -d -r1.88 -r1.88.2.1
*** xkeymap.c	30 Dec 2005 20:32:06 -0000	1.88
--- xkeymap.c	10 Mar 2006 08:50:43 -0000	1.88.2.1
***************
*** 590,593 ****
--- 590,598 ----
  			return True;
  			break;
+ 		case XK_Overlay1_Enable:
+ 			/* Toggle SeamlessRDP */
+ 			if (pressed)
+ 				ui_seamless_toggle();
+ 			break;
  
  	}

Index: xwin.c
===================================================================
RCS file: /cvsroot/rdesktop/rdesktop/xwin.c,v
retrieving revision 1.205
retrieving revision 1.205.2.1
diff -C2 -d -r1.205 -r1.205.2.1
*** xwin.c	7 Mar 2006 08:17:40 -0000	1.205
--- xwin.c	10 Mar 2006 08:50:43 -0000	1.205.2.1
***************
*** 49,52 ****
--- 49,65 ----
  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;
***************
*** 55,58 ****
--- 68,72 ----
  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).
***************
*** 134,141 ****
--- 148,190 ----
  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); \
***************
*** 152,155 ****
--- 201,205 ----
  	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)); \
  }
  
***************
*** 160,168 ****
  		case 0:	/* Outline */ \
  			XDrawArc(g_display, g_wnd, g_gc, x, y, 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); \
  			if (g_ownbackstore) \
  				XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
--- 210,221 ----
  		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); \
!                         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); \
***************
*** 202,207 ****
  #define RESET_FUNCTION(rop2)	{ if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); }
  
  static void
! mwm_hide_decorations(void)
  {
  	PropMotifWmHints motif_hints;
--- 255,304 ----
  #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
! 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;
***************
*** 220,225 ****
  	}
  
! 	XChangeProperty(g_display, g_wnd, hintsatom, hintsatom, 32, PropModeReplace,
  			(unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
  }
  
--- 317,323 ----
  	}
  
! 	XChangeProperty(g_display, wnd, hintsatom, hintsatom, 32, PropModeReplace,
  			(unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
+ 
  }
  
***************
*** 1299,1302 ****
--- 1397,1402 ----
  
  	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));
***************
*** 1324,1327 ****
--- 1424,1455 ----
  }
  
+ 
+ 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)
***************
*** 1346,1354 ****
  		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;
  
  	g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
--- 1474,1478 ----
  		g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height;
  
! 	get_window_attribs(&attribs);
  
  	g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
***************
*** 1358,1362 ****
--- 1482,1489 ----
  
  	if (g_gc == NULL)
+ 	{
  		g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
+ 		ui_reset_clip();
+ 	}
  
  	if (g_create_bitmap_gc == NULL)
***************
*** 1375,1379 ****
  
  	if (g_hide_decorations)
! 		mwm_hide_decorations();
  
  	classhints = XAllocClassHint();
--- 1502,1506 ----
  
  	if (g_hide_decorations)
! 		mwm_hide_decorations(g_wnd);
  
  	classhints = XAllocClassHint();
***************
*** 1402,1416 ****
  	}
  
! 	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;
  
  	if (g_IM != NULL)
--- 1529,1533 ----
  	}
  
! 	get_input_mask(&input_mask);
  
  	if (g_IM != NULL)
***************
*** 1425,1437 ****
  
  	XSelectInput(g_display, g_wnd, input_mask);
! 	XMapWindow(g_display, g_wnd);
! 
! 	/* wait for VisibilityNotify */
! 	do
  	{
! 		XMaskEvent(g_display, VisibilityChangeMask, &xevent);
  	}
- 	while (xevent.type != VisibilityNotify);
- 	g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
  
  	g_focused = False;
--- 1542,1556 ----
  
  	XSelectInput(g_display, g_wnd, input_mask);
! 	if (!g_seamless_rdp)
  	{
! 		XMapWindow(g_display, g_wnd);
! 		/* wait for VisibilityNotify */
! 		do
! 		{
! 			XMaskEvent(g_display, VisibilityChangeMask, &xevent);
! 		}
! 		while (xevent.type != VisibilityNotify);
! 		g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
  	}
  
  	g_focused = False;
***************
*** 1497,1500 ****
--- 1616,1623 ----
  	Pixmap contents = 0;
  
+ 	if (g_seamless_rdp)
+ 		/* Turn off SeamlessRDP mode */
+ 		ui_seamless_toggle();
+ 
  	if (!g_ownbackstore)
  	{
***************
*** 1585,1590 ****
  	}
  
! 	rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
! 		       flags | button, xevent.xbutton.x, xevent.xbutton.y);
  }
  
--- 1708,1722 ----
  	}
  
! 	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);
! 	}
  }
  
***************
*** 1614,1618 ****
  		{
  			case VisibilityNotify:
! 				g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
  				break;
  			case ClientMessage:
--- 1746,1753 ----
  		{
  			case VisibilityNotify:
! 				if (xevent.xvisibility.window == g_wnd)
! 					g_Unobscured =
! 						xevent.xvisibility.state == VisibilityUnobscured;
! 
  				break;
  			case ClientMessage:
***************
*** 1694,1699 ****
  					XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
  						       CurrentTime);
! 				rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
! 					       MOUSE_FLAG_MOVE, xevent.xmotion.x, xevent.xmotion.y);
  				break;
  
--- 1829,1845 ----
  					XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
  						       CurrentTime);
! 
! 				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;
  
***************
*** 1738,1746 ****
  
  			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);
  				break;
  
--- 1884,1909 ----
  
  			case Expose:
! 				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;
  
***************
*** 1773,1780 ****
  				break;
  			case MapNotify:
! 				rdp_send_client_window_status(1);
  				break;
  			case UnmapNotify:
! 				rdp_send_client_window_status(0);
  				break;
  		}
--- 1936,1945 ----
  				break;
  			case MapNotify:
! 				if (!g_seamless_rdp)
! 					rdp_send_client_window_status(1);
  				break;
  			case UnmapNotify:
! 				if (!g_seamless_rdp)
! 					rdp_send_client_window_status(0);
  				break;
  		}
***************
*** 1913,1920 ****
--- 2078,2091 ----
  		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));
  	}
  
***************
*** 2037,2040 ****
--- 2208,2212 ----
  	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));
  }
  
***************
*** 2178,2182 ****
--- 2350,2357 ----
  	}
  	else
+ 	{
  		XSetWindowColormap(g_display, g_wnd, (Colormap) map);
+ 		ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
+ 	}
  }
  
***************
*** 2184,2194 ****
  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);
  }
  
--- 2359,2367 ----
  ui_set_clip(int x, int y, int cx, int cy)
  {
! 	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);
  }
  
***************
*** 2196,2206 ****
  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);
  }
  
--- 2369,2377 ----
  ui_reset_clip(void)
  {
! 	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);
  }
  
***************
*** 2283,2286 ****
--- 2454,2460 ----
  	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));
  }
  
***************
*** 2293,2308 ****
  	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);
! 		}
  	}
  	else
--- 2467,2473 ----
  	if (g_ownbackstore)
  	{
! 		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
***************
*** 2310,2313 ****
--- 2475,2483 ----
  		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);
  }
***************
*** 2320,2323 ****
--- 2490,2496 ----
  	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);
***************
*** 2366,2369 ****
--- 2539,2545 ----
  	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);
***************
*** 2463,2466 ****
--- 2639,2646 ----
  		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);
  }
***************
*** 2683,2691 ****
--- 2863,2885 ----
  	{
  		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));
+ 		}
  	}
  }
***************
*** 2733,2740 ****
--- 2927,2940 ----
  		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));
  	}
  
***************
*** 2752,2753 ****
--- 2952,3128 ----
  {
  }
+ 
+ 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;
+ 	}
+ }



-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.