1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    User interface services - X Window System
4    Copyright (C) Matthew Chapman 1999-2005
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include <X11/Xlib.h>
22 #include <X11/Xutil.h>
23 #include <unistd.h>
24 #include <sys/time.h>
25 #include <time.h>
26 #include <errno.h>
27 #include <strings.h>
28 #include "rdesktop.h"
29 #include "xproto.h"
30 
31 /* We can't include Xproto.h because of conflicting defines for BOOL */
32 #define X_ConfigureWindow              12
33 
34 /* MWM decorations */
35 #define MWM_HINTS_DECORATIONS   (1L << 1)
36 #define PROP_MOTIF_WM_HINTS_ELEMENTS    5
37 typedef struct
38 {
39 	unsigned long flags;
40 	unsigned long functions;
41 	unsigned long decorations;
42 	long inputMode;
43 	unsigned long status;
44 }
45 PropMotifWmHints;
46 
47 typedef struct
48 {
49 	uint32 red;
50 	uint32 green;
51 	uint32 blue;
52 }
53 PixelColour;
54 
55 #define ON_ALL_SEAMLESS_WINDOWS(func, args) \
56         do { \
57                 seamless_window *sw; \
58                 XRectangle rect; \
59 		if (!This->xwin.seamless_windows) break; \
60                 for (sw = This->xwin.seamless_windows; sw; sw = sw->next) { \
61                     rect.x = This->xwin.clip_rectangle.x - sw->xoffset; \
62                     rect.y = This->xwin.clip_rectangle.y - sw->yoffset; \
63                     rect.width = This->xwin.clip_rectangle.width; \
64                     rect.height = This->xwin.clip_rectangle.height; \
65                     XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &rect, 1, YXBanded); \
66                     func args; \
67                 } \
68                 XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &This->xwin.clip_rectangle, 1, YXBanded); \
69         } while (0)
70 
71 static void
seamless_XFillPolygon(RDPCLIENT * This,Drawable d,XPoint * points,int npoints,int xoffset,int yoffset)72 seamless_XFillPolygon(RDPCLIENT * This, Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
73 {
74 	points[0].x -= xoffset;
75 	points[0].y -= yoffset;
76 	XFillPolygon(This->display, d, This->xwin.gc, points, npoints, Complex, CoordModePrevious);
77 	points[0].x += xoffset;
78 	points[0].y += yoffset;
79 }
80 
81 static void
seamless_XDrawLines(RDPCLIENT * This,Drawable d,XPoint * points,int npoints,int xoffset,int yoffset)82 seamless_XDrawLines(RDPCLIENT * This, Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
83 {
84 	points[0].x -= xoffset;
85 	points[0].y -= yoffset;
86 	XDrawLines(This->display, d, This->xwin.gc, points, npoints, CoordModePrevious);
87 	points[0].x += xoffset;
88 	points[0].y += yoffset;
89 }
90 
91 #define FILL_RECTANGLE(x,y,cx,cy)\
92 { \
93 	XFillRectangle(This->display, This->wnd, This->xwin.gc, x, y, cx, cy); \
94         ON_ALL_SEAMLESS_WINDOWS(XFillRectangle, (This->display, sw->wnd, This->xwin.gc, x-sw->xoffset, y-sw->yoffset, cx, cy)); \
95 	if (This->ownbackstore) \
96 		XFillRectangle(This->display, This->xwin.backstore, This->xwin.gc, x, y, cx, cy); \
97 }
98 
99 #define FILL_RECTANGLE_BACKSTORE(x,y,cx,cy)\
100 { \
101 	XFillRectangle(This->display, This->ownbackstore ? This->xwin.backstore : This->wnd, This->xwin.gc, x, y, cx, cy); \
102 }
103 
104 #define FILL_POLYGON(p,np)\
105 { \
106 	XFillPolygon(This->display, This->wnd, This->xwin.gc, p, np, Complex, CoordModePrevious); \
107 	if (This->ownbackstore) \
108 		XFillPolygon(This->display, This->xwin.backstore, This->xwin.gc, p, np, Complex, CoordModePrevious); \
109 	ON_ALL_SEAMLESS_WINDOWS(seamless_XFillPolygon, (This, sw->wnd, p, np, sw->xoffset, sw->yoffset)); \
110 }
111 
112 #define DRAW_ELLIPSE(x,y,cx,cy,m)\
113 { \
114 	switch (m) \
115 	{ \
116 		case 0:	/* Outline */ \
117 			XDrawArc(This->display, This->wnd, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
118                         ON_ALL_SEAMLESS_WINDOWS(XDrawArc, (This->display, sw->wnd, This->xwin.gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \
119 			if (This->ownbackstore) \
120 				XDrawArc(This->display, This->xwin.backstore, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
121 			break; \
122 		case 1: /* Filled */ \
123 			XFillArc(This->display, This->wnd, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
124                         ON_ALL_SEAMLESS_WINDOWS(XCopyArea, (This->display, This->ownbackstore ? This->xwin.backstore : This->wnd, sw->wnd, This->xwin.gc, \
125 							    x, y, cx, cy, x-sw->xoffset, y-sw->yoffset)); \
126 			if (This->ownbackstore) \
127 				XFillArc(This->display, This->xwin.backstore, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
128 			break; \
129 	} \
130 }
131 
132 #define TRANSLATE(col)		( This->server_depth != 8 ? translate_colour(This, col) : This->owncolmap ? col : This->xwin.colmap[col] )
133 #define SET_FOREGROUND(col)	XSetForeground(This->display, This->xwin.gc, TRANSLATE(col));
134 #define SET_BACKGROUND(col)	XSetBackground(This->display, This->xwin.gc, TRANSLATE(col));
135 
136 static const int rop2_map[] = {
137 	GXclear,		/* 0 */
138 	GXnor,			/* DPon */
139 	GXandInverted,		/* DPna */
140 	GXcopyInverted,		/* Pn */
141 	GXandReverse,		/* PDna */
142 	GXinvert,		/* Dn */
143 	GXxor,			/* DPx */
144 	GXnand,			/* DPan */
145 	GXand,			/* DPa */
146 	GXequiv,		/* DPxn */
147 	GXnoop,			/* D */
148 	GXorInverted,		/* DPno */
149 	GXcopy,			/* P */
150 	GXorReverse,		/* PDno */
151 	GXor,			/* DPo */
152 	GXset			/* 1 */
153 };
154 
155 #define SET_FUNCTION(rop2)	{ if (rop2 != ROP2_COPY) XSetFunction(This->display, This->xwin.gc, rop2_map[rop2]); }
156 #define RESET_FUNCTION(rop2)	{ if (rop2 != ROP2_COPY) XSetFunction(This->display, This->xwin.gc, GXcopy); }
157 
158 static seamless_window *
sw_get_window_by_id(RDPCLIENT * This,unsigned long id)159 sw_get_window_by_id(RDPCLIENT * This, unsigned long id)
160 {
161 	seamless_window *sw;
162 	for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
163 	{
164 		if (sw->id == id)
165 			return sw;
166 	}
167 	return NULL;
168 }
169 
170 
171 static seamless_window *
sw_get_window_by_wnd(RDPCLIENT * This,Window wnd)172 sw_get_window_by_wnd(RDPCLIENT * This, Window wnd)
173 {
174 	seamless_window *sw;
175 	for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
176 	{
177 		if (sw->wnd == wnd)
178 			return sw;
179 	}
180 	return NULL;
181 }
182 
183 
184 static void
sw_remove_window(RDPCLIENT * This,seamless_window * win)185 sw_remove_window(RDPCLIENT * This, seamless_window * win)
186 {
187 	seamless_window *sw, **prevnext = &This->xwin.seamless_windows;
188 	for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
189 	{
190 		if (sw == win)
191 		{
192 			*prevnext = sw->next;
193 			sw->group->refcnt--;
194 			if (sw->group->refcnt == 0)
195 			{
196 				XDestroyWindow(This->display, sw->group->wnd);
197 				xfree(sw->group);
198 			}
199 			xfree(sw->position_timer);
200 			xfree(sw);
201 			return;
202 		}
203 		prevnext = &sw->next;
204 	}
205 	return;
206 }
207 
208 
209 /* Move all windows except wnd to new desktop */
210 static void
sw_all_to_desktop(RDPCLIENT * This,Window wnd,unsigned int desktop)211 sw_all_to_desktop(RDPCLIENT * This, Window wnd, unsigned int desktop)
212 {
213 	seamless_window *sw;
214 	for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
215 	{
216 		if (sw->wnd == wnd)
217 			continue;
218 		if (sw->desktop != desktop)
219 		{
220 			ewmh_move_to_desktop(This, sw->wnd, desktop);
221 			sw->desktop = desktop;
222 		}
223 	}
224 }
225 
226 
227 /* Send our position */
228 static void
sw_update_position(RDPCLIENT * This,seamless_window * sw)229 sw_update_position(RDPCLIENT * This, seamless_window * sw)
230 {
231 	XWindowAttributes wa;
232 	int x, y;
233 	Window child_return;
234 	unsigned int serial;
235 
236 	XGetWindowAttributes(This->display, sw->wnd, &wa);
237 	XTranslateCoordinates(This->display, sw->wnd, wa.root,
238 			      -wa.border_width, -wa.border_width, &x, &y, &child_return);
239 
240 	serial = seamless_send_position(This, sw->id, x, y, wa.width, wa.height, 0);
241 
242 	sw->outstanding_position = True;
243 	sw->outpos_serial = serial;
244 
245 	sw->outpos_xoffset = x;
246 	sw->outpos_yoffset = y;
247 	sw->outpos_width = wa.width;
248 	sw->outpos_height = wa.height;
249 }
250 
251 
252 /* Check if it's time to send our position */
253 static void
sw_check_timers(RDPCLIENT * This)254 sw_check_timers(RDPCLIENT * This)
255 {
256 	seamless_window *sw;
257 	struct timeval now;
258 
259 	gettimeofday(&now, NULL);
260 	for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
261 	{
262 		if (timerisset(sw->position_timer) && timercmp(sw->position_timer, &now, <))
263 		{
264 			timerclear(sw->position_timer);
265 			sw_update_position(This, sw);
266 		}
267 	}
268 }
269 
270 
271 static void
sw_restack_window(RDPCLIENT * This,seamless_window * sw,unsigned long behind)272 sw_restack_window(RDPCLIENT * This, seamless_window * sw, unsigned long behind)
273 {
274 	seamless_window *sw_above;
275 
276 	/* Remove window from stack */
277 	for (sw_above = This->xwin.seamless_windows; sw_above; sw_above = sw_above->next)
278 	{
279 		if (sw_above->behind == sw->id)
280 			break;
281 	}
282 
283 	if (sw_above)
284 		sw_above->behind = sw->behind;
285 
286 	/* And then add it at the new position */
287 	for (sw_above = This->xwin.seamless_windows; sw_above; sw_above = sw_above->next)
288 	{
289 		if (sw_above->behind == behind)
290 			break;
291 	}
292 
293 	if (sw_above)
294 		sw_above->behind = sw->id;
295 
296 	sw->behind = behind;
297 }
298 
299 
300 static void
sw_handle_restack(RDPCLIENT * This,seamless_window * sw)301 sw_handle_restack(RDPCLIENT * This, seamless_window * sw)
302 {
303 	Status status;
304 	Window root, parent, *children;
305 	unsigned int nchildren, i;
306 	seamless_window *sw_below;
307 
308 	status = XQueryTree(This->display, RootWindowOfScreen(This->xwin.screen),
309 			    &root, &parent, &children, &nchildren);
310 	if (!status || !nchildren)
311 		return;
312 
313 	sw_below = NULL;
314 
315 	i = 0;
316 	while (children[i] != sw->wnd)
317 	{
318 		i++;
319 		if (i >= nchildren)
320 			goto end;
321 	}
322 
323 	for (i++; i < nchildren; i++)
324 	{
325 		sw_below = sw_get_window_by_wnd(This, children[i]);
326 		if (sw_below)
327 			break;
328 	}
329 
330 	if (!sw_below && !sw->behind)
331 		goto end;
332 	if (sw_below && (sw_below->id == sw->behind))
333 		goto end;
334 
335 	if (sw_below)
336 	{
337 		seamless_send_zchange(This, sw->id, sw_below->id, 0);
338 		sw_restack_window(This, sw, sw_below->id);
339 	}
340 	else
341 	{
342 		seamless_send_zchange(This, sw->id, 0, 0);
343 		sw_restack_window(This, sw, 0);
344 	}
345 
346       end:
347 	XFree(children);
348 }
349 
350 
351 static seamless_group *
sw_find_group(RDPCLIENT * This,unsigned long id,BOOL dont_create)352 sw_find_group(RDPCLIENT * This, unsigned long id, BOOL dont_create)
353 {
354 	seamless_window *sw;
355 	seamless_group *sg;
356 	XSetWindowAttributes attribs;
357 
358 	for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
359 	{
360 		if (sw->group->id == id)
361 			return sw->group;
362 	}
363 
364 	if (dont_create)
365 		return NULL;
366 
367 	sg = xmalloc(sizeof(seamless_group));
368 
369 	sg->wnd =
370 		XCreateWindow(This->display, RootWindowOfScreen(This->xwin.screen), -1, -1, 1, 1, 0,
371 			      CopyFromParent, CopyFromParent, CopyFromParent, 0, &attribs);
372 
373 	sg->id = id;
374 	sg->refcnt = 0;
375 
376 	return sg;
377 }
378 
379 
380 static void
mwm_hide_decorations(RDPCLIENT * This,Window wnd)381 mwm_hide_decorations(RDPCLIENT * This, Window wnd)
382 {
383 	PropMotifWmHints motif_hints;
384 	Atom hintsatom;
385 
386 	/* setup the property */
387 	motif_hints.flags = MWM_HINTS_DECORATIONS;
388 	motif_hints.decorations = 0;
389 
390 	/* get the atom for the property */
391 	hintsatom = XInternAtom(This->display, "_MOTIF_WM_HINTS", False);
392 	if (!hintsatom)
393 	{
394 		warning("Failed to get atom _MOTIF_WM_HINTS: probably your window manager does not support MWM hints\n");
395 		return;
396 	}
397 
398 	XChangeProperty(This->display, wnd, hintsatom, hintsatom, 32, PropModeReplace,
399 			(unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
400 
401 }
402 
403 #define SPLITCOLOUR15(colour, rv) \
404 { \
405 	rv.red = ((colour >> 7) & 0xf8) | ((colour >> 12) & 0x7); \
406 	rv.green = ((colour >> 2) & 0xf8) | ((colour >> 8) & 0x7); \
407 	rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
408 }
409 
410 #define SPLITCOLOUR16(colour, rv) \
411 { \
412 	rv.red = ((colour >> 8) & 0xf8) | ((colour >> 13) & 0x7); \
413 	rv.green = ((colour >> 3) & 0xfc) | ((colour >> 9) & 0x3); \
414 	rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
415 } \
416 
417 #define SPLITCOLOUR24(colour, rv) \
418 { \
419 	rv.blue = (colour & 0xff0000) >> 16; \
420 	rv.green = (colour & 0x00ff00) >> 8; \
421 	rv.red = (colour & 0x0000ff); \
422 }
423 
424 #define MAKECOLOUR(pc) \
425 	((pc.red >> This->xwin.red_shift_r) << This->xwin.red_shift_l) \
426 		| ((pc.green >> This->xwin.green_shift_r) << This->xwin.green_shift_l) \
427 		| ((pc.blue >> This->xwin.blue_shift_r) << This->xwin.blue_shift_l) \
428 
429 #define BSWAP16(x) { x = (((x & 0xff) << 8) | (x >> 8)); }
430 #define BSWAP24(x) { x = (((x & 0xff) << 16) | (x >> 16) | (x & 0xff00)); }
431 #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \
432 			x = (x << 16) | (x >> 16); }
433 
434 /* The following macros output the same octet sequences
435    on both BE and LE hosts: */
436 
437 #define BOUT16(o, x) { *(o++) = x >> 8; *(o++) = x; }
438 #define BOUT24(o, x) { *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
439 #define BOUT32(o, x) { *(o++) = x >> 24; *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
440 #define LOUT16(o, x) { *(o++) = x; *(o++) = x >> 8; }
441 #define LOUT24(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; }
442 #define LOUT32(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; *(o++) = x >> 24; }
443 
444 static uint32
translate_colour(RDPCLIENT * This,uint32 colour)445 translate_colour(RDPCLIENT * This, uint32 colour)
446 {
447 	PixelColour pc;
448 	switch (This->server_depth)
449 	{
450 		case 15:
451 			SPLITCOLOUR15(colour, pc);
452 			break;
453 		case 16:
454 			SPLITCOLOUR16(colour, pc);
455 			break;
456 		case 24:
457 			SPLITCOLOUR24(colour, pc);
458 			break;
459 		default:
460 			/* Avoid warning */
461 			pc.red = 0;
462 			pc.green = 0;
463 			pc.blue = 0;
464 			break;
465 	}
466 	return MAKECOLOUR(pc);
467 }
468 
469 /* indent is confused by UNROLL8 */
470 /* *INDENT-OFF* */
471 
472 /* repeat and unroll, similar to bitmap.c */
473 /* potentialy any of the following translate */
474 /* functions can use repeat but just doing */
475 /* the most common ones */
476 
477 #define UNROLL8(stm) { stm stm stm stm stm stm stm stm }
478 /* 2 byte output repeat */
479 #define REPEAT2(stm) \
480 { \
481 	while (out <= end - 8 * 2) \
482 		UNROLL8(stm) \
483 	while (out < end) \
484 		{ stm } \
485 }
486 /* 3 byte output repeat */
487 #define REPEAT3(stm) \
488 { \
489 	while (out <= end - 8 * 3) \
490 		UNROLL8(stm) \
491 	while (out < end) \
492 		{ stm } \
493 }
494 /* 4 byte output repeat */
495 #define REPEAT4(stm) \
496 { \
497 	while (out <= end - 8 * 4) \
498 		UNROLL8(stm) \
499 	while (out < end) \
500 		{ stm } \
501 }
502 /* *INDENT-ON* */
503 
504 static void
translate8to8(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)505 translate8to8(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
506 {
507 	while (out < end)
508 		*(out++) = (uint8) This->xwin.colmap[*(data++)];
509 }
510 
511 static void
translate8to16(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)512 translate8to16(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
513 {
514 	uint16 value;
515 
516 	if (This->xwin.compatible_arch)
517 	{
518 		/* *INDENT-OFF* */
519 		REPEAT2
520 		(
521 			*((uint16 *) out) = This->xwin.colmap[*(data++)];
522 			out += 2;
523 		)
524 		/* *INDENT-ON* */
525 	}
526 	else if (This->xwin.xserver_be)
527 	{
528 		while (out < end)
529 		{
530 			value = (uint16) This->xwin.colmap[*(data++)];
531 			BOUT16(out, value);
532 		}
533 	}
534 	else
535 	{
536 		while (out < end)
537 		{
538 			value = (uint16) This->xwin.colmap[*(data++)];
539 			LOUT16(out, value);
540 		}
541 	}
542 }
543 
544 /* little endian - conversion happens when colourmap is built */
545 static void
translate8to24(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)546 translate8to24(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
547 {
548 	uint32 value;
549 
550 	if (This->xwin.compatible_arch)
551 	{
552 		while (out < end)
553 		{
554 			value = This->xwin.colmap[*(data++)];
555 			BOUT24(out, value);
556 		}
557 	}
558 	else
559 	{
560 		while (out < end)
561 		{
562 			value = This->xwin.colmap[*(data++)];
563 			LOUT24(out, value);
564 		}
565 	}
566 }
567 
568 static void
translate8to32(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)569 translate8to32(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
570 {
571 	uint32 value;
572 
573 	if (This->xwin.compatible_arch)
574 	{
575 		/* *INDENT-OFF* */
576 		REPEAT4
577 		(
578 			*((uint32 *) out) = This->xwin.colmap[*(data++)];
579 			out += 4;
580 		)
581 		/* *INDENT-ON* */
582 	}
583 	else if (This->xwin.xserver_be)
584 	{
585 		while (out < end)
586 		{
587 			value = This->xwin.colmap[*(data++)];
588 			BOUT32(out, value);
589 		}
590 	}
591 	else
592 	{
593 		while (out < end)
594 		{
595 			value = This->xwin.colmap[*(data++)];
596 			LOUT32(out, value);
597 		}
598 	}
599 }
600 
601 static void
translate15to16(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)602 translate15to16(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
603 {
604 	uint16 pixel;
605 	uint16 value;
606 	PixelColour pc;
607 
608 	if (This->xwin.xserver_be)
609 	{
610 		while (out < end)
611 		{
612 			pixel = *(data++);
613 			if (This->xwin.host_be)
614 			{
615 				BSWAP16(pixel);
616 			}
617 			SPLITCOLOUR15(pixel, pc);
618 			value = MAKECOLOUR(pc);
619 			BOUT16(out, value);
620 		}
621 	}
622 	else
623 	{
624 		while (out < end)
625 		{
626 			pixel = *(data++);
627 			if (This->xwin.host_be)
628 			{
629 				BSWAP16(pixel);
630 			}
631 			SPLITCOLOUR15(pixel, pc);
632 			value = MAKECOLOUR(pc);
633 			LOUT16(out, value);
634 		}
635 	}
636 }
637 
638 static void
translate15to24(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)639 translate15to24(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
640 {
641 	uint32 value;
642 	uint16 pixel;
643 	PixelColour pc;
644 
645 	if (This->xwin.compatible_arch)
646 	{
647 		/* *INDENT-OFF* */
648 		REPEAT3
649 		(
650 			pixel = *(data++);
651 			SPLITCOLOUR15(pixel, pc);
652 			*(out++) = pc.blue;
653 			*(out++) = pc.green;
654 			*(out++) = pc.red;
655 		)
656 		/* *INDENT-ON* */
657 	}
658 	else if (This->xwin.xserver_be)
659 	{
660 		while (out < end)
661 		{
662 			pixel = *(data++);
663 			if (This->xwin.host_be)
664 			{
665 				BSWAP16(pixel);
666 			}
667 			SPLITCOLOUR15(pixel, pc);
668 			value = MAKECOLOUR(pc);
669 			BOUT24(out, value);
670 		}
671 	}
672 	else
673 	{
674 		while (out < end)
675 		{
676 			pixel = *(data++);
677 			if (This->xwin.host_be)
678 			{
679 				BSWAP16(pixel);
680 			}
681 			SPLITCOLOUR15(pixel, pc);
682 			value = MAKECOLOUR(pc);
683 			LOUT24(out, value);
684 		}
685 	}
686 }
687 
688 static void
translate15to32(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)689 translate15to32(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
690 {
691 	uint16 pixel;
692 	uint32 value;
693 	PixelColour pc;
694 
695 	if (This->xwin.compatible_arch)
696 	{
697 		/* *INDENT-OFF* */
698 		REPEAT4
699 		(
700 			pixel = *(data++);
701 			SPLITCOLOUR15(pixel, pc);
702 			*(out++) = pc.blue;
703 			*(out++) = pc.green;
704 			*(out++) = pc.red;
705 			*(out++) = 0;
706 		)
707 		/* *INDENT-ON* */
708 	}
709 	else if (This->xwin.xserver_be)
710 	{
711 		while (out < end)
712 		{
713 			pixel = *(data++);
714 			if (This->xwin.host_be)
715 			{
716 				BSWAP16(pixel);
717 			}
718 			SPLITCOLOUR15(pixel, pc);
719 			value = MAKECOLOUR(pc);
720 			BOUT32(out, value);
721 		}
722 	}
723 	else
724 	{
725 		while (out < end)
726 		{
727 			pixel = *(data++);
728 			if (This->xwin.host_be)
729 			{
730 				BSWAP16(pixel);
731 			}
732 			SPLITCOLOUR15(pixel, pc);
733 			value = MAKECOLOUR(pc);
734 			LOUT32(out, value);
735 		}
736 	}
737 }
738 
739 static void
translate16to16(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)740 translate16to16(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
741 {
742 	uint16 pixel;
743 	uint16 value;
744 	PixelColour pc;
745 
746 	if (This->xwin.xserver_be)
747 	{
748 		if (This->xwin.host_be)
749 		{
750 			while (out < end)
751 			{
752 				pixel = *(data++);
753 				BSWAP16(pixel);
754 				SPLITCOLOUR16(pixel, pc);
755 				value = MAKECOLOUR(pc);
756 				BOUT16(out, value);
757 			}
758 		}
759 		else
760 		{
761 			while (out < end)
762 			{
763 				pixel = *(data++);
764 				SPLITCOLOUR16(pixel, pc);
765 				value = MAKECOLOUR(pc);
766 				BOUT16(out, value);
767 			}
768 		}
769 	}
770 	else
771 	{
772 		if (This->xwin.host_be)
773 		{
774 			while (out < end)
775 			{
776 				pixel = *(data++);
777 				BSWAP16(pixel);
778 				SPLITCOLOUR16(pixel, pc);
779 				value = MAKECOLOUR(pc);
780 				LOUT16(out, value);
781 			}
782 		}
783 		else
784 		{
785 			while (out < end)
786 			{
787 				pixel = *(data++);
788 				SPLITCOLOUR16(pixel, pc);
789 				value = MAKECOLOUR(pc);
790 				LOUT16(out, value);
791 			}
792 		}
793 	}
794 }
795 
796 static void
translate16to24(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)797 translate16to24(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
798 {
799 	uint32 value;
800 	uint16 pixel;
801 	PixelColour pc;
802 
803 	if (This->xwin.compatible_arch)
804 	{
805 		/* *INDENT-OFF* */
806 		REPEAT3
807 		(
808 			pixel = *(data++);
809 			SPLITCOLOUR16(pixel, pc);
810 			*(out++) = pc.blue;
811 			*(out++) = pc.green;
812 			*(out++) = pc.red;
813 		)
814 		/* *INDENT-ON* */
815 	}
816 	else if (This->xwin.xserver_be)
817 	{
818 		if (This->xwin.host_be)
819 		{
820 			while (out < end)
821 			{
822 				pixel = *(data++);
823 				BSWAP16(pixel);
824 				SPLITCOLOUR16(pixel, pc);
825 				value = MAKECOLOUR(pc);
826 				BOUT24(out, value);
827 			}
828 		}
829 		else
830 		{
831 			while (out < end)
832 			{
833 				pixel = *(data++);
834 				SPLITCOLOUR16(pixel, pc);
835 				value = MAKECOLOUR(pc);
836 				BOUT24(out, value);
837 			}
838 		}
839 	}
840 	else
841 	{
842 		if (This->xwin.host_be)
843 		{
844 			while (out < end)
845 			{
846 				pixel = *(data++);
847 				BSWAP16(pixel);
848 				SPLITCOLOUR16(pixel, pc);
849 				value = MAKECOLOUR(pc);
850 				LOUT24(out, value);
851 			}
852 		}
853 		else
854 		{
855 			while (out < end)
856 			{
857 				pixel = *(data++);
858 				SPLITCOLOUR16(pixel, pc);
859 				value = MAKECOLOUR(pc);
860 				LOUT24(out, value);
861 			}
862 		}
863 	}
864 }
865 
866 static void
translate16to32(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)867 translate16to32(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
868 {
869 	uint16 pixel;
870 	uint32 value;
871 	PixelColour pc;
872 
873 	if (This->xwin.compatible_arch)
874 	{
875 		/* *INDENT-OFF* */
876 		REPEAT4
877 		(
878 			pixel = *(data++);
879 			SPLITCOLOUR16(pixel, pc);
880 			*(out++) = pc.blue;
881 			*(out++) = pc.green;
882 			*(out++) = pc.red;
883 			*(out++) = 0;
884 		)
885 		/* *INDENT-ON* */
886 	}
887 	else if (This->xwin.xserver_be)
888 	{
889 		if (This->xwin.host_be)
890 		{
891 			while (out < end)
892 			{
893 				pixel = *(data++);
894 				BSWAP16(pixel);
895 				SPLITCOLOUR16(pixel, pc);
896 				value = MAKECOLOUR(pc);
897 				BOUT32(out, value);
898 			}
899 		}
900 		else
901 		{
902 			while (out < end)
903 			{
904 				pixel = *(data++);
905 				SPLITCOLOUR16(pixel, pc);
906 				value = MAKECOLOUR(pc);
907 				BOUT32(out, value);
908 			}
909 		}
910 	}
911 	else
912 	{
913 		if (This->xwin.host_be)
914 		{
915 			while (out < end)
916 			{
917 				pixel = *(data++);
918 				BSWAP16(pixel);
919 				SPLITCOLOUR16(pixel, pc);
920 				value = MAKECOLOUR(pc);
921 				LOUT32(out, value);
922 			}
923 		}
924 		else
925 		{
926 			while (out < end)
927 			{
928 				pixel = *(data++);
929 				SPLITCOLOUR16(pixel, pc);
930 				value = MAKECOLOUR(pc);
931 				LOUT32(out, value);
932 			}
933 		}
934 	}
935 }
936 
937 static void
translate24to16(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)938 translate24to16(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
939 {
940 	uint32 pixel = 0;
941 	uint16 value;
942 	PixelColour pc;
943 
944 	while (out < end)
945 	{
946 		pixel = *(data++) << 16;
947 		pixel |= *(data++) << 8;
948 		pixel |= *(data++);
949 		SPLITCOLOUR24(pixel, pc);
950 		value = MAKECOLOUR(pc);
951 		if (This->xwin.xserver_be)
952 		{
953 			BOUT16(out, value);
954 		}
955 		else
956 		{
957 			LOUT16(out, value);
958 		}
959 	}
960 }
961 
962 static void
translate24to24(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)963 translate24to24(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
964 {
965 	uint32 pixel;
966 	uint32 value;
967 	PixelColour pc;
968 
969 	if (This->xwin.xserver_be)
970 	{
971 		while (out < end)
972 		{
973 			pixel = *(data++) << 16;
974 			pixel |= *(data++) << 8;
975 			pixel |= *(data++);
976 			SPLITCOLOUR24(pixel, pc);
977 			value = MAKECOLOUR(pc);
978 			BOUT24(out, value);
979 		}
980 	}
981 	else
982 	{
983 		while (out < end)
984 		{
985 			pixel = *(data++) << 16;
986 			pixel |= *(data++) << 8;
987 			pixel |= *(data++);
988 			SPLITCOLOUR24(pixel, pc);
989 			value = MAKECOLOUR(pc);
990 			LOUT24(out, value);
991 		}
992 	}
993 }
994 
995 static void
translate24to32(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)996 translate24to32(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
997 {
998 	uint32 pixel;
999 	uint32 value;
1000 	PixelColour pc;
1001 
1002 	if (This->xwin.compatible_arch)
1003 	{
1004 		/* *INDENT-OFF* */
1005 #ifdef NEED_ALIGN
1006 		REPEAT4
1007 		(
1008 			*(out++) = *(data++);
1009 			*(out++) = *(data++);
1010 			*(out++) = *(data++);
1011 			*(out++) = 0;
1012 		)
1013 #else
1014 		REPEAT4
1015 		(
1016 		 /* Only read 3 bytes. Reading 4 bytes means reading beyond buffer. */
1017 		 *((uint32 *) out) = *((uint16 *) data) + (*((uint8 *) data + 2) << 16);
1018 		 out += 4;
1019 		 data += 3;
1020 		)
1021 #endif
1022 		/* *INDENT-ON* */
1023 	}
1024 	else if (This->xwin.xserver_be)
1025 	{
1026 		while (out < end)
1027 		{
1028 			pixel = *(data++) << 16;
1029 			pixel |= *(data++) << 8;
1030 			pixel |= *(data++);
1031 			SPLITCOLOUR24(pixel, pc);
1032 			value = MAKECOLOUR(pc);
1033 			BOUT32(out, value);
1034 		}
1035 	}
1036 	else
1037 	{
1038 		while (out < end)
1039 		{
1040 			pixel = *(data++) << 16;
1041 			pixel |= *(data++) << 8;
1042 			pixel |= *(data++);
1043 			SPLITCOLOUR24(pixel, pc);
1044 			value = MAKECOLOUR(pc);
1045 			LOUT32(out, value);
1046 		}
1047 	}
1048 }
1049 
1050 static uint8 *
translate_image(RDPCLIENT * This,int width,int height,uint8 * data)1051 translate_image(RDPCLIENT * This, int width, int height, uint8 * data)
1052 {
1053 	int size;
1054 	uint8 *out;
1055 	uint8 *end;
1056 
1057 	/*
1058 	   If RDP depth and X Visual depths match,
1059 	   and arch(endian) matches, no need to translate:
1060 	   just return data.
1061 	   Note: select_visual should've already ensured g_no_translate
1062 	   is only set for compatible depths, but the RDP depth might've
1063 	   changed during connection negotiations.
1064 	 */
1065 	if (This->xwin.no_translate_image)
1066 	{
1067 		if ((This->xwin.depth == 15 && This->server_depth == 15) ||
1068 		    (This->xwin.depth == 16 && This->server_depth == 16) ||
1069 		    (This->xwin.depth == 24 && This->server_depth == 24))
1070 			return data;
1071 	}
1072 
1073 	size = width * height * (This->xwin.bpp / 8);
1074 	out = (uint8 *) xmalloc(size);
1075 	end = out + size;
1076 
1077 	switch (This->server_depth)
1078 	{
1079 		case 24:
1080 			switch (This->xwin.bpp)
1081 			{
1082 				case 32:
1083 					translate24to32(This, data, out, end);
1084 					break;
1085 				case 24:
1086 					translate24to24(This, data, out, end);
1087 					break;
1088 				case 16:
1089 					translate24to16(This, data, out, end);
1090 					break;
1091 			}
1092 			break;
1093 		case 16:
1094 			switch (This->xwin.bpp)
1095 			{
1096 				case 32:
1097 					translate16to32(This, (uint16 *) data, out, end);
1098 					break;
1099 				case 24:
1100 					translate16to24(This, (uint16 *) data, out, end);
1101 					break;
1102 				case 16:
1103 					translate16to16(This, (uint16 *) data, out, end);
1104 					break;
1105 			}
1106 			break;
1107 		case 15:
1108 			switch (This->xwin.bpp)
1109 			{
1110 				case 32:
1111 					translate15to32(This, (uint16 *) data, out, end);
1112 					break;
1113 				case 24:
1114 					translate15to24(This, (uint16 *) data, out, end);
1115 					break;
1116 				case 16:
1117 					translate15to16(This, (uint16 *) data, out, end);
1118 					break;
1119 			}
1120 			break;
1121 		case 8:
1122 			switch (This->xwin.bpp)
1123 			{
1124 				case 8:
1125 					translate8to8(This, data, out, end);
1126 					break;
1127 				case 16:
1128 					translate8to16(This, data, out, end);
1129 					break;
1130 				case 24:
1131 					translate8to24(This, data, out, end);
1132 					break;
1133 				case 32:
1134 					translate8to32(This, data, out, end);
1135 					break;
1136 			}
1137 			break;
1138 	}
1139 	return out;
1140 }
1141 
1142 BOOL
get_key_state(RDPCLIENT * This,unsigned int state,uint32 keysym)1143 get_key_state(RDPCLIENT * This, unsigned int state, uint32 keysym)
1144 {
1145 	int modifierpos, key, keysymMask = 0;
1146 	int offset;
1147 
1148 	KeyCode keycode = XKeysymToKeycode(This->display, keysym);
1149 
1150 	if (keycode == NoSymbol)
1151 		return False;
1152 
1153 	for (modifierpos = 0; modifierpos < 8; modifierpos++)
1154 	{
1155 		offset = This->xwin.mod_map->max_keypermod * modifierpos;
1156 
1157 		for (key = 0; key < This->xwin.mod_map->max_keypermod; key++)
1158 		{
1159 			if (This->xwin.mod_map->modifiermap[offset + key] == keycode)
1160 				keysymMask |= 1 << modifierpos;
1161 		}
1162 	}
1163 
1164 	return (state & keysymMask) ? True : False;
1165 }
1166 
1167 static void
calculate_shifts(uint32 mask,int * shift_r,int * shift_l)1168 calculate_shifts(uint32 mask, int *shift_r, int *shift_l)
1169 {
1170 	*shift_l = ffs(mask) - 1;
1171 	mask >>= *shift_l;
1172 	*shift_r = 8 - ffs(mask & ~(mask >> 1));
1173 }
1174 
1175 /* Given a mask of a colour channel (e.g. XVisualInfo.red_mask),
1176    calculates the bits-per-pixel of this channel (a.k.a. colour weight).
1177  */
1178 static unsigned
calculate_mask_weight(uint32 mask)1179 calculate_mask_weight(uint32 mask)
1180 {
1181 	unsigned weight = 0;
1182 	do
1183 	{
1184 		weight += (mask & 1);
1185 	}
1186 	while (mask >>= 1);
1187 	return weight;
1188 }
1189 
1190 static BOOL
select_visual(RDPCLIENT * This)1191 select_visual(RDPCLIENT * This)
1192 {
1193 	XPixmapFormatValues *pfm;
1194 	int pixmap_formats_count, visuals_count;
1195 	XVisualInfo *vmatches = NULL;
1196 	XVisualInfo template;
1197 	int i;
1198 	unsigned red_weight, blue_weight, green_weight;
1199 
1200 	red_weight = blue_weight = green_weight = 0;
1201 
1202 	if (This->server_depth == -1)
1203 	{
1204 		This->server_depth = DisplayPlanes(This->display, DefaultScreen(This->display));
1205 	}
1206 
1207 	pfm = XListPixmapFormats(This->display, &pixmap_formats_count);
1208 	if (pfm == NULL)
1209 	{
1210 		error("Unable to get list of pixmap formats from display.\n");
1211 		XCloseDisplay(This->display);
1212 		return False;
1213 	}
1214 
1215 	/* Search for best TrueColor visual */
1216 	template.class = TrueColor;
1217 	vmatches = XGetVisualInfo(This->display, VisualClassMask, &template, &visuals_count);
1218 	This->xwin.visual = NULL;
1219 	This->xwin.no_translate_image = False;
1220 	This->xwin.compatible_arch = False;
1221 	if (vmatches != NULL)
1222 	{
1223 		for (i = 0; i < visuals_count; ++i)
1224 		{
1225 			XVisualInfo *visual_info = &vmatches[i];
1226 			BOOL can_translate_to_bpp = False;
1227 			int j;
1228 
1229 			/* Try to find a no-translation visual that'll
1230 			   allow us to use RDP bitmaps directly as ZPixmaps. */
1231 			if (!This->xwin.xserver_be && (((visual_info->depth == 15) &&
1232 					       /* R5G5B5 */
1233 					       (visual_info->red_mask == 0x7c00) &&
1234 					       (visual_info->green_mask == 0x3e0) &&
1235 					       (visual_info->blue_mask == 0x1f)) ||
1236 					      ((visual_info->depth == 16) &&
1237 					       /* R5G6B5 */
1238 					       (visual_info->red_mask == 0xf800) &&
1239 					       (visual_info->green_mask == 0x7e0) &&
1240 					       (visual_info->blue_mask == 0x1f)) ||
1241 					      ((visual_info->depth == 24) &&
1242 					       /* R8G8B8 */
1243 					       (visual_info->red_mask == 0xff0000) &&
1244 					       (visual_info->green_mask == 0xff00) &&
1245 					       (visual_info->blue_mask == 0xff))))
1246 			{
1247 				This->xwin.visual = visual_info->visual;
1248 				This->xwin.depth = visual_info->depth;
1249 				This->xwin.compatible_arch = !This->xwin.host_be;
1250 				This->xwin.no_translate_image = (visual_info->depth == This->server_depth);
1251 				if (This->xwin.no_translate_image)
1252 					/* We found the best visual */
1253 					break;
1254 			}
1255 			else
1256 			{
1257 				This->xwin.compatible_arch = False;
1258 			}
1259 
1260 			if (visual_info->depth > 24)
1261 			{
1262 				/* Avoid 32-bit visuals and likes like the plague.
1263 				   They're either untested or proven to work bad
1264 				   (e.g. nvidia's Composite 32-bit visual).
1265 				   Most implementation offer a 24-bit visual anyway. */
1266 				continue;
1267 			}
1268 
1269 			/* Only care for visuals, for whose BPPs (not depths!)
1270 			   we have a translateXtoY function. */
1271 			for (j = 0; j < pixmap_formats_count; ++j)
1272 			{
1273 				if (pfm[j].depth == visual_info->depth)
1274 				{
1275 					if ((pfm[j].bits_per_pixel == 16) ||
1276 					    (pfm[j].bits_per_pixel == 24) ||
1277 					    (pfm[j].bits_per_pixel == 32))
1278 					{
1279 						can_translate_to_bpp = True;
1280 					}
1281 					break;
1282 				}
1283 			}
1284 
1285 			/* Prefer formats which have the most colour depth.
1286 			   We're being truly aristocratic here, minding each
1287 			   weight on its own. */
1288 			if (can_translate_to_bpp)
1289 			{
1290 				unsigned vis_red_weight =
1291 					calculate_mask_weight(visual_info->red_mask);
1292 				unsigned vis_green_weight =
1293 					calculate_mask_weight(visual_info->green_mask);
1294 				unsigned vis_blue_weight =
1295 					calculate_mask_weight(visual_info->blue_mask);
1296 				if ((vis_red_weight >= red_weight)
1297 				    && (vis_green_weight >= green_weight)
1298 				    && (vis_blue_weight >= blue_weight))
1299 				{
1300 					red_weight = vis_red_weight;
1301 					green_weight = vis_green_weight;
1302 					blue_weight = vis_blue_weight;
1303 					This->xwin.visual = visual_info->visual;
1304 					This->xwin.depth = visual_info->depth;
1305 				}
1306 			}
1307 		}
1308 		XFree(vmatches);
1309 	}
1310 
1311 	if (This->xwin.visual != NULL)
1312 	{
1313 		This->owncolmap = False;
1314 		calculate_shifts(This->xwin.visual->red_mask, &This->xwin.red_shift_r, &This->xwin.red_shift_l);
1315 		calculate_shifts(This->xwin.visual->green_mask, &This->xwin.green_shift_r, &This->xwin.green_shift_l);
1316 		calculate_shifts(This->xwin.visual->blue_mask, &This->xwin.blue_shift_r, &This->xwin.blue_shift_l);
1317 	}
1318 	else
1319 	{
1320 		template.class = PseudoColor;
1321 		template.depth = 8;
1322 		template.colormap_size = 256;
1323 		vmatches =
1324 			XGetVisualInfo(This->display,
1325 				       VisualClassMask | VisualDepthMask | VisualColormapSizeMask,
1326 				       &template, &visuals_count);
1327 		if (vmatches == NULL)
1328 		{
1329 			error("No usable TrueColor or PseudoColor visuals on this display.\n");
1330 			XCloseDisplay(This->display);
1331 			XFree(pfm);
1332 			return False;
1333 		}
1334 
1335 		/* we use a colourmap, so the default visual should do */
1336 		This->owncolmap = True;
1337 		This->xwin.visual = vmatches[0].visual;
1338 		This->xwin.depth = vmatches[0].depth;
1339 	}
1340 
1341 	This->xwin.bpp = 0;
1342 	for (i = 0; i < pixmap_formats_count; ++i)
1343 	{
1344 		XPixmapFormatValues *pf = &pfm[i];
1345 		if (pf->depth == This->xwin.depth)
1346 		{
1347 			This->xwin.bpp = pf->bits_per_pixel;
1348 
1349 			if (This->xwin.no_translate_image)
1350 			{
1351 				switch (This->server_depth)
1352 				{
1353 					case 15:
1354 					case 16:
1355 						if (This->xwin.bpp != 16)
1356 							This->xwin.no_translate_image = False;
1357 						break;
1358 					case 24:
1359 						/* Yes, this will force image translation
1360 						   on most modern servers which use 32 bits
1361 						   for R8G8B8. */
1362 						if (This->xwin.bpp != 24)
1363 							This->xwin.no_translate_image = False;
1364 						break;
1365 					default:
1366 						This->xwin.no_translate_image = False;
1367 						break;
1368 				}
1369 			}
1370 
1371 			/* Pixmap formats list is a depth-to-bpp mapping --
1372 			   there's just a single entry for every depth,
1373 			   so we can safely break here */
1374 			break;
1375 		}
1376 	}
1377 	XFree(pfm);
1378 	pfm = NULL;
1379 	return True;
1380 }
1381 
1382 /*
1383 static int
1384 error_handler(RDPCLIENT * This, Display * dpy, XErrorEvent * eev)
1385 {
1386 	if ((eev->error_code == BadMatch) && (eev->request_code == X_ConfigureWindow))
1387 	{
1388 		fprintf(stderr, "Got \"BadMatch\" when trying to restack windows.\n");
1389 		fprintf(stderr,
1390 			"This is most likely caused by a broken window manager (commonly KWin).\n");
1391 		return 0;
1392 	}
1393 
1394 	return This->xwin.old_error_handler(dpy, eev);
1395 }
1396 */
1397 
1398 BOOL
ui_init(RDPCLIENT * This)1399 ui_init(RDPCLIENT * This)
1400 {
1401 	int screen_num;
1402 
1403 	This->display = XOpenDisplay(NULL);
1404 	if (This->display == NULL)
1405 	{
1406 		error("Failed to open display: %s\n", XDisplayName(NULL));
1407 		return False;
1408 	}
1409 
1410 	{
1411 		uint16 endianess_test = 1;
1412 		This->xwin.host_be = !(BOOL) (*(uint8 *) (&endianess_test));
1413 	}
1414 
1415 	/*This->xwin.old_error_handler = XSetErrorHandler(error_handler);*/
1416 	This->xwin.xserver_be = (ImageByteOrder(This->display) == MSBFirst);
1417 	screen_num = DefaultScreen(This->display);
1418 	This->xwin.x_socket = ConnectionNumber(This->display);
1419 	This->xwin.screen = ScreenOfDisplay(This->display, screen_num);
1420 	This->xwin.depth = DefaultDepthOfScreen(This->xwin.screen);
1421 
1422 	if (!select_visual(This))
1423 		return False;
1424 
1425 	if (This->xwin.no_translate_image)
1426 	{
1427 		DEBUG(("Performance optimization possible: avoiding image translation (colour depth conversion).\n"));
1428 	}
1429 
1430 	if (This->server_depth > This->xwin.bpp)
1431 	{
1432 		warning("Remote desktop colour depth %d higher than display colour depth %d.\n",
1433 			This->server_depth, This->xwin.bpp);
1434 	}
1435 
1436 	DEBUG(("RDP depth: %d, display depth: %d, display bpp: %d, X server BE: %d, host BE: %d\n",
1437 	       This->server_depth, This->xwin.depth, This->xwin.bpp, This->xwin.xserver_be, This->xwin.host_be));
1438 
1439 	if (!This->owncolmap)
1440 	{
1441 		This->xwin.xcolmap =
1442 			XCreateColormap(This->display, RootWindowOfScreen(This->xwin.screen), This->xwin.visual,
1443 					AllocNone);
1444 		if (This->xwin.depth <= 8)
1445 			warning("Display colour depth is %d bit: you may want to use -C for a private colourmap.\n", This->xwin.depth);
1446 	}
1447 
1448 	if ((!This->ownbackstore) && (DoesBackingStore(This->xwin.screen) != Always))
1449 	{
1450 		warning("External BackingStore not available. Using internal.\n");
1451 		This->ownbackstore = True;
1452 	}
1453 
1454 	/*
1455 	 * Determine desktop size
1456 	 */
1457 	if (This->fullscreen)
1458 	{
1459 		This->width = WidthOfScreen(This->xwin.screen);
1460 		This->height = HeightOfScreen(This->xwin.screen);
1461 		This->xwin.using_full_workarea = True;
1462 	}
1463 	else if (This->width < 0)
1464 	{
1465 		/* Percent of screen */
1466 		if (-This->width >= 100)
1467 			This->xwin.using_full_workarea = True;
1468 		This->height = HeightOfScreen(This->xwin.screen) * (-This->width) / 100;
1469 		This->width = WidthOfScreen(This->xwin.screen) * (-This->width) / 100;
1470 	}
1471 	else if (This->width == 0)
1472 	{
1473 		/* Fetch geometry from _NET_WORKAREA */
1474 		uint32 x, y, cx, cy;
1475 		if (get_current_workarea(This, &x, &y, &cx, &cy) == 0)
1476 		{
1477 			This->width = cx;
1478 			This->height = cy;
1479 			This->xwin.using_full_workarea = True;
1480 		}
1481 		else
1482 		{
1483 			warning("Failed to get workarea: probably your window manager does not support extended hints\n");
1484 			This->width = WidthOfScreen(This->xwin.screen);
1485 			This->height = HeightOfScreen(This->xwin.screen);
1486 		}
1487 	}
1488 
1489 	/* make sure width is a multiple of 4 */
1490 	This->width = (This->width + 3) & ~3;
1491 
1492 	This->xwin.mod_map = XGetModifierMapping(This->display);
1493 
1494 	xkeymap_init(This);
1495 
1496 	if (This->enable_compose)
1497 		This->xwin.IM = XOpenIM(This->display, NULL, NULL, NULL);
1498 
1499 	xclip_init(This);
1500 	ewmh_init(This);
1501 	if (This->seamless_rdp)
1502 		seamless_init(This);
1503 
1504 	DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", This->server_depth, This->xwin.bpp, This->xwin.depth));
1505 
1506 	return True;
1507 }
1508 
1509 void
ui_deinit(RDPCLIENT * This)1510 ui_deinit(RDPCLIENT * This)
1511 {
1512 	while (This->xwin.seamless_windows)
1513 	{
1514 		XDestroyWindow(This->display, This->xwin.seamless_windows->wnd);
1515 		sw_remove_window(This, This->xwin.seamless_windows);
1516 	}
1517 
1518 	xclip_deinit(This);
1519 
1520 	if (This->xwin.IM != NULL)
1521 		XCloseIM(This->xwin.IM);
1522 
1523 	if (This->xwin.null_cursor != NULL)
1524 		ui_destroy_cursor(This, This->xwin.null_cursor);
1525 
1526 	XFreeModifiermap(This->xwin.mod_map);
1527 
1528 	if (This->ownbackstore)
1529 		XFreePixmap(This->display, This->xwin.backstore);
1530 
1531 	XFreeGC(This->display, This->xwin.gc);
1532 	XCloseDisplay(This->display);
1533 	This->display = NULL;
1534 }
1535 
1536 
1537 static void
get_window_attribs(RDPCLIENT * This,XSetWindowAttributes * attribs)1538 get_window_attribs(RDPCLIENT * This, XSetWindowAttributes * attribs)
1539 {
1540 	attribs->background_pixel = BlackPixelOfScreen(This->xwin.screen);
1541 	attribs->background_pixel = WhitePixelOfScreen(This->xwin.screen);
1542 	attribs->border_pixel = WhitePixelOfScreen(This->xwin.screen);
1543 	attribs->backing_store = This->ownbackstore ? NotUseful : Always;
1544 	attribs->override_redirect = This->fullscreen;
1545 	attribs->colormap = This->xwin.xcolmap;
1546 }
1547 
1548 static void
get_input_mask(RDPCLIENT * This,long * input_mask)1549 get_input_mask(RDPCLIENT * This, long *input_mask)
1550 {
1551 	*input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
1552 		VisibilityChangeMask | FocusChangeMask | StructureNotifyMask;
1553 
1554 	if (This->sendmotion)
1555 		*input_mask |= PointerMotionMask;
1556 	if (This->ownbackstore)
1557 		*input_mask |= ExposureMask;
1558 	if (This->fullscreen || This->grab_keyboard)
1559 		*input_mask |= EnterWindowMask;
1560 	if (This->grab_keyboard)
1561 		*input_mask |= LeaveWindowMask;
1562 }
1563 
1564 BOOL
ui_create_window(RDPCLIENT * This)1565 ui_create_window(RDPCLIENT * This)
1566 {
1567 	uint8 null_pointer_mask[1] = { 0x80 };
1568 	uint8 null_pointer_data[24] = { 0x00 };
1569 
1570 	XSetWindowAttributes attribs;
1571 	XClassHint *classhints;
1572 	XSizeHints *sizehints;
1573 	int wndwidth, wndheight;
1574 	long input_mask, ic_input_mask;
1575 	XEvent xevent;
1576 
1577 	wndwidth = This->fullscreen ? WidthOfScreen(This->xwin.screen) : This->width;
1578 	wndheight = This->fullscreen ? HeightOfScreen(This->xwin.screen) : This->height;
1579 
1580 	/* Handle -x-y portion of geometry string */
1581 	if (This->xpos < 0 || (This->xpos == 0 && (This->pos & 2)))
1582 		This->xpos = WidthOfScreen(This->xwin.screen) + This->xpos - This->width;
1583 	if (This->ypos < 0 || (This->ypos == 0 && (This->pos & 4)))
1584 		This->ypos = HeightOfScreen(This->xwin.screen) + This->ypos - This->height;
1585 
1586 	get_window_attribs(This, &attribs);
1587 
1588 	This->wnd = XCreateWindow(This->display, RootWindowOfScreen(This->xwin.screen), This->xpos, This->ypos, wndwidth,
1589 			      wndheight, 0, This->xwin.depth, InputOutput, This->xwin.visual,
1590 			      CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
1591 			      CWBorderPixel, &attribs);
1592 
1593 	if (This->xwin.gc == NULL)
1594 	{
1595 		This->xwin.gc = XCreateGC(This->display, This->wnd, 0, NULL);
1596 		ui_reset_clip(This);
1597 	}
1598 
1599 	if (This->xwin.create_bitmap_gc == NULL)
1600 		This->xwin.create_bitmap_gc = XCreateGC(This->display, This->wnd, 0, NULL);
1601 
1602 	if ((This->ownbackstore) && (This->xwin.backstore == 0))
1603 	{
1604 		This->xwin.backstore = XCreatePixmap(This->display, This->wnd, This->width, This->height, This->xwin.depth);
1605 
1606 		/* clear to prevent rubbish being exposed at startup */
1607 		XSetForeground(This->display, This->xwin.gc, BlackPixelOfScreen(This->xwin.screen));
1608 		XFillRectangle(This->display, This->xwin.backstore, This->xwin.gc, 0, 0, This->width, This->height);
1609 	}
1610 
1611 	XStoreName(This->display, This->wnd, This->title);
1612 
1613 	if (This->hide_decorations)
1614 		mwm_hide_decorations(This, This->wnd);
1615 
1616 	classhints = XAllocClassHint();
1617 	if (classhints != NULL)
1618 	{
1619 		classhints->res_name = classhints->res_class = "rdesktop";
1620 		XSetClassHint(This->display, This->wnd, classhints);
1621 		XFree(classhints);
1622 	}
1623 
1624 	sizehints = XAllocSizeHints();
1625 	if (sizehints)
1626 	{
1627 		sizehints->flags = PMinSize | PMaxSize;
1628 		if (This->pos)
1629 			sizehints->flags |= PPosition;
1630 		sizehints->min_width = sizehints->max_width = This->width;
1631 		sizehints->min_height = sizehints->max_height = This->height;
1632 		XSetWMNormalHints(This->display, This->wnd, sizehints);
1633 		XFree(sizehints);
1634 	}
1635 
1636 	if (This->embed_wnd)
1637 	{
1638 		XReparentWindow(This->display, This->wnd, (Window) This->embed_wnd, 0, 0);
1639 	}
1640 
1641 	get_input_mask(This, &input_mask);
1642 
1643 	if (This->xwin.IM != NULL)
1644 	{
1645 		This->xwin.IC = XCreateIC(This->xwin.IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
1646 				 XNClientWindow, This->wnd, XNFocusWindow, This->wnd, NULL);
1647 
1648 		if ((This->xwin.IC != NULL)
1649 		    && (XGetICValues(This->xwin.IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
1650 			input_mask |= ic_input_mask;
1651 	}
1652 
1653 	XSelectInput(This->display, This->wnd, input_mask);
1654 	XMapWindow(This->display, This->wnd);
1655 
1656 	/* wait for VisibilityNotify */
1657 	do
1658 	{
1659 		XMaskEvent(This->display, VisibilityChangeMask, &xevent);
1660 	}
1661 	while (xevent.type != VisibilityNotify);
1662 	This->Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1663 
1664 	This->xwin.focused = False;
1665 	This->xwin.mouse_in_wnd = False;
1666 
1667 	/* handle the WM_DELETE_WINDOW protocol */
1668 	This->xwin.protocol_atom = XInternAtom(This->display, "WM_PROTOCOLS", True);
1669 	This->xwin.kill_atom = XInternAtom(This->display, "WM_DELETE_WINDOW", True);
1670 	XSetWMProtocols(This->display, This->wnd, &This->xwin.kill_atom, 1);
1671 
1672 	/* create invisible 1x1 cursor to be used as null cursor */
1673 	if (This->xwin.null_cursor == NULL)
1674 		This->xwin.null_cursor = ui_create_cursor(This, 0, 0, 1, 1, null_pointer_mask, null_pointer_data);
1675 
1676 	return True;
1677 }
1678 
1679 void
ui_resize_window(RDPCLIENT * This)1680 ui_resize_window(RDPCLIENT * This)
1681 {
1682 	XSizeHints *sizehints;
1683 	Pixmap bs;
1684 
1685 	sizehints = XAllocSizeHints();
1686 	if (sizehints)
1687 	{
1688 		sizehints->flags = PMinSize | PMaxSize;
1689 		sizehints->min_width = sizehints->max_width = This->width;
1690 		sizehints->min_height = sizehints->max_height = This->height;
1691 		XSetWMNormalHints(This->display, This->wnd, sizehints);
1692 		XFree(sizehints);
1693 	}
1694 
1695 	if (!(This->fullscreen || This->embed_wnd))
1696 	{
1697 		XResizeWindow(This->display, This->wnd, This->width, This->height);
1698 	}
1699 
1700 	/* create new backstore pixmap */
1701 	if (This->xwin.backstore != 0)
1702 	{
1703 		bs = XCreatePixmap(This->display, This->wnd, This->width, This->height, This->xwin.depth);
1704 		XSetForeground(This->display, This->xwin.gc, BlackPixelOfScreen(This->xwin.screen));
1705 		XFillRectangle(This->display, bs, This->xwin.gc, 0, 0, This->width, This->height);
1706 		XCopyArea(This->display, This->xwin.backstore, bs, This->xwin.gc, 0, 0, This->width, This->height, 0, 0);
1707 		XFreePixmap(This->display, This->xwin.backstore);
1708 		This->xwin.backstore = bs;
1709 	}
1710 }
1711 
1712 void
ui_destroy_window(RDPCLIENT * This)1713 ui_destroy_window(RDPCLIENT * This)
1714 {
1715 	if (This->xwin.IC != NULL)
1716 		XDestroyIC(This->xwin.IC);
1717 
1718 	XDestroyWindow(This->display, This->wnd);
1719 }
1720 
1721 void
xwin_toggle_fullscreen(RDPCLIENT * This)1722 xwin_toggle_fullscreen(RDPCLIENT * This)
1723 {
1724 	Pixmap contents = 0;
1725 
1726 	if (This->xwin.seamless_active)
1727 		/* Turn off SeamlessRDP mode */
1728 		ui_seamless_toggle(This);
1729 
1730 	if (!This->ownbackstore)
1731 	{
1732 		/* need to save contents of window */
1733 		contents = XCreatePixmap(This->display, This->wnd, This->width, This->height, This->xwin.depth);
1734 		XCopyArea(This->display, This->wnd, contents, This->xwin.gc, 0, 0, This->width, This->height, 0, 0);
1735 	}
1736 
1737 	ui_destroy_window(This);
1738 	This->fullscreen = !This->fullscreen;
1739 	ui_create_window(This);
1740 
1741 	XDefineCursor(This->display, This->wnd, This->xwin.current_cursor);
1742 
1743 	if (!This->ownbackstore)
1744 	{
1745 		XCopyArea(This->display, contents, This->wnd, This->xwin.gc, 0, 0, This->width, This->height, 0, 0);
1746 		XFreePixmap(This->display, contents);
1747 	}
1748 }
1749 
1750 static void
handle_button_event(RDPCLIENT * This,XEvent xevent,BOOL down)1751 handle_button_event(RDPCLIENT * This, XEvent xevent, BOOL down)
1752 {
1753 	uint16 button, flags = 0;
1754 	This->last_gesturetime = xevent.xbutton.time;
1755 	button = xkeymap_translate_button(xevent.xbutton.button);
1756 	if (button == 0)
1757 		return;
1758 
1759 	if (down)
1760 		flags = MOUSE_FLAG_DOWN;
1761 
1762 	/* Stop moving window when button is released, regardless of cursor position */
1763 	if (This->xwin.moving_wnd && (xevent.type == ButtonRelease))
1764 		This->xwin.moving_wnd = False;
1765 
1766 	/* If win_button_sizee is nonzero, enable single app mode */
1767 	if (xevent.xbutton.y < This->win_button_size)
1768 	{
1769 		/*  Check from right to left: */
1770 		if (xevent.xbutton.x >= This->width - This->win_button_size)
1771 		{
1772 			/* The close button, continue */
1773 			;
1774 		}
1775 		else if (xevent.xbutton.x >= This->width - This->win_button_size * 2)
1776 		{
1777 			/* The maximize/restore button. Do not send to
1778 			   server.  It might be a good idea to change the
1779 			   cursor or give some other visible indication
1780 			   that rdesktop inhibited this click */
1781 			if (xevent.type == ButtonPress)
1782 				return;
1783 		}
1784 		else if (xevent.xbutton.x >= This->width - This->win_button_size * 3)
1785 		{
1786 			/* The minimize button. Iconify window. */
1787 			if (xevent.type == ButtonRelease)
1788 			{
1789 				/* Release the mouse button outside the minimize button, to prevent the
1790 				   actual minimazation to happen */
1791 				rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE, button, 1, 1);
1792 				XIconifyWindow(This->display, This->wnd, DefaultScreen(This->display));
1793 				return;
1794 			}
1795 		}
1796 		else if (xevent.xbutton.x <= This->win_button_size)
1797 		{
1798 			/* The system menu. Ignore. */
1799 			if (xevent.type == ButtonPress)
1800 				return;
1801 		}
1802 		else
1803 		{
1804 			/* The title bar. */
1805 			if (xevent.type == ButtonPress)
1806 			{
1807 				if (!This->fullscreen && This->hide_decorations && !This->xwin.using_full_workarea)
1808 				{
1809 					This->xwin.moving_wnd = True;
1810 					This->xwin.move_x_offset = xevent.xbutton.x;
1811 					This->xwin.move_y_offset = xevent.xbutton.y;
1812 				}
1813 				return;
1814 			}
1815 		}
1816 	}
1817 
1818 	if (xevent.xmotion.window == This->wnd)
1819 	{
1820 		rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE,
1821 			       flags | button, xevent.xbutton.x, xevent.xbutton.y);
1822 	}
1823 	else
1824 	{
1825 		/* SeamlessRDP */
1826 		rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE,
1827 			       flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root);
1828 	}
1829 }
1830 
1831 
1832 /* Process events in Xlib queue
1833    Returns 0 after user quit, 1 otherwise */
1834 static int
xwin_process_events(RDPCLIENT * This)1835 xwin_process_events(RDPCLIENT * This)
1836 {
1837 	XEvent xevent;
1838 	KeySym keysym;
1839 	uint32 ev_time;
1840 	char str[256];
1841 	Status status;
1842 	int events = 0;
1843 	seamless_window *sw;
1844 
1845 	while ((XPending(This->display) > 0) && events++ < 20)
1846 	{
1847 		XNextEvent(This->display, &xevent);
1848 
1849 		if ((This->xwin.IC != NULL) && (XFilterEvent(&xevent, None) == True))
1850 		{
1851 			DEBUG_KBD(("Filtering event\n"));
1852 			continue;
1853 		}
1854 
1855 		switch (xevent.type)
1856 		{
1857 			case VisibilityNotify:
1858 				if (xevent.xvisibility.window == This->wnd)
1859 					This->Unobscured =
1860 						xevent.xvisibility.state == VisibilityUnobscured;
1861 
1862 				break;
1863 			case ClientMessage:
1864 				/* the window manager told us to quit */
1865 				if ((xevent.xclient.message_type == This->xwin.protocol_atom)
1866 				    && ((Atom) xevent.xclient.data.l[0] == This->xwin.kill_atom))
1867 					/* Quit */
1868 					return 0;
1869 				break;
1870 
1871 			case KeyPress:
1872 				This->last_gesturetime = xevent.xkey.time;
1873 				if (This->xwin.IC != NULL)
1874 					/* Multi_key compatible version */
1875 				{
1876 					XmbLookupString(This->xwin.IC,
1877 							&xevent.xkey, str, sizeof(str), &keysym,
1878 							&status);
1879 					if (!((status == XLookupKeySym) || (status == XLookupBoth)))
1880 					{
1881 						error("XmbLookupString failed with status 0x%x\n",
1882 						      status);
1883 						break;
1884 					}
1885 				}
1886 				else
1887 				{
1888 					/* Plain old XLookupString */
1889 					DEBUG_KBD(("\nNo input context, using XLookupString\n"));
1890 					XLookupString((XKeyEvent *) & xevent,
1891 						      str, sizeof(str), &keysym, NULL);
1892 				}
1893 
1894 				DEBUG_KBD(("KeyPress for keysym (0x%lx, %s)\n", keysym,
1895 					   get_ksname(keysym)));
1896 
1897 				ev_time = time(NULL);
1898 				if (handle_special_keys(This, keysym, xevent.xkey.state, ev_time, True))
1899 					break;
1900 
1901 				xkeymap_send_keys(This, keysym, xevent.xkey.keycode, xevent.xkey.state,
1902 						  ev_time, True, 0);
1903 				break;
1904 
1905 			case KeyRelease:
1906 				This->last_gesturetime = xevent.xkey.time;
1907 				XLookupString((XKeyEvent *) & xevent, str,
1908 					      sizeof(str), &keysym, NULL);
1909 
1910 				DEBUG_KBD(("\nKeyRelease for keysym (0x%lx, %s)\n", keysym,
1911 					   get_ksname(keysym)));
1912 
1913 				ev_time = time(NULL);
1914 				if (handle_special_keys(This, keysym, xevent.xkey.state, ev_time, False))
1915 					break;
1916 
1917 				xkeymap_send_keys(This, keysym, xevent.xkey.keycode, xevent.xkey.state,
1918 						  ev_time, False, 0);
1919 				break;
1920 
1921 			case ButtonPress:
1922 				handle_button_event(This, xevent, True);
1923 				break;
1924 
1925 			case ButtonRelease:
1926 				handle_button_event(This, xevent, False);
1927 				break;
1928 
1929 			case MotionNotify:
1930 				if (This->xwin.moving_wnd)
1931 				{
1932 					XMoveWindow(This->display, This->wnd,
1933 						    xevent.xmotion.x_root - This->xwin.move_x_offset,
1934 						    xevent.xmotion.y_root - This->xwin.move_y_offset);
1935 					break;
1936 				}
1937 
1938 				if (This->fullscreen && !This->xwin.focused)
1939 					XSetInputFocus(This->display, This->wnd, RevertToPointerRoot,
1940 						       CurrentTime);
1941 
1942 				if (xevent.xmotion.window == This->wnd)
1943 				{
1944 					rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1945 						       xevent.xmotion.x, xevent.xmotion.y);
1946 				}
1947 				else
1948 				{
1949 					/* SeamlessRDP */
1950 					rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1951 						       xevent.xmotion.x_root,
1952 						       xevent.xmotion.y_root);
1953 				}
1954 				break;
1955 
1956 			case FocusIn:
1957 				if (xevent.xfocus.mode == NotifyGrab)
1958 					break;
1959 				This->xwin.focused = True;
1960 				reset_modifier_keys(This);
1961 				if (This->grab_keyboard && This->xwin.mouse_in_wnd)
1962 					XGrabKeyboard(This->display, This->wnd, True,
1963 						      GrabModeAsync, GrabModeAsync, CurrentTime);
1964 
1965 				sw = sw_get_window_by_wnd(This, xevent.xfocus.window);
1966 				if (!sw)
1967 					break;
1968 
1969 				if (sw->id != This->xwin.seamless_focused)
1970 				{
1971 					seamless_send_focus(This, sw->id, 0);
1972 					This->xwin.seamless_focused = sw->id;
1973 				}
1974 				break;
1975 
1976 			case FocusOut:
1977 				if (xevent.xfocus.mode == NotifyUngrab)
1978 					break;
1979 				This->xwin.focused = False;
1980 				if (xevent.xfocus.mode == NotifyWhileGrabbed)
1981 					XUngrabKeyboard(This->display, CurrentTime);
1982 				break;
1983 
1984 			case EnterNotify:
1985 				/* we only register for this event when in fullscreen mode */
1986 				/* or grab_keyboard */
1987 				This->xwin.mouse_in_wnd = True;
1988 				if (This->fullscreen)
1989 				{
1990 					XSetInputFocus(This->display, This->wnd, RevertToPointerRoot,
1991 						       CurrentTime);
1992 					break;
1993 				}
1994 				if (This->xwin.focused)
1995 					XGrabKeyboard(This->display, This->wnd, True,
1996 						      GrabModeAsync, GrabModeAsync, CurrentTime);
1997 				break;
1998 
1999 			case LeaveNotify:
2000 				/* we only register for this event when grab_keyboard */
2001 				This->xwin.mouse_in_wnd = False;
2002 				XUngrabKeyboard(This->display, CurrentTime);
2003 				break;
2004 
2005 			case Expose:
2006 				if (xevent.xexpose.window == This->wnd)
2007 				{
2008 					XCopyArea(This->display, This->xwin.backstore, xevent.xexpose.window,
2009 						  This->xwin.gc,
2010 						  xevent.xexpose.x, xevent.xexpose.y,
2011 						  xevent.xexpose.width, xevent.xexpose.height,
2012 						  xevent.xexpose.x, xevent.xexpose.y);
2013 				}
2014 				else
2015 				{
2016 					sw = sw_get_window_by_wnd(This, xevent.xexpose.window);
2017 					if (!sw)
2018 						break;
2019 					XCopyArea(This->display, This->xwin.backstore,
2020 						  xevent.xexpose.window, This->xwin.gc,
2021 						  xevent.xexpose.x + sw->xoffset,
2022 						  xevent.xexpose.y + sw->yoffset,
2023 						  xevent.xexpose.width,
2024 						  xevent.xexpose.height, xevent.xexpose.x,
2025 						  xevent.xexpose.y);
2026 				}
2027 
2028 				break;
2029 
2030 			case MappingNotify:
2031 				/* Refresh keyboard mapping if it has changed. This is important for
2032 				   Xvnc, since it allocates keycodes dynamically */
2033 				if (xevent.xmapping.request == MappingKeyboard
2034 				    || xevent.xmapping.request == MappingModifier)
2035 					XRefreshKeyboardMapping(&xevent.xmapping);
2036 
2037 				if (xevent.xmapping.request == MappingModifier)
2038 				{
2039 					XFreeModifiermap(This->xwin.mod_map);
2040 					This->xwin.mod_map = XGetModifierMapping(This->display);
2041 				}
2042 				break;
2043 
2044 				/* clipboard stuff */
2045 			case SelectionNotify:
2046 				xclip_handle_SelectionNotify(This, &xevent.xselection);
2047 				break;
2048 			case SelectionRequest:
2049 				xclip_handle_SelectionRequest(This, &xevent.xselectionrequest);
2050 				break;
2051 			case SelectionClear:
2052 				xclip_handle_SelectionClear(This);
2053 				break;
2054 			case PropertyNotify:
2055 				xclip_handle_PropertyNotify(This, &xevent.xproperty);
2056 				if (xevent.xproperty.window == This->wnd)
2057 					break;
2058 				if (xevent.xproperty.window == DefaultRootWindow(This->display))
2059 					break;
2060 
2061 				/* seamless */
2062 				sw = sw_get_window_by_wnd(This, xevent.xproperty.window);
2063 				if (!sw)
2064 					break;
2065 
2066 				if ((xevent.xproperty.atom == This->net_wm_state_atom)
2067 				    && (xevent.xproperty.state == PropertyNewValue))
2068 				{
2069 					sw->state = ewmh_get_window_state(This, sw->wnd);
2070 					seamless_send_state(This, sw->id, sw->state, 0);
2071 				}
2072 
2073 				if ((xevent.xproperty.atom == This->net_wm_desktop_atom)
2074 				    && (xevent.xproperty.state == PropertyNewValue))
2075 				{
2076 					sw->desktop = ewmh_get_window_desktop(This, sw->wnd);
2077 					sw_all_to_desktop(This, sw->wnd, sw->desktop);
2078 				}
2079 
2080 				break;
2081 			case MapNotify:
2082 				if (!This->xwin.seamless_active)
2083 					rdp_send_client_window_status(This, 1);
2084 				break;
2085 			case UnmapNotify:
2086 				if (!This->xwin.seamless_active)
2087 					rdp_send_client_window_status(This, 0);
2088 				break;
2089 			case ConfigureNotify:
2090 				if (!This->xwin.seamless_active)
2091 					break;
2092 
2093 				sw = sw_get_window_by_wnd(This, xevent.xconfigure.window);
2094 				if (!sw)
2095 					break;
2096 
2097 				gettimeofday(sw->position_timer, NULL);
2098 				if (sw->position_timer->tv_usec + SEAMLESSRDP_POSITION_TIMER >=
2099 				    1000000)
2100 				{
2101 					sw->position_timer->tv_usec +=
2102 						SEAMLESSRDP_POSITION_TIMER - 1000000;
2103 					sw->position_timer->tv_sec += 1;
2104 				}
2105 				else
2106 				{
2107 					sw->position_timer->tv_usec += SEAMLESSRDP_POSITION_TIMER;
2108 				}
2109 
2110 				sw_handle_restack(This, sw);
2111 				break;
2112 		}
2113 	}
2114 	/* Keep going */
2115 	return 1;
2116 }
2117 
2118 /* Returns 0 after user quit, 1 otherwise */
2119 int
ui_select(RDPCLIENT * This,int rdp_socket)2120 ui_select(RDPCLIENT * This, int rdp_socket)
2121 {
2122 	int n;
2123 	fd_set rfds, wfds;
2124 	struct timeval tv;
2125 	BOOL s_timeout = False;
2126 
2127 	while (True)
2128 	{
2129 		n = (rdp_socket > This->xwin.x_socket) ? rdp_socket : This->xwin.x_socket;
2130 		/* Process any events already waiting */
2131 		if (!xwin_process_events(This))
2132 			/* User quit */
2133 			return 0;
2134 
2135 		if (This->xwin.seamless_active)
2136 			sw_check_timers(This);
2137 
2138 		FD_ZERO(&rfds);
2139 		FD_ZERO(&wfds);
2140 		FD_SET(rdp_socket, &rfds);
2141 		FD_SET(This->xwin.x_socket, &rfds);
2142 
2143 #ifdef WITH_RDPSND
2144 		/* FIXME: there should be an API for registering fds */
2145 		if (This->dsp_busy)
2146 		{
2147 			FD_SET(This->dsp_fd, &wfds);
2148 			n = (This->dsp_fd > n) ? This->dsp_fd : n;
2149 		}
2150 #endif
2151 		/* default timeout */
2152 		tv.tv_sec = 60;
2153 		tv.tv_usec = 0;
2154 
2155 		/* add redirection handles */
2156 		rdpdr_add_fds(This, &n, &rfds, &wfds, &tv, &s_timeout);
2157 		seamless_select_timeout(This, &tv);
2158 
2159 		n++;
2160 
2161 		switch (select(n, &rfds, &wfds, NULL, &tv))
2162 		{
2163 			case -1:
2164 				error("select: %s\n", strerror(errno));
2165 
2166 			case 0:
2167 				/* Abort serial read calls */
2168 				if (s_timeout)
2169 					rdpdr_check_fds(This, &rfds, &wfds, (BOOL) True);
2170 				continue;
2171 		}
2172 
2173 		rdpdr_check_fds(This, &rfds, &wfds, (BOOL) False);
2174 
2175 		if (FD_ISSET(rdp_socket, &rfds))
2176 			return 1;
2177 
2178 #ifdef WITH_RDPSND
2179 		if (This->dsp_busy && FD_ISSET(This->dsp_fd, &wfds))
2180 			wave_out_play();
2181 #endif
2182 	}
2183 }
2184 
2185 void
ui_move_pointer(RDPCLIENT * This,int x,int y)2186 ui_move_pointer(RDPCLIENT * This, int x, int y)
2187 {
2188 	XWarpPointer(This->display, This->wnd, This->wnd, 0, 0, 0, 0, x, y);
2189 }
2190 
2191 HBITMAP
ui_create_bitmap(RDPCLIENT * This,int width,int height,uint8 * data)2192 ui_create_bitmap(RDPCLIENT * This, int width, int height, uint8 * data)
2193 {
2194 	XImage *image;
2195 	Pixmap bitmap;
2196 	uint8 *tdata;
2197 	int bitmap_pad;
2198 
2199 	if (This->server_depth == 8)
2200 	{
2201 		bitmap_pad = 8;
2202 	}
2203 	else
2204 	{
2205 		bitmap_pad = This->xwin.bpp;
2206 
2207 		if (This->xwin.bpp == 24)
2208 			bitmap_pad = 32;
2209 	}
2210 
2211 	tdata = (This->owncolmap ? data : translate_image(This, width, height, data));
2212 	bitmap = XCreatePixmap(This->display, This->wnd, width, height, This->xwin.depth);
2213 	image = XCreateImage(This->display, This->xwin.visual, This->xwin.depth, ZPixmap, 0,
2214 			     (char *) tdata, width, height, bitmap_pad, 0);
2215 
2216 	XPutImage(This->display, bitmap, This->xwin.create_bitmap_gc, image, 0, 0, 0, 0, width, height);
2217 
2218 	XFree(image);
2219 	if (tdata != data)
2220 		xfree(tdata);
2221 	return (HBITMAP) bitmap;
2222 }
2223 
2224 void
ui_paint_bitmap(RDPCLIENT * This,int x,int y,int cx,int cy,int width,int height,uint8 * data)2225 ui_paint_bitmap(RDPCLIENT * This, int x, int y, int cx, int cy, int width, int height, uint8 * data)
2226 {
2227 	XImage *image;
2228 	uint8 *tdata;
2229 	int bitmap_pad;
2230 
2231 	if (This->server_depth == 8)
2232 	{
2233 		bitmap_pad = 8;
2234 	}
2235 	else
2236 	{
2237 		bitmap_pad = This->xwin.bpp;
2238 
2239 		if (This->xwin.bpp == 24)
2240 			bitmap_pad = 32;
2241 	}
2242 
2243 	tdata = (This->owncolmap ? data : translate_image(This, width, height, data));
2244 	image = XCreateImage(This->display, This->xwin.visual, This->xwin.depth, ZPixmap, 0,
2245 			     (char *) tdata, width, height, bitmap_pad, 0);
2246 
2247 	if (This->ownbackstore)
2248 	{
2249 		XPutImage(This->display, This->xwin.backstore, This->xwin.gc, image, 0, 0, x, y, cx, cy);
2250 		XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, x, y, cx, cy, x, y);
2251 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2252 					(This->display, This->xwin.backstore, sw->wnd, This->xwin.gc, x, y, cx, cy,
2253 					 x - sw->xoffset, y - sw->yoffset));
2254 	}
2255 	else
2256 	{
2257 		XPutImage(This->display, This->wnd, This->xwin.gc, image, 0, 0, x, y, cx, cy);
2258 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2259 					(This->display, This->wnd, sw->wnd, This->xwin.gc, x, y, cx, cy,
2260 					 x - sw->xoffset, y - sw->yoffset));
2261 	}
2262 
2263 	XFree(image);
2264 	if (tdata != data)
2265 		xfree(tdata);
2266 }
2267 
2268 void
ui_destroy_bitmap(RDPCLIENT * This,HBITMAP bmp)2269 ui_destroy_bitmap(RDPCLIENT * This, HBITMAP bmp)
2270 {
2271 	XFreePixmap(This->display, (Pixmap) bmp);
2272 }
2273 
2274 HGLYPH
ui_create_glyph(RDPCLIENT * This,int width,int height,const uint8 * data)2275 ui_create_glyph(RDPCLIENT * This, int width, int height, const uint8 * data)
2276 {
2277 	XImage *image;
2278 	Pixmap bitmap;
2279 	int scanline;
2280 
2281 	scanline = (width + 7) / 8;
2282 
2283 	bitmap = XCreatePixmap(This->display, This->wnd, width, height, 1);
2284 	if (This->xwin.create_glyph_gc == 0)
2285 		This->xwin.create_glyph_gc = XCreateGC(This->display, bitmap, 0, NULL);
2286 
2287 	image = XCreateImage(This->display, This->xwin.visual, 1, ZPixmap, 0, (char *) data,
2288 			     width, height, 8, scanline);
2289 	image->byte_order = MSBFirst;
2290 	image->bitmap_bit_order = MSBFirst;
2291 	XInitImage(image);
2292 
2293 	XPutImage(This->display, bitmap, This->xwin.create_glyph_gc, image, 0, 0, 0, 0, width, height);
2294 
2295 	XFree(image);
2296 	return (HGLYPH) bitmap;
2297 }
2298 
2299 void
ui_destroy_glyph(RDPCLIENT * This,HGLYPH glyph)2300 ui_destroy_glyph(RDPCLIENT * This, HGLYPH glyph)
2301 {
2302 	XFreePixmap(This->display, (Pixmap) glyph);
2303 }
2304 
2305 HCURSOR
ui_create_cursor(RDPCLIENT * This,unsigned int x,unsigned int y,int width,int height,uint8 * andmask,uint8 * xormask)2306 ui_create_cursor(RDPCLIENT * This, unsigned int x, unsigned int y, int width, int height,
2307 		 uint8 * andmask, uint8 * xormask)
2308 {
2309 	HGLYPH maskglyph, cursorglyph;
2310 	XColor bg, fg;
2311 	Cursor xcursor;
2312 	uint8 *cursor, *pcursor;
2313 	uint8 *mask, *pmask;
2314 	uint8 nextbit;
2315 	int scanline, offset;
2316 	int i, j;
2317 
2318 	scanline = (width + 7) / 8;
2319 	offset = scanline * height;
2320 
2321 	cursor = (uint8 *) xmalloc(offset);
2322 	memset(cursor, 0, offset);
2323 
2324 	mask = (uint8 *) xmalloc(offset);
2325 	memset(mask, 0, offset);
2326 
2327 	/* approximate AND and XOR masks with a monochrome X pointer */
2328 	for (i = 0; i < height; i++)
2329 	{
2330 		offset -= scanline;
2331 		pcursor = &cursor[offset];
2332 		pmask = &mask[offset];
2333 
2334 		for (j = 0; j < scanline; j++)
2335 		{
2336 			for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
2337 			{
2338 				if (xormask[0] || xormask[1] || xormask[2])
2339 				{
2340 					*pcursor |= (~(*andmask) & nextbit);
2341 					*pmask |= nextbit;
2342 				}
2343 				else
2344 				{
2345 					*pcursor |= ((*andmask) & nextbit);
2346 					*pmask |= (~(*andmask) & nextbit);
2347 				}
2348 
2349 				xormask += 3;
2350 			}
2351 
2352 			andmask++;
2353 			pcursor++;
2354 			pmask++;
2355 		}
2356 	}
2357 
2358 	fg.red = fg.blue = fg.green = 0xffff;
2359 	bg.red = bg.blue = bg.green = 0x0000;
2360 	fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
2361 
2362 	cursorglyph = ui_create_glyph(This, width, height, cursor);
2363 	maskglyph = ui_create_glyph(This, width, height, mask);
2364 
2365 	xcursor =
2366 		XCreatePixmapCursor(This->display, (Pixmap) cursorglyph,
2367 				    (Pixmap) maskglyph, &fg, &bg, x, y);
2368 
2369 	ui_destroy_glyph(This, maskglyph);
2370 	ui_destroy_glyph(This, cursorglyph);
2371 	xfree(mask);
2372 	xfree(cursor);
2373 	return (HCURSOR) xcursor;
2374 }
2375 
2376 void
ui_set_cursor(RDPCLIENT * This,HCURSOR cursor)2377 ui_set_cursor(RDPCLIENT * This, HCURSOR cursor)
2378 {
2379 	This->xwin.current_cursor = (Cursor) cursor;
2380 	XDefineCursor(This->display, This->wnd, This->xwin.current_cursor);
2381 	ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (This->display, sw->wnd, This->xwin.current_cursor));
2382 }
2383 
2384 void
ui_destroy_cursor(RDPCLIENT * This,HCURSOR cursor)2385 ui_destroy_cursor(RDPCLIENT * This, HCURSOR cursor)
2386 {
2387 	XFreeCursor(This->display, (Cursor) cursor);
2388 }
2389 
2390 void
ui_set_null_cursor(RDPCLIENT * This)2391 ui_set_null_cursor(RDPCLIENT * This)
2392 {
2393 	ui_set_cursor(This, This->xwin.null_cursor);
2394 }
2395 
2396 #define MAKE_XCOLOR(xc,c) \
2397 		(xc)->red   = ((c)->red   << 8) | (c)->red; \
2398 		(xc)->green = ((c)->green << 8) | (c)->green; \
2399 		(xc)->blue  = ((c)->blue  << 8) | (c)->blue; \
2400 		(xc)->flags = DoRed | DoGreen | DoBlue;
2401 
2402 
2403 HCOLOURMAP
ui_create_colourmap(RDPCLIENT * This,COLOURMAP * colours)2404 ui_create_colourmap(RDPCLIENT * This, COLOURMAP * colours)
2405 {
2406 	COLOURENTRY *entry;
2407 	int i, ncolours = colours->ncolours;
2408 	if (!This->owncolmap)
2409 	{
2410 		uint32 *map = (uint32 *) xmalloc(sizeof(*This->xwin.colmap) * ncolours);
2411 		XColor xentry;
2412 		XColor xc_cache[256];
2413 		uint32 colour;
2414 		int colLookup = 256;
2415 		for (i = 0; i < ncolours; i++)
2416 		{
2417 			entry = &colours->colours[i];
2418 			MAKE_XCOLOR(&xentry, entry);
2419 
2420 			if (XAllocColor(This->display, This->xwin.xcolmap, &xentry) == 0)
2421 			{
2422 				/* Allocation failed, find closest match. */
2423 				int j = 256;
2424 				int nMinDist = 3 * 256 * 256;
2425 				long nDist = nMinDist;
2426 
2427 				/* only get the colors once */
2428 				while (colLookup--)
2429 				{
2430 					xc_cache[colLookup].pixel = colLookup;
2431 					xc_cache[colLookup].red = xc_cache[colLookup].green =
2432 						xc_cache[colLookup].blue = 0;
2433 					xc_cache[colLookup].flags = 0;
2434 					XQueryColor(This->display,
2435 						    DefaultColormap(This->display,
2436 								    DefaultScreen(This->display)),
2437 						    &xc_cache[colLookup]);
2438 				}
2439 				colLookup = 0;
2440 
2441 				/* approximate the pixel */
2442 				while (j--)
2443 				{
2444 					if (xc_cache[j].flags)
2445 					{
2446 						nDist = ((long) (xc_cache[j].red >> 8) -
2447 							 (long) (xentry.red >> 8)) *
2448 							((long) (xc_cache[j].red >> 8) -
2449 							 (long) (xentry.red >> 8)) +
2450 							((long) (xc_cache[j].green >> 8) -
2451 							 (long) (xentry.green >> 8)) *
2452 							((long) (xc_cache[j].green >> 8) -
2453 							 (long) (xentry.green >> 8)) +
2454 							((long) (xc_cache[j].blue >> 8) -
2455 							 (long) (xentry.blue >> 8)) *
2456 							((long) (xc_cache[j].blue >> 8) -
2457 							 (long) (xentry.blue >> 8));
2458 					}
2459 					if (nDist < nMinDist)
2460 					{
2461 						nMinDist = nDist;
2462 						xentry.pixel = j;
2463 					}
2464 				}
2465 			}
2466 			colour = xentry.pixel;
2467 
2468 			/* update our cache */
2469 			if (xentry.pixel < 256)
2470 			{
2471 				xc_cache[xentry.pixel].red = xentry.red;
2472 				xc_cache[xentry.pixel].green = xentry.green;
2473 				xc_cache[xentry.pixel].blue = xentry.blue;
2474 
2475 			}
2476 
2477 			map[i] = colour;
2478 		}
2479 		return map;
2480 	}
2481 	else
2482 	{
2483 		XColor *xcolours, *xentry;
2484 		Colormap map;
2485 
2486 		xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
2487 		for (i = 0; i < ncolours; i++)
2488 		{
2489 			entry = &colours->colours[i];
2490 			xentry = &xcolours[i];
2491 			xentry->pixel = i;
2492 			MAKE_XCOLOR(xentry, entry);
2493 		}
2494 
2495 		map = XCreateColormap(This->display, This->wnd, This->xwin.visual, AllocAll);
2496 		XStoreColors(This->display, map, xcolours, ncolours);
2497 
2498 		xfree(xcolours);
2499 		return (HCOLOURMAP) map;
2500 	}
2501 }
2502 
2503 void
ui_destroy_colourmap(RDPCLIENT * This,HCOLOURMAP map)2504 ui_destroy_colourmap(RDPCLIENT * This, HCOLOURMAP map)
2505 {
2506 	if (!This->owncolmap)
2507 		xfree(map);
2508 	else
2509 		XFreeColormap(This->display, (Colormap) map);
2510 }
2511 
2512 void
ui_set_colourmap(RDPCLIENT * This,HCOLOURMAP map)2513 ui_set_colourmap(RDPCLIENT * This, HCOLOURMAP map)
2514 {
2515 	if (!This->owncolmap)
2516 	{
2517 		if (This->xwin.colmap)
2518 			xfree(This->xwin.colmap);
2519 
2520 		This->xwin.colmap = (uint32 *) map;
2521 	}
2522 	else
2523 	{
2524 		XSetWindowColormap(This->display, This->wnd, (Colormap) map);
2525 		ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (This->display, sw->wnd, (Colormap) map));
2526 	}
2527 }
2528 
2529 void
ui_set_clip(RDPCLIENT * This,int x,int y,int cx,int cy)2530 ui_set_clip(RDPCLIENT * This, int x, int y, int cx, int cy)
2531 {
2532 	This->xwin.clip_rectangle.x = x;
2533 	This->xwin.clip_rectangle.y = y;
2534 	This->xwin.clip_rectangle.width = cx;
2535 	This->xwin.clip_rectangle.height = cy;
2536 	XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &This->xwin.clip_rectangle, 1, YXBanded);
2537 }
2538 
2539 void
ui_reset_clip(RDPCLIENT * This)2540 ui_reset_clip(RDPCLIENT * This)
2541 {
2542 	This->xwin.clip_rectangle.x = 0;
2543 	This->xwin.clip_rectangle.y = 0;
2544 	This->xwin.clip_rectangle.width = This->width;
2545 	This->xwin.clip_rectangle.height = This->height;
2546 	XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &This->xwin.clip_rectangle, 1, YXBanded);
2547 }
2548 
2549 void
ui_bell(RDPCLIENT * This)2550 ui_bell(RDPCLIENT * This)
2551 {
2552 	XBell(This->display, 0);
2553 }
2554 
2555 void
ui_destblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy)2556 ui_destblt(RDPCLIENT * This, uint8 opcode,
2557 	   /* dest */ int x, int y, int cx, int cy)
2558 {
2559 	SET_FUNCTION(opcode);
2560 	FILL_RECTANGLE(x, y, cx, cy);
2561 	RESET_FUNCTION(opcode);
2562 }
2563 
2564 static const uint8 hatch_patterns[] = {
2565 	0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,	/* 0 - bsHorizontal */
2566 	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,	/* 1 - bsVertical */
2567 	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,	/* 2 - bsFDiagonal */
2568 	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,	/* 3 - bsBDiagonal */
2569 	0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08,	/* 4 - bsCross */
2570 	0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81	/* 5 - bsDiagCross */
2571 };
2572 
2573 void
ui_patblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,BRUSH * brush,int bgcolour,int fgcolour)2574 ui_patblt(RDPCLIENT * This, uint8 opcode,
2575 	  /* dest */ int x, int y, int cx, int cy,
2576 	  /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2577 {
2578 	Pixmap fill;
2579 	uint8 i, ipattern[8];
2580 
2581 	SET_FUNCTION(opcode);
2582 
2583 	switch (brush->style)
2584 	{
2585 		case 0:	/* Solid */
2586 			SET_FOREGROUND(fgcolour);
2587 			FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2588 			break;
2589 
2590 		case 2:	/* Hatch */
2591 			fill = (Pixmap) ui_create_glyph(This, 8, 8,
2592 							hatch_patterns + brush->pattern[0] * 8);
2593 			SET_FOREGROUND(fgcolour);
2594 			SET_BACKGROUND(bgcolour);
2595 			XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2596 			XSetStipple(This->display, This->xwin.gc, fill);
2597 			XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2598 			FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2599 			XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2600 			XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2601 			ui_destroy_glyph(This, (HGLYPH) fill);
2602 			break;
2603 
2604 		case 3:	/* Pattern */
2605 			for (i = 0; i != 8; i++)
2606 				ipattern[7 - i] = brush->pattern[i];
2607 			fill = (Pixmap) ui_create_glyph(This, 8, 8, ipattern);
2608 			SET_FOREGROUND(bgcolour);
2609 			SET_BACKGROUND(fgcolour);
2610 			XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2611 			XSetStipple(This->display, This->xwin.gc, fill);
2612 			XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2613 			FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2614 			XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2615 			XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2616 			ui_destroy_glyph(This, (HGLYPH) fill);
2617 			break;
2618 
2619 		default:
2620 			unimpl("brush %d\n", brush->style);
2621 	}
2622 
2623 	RESET_FUNCTION(opcode);
2624 
2625 	if (This->ownbackstore)
2626 		XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, x, y, cx, cy, x, y);
2627 	ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2628 				(This->display, This->ownbackstore ? This->xwin.backstore : This->wnd, sw->wnd, This->xwin.gc,
2629 				 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2630 }
2631 
2632 void
ui_screenblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,int srcx,int srcy)2633 ui_screenblt(RDPCLIENT * This, uint8 opcode,
2634 	     /* dest */ int x, int y, int cx, int cy,
2635 	     /* src */ int srcx, int srcy)
2636 {
2637 	SET_FUNCTION(opcode);
2638 	if (This->ownbackstore)
2639 	{
2640 		XCopyArea(This->display, This->Unobscured ? This->wnd : This->xwin.backstore,
2641 			  This->wnd, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2642 		XCopyArea(This->display, This->xwin.backstore, This->xwin.backstore, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2643 	}
2644 	else
2645 	{
2646 		XCopyArea(This->display, This->wnd, This->wnd, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2647 	}
2648 
2649 	ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2650 				(This->display, This->ownbackstore ? This->xwin.backstore : This->wnd,
2651 				 sw->wnd, This->xwin.gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2652 
2653 	RESET_FUNCTION(opcode);
2654 }
2655 
2656 void
ui_memblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,HBITMAP src,int srcx,int srcy)2657 ui_memblt(RDPCLIENT * This, uint8 opcode,
2658 	  /* dest */ int x, int y, int cx, int cy,
2659 	  /* src */ HBITMAP src, int srcx, int srcy)
2660 {
2661 	SET_FUNCTION(opcode);
2662 	XCopyArea(This->display, (Pixmap) src, This->wnd, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2663 	ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2664 				(This->display, (Pixmap) src, sw->wnd, This->xwin.gc,
2665 				 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
2666 	if (This->ownbackstore)
2667 		XCopyArea(This->display, (Pixmap) src, This->xwin.backstore, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2668 	RESET_FUNCTION(opcode);
2669 }
2670 
2671 void
ui_triblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,HBITMAP src,int srcx,int srcy,BRUSH * brush,int bgcolour,int fgcolour)2672 ui_triblt(RDPCLIENT * This, uint8 opcode,
2673 	  /* dest */ int x, int y, int cx, int cy,
2674 	  /* src */ HBITMAP src, int srcx, int srcy,
2675 	  /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2676 {
2677 	/* This is potentially difficult to do in general. Until someone
2678 	   comes up with a more efficient way of doing it I am using cases. */
2679 
2680 	switch (opcode)
2681 	{
2682 		case 0x69:	/* PDSxxn */
2683 			ui_memblt(This, ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
2684 			ui_patblt(This, ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2685 			break;
2686 
2687 		case 0xb8:	/* PSDPxax */
2688 			ui_patblt(This, ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2689 			ui_memblt(This, ROP2_AND, x, y, cx, cy, src, srcx, srcy);
2690 			ui_patblt(This, ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2691 			break;
2692 
2693 		case 0xc0:	/* PSa */
2694 			ui_memblt(This, ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2695 			ui_patblt(This, ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
2696 			break;
2697 
2698 		default:
2699 			unimpl("triblt 0x%x\n", opcode);
2700 			ui_memblt(This, ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2701 	}
2702 }
2703 
2704 void
ui_line(RDPCLIENT * This,uint8 opcode,int startx,int starty,int endx,int endy,PEN * pen)2705 ui_line(RDPCLIENT * This, uint8 opcode,
2706 	/* dest */ int startx, int starty, int endx, int endy,
2707 	/* pen */ PEN * pen)
2708 {
2709 	SET_FUNCTION(opcode);
2710 	SET_FOREGROUND(pen->colour);
2711 	XDrawLine(This->display, This->wnd, This->xwin.gc, startx, starty, endx, endy);
2712 	ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (This->display, sw->wnd, This->xwin.gc,
2713 					    startx - sw->xoffset, starty - sw->yoffset,
2714 					    endx - sw->xoffset, endy - sw->yoffset));
2715 	if (This->ownbackstore)
2716 		XDrawLine(This->display, This->xwin.backstore, This->xwin.gc, startx, starty, endx, endy);
2717 	RESET_FUNCTION(opcode);
2718 }
2719 
2720 void
ui_rect(RDPCLIENT * This,int x,int y,int cx,int cy,int colour)2721 ui_rect(RDPCLIENT * This,
2722 	       /* dest */ int x, int y, int cx, int cy,
2723 	       /* brush */ int colour)
2724 {
2725 	SET_FOREGROUND(colour);
2726 	FILL_RECTANGLE(x, y, cx, cy);
2727 }
2728 
2729 void
ui_polygon(RDPCLIENT * This,uint8 opcode,uint8 fillmode,POINT * point,int npoints,BRUSH * brush,int bgcolour,int fgcolour)2730 ui_polygon(RDPCLIENT * This, uint8 opcode,
2731 	   /* mode */ uint8 fillmode,
2732 	   /* dest */ POINT * point, int npoints,
2733 	   /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2734 {
2735 	uint8 style, i, ipattern[8];
2736 	Pixmap fill;
2737 
2738 	SET_FUNCTION(opcode);
2739 
2740 	switch (fillmode)
2741 	{
2742 		case ALTERNATE:
2743 			XSetFillRule(This->display, This->xwin.gc, EvenOddRule);
2744 			break;
2745 		case WINDING:
2746 			XSetFillRule(This->display, This->xwin.gc, WindingRule);
2747 			break;
2748 		default:
2749 			unimpl("fill mode %d\n", fillmode);
2750 	}
2751 
2752 	if (brush)
2753 		style = brush->style;
2754 	else
2755 		style = 0;
2756 
2757 	switch (style)
2758 	{
2759 		case 0:	/* Solid */
2760 			SET_FOREGROUND(fgcolour);
2761 			FILL_POLYGON((XPoint *) point, npoints);
2762 			break;
2763 
2764 		case 2:	/* Hatch */
2765 			fill = (Pixmap) ui_create_glyph(This, 8, 8,
2766 							hatch_patterns + brush->pattern[0] * 8);
2767 			SET_FOREGROUND(fgcolour);
2768 			SET_BACKGROUND(bgcolour);
2769 			XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2770 			XSetStipple(This->display, This->xwin.gc, fill);
2771 			XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2772 			FILL_POLYGON((XPoint *) point, npoints);
2773 			XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2774 			XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2775 			ui_destroy_glyph(This, (HGLYPH) fill);
2776 			break;
2777 
2778 		case 3:	/* Pattern */
2779 			for (i = 0; i != 8; i++)
2780 				ipattern[7 - i] = brush->pattern[i];
2781 			fill = (Pixmap) ui_create_glyph(This, 8, 8, ipattern);
2782 			SET_FOREGROUND(bgcolour);
2783 			SET_BACKGROUND(fgcolour);
2784 			XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2785 			XSetStipple(This->display, This->xwin.gc, fill);
2786 			XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2787 			FILL_POLYGON((XPoint *) point, npoints);
2788 			XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2789 			XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2790 			ui_destroy_glyph(This, (HGLYPH) fill);
2791 			break;
2792 
2793 		default:
2794 			unimpl("brush %d\n", brush->style);
2795 	}
2796 
2797 	RESET_FUNCTION(opcode);
2798 }
2799 
2800 void
ui_polyline(RDPCLIENT * This,uint8 opcode,POINT * points,int npoints,PEN * pen)2801 ui_polyline(RDPCLIENT * This, uint8 opcode,
2802 	    /* dest */ POINT * points, int npoints,
2803 	    /* pen */ PEN * pen)
2804 {
2805 	/* TODO: set join style */
2806 	SET_FUNCTION(opcode);
2807 	SET_FOREGROUND(pen->colour);
2808 	XDrawLines(This->display, This->wnd, This->xwin.gc, (XPoint *) points, npoints, CoordModePrevious);
2809 	if (This->ownbackstore)
2810 		XDrawLines(This->display, This->xwin.backstore, This->xwin.gc, (XPoint *) points, npoints,
2811 			   CoordModePrevious);
2812 
2813 	ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
2814 				(This, sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
2815 
2816 	RESET_FUNCTION(opcode);
2817 }
2818 
2819 void
ui_ellipse(RDPCLIENT * This,uint8 opcode,uint8 fillmode,int x,int y,int cx,int cy,BRUSH * brush,int bgcolour,int fgcolour)2820 ui_ellipse(RDPCLIENT * This, uint8 opcode,
2821 	   /* mode */ uint8 fillmode,
2822 	   /* dest */ int x, int y, int cx, int cy,
2823 	   /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2824 {
2825 	uint8 style, i, ipattern[8];
2826 	Pixmap fill;
2827 
2828 	SET_FUNCTION(opcode);
2829 
2830 	if (brush)
2831 		style = brush->style;
2832 	else
2833 		style = 0;
2834 
2835 	switch (style)
2836 	{
2837 		case 0:	/* Solid */
2838 			SET_FOREGROUND(fgcolour);
2839 			DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2840 			break;
2841 
2842 		case 2:	/* Hatch */
2843 			fill = (Pixmap) ui_create_glyph(This, 8, 8,
2844 							hatch_patterns + brush->pattern[0] * 8);
2845 			SET_FOREGROUND(fgcolour);
2846 			SET_BACKGROUND(bgcolour);
2847 			XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2848 			XSetStipple(This->display, This->xwin.gc, fill);
2849 			XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2850 			DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2851 			XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2852 			XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2853 			ui_destroy_glyph(This, (HGLYPH) fill);
2854 			break;
2855 
2856 		case 3:	/* Pattern */
2857 			for (i = 0; i != 8; i++)
2858 				ipattern[7 - i] = brush->pattern[i];
2859 			fill = (Pixmap) ui_create_glyph(This, 8, 8, ipattern);
2860 			SET_FOREGROUND(bgcolour);
2861 			SET_BACKGROUND(fgcolour);
2862 			XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2863 			XSetStipple(This->display, This->xwin.gc, fill);
2864 			XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2865 			DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2866 			XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2867 			XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2868 			ui_destroy_glyph(This, (HGLYPH) fill);
2869 			break;
2870 
2871 		default:
2872 			unimpl("brush %d\n", brush->style);
2873 	}
2874 
2875 	RESET_FUNCTION(opcode);
2876 }
2877 
2878 /* warning, this function only draws on wnd or backstore, not both */
2879 void
ui_draw_glyph(RDPCLIENT * This,int mixmode,int x,int y,int cx,int cy,HGLYPH glyph,int srcx,int srcy,int bgcolour,int fgcolour)2880 ui_draw_glyph(RDPCLIENT * This, int mixmode,
2881 	      /* dest */ int x, int y, int cx, int cy,
2882 	      /* src */ HGLYPH glyph, int srcx, int srcy,
2883 	      int bgcolour, int fgcolour)
2884 {
2885 	SET_FOREGROUND(fgcolour);
2886 	SET_BACKGROUND(bgcolour);
2887 
2888 	XSetFillStyle(This->display, This->xwin.gc,
2889 		      (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
2890 	XSetStipple(This->display, This->xwin.gc, (Pixmap) glyph);
2891 	XSetTSOrigin(This->display, This->xwin.gc, x, y);
2892 
2893 	FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2894 
2895 	XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2896 }
2897 
2898 #define DO_GLYPH(ttext,idx) \
2899 {\
2900   glyph = cache_get_font (This, font, ttext[idx]);\
2901   if (!(flags & TEXT2_IMPLICIT_X))\
2902   {\
2903     xyoffset = ttext[++idx];\
2904     if ((xyoffset & 0x80))\
2905     {\
2906       if (flags & TEXT2_VERTICAL)\
2907         y += ttext[idx+1] | (ttext[idx+2] << 8);\
2908       else\
2909         x += ttext[idx+1] | (ttext[idx+2] << 8);\
2910       idx += 2;\
2911     }\
2912     else\
2913     {\
2914       if (flags & TEXT2_VERTICAL)\
2915         y += xyoffset;\
2916       else\
2917         x += xyoffset;\
2918     }\
2919   }\
2920   if (glyph != NULL)\
2921   {\
2922     x1 = x + glyph->offset;\
2923     y1 = y + glyph->baseline;\
2924     XSetStipple(This->display, This->xwin.gc, (Pixmap) glyph->pixmap);\
2925     XSetTSOrigin(This->display, This->xwin.gc, x1, y1);\
2926     FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
2927     if (flags & TEXT2_IMPLICIT_X)\
2928       x += glyph->width;\
2929   }\
2930 }
2931 
2932 void
ui_draw_text(RDPCLIENT * This,uint8 font,uint8 flags,uint8 opcode,int mixmode,int x,int y,int clipx,int clipy,int clipcx,int clipcy,int boxx,int boxy,int boxcx,int boxcy,BRUSH * brush,int bgcolour,int fgcolour,uint8 * text,uint8 length)2933 ui_draw_text(RDPCLIENT * This, uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
2934 	     int clipx, int clipy, int clipcx, int clipcy,
2935 	     int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
2936 	     int bgcolour, int fgcolour, uint8 * text, uint8 length)
2937 {
2938 	/* TODO: use brush appropriately */
2939 
2940 	FONTGLYPH *glyph;
2941 	int i, j, xyoffset, x1, y1;
2942 	DATABLOB *entry;
2943 
2944 	SET_FOREGROUND(bgcolour);
2945 
2946 	/* Sometimes, the boxcx value is something really large, like
2947 	   32691. This makes XCopyArea fail with Xvnc. The code below
2948 	   is a quick fix. */
2949 	if (boxx + boxcx > This->width)
2950 		boxcx = This->width - boxx;
2951 
2952 	if (boxcx > 1)
2953 	{
2954 		FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
2955 	}
2956 	else if (mixmode == MIX_OPAQUE)
2957 	{
2958 		FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
2959 	}
2960 
2961 	SET_FOREGROUND(fgcolour);
2962 	SET_BACKGROUND(bgcolour);
2963 	XSetFillStyle(This->display, This->xwin.gc, FillStippled);
2964 
2965 	/* Paint text, character by character */
2966 	for (i = 0; i < length;)
2967 	{
2968 		switch (text[i])
2969 		{
2970 			case 0xff:
2971 				/* At least two bytes needs to follow */
2972 				if (i + 3 > length)
2973 				{
2974 					warning("Skipping short 0xff command:");
2975 					for (j = 0; j < length; j++)
2976 						fprintf(stderr, "%02x ", text[j]);
2977 					fprintf(stderr, "\n");
2978 					i = length = 0;
2979 					break;
2980 				}
2981 				cache_put_text(This, text[i + 1], text, text[i + 2]);
2982 				i += 3;
2983 				length -= i;
2984 				/* this will move pointer from start to first character after FF command */
2985 				text = &(text[i]);
2986 				i = 0;
2987 				break;
2988 
2989 			case 0xfe:
2990 				/* At least one byte needs to follow */
2991 				if (i + 2 > length)
2992 				{
2993 					warning("Skipping short 0xfe command:");
2994 					for (j = 0; j < length; j++)
2995 						fprintf(stderr, "%02x ", text[j]);
2996 					fprintf(stderr, "\n");
2997 					i = length = 0;
2998 					break;
2999 				}
3000 				entry = cache_get_text(This, text[i + 1]);
3001 				if (entry->data != NULL)
3002 				{
3003 					if ((((uint8 *) (entry->data))[1] == 0)
3004 					    && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
3005 					{
3006 						if (flags & TEXT2_VERTICAL)
3007 							y += text[i + 2];
3008 						else
3009 							x += text[i + 2];
3010 					}
3011 					for (j = 0; j < entry->size; j++)
3012 						DO_GLYPH(((uint8 *) (entry->data)), j);
3013 				}
3014 				if (i + 2 < length)
3015 					i += 3;
3016 				else
3017 					i += 2;
3018 				length -= i;
3019 				/* this will move pointer from start to first character after FE command */
3020 				text = &(text[i]);
3021 				i = 0;
3022 				break;
3023 
3024 			default:
3025 				DO_GLYPH(text, i);
3026 				i++;
3027 				break;
3028 		}
3029 	}
3030 
3031 	XSetFillStyle(This->display, This->xwin.gc, FillSolid);
3032 
3033 	if (This->ownbackstore)
3034 	{
3035 		if (boxcx > 1)
3036 		{
3037 			XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, boxx,
3038 				  boxy, boxcx, boxcy, boxx, boxy);
3039 			ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3040 						(This->display, This->xwin.backstore, sw->wnd, This->xwin.gc,
3041 						 boxx, boxy,
3042 						 boxcx, boxcy,
3043 						 boxx - sw->xoffset, boxy - sw->yoffset));
3044 		}
3045 		else
3046 		{
3047 			XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, clipx,
3048 				  clipy, clipcx, clipcy, clipx, clipy);
3049 			ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3050 						(This->display, This->xwin.backstore, sw->wnd, This->xwin.gc,
3051 						 clipx, clipy,
3052 						 clipcx, clipcy, clipx - sw->xoffset,
3053 						 clipy - sw->yoffset));
3054 		}
3055 	}
3056 }
3057 
3058 void
ui_desktop_save(RDPCLIENT * This,uint32 offset,int x,int y,int cx,int cy)3059 ui_desktop_save(RDPCLIENT * This, uint32 offset, int x, int y, int cx, int cy)
3060 {
3061 	Pixmap pix;
3062 	XImage *image;
3063 
3064 	if (This->ownbackstore)
3065 	{
3066 		image = XGetImage(This->display, This->xwin.backstore, x, y, cx, cy, AllPlanes, ZPixmap);
3067 	}
3068 	else
3069 	{
3070 		pix = XCreatePixmap(This->display, This->wnd, cx, cy, This->xwin.depth);
3071 		XCopyArea(This->display, This->wnd, pix, This->xwin.gc, x, y, cx, cy, 0, 0);
3072 		image = XGetImage(This->display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
3073 		XFreePixmap(This->display, pix);
3074 	}
3075 
3076 	offset *= This->xwin.bpp / 8;
3077 	cache_put_desktop(This, offset, cx, cy, image->bytes_per_line, This->xwin.bpp / 8, (uint8 *) image->data);
3078 
3079 	XDestroyImage(image);
3080 }
3081 
3082 void
ui_desktop_restore(RDPCLIENT * This,uint32 offset,int x,int y,int cx,int cy)3083 ui_desktop_restore(RDPCLIENT * This, uint32 offset, int x, int y, int cx, int cy)
3084 {
3085 	XImage *image;
3086 	uint8 *data;
3087 
3088 	offset *= This->xwin.bpp / 8;
3089 	data = cache_get_desktop(This, offset, cx, cy, This->xwin.bpp / 8);
3090 	if (data == NULL)
3091 		return;
3092 
3093 	image = XCreateImage(This->display, This->xwin.visual, This->xwin.depth, ZPixmap, 0,
3094 			     (char *) data, cx, cy, BitmapPad(This->display), cx * This->xwin.bpp / 8);
3095 
3096 	if (This->ownbackstore)
3097 	{
3098 		XPutImage(This->display, This->xwin.backstore, This->xwin.gc, image, 0, 0, x, y, cx, cy);
3099 		XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, x, y, cx, cy, x, y);
3100 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3101 					(This->display, This->xwin.backstore, sw->wnd, This->xwin.gc,
3102 					 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3103 	}
3104 	else
3105 	{
3106 		XPutImage(This->display, This->wnd, This->xwin.gc, image, 0, 0, x, y, cx, cy);
3107 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3108 					(This->display, This->wnd, sw->wnd, This->xwin.gc, x, y, cx, cy,
3109 					 x - sw->xoffset, y - sw->yoffset));
3110 	}
3111 
3112 	XFree(image);
3113 }
3114 
3115 /* these do nothing here but are used in uiports */
3116 void
ui_begin_update(RDPCLIENT * This)3117 ui_begin_update(RDPCLIENT * This)
3118 {
3119 }
3120 
3121 void
ui_end_update(RDPCLIENT * This)3122 ui_end_update(RDPCLIENT * This)
3123 {
3124 }
3125 
3126 
3127 void
ui_seamless_begin(RDPCLIENT * This,BOOL hidden)3128 ui_seamless_begin(RDPCLIENT * This, BOOL hidden)
3129 {
3130 	if (!This->seamless_rdp)
3131 		return;
3132 
3133 	if (This->xwin.seamless_started)
3134 		return;
3135 
3136 	This->xwin.seamless_started = True;
3137 	This->xwin.seamless_hidden = hidden;
3138 
3139 	if (!hidden)
3140 		ui_seamless_toggle(This);
3141 }
3142 
3143 
3144 void
ui_seamless_hide_desktop(RDPCLIENT * This)3145 ui_seamless_hide_desktop(RDPCLIENT * This)
3146 {
3147 	if (!This->seamless_rdp)
3148 		return;
3149 
3150 	if (!This->xwin.seamless_started)
3151 		return;
3152 
3153 	if (This->xwin.seamless_active)
3154 		ui_seamless_toggle(This);
3155 
3156 	This->xwin.seamless_hidden = True;
3157 }
3158 
3159 
3160 void
ui_seamless_unhide_desktop(RDPCLIENT * This)3161 ui_seamless_unhide_desktop(RDPCLIENT * This)
3162 {
3163 	if (!This->seamless_rdp)
3164 		return;
3165 
3166 	if (!This->xwin.seamless_started)
3167 		return;
3168 
3169 	This->xwin.seamless_hidden = False;
3170 
3171 	ui_seamless_toggle(This);
3172 }
3173 
3174 
3175 void
ui_seamless_toggle(RDPCLIENT * This)3176 ui_seamless_toggle(RDPCLIENT * This)
3177 {
3178 	if (!This->seamless_rdp)
3179 		return;
3180 
3181 	if (!This->xwin.seamless_started)
3182 		return;
3183 
3184 	if (This->xwin.seamless_hidden)
3185 		return;
3186 
3187 	if (This->xwin.seamless_active)
3188 	{
3189 		/* Deactivate */
3190 		while (This->xwin.seamless_windows)
3191 		{
3192 			XDestroyWindow(This->display, This->xwin.seamless_windows->wnd);
3193 			sw_remove_window(This, This->xwin.seamless_windows);
3194 		}
3195 		XMapWindow(This->display, This->wnd);
3196 	}
3197 	else
3198 	{
3199 		/* Activate */
3200 		XUnmapWindow(This->display, This->wnd);
3201 		seamless_send_sync(This);
3202 	}
3203 
3204 	This->xwin.seamless_active = !This->xwin.seamless_active;
3205 }
3206 
3207 
3208 void
ui_seamless_create_window(RDPCLIENT * This,unsigned long id,unsigned long group,unsigned long parent,unsigned long flags)3209 ui_seamless_create_window(RDPCLIENT * This, unsigned long id, unsigned long group, unsigned long parent,
3210 			  unsigned long flags)
3211 {
3212 	Window wnd;
3213 	XSetWindowAttributes attribs;
3214 	XClassHint *classhints;
3215 	XSizeHints *sizehints;
3216 	XWMHints *wmhints;
3217 	long input_mask;
3218 	seamless_window *sw, *sw_parent;
3219 
3220 	if (!This->xwin.seamless_active)
3221 		return;
3222 
3223 	/* Ignore CREATEs for existing windows */
3224 	sw = sw_get_window_by_id(This, id);
3225 	if (sw)
3226 		return;
3227 
3228 	get_window_attribs(This, &attribs);
3229 	wnd = XCreateWindow(This->display, RootWindowOfScreen(This->xwin.screen), -1, -1, 1, 1, 0, This->xwin.depth,
3230 			    InputOutput, This->xwin.visual,
3231 			    CWBackPixel | CWBackingStore | CWColormap | CWBorderPixel, &attribs);
3232 
3233 	XStoreName(This->display, wnd, "SeamlessRDP");
3234 	ewmh_set_wm_name(This, wnd, "SeamlessRDP");
3235 
3236 	mwm_hide_decorations(This, wnd);
3237 
3238 	classhints = XAllocClassHint();
3239 	if (classhints != NULL)
3240 	{
3241 		classhints->res_name = "rdesktop";
3242 		classhints->res_class = "SeamlessRDP";
3243 		XSetClassHint(This->display, wnd, classhints);
3244 		XFree(classhints);
3245 	}
3246 
3247 	/* WM_NORMAL_HINTS */
3248 	sizehints = XAllocSizeHints();
3249 	if (sizehints != NULL)
3250 	{
3251 		sizehints->flags = USPosition;
3252 		XSetWMNormalHints(This->display, wnd, sizehints);
3253 		XFree(sizehints);
3254 	}
3255 
3256 	/* Parent-less transient windows */
3257 	if (parent == 0xFFFFFFFF)
3258 	{
3259 		XSetTransientForHint(This->display, wnd, RootWindowOfScreen(This->xwin.screen));
3260 		/* Some buggy wm:s (kwin) do not handle the above, so fake it
3261 		   using some other hints. */
3262 		ewmh_set_window_popup(This, wnd);
3263 	}
3264 	/* Normal transient windows */
3265 	else if (parent != 0x00000000)
3266 	{
3267 		sw_parent = sw_get_window_by_id(This, parent);
3268 		if (sw_parent)
3269 			XSetTransientForHint(This->display, wnd, sw_parent->wnd);
3270 		else
3271 			warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3272 	}
3273 
3274 	if (flags & SEAMLESSRDP_CREATE_MODAL)
3275 	{
3276 		/* We do this to support buggy wm:s (*cough* metacity *cough*)
3277 		   somewhat at least */
3278 		if (parent == 0x00000000)
3279 			XSetTransientForHint(This->display, wnd, RootWindowOfScreen(This->xwin.screen));
3280 		ewmh_set_window_modal(This, wnd);
3281 	}
3282 
3283 	/* FIXME: Support for Input Context:s */
3284 
3285 	get_input_mask(This, &input_mask);
3286 	input_mask |= PropertyChangeMask;
3287 
3288 	XSelectInput(This->display, wnd, input_mask);
3289 
3290 	/* handle the WM_DELETE_WINDOW protocol. FIXME: When killing a
3291 	   seamless window, we could try to close the window on the
3292 	   serverside, instead of terminating rdesktop */
3293 	XSetWMProtocols(This->display, wnd, &This->xwin.kill_atom, 1);
3294 
3295 	sw = xmalloc(sizeof(seamless_window));
3296 	sw->wnd = wnd;
3297 	sw->id = id;
3298 	sw->behind = 0;
3299 	sw->group = sw_find_group(This, group, False);
3300 	sw->group->refcnt++;
3301 	sw->xoffset = 0;
3302 	sw->yoffset = 0;
3303 	sw->width = 0;
3304 	sw->height = 0;
3305 	sw->state = SEAMLESSRDP_NOTYETMAPPED;
3306 	sw->desktop = 0;
3307 	sw->position_timer = xmalloc(sizeof(struct timeval));
3308 	timerclear(sw->position_timer);
3309 
3310 	sw->outstanding_position = False;
3311 	sw->outpos_serial = 0;
3312 	sw->outpos_xoffset = sw->outpos_yoffset = 0;
3313 	sw->outpos_width = sw->outpos_height = 0;
3314 
3315 	sw->next = This->xwin.seamless_windows;
3316 	This->xwin.seamless_windows = sw;
3317 
3318 	/* WM_HINTS */
3319 	wmhints = XAllocWMHints();
3320 	if (wmhints)
3321 	{
3322 		wmhints->flags = WindowGroupHint;
3323 		wmhints->window_group = sw->group->wnd;
3324 		XSetWMHints(This->display, sw->wnd, wmhints);
3325 		XFree(wmhints);
3326 	}
3327 }
3328 
3329 
3330 void
ui_seamless_destroy_window(RDPCLIENT * This,unsigned long id,unsigned long flags)3331 ui_seamless_destroy_window(RDPCLIENT * This, unsigned long id, unsigned long flags)
3332 {
3333 	seamless_window *sw;
3334 
3335 	if (!This->xwin.seamless_active)
3336 		return;
3337 
3338 	sw = sw_get_window_by_id(This, id);
3339 	if (!sw)
3340 	{
3341 		warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3342 		return;
3343 	}
3344 
3345 	XDestroyWindow(This->display, sw->wnd);
3346 	sw_remove_window(This, sw);
3347 }
3348 
3349 
3350 void
ui_seamless_destroy_group(RDPCLIENT * This,unsigned long id,unsigned long flags)3351 ui_seamless_destroy_group(RDPCLIENT * This, unsigned long id, unsigned long flags)
3352 {
3353 	seamless_window *sw, *sw_next;
3354 
3355 	if (!This->xwin.seamless_active)
3356 		return;
3357 
3358 	for (sw = This->xwin.seamless_windows; sw; sw = sw_next)
3359 	{
3360 		sw_next = sw->next;
3361 
3362 		if (sw->group->id == id)
3363 		{
3364 			XDestroyWindow(This->display, sw->wnd);
3365 			sw_remove_window(This, sw);
3366 		}
3367 	}
3368 }
3369 
3370 
3371 void
ui_seamless_move_window(RDPCLIENT * This,unsigned long id,int x,int y,int width,int height,unsigned long flags)3372 ui_seamless_move_window(RDPCLIENT * This, unsigned long id, int x, int y, int width, int height, unsigned long flags)
3373 {
3374 	seamless_window *sw;
3375 
3376 	if (!This->xwin.seamless_active)
3377 		return;
3378 
3379 	sw = sw_get_window_by_id(This, id);
3380 	if (!sw)
3381 	{
3382 		warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3383 		return;
3384 	}
3385 
3386 	/* We ignore server updates until it has handled our request. */
3387 	if (sw->outstanding_position)
3388 		return;
3389 
3390 	if (!width || !height)
3391 		/* X11 windows must be at least 1x1 */
3392 		return;
3393 
3394 	sw->xoffset = x;
3395 	sw->yoffset = y;
3396 	sw->width = width;
3397 	sw->height = height;
3398 
3399 	/* If we move the window in a maximized state, then KDE won't
3400 	   accept restoration */
3401 	switch (sw->state)
3402 	{
3403 		case SEAMLESSRDP_MINIMIZED:
3404 		case SEAMLESSRDP_MAXIMIZED:
3405 			return;
3406 	}
3407 
3408 	/* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3409 	XMoveResizeWindow(This->display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3410 }
3411 
3412 
3413 void
ui_seamless_restack_window(RDPCLIENT * This,unsigned long id,unsigned long behind,unsigned long flags)3414 ui_seamless_restack_window(RDPCLIENT * This, unsigned long id, unsigned long behind, unsigned long flags)
3415 {
3416 	seamless_window *sw;
3417 
3418 	if (!This->xwin.seamless_active)
3419 		return;
3420 
3421 	sw = sw_get_window_by_id(This, id);
3422 	if (!sw)
3423 	{
3424 		warning("ui_seamless_restack_window: No information for window 0x%lx\n", id);
3425 		return;
3426 	}
3427 
3428 	if (behind)
3429 	{
3430 		seamless_window *sw_behind;
3431 		Window wnds[2];
3432 
3433 		sw_behind = sw_get_window_by_id(This, behind);
3434 		if (!sw_behind)
3435 		{
3436 			warning("ui_seamless_restack_window: No information for window 0x%lx\n",
3437 				behind);
3438 			return;
3439 		}
3440 
3441 		wnds[1] = sw_behind->wnd;
3442 		wnds[0] = sw->wnd;
3443 
3444 		XRestackWindows(This->display, wnds, 2);
3445 	}
3446 	else
3447 	{
3448 		XRaiseWindow(This->display, sw->wnd);
3449 	}
3450 
3451 	sw_restack_window(This, sw, behind);
3452 }
3453 
3454 
3455 void
ui_seamless_settitle(RDPCLIENT * This,unsigned long id,const char * title,unsigned long flags)3456 ui_seamless_settitle(RDPCLIENT * This, unsigned long id, const char *title, unsigned long flags)
3457 {
3458 	seamless_window *sw;
3459 
3460 	if (!This->xwin.seamless_active)
3461 		return;
3462 
3463 	sw = sw_get_window_by_id(This, id);
3464 	if (!sw)
3465 	{
3466 		warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
3467 		return;
3468 	}
3469 
3470 	/* FIXME: Might want to convert the name for non-EWMH WMs */
3471 	XStoreName(This->display, sw->wnd, title);
3472 	ewmh_set_wm_name(This, sw->wnd, title);
3473 }
3474 
3475 
3476 void
ui_seamless_setstate(RDPCLIENT * This,unsigned long id,unsigned int state,unsigned long flags)3477 ui_seamless_setstate(RDPCLIENT * This, unsigned long id, unsigned int state, unsigned long flags)
3478 {
3479 	seamless_window *sw;
3480 
3481 	if (!This->xwin.seamless_active)
3482 		return;
3483 
3484 	sw = sw_get_window_by_id(This, id);
3485 	if (!sw)
3486 	{
3487 		warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
3488 		return;
3489 	}
3490 
3491 	switch (state)
3492 	{
3493 		case SEAMLESSRDP_NORMAL:
3494 		case SEAMLESSRDP_MAXIMIZED:
3495 			ewmh_change_state(This, sw->wnd, state);
3496 			XMapWindow(This->display, sw->wnd);
3497 			break;
3498 		case SEAMLESSRDP_MINIMIZED:
3499 			/* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
3500 			   the Window Manager should probably just ignore the request, since
3501 			   _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
3502 			   such as minimization, rather than an independent state." Besides,
3503 			   XIconifyWindow is easier. */
3504 			if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
3505 			{
3506 				XWMHints *hints;
3507 				hints = XGetWMHints(This->display, sw->wnd);
3508 				if (hints)
3509 				{
3510 					hints->flags |= StateHint;
3511 					hints->initial_state = IconicState;
3512 					XSetWMHints(This->display, sw->wnd, hints);
3513 					XFree(hints);
3514 				}
3515 				XMapWindow(This->display, sw->wnd);
3516 			}
3517 			else
3518 				XIconifyWindow(This->display, sw->wnd, DefaultScreen(This->display));
3519 			break;
3520 		default:
3521 			warning("SeamlessRDP: Invalid state %d\n", state);
3522 			break;
3523 	}
3524 
3525 	sw->state = state;
3526 }
3527 
3528 
3529 void
ui_seamless_syncbegin(RDPCLIENT * This,unsigned long flags)3530 ui_seamless_syncbegin(RDPCLIENT * This, unsigned long flags)
3531 {
3532 	if (!This->xwin.seamless_active)
3533 		return;
3534 
3535 	/* Destroy all seamless windows */
3536 	while (This->xwin.seamless_windows)
3537 	{
3538 		XDestroyWindow(This->display, This->xwin.seamless_windows->wnd);
3539 		sw_remove_window(This, This->xwin.seamless_windows);
3540 	}
3541 }
3542 
3543 
3544 void
ui_seamless_ack(RDPCLIENT * This,unsigned int serial)3545 ui_seamless_ack(RDPCLIENT * This, unsigned int serial)
3546 {
3547 	seamless_window *sw;
3548 	for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
3549 	{
3550 		if (sw->outstanding_position && (sw->outpos_serial == serial))
3551 		{
3552 			sw->xoffset = sw->outpos_xoffset;
3553 			sw->yoffset = sw->outpos_yoffset;
3554 			sw->width = sw->outpos_width;
3555 			sw->height = sw->outpos_height;
3556 			sw->outstanding_position = False;
3557 
3558 			/* Do a complete redraw of the window as part of the
3559 			   completion of the move. This is to remove any
3560 			   artifacts caused by our lack of synchronization. */
3561 			XCopyArea(This->display, This->xwin.backstore,
3562 				  sw->wnd, This->xwin.gc,
3563 				  sw->xoffset, sw->yoffset, sw->width, sw->height, 0, 0);
3564 
3565 			break;
3566 		}
3567 	}
3568 }
3569