1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    User interface services - X Window System
4    Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
5    Copyright 2007-2008 Pierre Ossman <ossman@cendio.se> for Cendio AB
6    Copyright 2002-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
7    Copyright 2012-2018 Henrik Andersson <hean01@cendio.se> for Cendio AB
8    Copyright 2017-2019 Alexander Zakharov <uglym8@gmail.com>
9 
10    This program is free software: you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation, either version 3 of the License, or
13    (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23 
24 #include <X11/Xatom.h>
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <X11/Xproto.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <errno.h>
33 #include <strings.h>
34 #include "rdesktop.h"
35 #include "xproto.h"
36 #include <X11/Xcursor/Xcursor.h>
37 #ifdef HAVE_XRANDR
38 #include <X11/extensions/Xrandr.h>
39 #endif
40 
41 #ifdef __APPLE__
42 #include <sys/param.h>
43 #define HOST_NAME_MAX MAXHOSTNAMELEN
44 #endif
45 
46 #ifdef __sun
47 #include <netdb.h>
48 #define HOST_NAME_MAX MAXHOSTNAMELEN
49 #endif
50 
51 #ifdef __DragonFly__
52 #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
53 #endif
54 
55 extern RD_BOOL g_user_quit;
56 extern RD_BOOL g_exit_mainloop;
57 
58 extern window_size_type_t g_window_size_type;
59 extern uint32 g_requested_session_width;
60 extern uint32 g_requested_session_height;
61 extern uint16 g_session_width;
62 extern uint16 g_session_height;
63 extern int g_xpos;
64 extern int g_ypos;
65 extern int g_pos;
66 extern RD_BOOL g_sendmotion;
67 extern RD_BOOL g_fullscreen;
68 extern RD_BOOL g_grab_keyboard;
69 extern RD_BOOL g_hide_decorations;
70 extern RD_BOOL g_pending_resize;
71 extern RD_BOOL g_pending_resize_defer;
72 extern struct timeval g_pending_resize_defer_timer;
73 
74 extern char g_title[];
75 extern char g_seamless_spawn_cmd[];
76 /* Color depth of the RDP session.
77    As of RDP 5.1, it may be 8, 15, 16 or 24. */
78 extern int g_server_depth;
79 extern int g_win_button_size;
80 
81 /* This is a timer used to rate limit actual resizing */
82 static struct timeval g_resize_timer = { 0 };
83 
84 Display *g_display;
85 Time g_last_gesturetime;
86 static int g_x_socket;
87 static Screen *g_screen;
88 Window g_wnd;
89 
90 static RD_BOOL g_has_wm = False;
91 
92 RD_BOOL g_dynamic_session_resize = True;
93 
94 /* These are the last known window sizes. They are updated whenever the window size is changed. */
95 static uint32 g_window_width;
96 static uint32 g_window_height;
97 
98 /* SeamlessRDP support */
99 typedef struct _seamless_group
100 {
101 	Window wnd;
102 	unsigned long id;
103 	unsigned int refcnt;
104 } seamless_group;
105 typedef struct _seamless_window
106 {
107 	Window wnd;
108 	unsigned long id;
109 	unsigned long behind;
110 	seamless_group *group;
111 	int xoffset, yoffset;
112 	int width, height;
113 	int state;		/* normal/minimized/maximized. */
114 	unsigned int desktop;
115 	struct timeval *position_timer;
116 
117 	RD_BOOL outstanding_position;
118 	unsigned int outpos_serial;
119 	int outpos_xoffset, outpos_yoffset;
120 	int outpos_width, outpos_height;
121 
122 	unsigned int icon_size;
123 	unsigned int icon_offset;
124 	char icon_buffer[32 * 32 * 4];
125 
126 	struct _seamless_window *next;
127 } seamless_window;
128 static seamless_window *g_seamless_windows = NULL;
129 static unsigned long g_seamless_focused = 0;
130 static RD_BOOL g_seamless_started = False;	/* Server end is up and running */
131 RD_BOOL g_seamless_active = False;	/* We are currently in seamless mode */
132 static RD_BOOL g_seamless_hidden = False;	/* Desktop is hidden on server */
133 static RD_BOOL g_seamless_broken_restack = False;	/* WM does not properly restack */
134 extern RD_BOOL g_seamless_rdp;
135 extern RD_BOOL g_seamless_persistent_mode;
136 
137 extern uint32 g_embed_wnd;
138 RD_BOOL g_enable_compose = False;
139 RD_BOOL g_Unobscured;		/* used for screenblt */
140 static GC g_gc = NULL;
141 static GC g_create_bitmap_gc = NULL;
142 static GC g_create_glyph_gc = NULL;
143 static XRectangle g_clip_rectangle;
144 static Visual *g_visual;
145 /* Color depth of the X11 visual of our window (e.g. 24 for True Color R8G8B visual).
146    This may be 32 for R8G8B8 visuals, and then the rest of the bits are undefined
147    as far as we're concerned. */
148 static int g_depth;
149 /* Bits-per-Pixel of the pixmaps we'll be using to draw on our window.
150    This may be larger than g_depth, in which case some of the bits would
151    be kept solely for alignment (e.g. 32bpp pixmaps on a 24bpp visual). */
152 static int g_bpp;
153 static XIM g_IM;
154 static XIC g_IC;
155 static XModifierKeymap *g_mod_map;
156 /* Maps logical (xmodmap -pp) pointing device buttons (0-based) back
157    to physical (1-based) indices. */
158 static unsigned char g_pointer_log_to_phys_map[32];
159 static Cursor g_current_cursor;
160 static RD_HCURSOR g_null_cursor = NULL;
161 static Atom g_protocol_atom, g_kill_atom;
162 extern Atom g_net_wm_state_atom;
163 extern Atom g_net_wm_desktop_atom;
164 extern Atom g_net_wm_ping_atom;
165 
166 static RD_BOOL g_focused;
167 static RD_BOOL g_mouse_in_wnd;
168 /* Indicates that:
169    1) visual has 15, 16 or 24 depth and the same color channel masks
170       as its RDP equivalent (implies X server is LE),
171    2) host is LE
172    This will trigger an optimization whose real value is questionable.
173 */
174 static RD_BOOL g_compatible_arch;
175 /* Indicates whether RDP's bitmaps and our XImages have the same
176    binary format. If so, we can avoid an expensive translation.
177    Note that this can be true when g_compatible_arch is false,
178    e.g.:
179 
180      RDP(LE) <-> host(BE) <-> X-Server(LE)
181 
182    ('host' is the machine running rdesktop; the host simply memcpy's
183     so its endianness doesn't matter)
184  */
185 static RD_BOOL g_no_translate_image = False;
186 
187 /* endianness */
188 static RD_BOOL g_host_be;
189 static RD_BOOL g_xserver_be;
190 static int g_red_shift_r, g_blue_shift_r, g_green_shift_r;
191 static int g_red_shift_l, g_blue_shift_l, g_green_shift_l;
192 
193 /* software backing store */
194 extern RD_BOOL g_ownbackstore;
195 static Pixmap g_backstore = 0;
196 
197 /* Moving in single app mode */
198 static RD_BOOL g_moving_wnd;
199 static int g_move_x_offset = 0;
200 static int g_move_y_offset = 0;
201 static RD_BOOL g_using_full_workarea = False;
202 
203 #ifdef WITH_RDPSND
204 extern RD_BOOL g_rdpsnd;
205 #endif
206 
207 /* MWM decorations */
208 #define MWM_HINTS_DECORATIONS   (1L << 1)
209 #define PROP_MOTIF_WM_HINTS_ELEMENTS    5
210 typedef struct
211 {
212 	unsigned long flags;
213 	unsigned long functions;
214 	unsigned long decorations;
215 	long inputMode;
216 	unsigned long status;
217 }
218 PropMotifWmHints;
219 
220 typedef struct
221 {
222 	uint32 red;
223 	uint32 green;
224 	uint32 blue;
225 }
226 PixelColour;
227 
228 #define ON_ALL_SEAMLESS_WINDOWS(func, args) \
229         do { \
230                 seamless_window *sw; \
231                 XRectangle rect; \
232 		if (!g_seamless_windows) break; \
233                 for (sw = g_seamless_windows; sw; sw = sw->next) { \
234                     rect.x = g_clip_rectangle.x - sw->xoffset; \
235                     rect.y = g_clip_rectangle.y - sw->yoffset; \
236                     rect.width = g_clip_rectangle.width; \
237                     rect.height = g_clip_rectangle.height; \
238                     XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded); \
239                     func args; \
240                 } \
241                 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded); \
242         } while (0)
243 
244 static void
seamless_XFillPolygon(Drawable d,XPoint * points,int npoints,int xoffset,int yoffset)245 seamless_XFillPolygon(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
246 {
247 	points[0].x -= xoffset;
248 	points[0].y -= yoffset;
249 	XFillPolygon(g_display, d, g_gc, points, npoints, Complex, CoordModePrevious);
250 	points[0].x += xoffset;
251 	points[0].y += yoffset;
252 }
253 
254 static void
seamless_XDrawLines(Drawable d,XPoint * points,int npoints,int xoffset,int yoffset)255 seamless_XDrawLines(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
256 {
257 	points[0].x -= xoffset;
258 	points[0].y -= yoffset;
259 	XDrawLines(g_display, d, g_gc, points, npoints, CoordModePrevious);
260 	points[0].x += xoffset;
261 	points[0].y += yoffset;
262 }
263 
264 #define FILL_RECTANGLE(x,y,cx,cy)\
265 { \
266 	XFillRectangle(g_display, g_wnd, g_gc, x, y, cx, cy); \
267         ON_ALL_SEAMLESS_WINDOWS(XFillRectangle, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy)); \
268 	if (g_ownbackstore) \
269 		XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \
270 }
271 
272 #define FILL_RECTANGLE_BACKSTORE(x,y,cx,cy)\
273 { \
274 	XFillRectangle(g_display, g_ownbackstore ? g_backstore : g_wnd, g_gc, x, y, cx, cy); \
275 }
276 
277 #define FILL_POLYGON(p,np)\
278 { \
279 	XFillPolygon(g_display, g_wnd, g_gc, p, np, Complex, CoordModePrevious); \
280 	if (g_ownbackstore) \
281 		XFillPolygon(g_display, g_backstore, g_gc, p, np, Complex, CoordModePrevious); \
282 	ON_ALL_SEAMLESS_WINDOWS(seamless_XFillPolygon, (sw->wnd, p, np, sw->xoffset, sw->yoffset)); \
283 }
284 
285 #define DRAW_ELLIPSE(x,y,cx,cy,m)\
286 { \
287 	switch (m) \
288 	{ \
289 		case 0:	/* Outline */ \
290 			XDrawArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \
291                         ON_ALL_SEAMLESS_WINDOWS(XDrawArc, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \
292 			if (g_ownbackstore) \
293 				XDrawArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
294 			break; \
295 		case 1: /* Filled */ \
296 			XFillArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \
297 			ON_ALL_SEAMLESS_WINDOWS(XFillArc, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \
298 			if (g_ownbackstore) \
299 				XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
300 			break; \
301 	} \
302 }
303 
304 /* colour maps */
305 extern RD_BOOL g_owncolmap;
306 static Colormap g_xcolmap;
307 static uint32 *g_colmap = NULL;
308 
309 #define TRANSLATE(col)		( g_server_depth != 8 ? translate_colour(col) : g_owncolmap ? col : g_colmap[col] )
310 #define SET_FOREGROUND(col)	XSetForeground(g_display, g_gc, TRANSLATE(col));
311 #define SET_BACKGROUND(col)	XSetBackground(g_display, g_gc, TRANSLATE(col));
312 
313 static int rop2_map[] = {
314 	GXclear,		/* 0 */
315 	GXnor,			/* DPon */
316 	GXandInverted,		/* DPna */
317 	GXcopyInverted,		/* Pn */
318 	GXandReverse,		/* PDna */
319 	GXinvert,		/* Dn */
320 	GXxor,			/* DPx */
321 	GXnand,			/* DPan */
322 	GXand,			/* DPa */
323 	GXequiv,		/* DPxn */
324 	GXnoop,			/* D */
325 	GXorInverted,		/* DPno */
326 	GXcopy,			/* P */
327 	GXorReverse,		/* PDno */
328 	GXor,			/* DPo */
329 	GXset			/* 1 */
330 };
331 
332 #define SET_FUNCTION(rop2)	{ if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, rop2_map[rop2]); }
333 #define RESET_FUNCTION(rop2)	{ if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); }
334 
335 static seamless_window *
sw_get_window_by_id(unsigned long id)336 sw_get_window_by_id(unsigned long id)
337 {
338 	seamless_window *sw;
339 	for (sw = g_seamless_windows; sw; sw = sw->next)
340 	{
341 		if (sw->id == id)
342 			return sw;
343 	}
344 	return NULL;
345 }
346 
347 
348 static seamless_window *
sw_get_window_by_wnd(Window wnd)349 sw_get_window_by_wnd(Window wnd)
350 {
351 	seamless_window *sw;
352 	for (sw = g_seamless_windows; sw; sw = sw->next)
353 	{
354 		if (sw->wnd == wnd)
355 			return sw;
356 	}
357 	return NULL;
358 }
359 
360 
361 static void
sw_remove_window(seamless_window * win)362 sw_remove_window(seamless_window * win)
363 {
364 	seamless_window *sw, **prevnext = &g_seamless_windows;
365 	for (sw = g_seamless_windows; sw; sw = sw->next)
366 	{
367 		if (sw == win)
368 		{
369 			*prevnext = sw->next;
370 			sw->group->refcnt--;
371 			if (sw->group->refcnt == 0)
372 			{
373 				XDestroyWindow(g_display, sw->group->wnd);
374 				xfree(sw->group);
375 			}
376 			xfree(sw->position_timer);
377 			xfree(sw);
378 			return;
379 		}
380 		prevnext = &sw->next;
381 	}
382 	return;
383 }
384 
385 
386 /* Move all windows except wnd to new desktop */
387 static void
sw_all_to_desktop(Window wnd,unsigned int desktop)388 sw_all_to_desktop(Window wnd, unsigned int desktop)
389 {
390 	seamless_window *sw;
391 	for (sw = g_seamless_windows; sw; sw = sw->next)
392 	{
393 		if (sw->wnd == wnd)
394 			continue;
395 		if (sw->desktop != desktop)
396 		{
397 			ewmh_move_to_desktop(sw->wnd, desktop);
398 			sw->desktop = desktop;
399 		}
400 	}
401 }
402 
403 
404 /* Send our position */
405 static void
sw_update_position(seamless_window * sw)406 sw_update_position(seamless_window * sw)
407 {
408 	XWindowAttributes wa;
409 	int x, y;
410 	Window child_return;
411 	unsigned int serial;
412 
413 	XGetWindowAttributes(g_display, sw->wnd, &wa);
414 	XTranslateCoordinates(g_display, sw->wnd, wa.root,
415 			      -wa.border_width, -wa.border_width, &x, &y, &child_return);
416 
417 	serial = seamless_send_position(sw->id, x, y, wa.width, wa.height, 0);
418 
419 	sw->outstanding_position = True;
420 	sw->outpos_serial = serial;
421 
422 	sw->outpos_xoffset = x;
423 	sw->outpos_yoffset = y;
424 	sw->outpos_width = wa.width;
425 	sw->outpos_height = wa.height;
426 }
427 
428 
429 /* Check if it's time to send our position */
430 static void
sw_check_timers()431 sw_check_timers()
432 {
433 	seamless_window *sw;
434 	struct timeval now;
435 
436 	gettimeofday(&now, NULL);
437 	for (sw = g_seamless_windows; sw; sw = sw->next)
438 	{
439 		if (timerisset(sw->position_timer) && timercmp(sw->position_timer, &now, <))
440 		{
441 			timerclear(sw->position_timer);
442 			sw_update_position(sw);
443 		}
444 	}
445 }
446 
447 
448 static void
sw_restack_window(seamless_window * sw,unsigned long behind)449 sw_restack_window(seamless_window * sw, unsigned long behind)
450 {
451 	seamless_window *sw_above;
452 
453 	/* Remove window from stack */
454 	for (sw_above = g_seamless_windows; sw_above; sw_above = sw_above->next)
455 	{
456 		if (sw_above->behind == sw->id)
457 			break;
458 	}
459 
460 	if (sw_above)
461 		sw_above->behind = sw->behind;
462 
463 	/* And then add it at the new position */
464 	for (sw_above = g_seamless_windows; sw_above; sw_above = sw_above->next)
465 	{
466 		if (sw_above->behind == behind)
467 			break;
468 	}
469 
470 	if (sw_above)
471 		sw_above->behind = sw->id;
472 
473 	sw->behind = behind;
474 }
475 
476 
477 static void
sw_handle_restack(seamless_window * sw)478 sw_handle_restack(seamless_window * sw)
479 {
480 	Status status;
481 	Window root, parent, *children;
482 	unsigned int nchildren, i;
483 	seamless_window *sw_below;
484 
485 	status = XQueryTree(g_display, RootWindowOfScreen(g_screen),
486 			    &root, &parent, &children, &nchildren);
487 	if (!status || !nchildren)
488 		return;
489 
490 	sw_below = NULL;
491 
492 	i = 0;
493 	while (children[i] != sw->wnd)
494 	{
495 		i++;
496 		if (i >= nchildren)
497 			goto end;
498 	}
499 
500 	for (i++; i < nchildren; i++)
501 	{
502 		sw_below = sw_get_window_by_wnd(children[i]);
503 		if (sw_below)
504 			break;
505 	}
506 
507 	if (!sw_below && !sw->behind)
508 		goto end;
509 	if (sw_below && (sw_below->id == sw->behind))
510 		goto end;
511 
512 	if (sw_below)
513 	{
514 		seamless_send_zchange(sw->id, sw_below->id, 0);
515 		sw_restack_window(sw, sw_below->id);
516 	}
517 	else
518 	{
519 		seamless_send_zchange(sw->id, 0, 0);
520 		sw_restack_window(sw, 0);
521 	}
522 
523       end:
524 	XFree(children);
525 }
526 
527 
528 static seamless_group *
sw_find_group(unsigned long id,RD_BOOL dont_create)529 sw_find_group(unsigned long id, RD_BOOL dont_create)
530 {
531 	seamless_window *sw;
532 	seamless_group *sg;
533 	XSetWindowAttributes attribs;
534 
535 	for (sw = g_seamless_windows; sw; sw = sw->next)
536 	{
537 		if (sw->group->id == id)
538 			return sw->group;
539 	}
540 
541 	if (dont_create)
542 		return NULL;
543 
544 	sg = xmalloc(sizeof(seamless_group));
545 
546 	sg->wnd =
547 		XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0,
548 			      CopyFromParent, CopyFromParent, CopyFromParent, 0, &attribs);
549 	ewmh_set_wm_pid(sg->wnd, getpid());
550 
551 	sg->id = id;
552 	sg->refcnt = 0;
553 
554 	return sg;
555 }
556 
557 
558 static void
mwm_hide_decorations(Window wnd)559 mwm_hide_decorations(Window wnd)
560 {
561 	PropMotifWmHints motif_hints;
562 	Atom hintsatom;
563 
564 	/* setup the property */
565 	motif_hints.flags = MWM_HINTS_DECORATIONS;
566 	motif_hints.decorations = 0;
567 
568 	/* get the atom for the property */
569 	hintsatom = XInternAtom(g_display, "_MOTIF_WM_HINTS", False);
570 	if (!hintsatom)
571 	{
572 		logger(GUI, Warning,
573 		       "Failed to get atom _MOTIF_WM_HINTS: probably your window manager does not support MWM hints\n");
574 		return;
575 	}
576 
577 	XChangeProperty(g_display, wnd, hintsatom, hintsatom, 32, PropModeReplace,
578 			(unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
579 
580 }
581 
582 typedef struct _sw_configurenotify_context
583 {
584 	Window window;
585 	unsigned long serial;
586 } sw_configurenotify_context;
587 
588 /* Predicate procedure for sw_wait_configurenotify */
589 static Bool
sw_configurenotify_p(Display * display,XEvent * xevent,XPointer arg)590 sw_configurenotify_p(Display * display, XEvent * xevent, XPointer arg)
591 {
592 	UNUSED(display);
593 	sw_configurenotify_context *context = (sw_configurenotify_context *) arg;
594 	if (xevent->xany.type == ConfigureNotify
595 	    && xevent->xconfigure.window == context->window
596 	    && xevent->xany.serial >= context->serial)
597 		return True;
598 
599 	return False;
600 }
601 
602 /* Wait for a ConfigureNotify, with a equal or larger serial, on the
603    specified window. The event will be removed from the queue. We
604    could use XMaskEvent(StructureNotifyMask), but we would then risk
605    throwing away crucial events like DestroyNotify.
606 
607    After a ConfigureWindow, according to ICCCM section 4.1.5, we
608    should receive a ConfigureNotify, either a real or synthetic
609    one. This indicates that the configure has been "completed".
610    However, some WMs such as several versions of Metacity fail to
611    send synthetic events. See bug
612    http://bugzilla.gnome.org/show_bug.cgi?id=322840. We need to use a
613    timeout to avoid a hang. Tk uses the same approach. */
614 static void
sw_wait_configurenotify(Window wnd,unsigned long serial)615 sw_wait_configurenotify(Window wnd, unsigned long serial)
616 {
617 	XEvent xevent;
618 	sw_configurenotify_context context;
619 	struct timeval now;
620 	struct timeval future;
621 	RD_BOOL got = False;
622 
623 	context.window = wnd;
624 	context.serial = serial;
625 
626 	gettimeofday(&future, NULL);
627 	future.tv_usec += 500000;
628 
629 	do
630 	{
631 		if (XCheckIfEvent(g_display, &xevent, sw_configurenotify_p, (XPointer) & context))
632 		{
633 			got = True;
634 			break;
635 		}
636 		usleep(100000);
637 		gettimeofday(&now, NULL);
638 	}
639 	while (timercmp(&now, &future, <));
640 
641 	if (!got)
642 	{
643 		logger(GUI, Warning,
644 		       "Broken Window Manager: Timeout while waiting for ConfigureNotify\n");
645 	}
646 }
647 
648 /* Get the toplevel window, in case of reparenting */
649 static Window
sw_get_toplevel(Window wnd)650 sw_get_toplevel(Window wnd)
651 {
652 	Window root, parent;
653 	Window *child_list;
654 	unsigned int num_children;
655 
656 	while (1)
657 	{
658 		XQueryTree(g_display, wnd, &root, &parent, &child_list, &num_children);
659 		if (root == parent)
660 		{
661 			break;
662 		}
663 		else if (!parent)
664 		{
665 			logger(GUI, Error, "sw_get_toplevel called with root window\n");
666 		}
667 
668 		wnd = parent;
669 	}
670 
671 	return wnd;
672 }
673 
674 
675 /* Check if wnd is already behind a window wrt stacking order */
676 static RD_BOOL
sw_window_is_behind(Window wnd,Window behind)677 sw_window_is_behind(Window wnd, Window behind)
678 {
679 	Window dummy1, dummy2;
680 	Window *child_list;
681 	unsigned int num_children;
682 	int i;
683 	RD_BOOL found_behind = False;
684 	RD_BOOL found_wnd = False;
685 
686 	wnd = sw_get_toplevel(wnd);
687 	behind = sw_get_toplevel(behind);
688 
689 	XQueryTree(g_display, RootWindowOfScreen(g_screen), &dummy1, &dummy2, &child_list,
690 		   &num_children);
691 
692 	for (i = num_children - 1; i >= 0; i--)
693 	{
694 		if (child_list[i] == behind)
695 		{
696 			found_behind = True;
697 		}
698 		else if (child_list[i] == wnd)
699 		{
700 			found_wnd = True;
701 			break;
702 		}
703 	}
704 
705 	if (child_list)
706 		XFree(child_list);
707 
708 	if (!found_wnd)
709 	{
710 		logger(GUI, Warning, "sw_window_is_behind: Unable to find window 0x%lx", wnd);
711 
712 		if (!found_behind)
713 		{
714 			logger(GUI, Warning,
715 			       "sw_window_is_behind: Unable to find behind window 0x%lx", behind);
716 		}
717 	}
718 
719 	return found_behind;
720 }
721 
722 
723 /* Test if the window manager correctly handles window restacking. In
724    particular, we are testing if it's possible to place a window
725    between two other windows. Many WMs such as Metacity can only stack
726    windows on the top or bottom. The window creation should mostly
727    match ui_seamless_create_window. */
728 static void
seamless_restack_test()729 seamless_restack_test()
730 {
731 	/* The goal is to have the middle window between top and
732 	   bottom.  The middle window is initially at the top,
733 	   though. */
734 	Window wnds[3];		/* top, middle and bottom */
735 	int i;
736 	XEvent xevent;
737 	XWindowChanges values;
738 	unsigned long restack_serial;
739 
740 	for (i = 0; i < 3; i++)
741 	{
742 		char name[64];
743 		wnds[i] =
744 			XCreateSimpleWindow(g_display, RootWindowOfScreen(g_screen), 0, 0, 20, 20,
745 					    0, 0, 0);
746 		snprintf(name, sizeof(name), "SeamlessRDP restack test - window %d", i);
747 		XStoreName(g_display, wnds[i], name);
748 		ewmh_set_wm_name(wnds[i], name);
749 
750 		/* Hide decorations. Often this means that no
751 		   reparenting will be done, which makes the restack
752 		   easier. Besides, we want to mimic our other
753 		   seamless windows as much as possible. We must still
754 		   handle the case with reparenting, though. */
755 		mwm_hide_decorations(wnds[i]);
756 
757 		/* Prevent windows from appearing in task bar */
758 		XSetTransientForHint(g_display, wnds[i], RootWindowOfScreen(g_screen));
759 		ewmh_set_window_popup(wnds[i]);
760 
761 		/* We need to catch MapNotify/ConfigureNotify */
762 		XSelectInput(g_display, wnds[i], StructureNotifyMask);
763 	}
764 
765 	/* Map Windows. Currently, we assume that XMapRaised places
766 	   the window on the top of the stack. Should be fairly safe;
767 	   the window is configured before it's mapped. */
768 	XMapRaised(g_display, wnds[2]);	/* bottom */
769 	do
770 	{
771 		XWindowEvent(g_display, wnds[2], StructureNotifyMask, &xevent);
772 	}
773 	while (xevent.type != MapNotify);
774 	XMapRaised(g_display, wnds[0]);	/* top */
775 	do
776 	{
777 		XWindowEvent(g_display, wnds[0], StructureNotifyMask, &xevent);
778 	}
779 	while (xevent.type != MapNotify);
780 	XMapRaised(g_display, wnds[1]);	/* middle */
781 	do
782 	{
783 		XWindowEvent(g_display, wnds[1], StructureNotifyMask, &xevent);
784 	}
785 	while (xevent.type != MapNotify);
786 
787 	/* The stacking order should now be 1 - 0 - 2 */
788 	if (!sw_window_is_behind(wnds[0], wnds[1]) || !sw_window_is_behind(wnds[2], wnds[1]))
789 	{
790 		/* Ok, technically a WM is allowed to stack windows arbitrarily, but... */
791 		logger(GUI, Warning, "Broken Window Manager: Unable to test window restacking");
792 		g_seamless_broken_restack = True;
793 		for (i = 0; i < 3; i++)
794 			XDestroyWindow(g_display, wnds[i]);
795 		return;
796 	}
797 
798 	/* Restack, using XReconfigureWMWindow, which should correctly
799 	   handle reparented windows as well as nonreparenting WMs. */
800 	values.stack_mode = Below;
801 	values.sibling = wnds[0];
802 	restack_serial = XNextRequest(g_display);
803 	XReconfigureWMWindow(g_display, wnds[1], DefaultScreen(g_display), CWStackMode | CWSibling,
804 			     &values);
805 	sw_wait_configurenotify(wnds[1], restack_serial);
806 
807 	/* Now verify that middle is behind top but not behind
808 	   bottom */
809 	if (!sw_window_is_behind(wnds[1], wnds[0]))
810 	{
811 		logger(GUI, Warning,
812 		       "Broken Window Manager: doesn't handle restack (restack request was ignored)");
813 		g_seamless_broken_restack = True;
814 	}
815 	else if (sw_window_is_behind(wnds[1], wnds[2]))
816 	{
817 		logger(GUI, Warning,
818 		       "Broken Window Manager: doesn't handle restack (window was moved to bottom)");
819 		g_seamless_broken_restack = True;
820 	}
821 
822 	/* Destroy windows */
823 	for (i = 0; i < 3; i++)
824 	{
825 		XDestroyWindow(g_display, wnds[i]);
826 		do
827 		{
828 			XWindowEvent(g_display, wnds[i], StructureNotifyMask, &xevent);
829 		}
830 		while (xevent.type != DestroyNotify);
831 	}
832 }
833 
834 #define SPLITCOLOUR15(colour, rv) \
835 { \
836 	rv.red = ((colour >> 7) & 0xf8) | ((colour >> 12) & 0x7); \
837 	rv.green = ((colour >> 2) & 0xf8) | ((colour >> 8) & 0x7); \
838 	rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
839 }
840 
841 #define SPLITCOLOUR16(colour, rv) \
842 { \
843 	rv.red = ((colour >> 8) & 0xf8) | ((colour >> 13) & 0x7); \
844 	rv.green = ((colour >> 3) & 0xfc) | ((colour >> 9) & 0x3); \
845 	rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
846 } \
847 
848 #define SPLITCOLOUR24(colour, rv) \
849 { \
850 	rv.blue = (colour & 0xff0000) >> 16; \
851 	rv.green = (colour & 0x00ff00) >> 8; \
852 	rv.red = (colour & 0x0000ff); \
853 }
854 
855 #define MAKECOLOUR(pc) \
856 	((pc.red >> g_red_shift_r) << g_red_shift_l) \
857 		| ((pc.green >> g_green_shift_r) << g_green_shift_l) \
858 		| ((pc.blue >> g_blue_shift_r) << g_blue_shift_l) \
859 
860 #define BSWAP16(x) { x = (((x & 0xff) << 8) | (x >> 8)); }
861 #define BSWAP24(x) { x = (((x & 0xff) << 16) | (x >> 16) | (x & 0xff00)); }
862 #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \
863 			x = (x << 16) | (x >> 16); }
864 
865 /* The following macros output the same octet sequences
866    on both BE and LE hosts: */
867 
868 #define BOUT16(o, x) { *(o++) = x >> 8; *(o++) = x; }
869 #define BOUT24(o, x) { *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
870 #define BOUT32(o, x) { *(o++) = x >> 24; *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
871 #define LOUT16(o, x) { *(o++) = x; *(o++) = x >> 8; }
872 #define LOUT24(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; }
873 #define LOUT32(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; *(o++) = x >> 24; }
874 
875 static uint32
translate_colour(uint32 colour)876 translate_colour(uint32 colour)
877 {
878 	PixelColour pc;
879 	switch (g_server_depth)
880 	{
881 		case 15:
882 			SPLITCOLOUR15(colour, pc);
883 			break;
884 		case 16:
885 			SPLITCOLOUR16(colour, pc);
886 			break;
887 		case 24:
888 		case 32:
889 			SPLITCOLOUR24(colour, pc);
890 			break;
891 		default:
892 			/* Avoid warning */
893 			pc.red = 0;
894 			pc.green = 0;
895 			pc.blue = 0;
896 			break;
897 	}
898 	return MAKECOLOUR(pc);
899 }
900 
901 /* indent is confused by UNROLL8 */
902 /* *INDENT-OFF* */
903 
904 /* repeat and unroll, similar to bitmap.c */
905 /* potentially any of the following translate */
906 /* functions can use repeat but just doing */
907 /* the most common ones */
908 
909 #define UNROLL8(stm) { stm stm stm stm stm stm stm stm }
910 /* 2 byte output repeat */
911 #define REPEAT2(stm) \
912 { \
913 	while (out <= end - 8 * 2) \
914 		UNROLL8(stm) \
915 	while (out < end) \
916 		{ stm } \
917 }
918 /* 3 byte output repeat */
919 #define REPEAT3(stm) \
920 { \
921 	while (out <= end - 8 * 3) \
922 		UNROLL8(stm) \
923 	while (out < end) \
924 		{ stm } \
925 }
926 /* 4 byte output repeat */
927 #define REPEAT4(stm) \
928 { \
929 	while (out <= end - 8 * 4) \
930 		UNROLL8(stm) \
931 	while (out < end) \
932 		{ stm } \
933 }
934 /* *INDENT-ON* */
935 
936 static void
translate8to8(const uint8 * data,uint8 * out,uint8 * end)937 translate8to8(const uint8 * data, uint8 * out, uint8 * end)
938 {
939 	while (out < end)
940 		*(out++) = (uint8) g_colmap[*(data++)];
941 }
942 
943 static void
translate8to16(const uint8 * data,uint8 * out,uint8 * end)944 translate8to16(const uint8 * data, uint8 * out, uint8 * end)
945 {
946 	uint16 value;
947 
948 	if (g_compatible_arch)
949 	{
950 		/* *INDENT-OFF* */
951 		REPEAT2
952 		(
953 			*((uint16 *) out) = g_colmap[*(data++)];
954 			out += 2;
955 		)
956 		/* *INDENT-ON* */
957 	}
958 	else if (g_xserver_be)
959 	{
960 		while (out < end)
961 		{
962 			value = (uint16) g_colmap[*(data++)];
963 			BOUT16(out, value);
964 		}
965 	}
966 	else
967 	{
968 		while (out < end)
969 		{
970 			value = (uint16) g_colmap[*(data++)];
971 			LOUT16(out, value);
972 		}
973 	}
974 }
975 
976 /* little endian - conversion happens when colourmap is built */
977 static void
translate8to24(const uint8 * data,uint8 * out,uint8 * end)978 translate8to24(const uint8 * data, uint8 * out, uint8 * end)
979 {
980 	uint32 value;
981 
982 	if (g_compatible_arch)
983 	{
984 		while (out < end)
985 		{
986 			value = g_colmap[*(data++)];
987 			BOUT24(out, value);
988 		}
989 	}
990 	else
991 	{
992 		while (out < end)
993 		{
994 			value = g_colmap[*(data++)];
995 			LOUT24(out, value);
996 		}
997 	}
998 }
999 
1000 static void
translate8to32(const uint8 * data,uint8 * out,uint8 * end)1001 translate8to32(const uint8 * data, uint8 * out, uint8 * end)
1002 {
1003 	uint32 value;
1004 
1005 	if (g_compatible_arch)
1006 	{
1007 		/* *INDENT-OFF* */
1008 		REPEAT4
1009 		(
1010 			*((uint32 *) out) = g_colmap[*(data++)];
1011 			out += 4;
1012 		)
1013 		/* *INDENT-ON* */
1014 	}
1015 	else if (g_xserver_be)
1016 	{
1017 		while (out < end)
1018 		{
1019 			value = g_colmap[*(data++)];
1020 			BOUT32(out, value);
1021 		}
1022 	}
1023 	else
1024 	{
1025 		while (out < end)
1026 		{
1027 			value = g_colmap[*(data++)];
1028 			LOUT32(out, value);
1029 		}
1030 	}
1031 }
1032 
1033 static void
translate15to16(const uint16 * data,uint8 * out,uint8 * end)1034 translate15to16(const uint16 * data, uint8 * out, uint8 * end)
1035 {
1036 	uint16 pixel;
1037 	uint16 value;
1038 	PixelColour pc;
1039 
1040 	if (g_xserver_be)
1041 	{
1042 		while (out < end)
1043 		{
1044 			pixel = *(data++);
1045 			if (g_host_be)
1046 			{
1047 				BSWAP16(pixel);
1048 			}
1049 			SPLITCOLOUR15(pixel, pc);
1050 			value = MAKECOLOUR(pc);
1051 			BOUT16(out, value);
1052 		}
1053 	}
1054 	else
1055 	{
1056 		while (out < end)
1057 		{
1058 			pixel = *(data++);
1059 			if (g_host_be)
1060 			{
1061 				BSWAP16(pixel);
1062 			}
1063 			SPLITCOLOUR15(pixel, pc);
1064 			value = MAKECOLOUR(pc);
1065 			LOUT16(out, value);
1066 		}
1067 	}
1068 }
1069 
1070 static void
translate15to24(const uint16 * data,uint8 * out,uint8 * end)1071 translate15to24(const uint16 * data, uint8 * out, uint8 * end)
1072 {
1073 	uint32 value;
1074 	uint16 pixel;
1075 	PixelColour pc;
1076 
1077 	if (g_compatible_arch)
1078 	{
1079 		/* *INDENT-OFF* */
1080 		REPEAT3
1081 		(
1082 			pixel = *(data++);
1083 			SPLITCOLOUR15(pixel, pc);
1084 			*(out++) = pc.blue;
1085 			*(out++) = pc.green;
1086 			*(out++) = pc.red;
1087 		)
1088 		/* *INDENT-ON* */
1089 	}
1090 	else if (g_xserver_be)
1091 	{
1092 		while (out < end)
1093 		{
1094 			pixel = *(data++);
1095 			if (g_host_be)
1096 			{
1097 				BSWAP16(pixel);
1098 			}
1099 			SPLITCOLOUR15(pixel, pc);
1100 			value = MAKECOLOUR(pc);
1101 			BOUT24(out, value);
1102 		}
1103 	}
1104 	else
1105 	{
1106 		while (out < end)
1107 		{
1108 			pixel = *(data++);
1109 			if (g_host_be)
1110 			{
1111 				BSWAP16(pixel);
1112 			}
1113 			SPLITCOLOUR15(pixel, pc);
1114 			value = MAKECOLOUR(pc);
1115 			LOUT24(out, value);
1116 		}
1117 	}
1118 }
1119 
1120 static void
translate15to32(const uint16 * data,uint8 * out,uint8 * end)1121 translate15to32(const uint16 * data, uint8 * out, uint8 * end)
1122 {
1123 	uint16 pixel;
1124 	uint32 value;
1125 	PixelColour pc;
1126 
1127 	if (g_compatible_arch)
1128 	{
1129 		/* *INDENT-OFF* */
1130 		REPEAT4
1131 		(
1132 			pixel = *(data++);
1133 			SPLITCOLOUR15(pixel, pc);
1134 			*(out++) = pc.blue;
1135 			*(out++) = pc.green;
1136 			*(out++) = pc.red;
1137 			*(out++) = 0;
1138 		)
1139 		/* *INDENT-ON* */
1140 	}
1141 	else if (g_xserver_be)
1142 	{
1143 		while (out < end)
1144 		{
1145 			pixel = *(data++);
1146 			if (g_host_be)
1147 			{
1148 				BSWAP16(pixel);
1149 			}
1150 			SPLITCOLOUR15(pixel, pc);
1151 			value = MAKECOLOUR(pc);
1152 			BOUT32(out, value);
1153 		}
1154 	}
1155 	else
1156 	{
1157 		while (out < end)
1158 		{
1159 			pixel = *(data++);
1160 			if (g_host_be)
1161 			{
1162 				BSWAP16(pixel);
1163 			}
1164 			SPLITCOLOUR15(pixel, pc);
1165 			value = MAKECOLOUR(pc);
1166 			LOUT32(out, value);
1167 		}
1168 	}
1169 }
1170 
1171 static void
translate16to16(const uint16 * data,uint8 * out,uint8 * end)1172 translate16to16(const uint16 * data, uint8 * out, uint8 * end)
1173 {
1174 	uint16 pixel;
1175 	uint16 value;
1176 	PixelColour pc;
1177 
1178 	if (g_xserver_be)
1179 	{
1180 		if (g_host_be)
1181 		{
1182 			while (out < end)
1183 			{
1184 				pixel = *(data++);
1185 				BSWAP16(pixel);
1186 				SPLITCOLOUR16(pixel, pc);
1187 				value = MAKECOLOUR(pc);
1188 				BOUT16(out, value);
1189 			}
1190 		}
1191 		else
1192 		{
1193 			while (out < end)
1194 			{
1195 				pixel = *(data++);
1196 				SPLITCOLOUR16(pixel, pc);
1197 				value = MAKECOLOUR(pc);
1198 				BOUT16(out, value);
1199 			}
1200 		}
1201 	}
1202 	else
1203 	{
1204 		if (g_host_be)
1205 		{
1206 			while (out < end)
1207 			{
1208 				pixel = *(data++);
1209 				BSWAP16(pixel);
1210 				SPLITCOLOUR16(pixel, pc);
1211 				value = MAKECOLOUR(pc);
1212 				LOUT16(out, value);
1213 			}
1214 		}
1215 		else
1216 		{
1217 			while (out < end)
1218 			{
1219 				pixel = *(data++);
1220 				SPLITCOLOUR16(pixel, pc);
1221 				value = MAKECOLOUR(pc);
1222 				LOUT16(out, value);
1223 			}
1224 		}
1225 	}
1226 }
1227 
1228 static void
translate16to24(const uint16 * data,uint8 * out,uint8 * end)1229 translate16to24(const uint16 * data, uint8 * out, uint8 * end)
1230 {
1231 	uint32 value;
1232 	uint16 pixel;
1233 	PixelColour pc;
1234 
1235 	if (g_compatible_arch)
1236 	{
1237 		/* *INDENT-OFF* */
1238 		REPEAT3
1239 		(
1240 			pixel = *(data++);
1241 			SPLITCOLOUR16(pixel, pc);
1242 			*(out++) = pc.blue;
1243 			*(out++) = pc.green;
1244 			*(out++) = pc.red;
1245 		)
1246 		/* *INDENT-ON* */
1247 	}
1248 	else if (g_xserver_be)
1249 	{
1250 		if (g_host_be)
1251 		{
1252 			while (out < end)
1253 			{
1254 				pixel = *(data++);
1255 				BSWAP16(pixel);
1256 				SPLITCOLOUR16(pixel, pc);
1257 				value = MAKECOLOUR(pc);
1258 				BOUT24(out, value);
1259 			}
1260 		}
1261 		else
1262 		{
1263 			while (out < end)
1264 			{
1265 				pixel = *(data++);
1266 				SPLITCOLOUR16(pixel, pc);
1267 				value = MAKECOLOUR(pc);
1268 				BOUT24(out, value);
1269 			}
1270 		}
1271 	}
1272 	else
1273 	{
1274 		if (g_host_be)
1275 		{
1276 			while (out < end)
1277 			{
1278 				pixel = *(data++);
1279 				BSWAP16(pixel);
1280 				SPLITCOLOUR16(pixel, pc);
1281 				value = MAKECOLOUR(pc);
1282 				LOUT24(out, value);
1283 			}
1284 		}
1285 		else
1286 		{
1287 			while (out < end)
1288 			{
1289 				pixel = *(data++);
1290 				SPLITCOLOUR16(pixel, pc);
1291 				value = MAKECOLOUR(pc);
1292 				LOUT24(out, value);
1293 			}
1294 		}
1295 	}
1296 }
1297 
1298 static void
translate16to32(const uint16 * data,uint8 * out,uint8 * end)1299 translate16to32(const uint16 * data, uint8 * out, uint8 * end)
1300 {
1301 	uint16 pixel;
1302 	uint32 value;
1303 	PixelColour pc;
1304 
1305 	if (g_compatible_arch)
1306 	{
1307 		/* *INDENT-OFF* */
1308 		REPEAT4
1309 		(
1310 			pixel = *(data++);
1311 			SPLITCOLOUR16(pixel, pc);
1312 			*(out++) = pc.blue;
1313 			*(out++) = pc.green;
1314 			*(out++) = pc.red;
1315 			*(out++) = 0;
1316 		)
1317 		/* *INDENT-ON* */
1318 	}
1319 	else if (g_xserver_be)
1320 	{
1321 		if (g_host_be)
1322 		{
1323 			while (out < end)
1324 			{
1325 				pixel = *(data++);
1326 				BSWAP16(pixel);
1327 				SPLITCOLOUR16(pixel, pc);
1328 				value = MAKECOLOUR(pc);
1329 				BOUT32(out, value);
1330 			}
1331 		}
1332 		else
1333 		{
1334 			while (out < end)
1335 			{
1336 				pixel = *(data++);
1337 				SPLITCOLOUR16(pixel, pc);
1338 				value = MAKECOLOUR(pc);
1339 				BOUT32(out, value);
1340 			}
1341 		}
1342 	}
1343 	else
1344 	{
1345 		if (g_host_be)
1346 		{
1347 			while (out < end)
1348 			{
1349 				pixel = *(data++);
1350 				BSWAP16(pixel);
1351 				SPLITCOLOUR16(pixel, pc);
1352 				value = MAKECOLOUR(pc);
1353 				LOUT32(out, value);
1354 			}
1355 		}
1356 		else
1357 		{
1358 			while (out < end)
1359 			{
1360 				pixel = *(data++);
1361 				SPLITCOLOUR16(pixel, pc);
1362 				value = MAKECOLOUR(pc);
1363 				LOUT32(out, value);
1364 			}
1365 		}
1366 	}
1367 }
1368 
1369 static void
translate24to16(const uint8 * data,uint8 * out,uint8 * end)1370 translate24to16(const uint8 * data, uint8 * out, uint8 * end)
1371 {
1372 	uint32 pixel = 0;
1373 	uint16 value;
1374 	PixelColour pc;
1375 
1376 	while (out < end)
1377 	{
1378 		pixel = *(data++) << 16;
1379 		pixel |= *(data++) << 8;
1380 		pixel |= *(data++);
1381 		SPLITCOLOUR24(pixel, pc);
1382 		value = MAKECOLOUR(pc);
1383 		if (g_xserver_be)
1384 		{
1385 			BOUT16(out, value);
1386 		}
1387 		else
1388 		{
1389 			LOUT16(out, value);
1390 		}
1391 	}
1392 }
1393 
1394 static void
translate24to24(const uint8 * data,uint8 * out,uint8 * end)1395 translate24to24(const uint8 * data, uint8 * out, uint8 * end)
1396 {
1397 	uint32 pixel;
1398 	uint32 value;
1399 	PixelColour pc;
1400 
1401 	if (g_xserver_be)
1402 	{
1403 		while (out < end)
1404 		{
1405 			pixel = *(data++) << 16;
1406 			pixel |= *(data++) << 8;
1407 			pixel |= *(data++);
1408 			SPLITCOLOUR24(pixel, pc);
1409 			value = MAKECOLOUR(pc);
1410 			BOUT24(out, value);
1411 		}
1412 	}
1413 	else
1414 	{
1415 		while (out < end)
1416 		{
1417 			pixel = *(data++) << 16;
1418 			pixel |= *(data++) << 8;
1419 			pixel |= *(data++);
1420 			SPLITCOLOUR24(pixel, pc);
1421 			value = MAKECOLOUR(pc);
1422 			LOUT24(out, value);
1423 		}
1424 	}
1425 }
1426 
1427 static void
translate24to32(const uint8 * data,uint8 * out,uint8 * end)1428 translate24to32(const uint8 * data, uint8 * out, uint8 * end)
1429 {
1430 	uint32 pixel;
1431 	uint32 value;
1432 	PixelColour pc;
1433 
1434 	if (g_compatible_arch)
1435 	{
1436 		/* *INDENT-OFF* */
1437 #ifdef NEED_ALIGN
1438 		REPEAT4
1439 		(
1440 			*(out++) = *(data++);
1441 			*(out++) = *(data++);
1442 			*(out++) = *(data++);
1443 			*(out++) = 0;
1444 		)
1445 #else
1446 		REPEAT4
1447 		(
1448 		 /* Only read 3 bytes. Reading 4 bytes means reading beyond buffer. */
1449 		 *((uint32 *) out) = *((uint16 *) data) + (*((uint8 *) data + 2) << 16);
1450 		 out += 4;
1451 		 data += 3;
1452 		)
1453 #endif
1454 		/* *INDENT-ON* */
1455 	}
1456 	else if (g_xserver_be)
1457 	{
1458 		while (out < end)
1459 		{
1460 			pixel = *(data++) << 16;
1461 			pixel |= *(data++) << 8;
1462 			pixel |= *(data++);
1463 			SPLITCOLOUR24(pixel, pc);
1464 			value = MAKECOLOUR(pc);
1465 			BOUT32(out, value);
1466 		}
1467 	}
1468 	else
1469 	{
1470 		while (out < end)
1471 		{
1472 			pixel = *(data++) << 16;
1473 			pixel |= *(data++) << 8;
1474 			pixel |= *(data++);
1475 			SPLITCOLOUR24(pixel, pc);
1476 			value = MAKECOLOUR(pc);
1477 			LOUT32(out, value);
1478 		}
1479 	}
1480 }
1481 
1482 static uint8 *
translate_image(int width,int height,uint8 * data)1483 translate_image(int width, int height, uint8 * data)
1484 {
1485 	int size;
1486 	uint8 *out;
1487 	uint8 *end;
1488 
1489 	/*
1490 	   If RDP depth and X Visual depths match,
1491 	   and arch(endian) matches, no need to translate:
1492 	   just return data.
1493 	   Note: select_visual should've already ensured g_no_translate
1494 	   is only set for compatible depths, but the RDP depth might've
1495 	   changed during connection negotiations.
1496 	 */
1497 
1498 	/* todo */
1499 	if (g_server_depth == 32 && g_depth == 24)
1500 	{
1501 		return data;
1502 	}
1503 
1504 	if (g_no_translate_image)
1505 	{
1506 		if ((g_depth == 15 && g_server_depth == 15) ||
1507 		    (g_depth == 16 && g_server_depth == 16) ||
1508 		    (g_depth == 24 && g_server_depth == 24))
1509 			return data;
1510 	}
1511 
1512 	size = width * height * (g_bpp / 8);
1513 	out = (uint8 *) xmalloc(size);
1514 	end = out + size;
1515 
1516 	switch (g_server_depth)
1517 	{
1518 		case 24:
1519 			switch (g_bpp)
1520 			{
1521 				case 32:
1522 					translate24to32(data, out, end);
1523 					break;
1524 				case 24:
1525 					translate24to24(data, out, end);
1526 					break;
1527 				case 16:
1528 					translate24to16(data, out, end);
1529 					break;
1530 			}
1531 			break;
1532 		case 16:
1533 			switch (g_bpp)
1534 			{
1535 				case 32:
1536 					translate16to32((uint16 *) data, out, end);
1537 					break;
1538 				case 24:
1539 					translate16to24((uint16 *) data, out, end);
1540 					break;
1541 				case 16:
1542 					translate16to16((uint16 *) data, out, end);
1543 					break;
1544 			}
1545 			break;
1546 		case 15:
1547 			switch (g_bpp)
1548 			{
1549 				case 32:
1550 					translate15to32((uint16 *) data, out, end);
1551 					break;
1552 				case 24:
1553 					translate15to24((uint16 *) data, out, end);
1554 					break;
1555 				case 16:
1556 					translate15to16((uint16 *) data, out, end);
1557 					break;
1558 			}
1559 			break;
1560 		case 8:
1561 			switch (g_bpp)
1562 			{
1563 				case 8:
1564 					translate8to8(data, out, end);
1565 					break;
1566 				case 16:
1567 					translate8to16(data, out, end);
1568 					break;
1569 				case 24:
1570 					translate8to24(data, out, end);
1571 					break;
1572 				case 32:
1573 					translate8to32(data, out, end);
1574 					break;
1575 			}
1576 			break;
1577 	}
1578 	return out;
1579 }
1580 
1581 static void
xwin_refresh_pointer_map(void)1582 xwin_refresh_pointer_map(void)
1583 {
1584 	unsigned char phys_to_log_map[sizeof(g_pointer_log_to_phys_map)];
1585 	int i;
1586 	unsigned int pointer_buttons;
1587 
1588 	pointer_buttons =
1589 		(unsigned int) XGetPointerMapping(g_display, phys_to_log_map,
1590 						  sizeof(phys_to_log_map));
1591 	if (pointer_buttons > sizeof(phys_to_log_map))
1592 		pointer_buttons = sizeof(phys_to_log_map);
1593 
1594 	/* if multiple physical buttons map to the same logical button, then
1595 	 * use the lower numbered physical one */
1596 	for (i = pointer_buttons - 1; i >= 0; i--)
1597 	{
1598 		/* a user could specify arbitrary values for the logical button
1599 		 * number, ignore any that are abnormally large */
1600 		if (phys_to_log_map[i] > sizeof(g_pointer_log_to_phys_map))
1601 			continue;
1602 		g_pointer_log_to_phys_map[phys_to_log_map[i] - 1] = i + 1;
1603 	}
1604 }
1605 
1606 RD_BOOL
get_key_state(unsigned int state,uint32 keysym)1607 get_key_state(unsigned int state, uint32 keysym)
1608 {
1609 	int modifierpos, key, keysymMask = 0;
1610 	int offset;
1611 
1612 	KeyCode keycode = XKeysymToKeycode(g_display, keysym);
1613 
1614 	if (keycode == NoSymbol)
1615 		return False;
1616 
1617 	for (modifierpos = 0; modifierpos < 8; modifierpos++)
1618 	{
1619 		offset = g_mod_map->max_keypermod * modifierpos;
1620 
1621 		for (key = 0; key < g_mod_map->max_keypermod; key++)
1622 		{
1623 			if (g_mod_map->modifiermap[offset + key] == keycode)
1624 				keysymMask |= 1 << modifierpos;
1625 		}
1626 	}
1627 
1628 	return (state & keysymMask) ? True : False;
1629 }
1630 
1631 static void
calculate_shifts(uint32 mask,int * shift_r,int * shift_l)1632 calculate_shifts(uint32 mask, int *shift_r, int *shift_l)
1633 {
1634 	*shift_l = ffs(mask) - 1;
1635 	mask >>= *shift_l;
1636 	*shift_r = 8 - ffs(mask & ~(mask >> 1));
1637 }
1638 
1639 /* Given a mask of a colour channel (e.g. XVisualInfo.red_mask),
1640    calculates the bits-per-pixel of this channel (a.k.a. colour weight).
1641  */
1642 static unsigned
calculate_mask_weight(uint32 mask)1643 calculate_mask_weight(uint32 mask)
1644 {
1645 	unsigned weight = 0;
1646 	do
1647 	{
1648 		weight += (mask & 1);
1649 	}
1650 	while (mask >>= 1);
1651 	return weight;
1652 }
1653 
1654 static RD_BOOL
select_visual(int screen_num)1655 select_visual(int screen_num)
1656 {
1657 	XPixmapFormatValues *pfm;
1658 	int pixmap_formats_count, visuals_count;
1659 	XVisualInfo *vmatches = NULL;
1660 	XVisualInfo template;
1661 	int i;
1662 	unsigned red_weight, blue_weight, green_weight;
1663 
1664 	red_weight = blue_weight = green_weight = 0;
1665 
1666 	if (g_server_depth == -1)
1667 	{
1668 		g_server_depth = DisplayPlanes(g_display, DefaultScreen(g_display));
1669 	}
1670 
1671 	pfm = XListPixmapFormats(g_display, &pixmap_formats_count);
1672 	if (pfm == NULL)
1673 	{
1674 		logger(GUI, Error, "Unable to get list of pixmap formats from display");
1675 		XCloseDisplay(g_display);
1676 		return False;
1677 	}
1678 
1679 	/* Search for best TrueColor visual */
1680 	template.class = TrueColor;
1681 	template.screen = screen_num;
1682 	vmatches =
1683 		XGetVisualInfo(g_display, VisualClassMask | VisualScreenMask, &template,
1684 			       &visuals_count);
1685 	g_visual = NULL;
1686 	g_no_translate_image = False;
1687 	g_compatible_arch = False;
1688 	if (vmatches != NULL)
1689 	{
1690 		for (i = 0; i < visuals_count; ++i)
1691 		{
1692 			XVisualInfo *visual_info = &vmatches[i];
1693 			RD_BOOL can_translate_to_bpp = False;
1694 			int j;
1695 
1696 			/* Try to find a no-translation visual that'll
1697 			   allow us to use RDP bitmaps directly as ZPixmaps. */
1698 			if (!g_xserver_be && (((visual_info->depth == 15) &&
1699 					       /* R5G5B5 */
1700 					       (visual_info->red_mask == 0x7c00) &&
1701 					       (visual_info->green_mask == 0x3e0) &&
1702 					       (visual_info->blue_mask == 0x1f)) ||
1703 					      ((visual_info->depth == 16) &&
1704 					       /* R5G6B5 */
1705 					       (visual_info->red_mask == 0xf800) &&
1706 					       (visual_info->green_mask == 0x7e0) &&
1707 					       (visual_info->blue_mask == 0x1f)) ||
1708 					      ((visual_info->depth == 24) &&
1709 					       /* R8G8B8 */
1710 					       (visual_info->red_mask == 0xff0000) &&
1711 					       (visual_info->green_mask == 0xff00) &&
1712 					       (visual_info->blue_mask == 0xff))))
1713 			{
1714 				g_visual = visual_info->visual;
1715 				g_depth = visual_info->depth;
1716 				g_compatible_arch = !g_host_be;
1717 				g_no_translate_image = (visual_info->depth == g_server_depth);
1718 				if (g_no_translate_image)
1719 					/* We found the best visual */
1720 					break;
1721 			}
1722 			else
1723 			{
1724 				g_compatible_arch = False;
1725 			}
1726 
1727 			if (visual_info->depth > 24)
1728 			{
1729 				/* Avoid 32-bit visuals and likes like the plague.
1730 				   They're either untested or proven to work bad
1731 				   (e.g. nvidia's Composite 32-bit visual).
1732 				   Most implementation offer a 24-bit visual anyway. */
1733 				continue;
1734 			}
1735 
1736 			/* Only care for visuals, for whose BPPs (not depths!)
1737 			   we have a translateXtoY function. */
1738 			for (j = 0; j < pixmap_formats_count; ++j)
1739 			{
1740 				if (pfm[j].depth == visual_info->depth)
1741 				{
1742 					if ((pfm[j].bits_per_pixel == 16) ||
1743 					    (pfm[j].bits_per_pixel == 24) ||
1744 					    (pfm[j].bits_per_pixel == 32))
1745 					{
1746 						can_translate_to_bpp = True;
1747 					}
1748 					break;
1749 				}
1750 			}
1751 
1752 			/* Prefer formats which have the most colour depth.
1753 			   We're being truly aristocratic here, minding each
1754 			   weight on its own. */
1755 			if (can_translate_to_bpp)
1756 			{
1757 				unsigned vis_red_weight =
1758 					calculate_mask_weight(visual_info->red_mask);
1759 				unsigned vis_green_weight =
1760 					calculate_mask_weight(visual_info->green_mask);
1761 				unsigned vis_blue_weight =
1762 					calculate_mask_weight(visual_info->blue_mask);
1763 				if ((vis_red_weight >= red_weight)
1764 				    && (vis_green_weight >= green_weight)
1765 				    && (vis_blue_weight >= blue_weight))
1766 				{
1767 					red_weight = vis_red_weight;
1768 					green_weight = vis_green_weight;
1769 					blue_weight = vis_blue_weight;
1770 					g_visual = visual_info->visual;
1771 					g_depth = visual_info->depth;
1772 				}
1773 			}
1774 		}
1775 		XFree(vmatches);
1776 	}
1777 
1778 	if (g_visual != NULL)
1779 	{
1780 		g_owncolmap = False;
1781 		calculate_shifts(g_visual->red_mask, &g_red_shift_r, &g_red_shift_l);
1782 		calculate_shifts(g_visual->green_mask, &g_green_shift_r, &g_green_shift_l);
1783 		calculate_shifts(g_visual->blue_mask, &g_blue_shift_r, &g_blue_shift_l);
1784 	}
1785 	else
1786 	{
1787 		template.class = PseudoColor;
1788 		template.depth = 8;
1789 		template.colormap_size = 256;
1790 		vmatches =
1791 			XGetVisualInfo(g_display,
1792 				       VisualClassMask | VisualDepthMask | VisualColormapSizeMask,
1793 				       &template, &visuals_count);
1794 		if (vmatches == NULL)
1795 		{
1796 			logger(GUI, Error,
1797 			       "No usable TrueColor or PseudoColor visuals on this display");
1798 			XCloseDisplay(g_display);
1799 			XFree(pfm);
1800 			return False;
1801 		}
1802 
1803 		/* we use a colourmap, so the default visual should do */
1804 		g_owncolmap = True;
1805 		g_visual = vmatches[0].visual;
1806 		g_depth = vmatches[0].depth;
1807 	}
1808 
1809 	g_bpp = 0;
1810 	for (i = 0; i < pixmap_formats_count; ++i)
1811 	{
1812 		XPixmapFormatValues *pf = &pfm[i];
1813 		if (pf->depth == g_depth)
1814 		{
1815 			g_bpp = pf->bits_per_pixel;
1816 
1817 			if (g_no_translate_image)
1818 			{
1819 				switch (g_server_depth)
1820 				{
1821 					case 15:
1822 					case 16:
1823 						if (g_bpp != 16)
1824 							g_no_translate_image = False;
1825 						break;
1826 					case 24:
1827 						/* Yes, this will force image translation
1828 						   on most modern servers which use 32 bits
1829 						   for R8G8B8. */
1830 						if (g_bpp != 24)
1831 							g_no_translate_image = False;
1832 						break;
1833 					default:
1834 						g_no_translate_image = False;
1835 						break;
1836 				}
1837 			}
1838 
1839 			/* Pixmap formats list is a depth-to-bpp mapping --
1840 			   there's just a single entry for every depth,
1841 			   so we can safely break here */
1842 			break;
1843 		}
1844 	}
1845 	XFree(pfm);
1846 	pfm = NULL;
1847 	return True;
1848 }
1849 
1850 static XErrorHandler g_old_error_handler;
1851 static RD_BOOL g_error_expected = False;
1852 
1853 /* Check if the X11 window corresponding to a seamless window with
1854    specified id exists. */
1855 RD_BOOL
sw_window_exists(unsigned long id)1856 sw_window_exists(unsigned long id)
1857 {
1858 	seamless_window *sw;
1859 	char *name;
1860 	Status sts = 0;
1861 
1862 	sw = sw_get_window_by_id(id);
1863 	if (!sw)
1864 		return False;
1865 
1866 	g_error_expected = True;
1867 	sts = XFetchName(g_display, sw->wnd, &name);
1868 	g_error_expected = False;
1869 	if (sts)
1870 	{
1871 		XFree(name);
1872 	}
1873 
1874 	return sts;
1875 }
1876 
1877 static int
error_handler(Display * dpy,XErrorEvent * eev)1878 error_handler(Display * dpy, XErrorEvent * eev)
1879 {
1880 	if (g_error_expected)
1881 		return 0;
1882 
1883 	return g_old_error_handler(dpy, eev);
1884 }
1885 
1886 static void
set_wm_client_machine(Display * dpy,Window win)1887 set_wm_client_machine(Display * dpy, Window win)
1888 {
1889 	XTextProperty tp;
1890 	char hostname[HOST_NAME_MAX];
1891 
1892 	if (gethostname(hostname, sizeof(hostname)) != 0)
1893 		return;
1894 
1895 	tp.value = (unsigned char *) hostname;
1896 	tp.nitems = strlen(hostname);
1897 	tp.encoding = XA_STRING;
1898 	tp.format = 8;
1899 
1900 	XSetWMClientMachine(dpy, win, &tp);
1901 }
1902 
is_wm_active(void)1903 RD_BOOL is_wm_active(void)
1904 {
1905 	Atom prop, actual_type;
1906 	int actual_fmt;
1907 	unsigned long nitems, bytes_left;
1908 	unsigned char *data;
1909 	Window wid;
1910 
1911 	prop = XInternAtom(g_display, "_NET_SUPPORTING_WM_CHECK", True);
1912 
1913 	if (prop == None) return False;
1914 
1915 	if (XGetWindowProperty(g_display, DefaultRootWindow(g_display), prop, 0, 1, False,
1916 				XA_WINDOW, &actual_type, &actual_fmt, &nitems, &bytes_left, &data) != Success) {
1917 		return False;
1918 	}
1919 
1920 	if (!nitems) {
1921 		XFree(data);
1922 		return False;
1923 	}
1924 
1925 	wid = ((Window *)data)[0];
1926 	XFree(data);
1927 
1928 	if (XGetWindowProperty(g_display, wid, prop, 0, 1, False,
1929 				XA_WINDOW, &actual_type, &actual_fmt, &nitems, &bytes_left, &data) != Success) {
1930 		return False;
1931 	}
1932 
1933 	if (!nitems) {
1934 		XFree(data);
1935 		return False;
1936 	}
1937 
1938 	if (wid != ((Window *)data)[0]) {
1939 		XFree(data);
1940 		return False;
1941 	}
1942 
1943 	XFree(data);
1944 
1945 	/* Just for the curious minds */
1946 	prop = XInternAtom(g_display, "_NET_WM_NAME", True);
1947 
1948 	if (prop == None) return False;
1949 
1950 
1951 	if (XGetWindowProperty(g_display, wid, prop, 0, 1, False,
1952 				AnyPropertyType, &actual_type, &actual_fmt, &nitems, &bytes_left, &data) == Success) {
1953 		if (nitems) {
1954 			logger(GUI, Verbose, "%s(): WM name: %s", __func__, data);
1955 		}
1956 
1957 		XFree(data);
1958 	}
1959 
1960 	return True;
1961 }
1962 
1963 
1964 /* Initialize the UI. This is done once per process. */
1965 RD_BOOL
ui_init(void)1966 ui_init(void)
1967 {
1968 	int screen_num;
1969 
1970 	g_display = XOpenDisplay(NULL);
1971 	if (g_display == NULL)
1972 	{
1973 		logger(GUI, Error, "ui_init(), failed to open X11 display: %s", XDisplayName(NULL));
1974 		return False;
1975 	}
1976 
1977 	{
1978 		uint16 endianness_test = 1;
1979 		g_host_be = !(RD_BOOL) (*(uint8 *) (&endianness_test));
1980 	}
1981 
1982 	g_old_error_handler = XSetErrorHandler(error_handler);
1983 	g_xserver_be = (ImageByteOrder(g_display) == MSBFirst);
1984 	screen_num = DefaultScreen(g_display);
1985 	g_x_socket = ConnectionNumber(g_display);
1986 	g_screen = ScreenOfDisplay(g_display, screen_num);
1987 	g_depth = DefaultDepthOfScreen(g_screen);
1988 
1989 	if (!select_visual(screen_num))
1990 		return False;
1991 
1992 	if (g_no_translate_image)
1993 	{
1994 		logger(GUI, Debug,
1995 		       "Performance optimization possible: avoiding image translation (colour depth conversion)");
1996 	}
1997 
1998 	if (g_server_depth > g_bpp)
1999 	{
2000 		logger(GUI, Warning,
2001 		       "Remote desktop colour depth %d higher than display colour depth %d",
2002 		       g_server_depth, g_bpp);
2003 	}
2004 
2005 	logger(GUI, Debug,
2006 	       "RDP depth: %d, display depth: %d, display bpp: %d, X server BE: %d, host BE: %d\n",
2007 	       g_server_depth, g_depth, g_bpp, g_xserver_be, g_host_be);
2008 
2009 	if (!g_owncolmap)
2010 	{
2011 		g_xcolmap =
2012 			XCreateColormap(g_display, RootWindowOfScreen(g_screen), g_visual,
2013 					AllocNone);
2014 		if (g_depth <= 8)
2015 			logger(GUI, Warning,
2016 			       "Display colour depth is %d bit: you may want to use -C for a private colourmap",
2017 			       g_depth);
2018 	}
2019 
2020 	if ((!g_ownbackstore) && (DoesBackingStore(g_screen) != Always))
2021 	{
2022 		logger(GUI, Warning, "External BackingStore not available. Using internal");
2023 		g_ownbackstore = True;
2024 	}
2025 
2026 	g_mod_map = XGetModifierMapping(g_display);
2027 	xwin_refresh_pointer_map();
2028 
2029 	xkeymap_init();
2030 
2031 	if (g_enable_compose)
2032 		g_IM = XOpenIM(g_display, NULL, NULL, NULL);
2033 
2034 	xclip_init();
2035 	ewmh_init();
2036 	if (g_seamless_rdp)
2037 	{
2038 		seamless_init();
2039 	}
2040 
2041 	g_has_wm = is_wm_active();
2042 
2043 	return True;
2044 }
2045 
2046 void
ui_get_screen_size(uint32 * width,uint32 * height)2047 ui_get_screen_size(uint32 * width, uint32 * height)
2048 {
2049 	*width = WidthOfScreen(g_screen);
2050 	*height = HeightOfScreen(g_screen);
2051 }
2052 
2053 void
ui_get_screen_size_from_percentage(uint32 pw,uint32 ph,uint32 * width,uint32 * height)2054 ui_get_screen_size_from_percentage(uint32 pw, uint32 ph, uint32 * width, uint32 * height)
2055 {
2056 	uint32 sw, sh;
2057 	ui_get_screen_size(&sw, &sh);
2058 	*width = sw * pw / 100;
2059 	*height = sh * ph / 100;
2060 }
2061 
2062 void
ui_get_workarea_size(uint32 * width,uint32 * height)2063 ui_get_workarea_size(uint32 * width, uint32 * height)
2064 {
2065 	uint32 x, y, w, h;
2066 	if (get_current_workarea(&x, &y, &w, &h) == 0)
2067 	{
2068 		*width = w;
2069 		*height = h;
2070 		g_using_full_workarea = True;
2071 	}
2072 	else
2073 	{
2074 		logger(GUI, Warning,
2075 		       "Failed to get workarea: probably your window manager does not support extended hints, using full screensize as fallback\n");
2076 		ui_get_screen_size(width, height);
2077 	}
2078 }
2079 
2080 
2081 void
ui_deinit(void)2082 ui_deinit(void)
2083 {
2084 	xclip_deinit();
2085 
2086 	if (g_IM != NULL)
2087 		XCloseIM(g_IM);
2088 
2089 	if (g_null_cursor != NULL)
2090 		XFreeCursor(g_display, (Cursor) g_null_cursor);
2091 
2092 	XFreeModifiermap(g_mod_map);
2093 
2094 	XFreeGC(g_display, g_gc);
2095 	XCloseDisplay(g_display);
2096 	g_display = NULL;
2097 }
2098 
2099 
2100 static unsigned long
get_window_attribs(XSetWindowAttributes * attribs)2101 get_window_attribs(XSetWindowAttributes * attribs)
2102 {
2103 	unsigned long vmask = 0;
2104 
2105 	vmask = CWBackPixel | CWBorderPixel | CWBackingStore | CWOverrideRedirect | CWColormap;
2106 
2107 	attribs->background_pixel = BlackPixelOfScreen(g_screen);
2108 	attribs->border_pixel = WhitePixelOfScreen(g_screen);
2109 	attribs->backing_store = g_ownbackstore ? NotUseful : Always;
2110 	if (g_has_wm) {
2111 		attribs->override_redirect = 0;
2112 	} else {
2113 		attribs->override_redirect = g_fullscreen;
2114 	}
2115 	attribs->colormap = g_xcolmap;
2116 
2117 	return vmask;
2118 }
2119 
2120 static unsigned long
get_window_attribs_seamless(XSetWindowAttributes * attribs)2121 get_window_attribs_seamless(XSetWindowAttributes * attribs)
2122 {
2123 	return (get_window_attribs(attribs) & ~CWOverrideRedirect);
2124 }
2125 
2126 static void
get_input_mask(long * input_mask)2127 get_input_mask(long *input_mask)
2128 {
2129 	*input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
2130 		VisibilityChangeMask | FocusChangeMask | StructureNotifyMask;
2131 
2132 	if (g_sendmotion)
2133 		*input_mask |= PointerMotionMask;
2134 	if (g_ownbackstore)
2135 		*input_mask |= ExposureMask;
2136 	if (g_fullscreen || g_grab_keyboard)
2137 		*input_mask |= EnterWindowMask;
2138 	if (g_grab_keyboard)
2139 		*input_mask |= LeaveWindowMask;
2140 }
2141 
2142 static void
get_sizehints(XSizeHints * sizehints,uint32 width,uint32 height)2143 get_sizehints(XSizeHints * sizehints, uint32 width, uint32 height)
2144 {
2145 	if (sizehints == NULL)
2146 		return;
2147 
2148 	/* user specified position, this is needed to override the choice of
2149 	   window position by window manager when a window is mapped. */
2150 	sizehints->flags = USPosition;
2151 
2152 	/* set minimal size of rdesktop main window */
2153 	sizehints->flags |= PMinSize;
2154 	sizehints->min_width = 200;
2155 	sizehints->min_height = 200;
2156 
2157 	/* resize increment */
2158 	sizehints->flags |= PResizeInc;
2159 	sizehints->width_inc = 2;	/* session width must be divisible by two */
2160 	sizehints->height_inc = 1;
2161 
2162 	if (g_seamless_rdp || !g_dynamic_session_resize)
2163 	{
2164 		/* disable dynamic session resize based on window size for
2165 		   rdesktop main window when seamless is enabled */
2166 		sizehints->flags |= PMaxSize;
2167 		sizehints->min_width = sizehints->max_width = width;
2168 		sizehints->min_height = sizehints->max_height = height;
2169 	}
2170 
2171 	ui_reset_clip();
2172 }
2173 
2174 void
ui_update_window_sizehints(uint32 width,uint32 height)2175 ui_update_window_sizehints(uint32 width, uint32 height)
2176 {
2177 	XSizeHints *sizehints;
2178 	sizehints = XAllocSizeHints();
2179 	if (sizehints)
2180 	{
2181 		get_sizehints(sizehints, width, height);
2182 		XSetWMNormalHints(g_display, g_wnd, sizehints);
2183 		XFree(sizehints);
2184 	}
2185 }
2186 
request_wm_fullscreen(Display * dpy,Window win)2187 void request_wm_fullscreen(Display *dpy, Window win)
2188 {
2189 	Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
2190 	XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_STATE", False), XA_ATOM, 32, PropModeReplace, (unsigned char *)&atom, 1);
2191 	XFlush(dpy);
2192 }
2193 
2194 RD_BOOL
ui_create_window(uint32 width,uint32 height)2195 ui_create_window(uint32 width, uint32 height)
2196 {
2197 	uint8 null_pointer_mask[1] = { 0x80 };
2198 	uint8 null_pointer_data[24] = { 0x00 };
2199 
2200 	XSetWindowAttributes attribs;
2201 	XClassHint *classhints;
2202 	XSizeHints *sizehints;
2203 	unsigned long value_mask;
2204 	long input_mask, ic_input_mask;
2205 	XEvent xevent;
2206 
2207 	/* reset stored window sizes */
2208 	g_window_width = 0;
2209 	g_window_height = 0;
2210 
2211 	logger(GUI, Debug, "ui_create_window() width = %d, height = %d", width, height);
2212 
2213 	/* Handle -x-y portion of geometry string */
2214 	if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
2215 		g_xpos = WidthOfScreen(g_screen) + g_xpos - width;
2216 	if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4)))
2217 		g_ypos = HeightOfScreen(g_screen) + g_ypos - height;
2218 
2219 	value_mask = get_window_attribs(&attribs);
2220 
2221 	g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, width,
2222 			      height, 0, g_depth, InputOutput, g_visual, value_mask, &attribs);
2223 
2224 	ewmh_set_wm_pid(g_wnd, getpid());
2225 	set_wm_client_machine(g_display, g_wnd);
2226 
2227 	if (g_gc == NULL)
2228 	{
2229 		g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
2230 		ui_reset_clip();
2231 	}
2232 
2233 	if (g_create_bitmap_gc == NULL)
2234 		g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL);
2235 
2236 	if ((g_ownbackstore) && (g_backstore == 0))
2237 	{
2238 		g_backstore = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
2239 
2240 		/* clear to prevent rubbish being exposed at startup */
2241 		XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
2242 		XFillRectangle(g_display, g_backstore, g_gc, 0, 0, width, height);
2243 	}
2244 
2245 	XStoreName(g_display, g_wnd, g_title);
2246 	ewmh_set_wm_name(g_wnd, g_title);
2247 
2248 	if (g_hide_decorations)
2249 		mwm_hide_decorations(g_wnd);
2250 
2251 	classhints = XAllocClassHint();
2252 	if (classhints != NULL)
2253 	{
2254 		classhints->res_name = classhints->res_class = "rdesktop";
2255 		XSetClassHint(g_display, g_wnd, classhints);
2256 		XFree(classhints);
2257 	}
2258 
2259 	sizehints = XAllocSizeHints();
2260 	if (sizehints)
2261 	{
2262 		get_sizehints(sizehints, width, height);
2263 		XSetWMNormalHints(g_display, g_wnd, sizehints);
2264 		XFree(sizehints);
2265 	}
2266 
2267 	if (g_embed_wnd)
2268 	{
2269 		XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);
2270 	}
2271 
2272 	get_input_mask(&input_mask);
2273 
2274 	if (g_IM != NULL)
2275 	{
2276 		g_IC = XCreateIC(g_IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
2277 				 XNClientWindow, g_wnd, XNFocusWindow, g_wnd, NULL);
2278 
2279 		if ((g_IC != NULL)
2280 		    && (XGetICValues(g_IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
2281 			input_mask |= ic_input_mask;
2282 	}
2283 
2284 	XSelectInput(g_display, g_wnd, input_mask);
2285 #ifdef HAVE_XRANDR
2286 	XSelectInput(g_display, RootWindowOfScreen(g_screen), StructureNotifyMask);
2287 #endif
2288 	if (g_fullscreen && g_has_wm) {
2289 		request_wm_fullscreen(g_display, g_wnd);
2290 	}
2291 	XMapWindow(g_display, g_wnd);
2292 
2293 	/* wait for VisibilityNotify */
2294 	do
2295 	{
2296 		XMaskEvent(g_display, VisibilityChangeMask, &xevent);
2297 	}
2298 	while (xevent.type != VisibilityNotify);
2299 	g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
2300 
2301 	g_focused = False;
2302 	g_mouse_in_wnd = False;
2303 
2304 	/* handle the WM_DELETE_WINDOW protocol */
2305 	g_protocol_atom = XInternAtom(g_display, "WM_PROTOCOLS", True);
2306 	g_kill_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", True);
2307 
2308 	Atom supported[] = {
2309 		g_net_wm_ping_atom,
2310 		g_kill_atom
2311 	};
2312 
2313 	XSetWMProtocols(g_display, g_wnd, supported, 2);
2314 
2315 	/* create invisible 1x1 cursor to be used as null cursor */
2316 	if (g_null_cursor == NULL)
2317 		g_null_cursor =
2318 			ui_create_cursor(0, 0, 1, 1, null_pointer_mask, null_pointer_data, 24);
2319 
2320 	if (g_seamless_rdp)
2321 	{
2322 		seamless_reset_state();
2323 		seamless_restack_test();
2324 	}
2325 
2326 	return True;
2327 }
2328 
2329 void
ui_resize_window(uint32 width,uint32 height)2330 ui_resize_window(uint32 width, uint32 height)
2331 {
2332 	XWindowAttributes attr;
2333 	XSizeHints *sizehints;
2334 	Pixmap bs;
2335 
2336 	XGetWindowAttributes(g_display, g_wnd, &attr);
2337 
2338 	if ((attr.width == (int) width && attr.height == (int) height))
2339 	{
2340 		/* no-op */
2341 		return;
2342 	}
2343 
2344 	logger(GUI, Debug,
2345 	       "ui_resize_window(), Changing window %dx%d to match new session %dx%d size",
2346 	       attr.width, attr.height, width, height);
2347 
2348 	sizehints = XAllocSizeHints();
2349 	if (sizehints)
2350 	{
2351 		get_sizehints(sizehints, width, height);
2352 		XSetWMNormalHints(g_display, g_wnd, sizehints);
2353 		XFree(sizehints);
2354 	}
2355 
2356 	if (!g_embed_wnd)
2357 	{
2358 		XResizeWindow(g_display, g_wnd, width, height);
2359 	}
2360 
2361 	/* create new backstore pixmap */
2362 	if (g_backstore != 0)
2363 	{
2364 		bs = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
2365 		XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
2366 		XFillRectangle(g_display, bs, g_gc, 0, 0, width, height);
2367 		XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, width, height, 0, 0);
2368 		XFreePixmap(g_display, g_backstore);
2369 		g_backstore = bs;
2370 	}
2371 
2372 	ui_set_clip(0, 0, width, height);
2373 }
2374 
2375 RD_BOOL
ui_have_window()2376 ui_have_window()
2377 {
2378 	return g_wnd ? True : False;
2379 }
2380 
2381 void
ui_destroy_window(void)2382 ui_destroy_window(void)
2383 {
2384 	if (g_IC != NULL)
2385 		XDestroyIC(g_IC);
2386 
2387 	XDestroyWindow(g_display, g_wnd);
2388 	g_wnd = 0;
2389 
2390 	if (g_backstore)
2391 	{
2392 		XFreePixmap(g_display, g_backstore);
2393 		g_backstore = 0;
2394 	}
2395 }
2396 
2397 void
xwin_toggle_fullscreen(void)2398 xwin_toggle_fullscreen(void)
2399 {
2400 	uint32 x, y, width, height;
2401 	XWindowAttributes attr;
2402 	Pixmap contents = 0;
2403 	Window unused;
2404 	int dest_x, dest_y;
2405 	static uint32 windowed_x = 0;
2406 	static uint32 windowed_y = 0;
2407 	static uint32 windowed_height = 0;
2408 	static uint32 windowed_width = 0;
2409 
2410 	/* When running rdesktop in seamless mode, toggling of fullscreen is not allowed */
2411 	if (g_seamless_rdp)
2412 		return;
2413 
2414 	/* get current window size and store it to be used when switching back
2415 	 * from fullscreen mode.
2416 	 */
2417 	XGetWindowAttributes(g_display, g_wnd, &attr);
2418 
2419 	if (!g_fullscreen || (windowed_width == 0 || windowed_height == 0))
2420 	{
2421 		/* only stored if we toggle from windowed -> fullscreen or when
2422 		 * going from fullscreen -> windowed when started in fullscreen mode.
2423 		 */
2424 
2425 		XTranslateCoordinates(g_display, g_wnd,
2426 				      DefaultRootWindow(g_display),
2427 				      0, 0, &dest_x, &dest_y, &unused);
2428 
2429 		windowed_x = dest_x;
2430 		windowed_y = dest_y;
2431 		windowed_width = attr.width;
2432 		windowed_height = attr.height;
2433 	}
2434 
2435 	if (!g_ownbackstore)
2436 	{
2437 		/* need to save contents of current window */
2438 		contents = XCreatePixmap(g_display, g_wnd, attr.width, attr.height, g_depth);
2439 		XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, attr.width, attr.height, 0, 0);
2440 	}
2441 
2442 	g_fullscreen = !g_fullscreen;
2443 
2444 
2445 	/* What size should the new window have? */
2446 	if (g_fullscreen)
2447 	{
2448 		/* Since we need to create a fullscreen window we need to know screen size */
2449 		x = 0;
2450 		y = 0;
2451 		width = WidthOfScreen(g_screen);
2452 		height = HeightOfScreen(g_screen);
2453 	}
2454 	else
2455 	{
2456 		/* Switch from fullscreen to window mode */
2457 		x = windowed_x;
2458 		y = windowed_y;
2459 
2460 		if (g_dynamic_session_resize)
2461 		{
2462 			/* Restore "old" window size, resize session to fit */
2463 			width = windowed_width;
2464 			height = windowed_height;
2465 		}
2466 		else
2467 		{
2468 			/* Resize window to fit session size */
2469 			width = g_session_width;
2470 			height = g_session_height;
2471 		}
2472 	}
2473 
2474 	logger(GUI, Debug, "xwin_toggle_fullscreen(), new window: %dx%d+%d+%d, last window: %dx%d",
2475 	       width, height, x, y, windowed_width, windowed_height);
2476 
2477 	/* Re-create the rdesktop window using new size and window
2478 	   attributes. */
2479 	g_xpos = x;
2480 	g_ypos = y;
2481 	ui_destroy_window();
2482 	ui_create_window(width, height);
2483 
2484 	/* If the window manager overrides our window size request, we trust
2485 	   the normal window resize mechanism to take care of resizing the
2486 	   session. When window is configured as override-redirect
2487 	   (i.e. fullscreen), this disables the normal window resize
2488 	   mechanism. In that case, we have to take care of the resize
2489 	   ourselves setting g_pending_resize. */
2490 	if (g_fullscreen)
2491 	{
2492 		g_pending_resize = True;
2493 		g_window_width = width;
2494 		g_window_height = height;
2495 	}
2496 
2497 	XDefineCursor(g_display, g_wnd, g_current_cursor);
2498 
2499 	if (!g_ownbackstore)
2500 	{
2501 		/* copy back saved contents into new window */
2502 		XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, attr.width, attr.height, 0, 0);
2503 		XFreePixmap(g_display, contents);
2504 	}
2505 }
2506 
2507 static void
handle_button_event(XEvent xevent,RD_BOOL down)2508 handle_button_event(XEvent xevent, RD_BOOL down)
2509 {
2510 	XWindowAttributes attr;
2511 	uint16 button, input_type, flags = 0;
2512 
2513 	XGetWindowAttributes(g_display, g_wnd, &attr);
2514 
2515 	g_last_gesturetime = xevent.xbutton.time;
2516 	/* Reverse the pointer button mapping, e.g. in the case of
2517 	   "left-handed mouse mode"; the RDP session expects to
2518 	   receive physical buttons (true in mstsc as well) and
2519 	   logical button behavior depends on the remote desktop's own
2520 	   mouse settings */
2521 	xevent.xbutton.button = g_pointer_log_to_phys_map[xevent.xbutton.button - 1];
2522 	button = xkeymap_translate_button(xevent.xbutton.button, &input_type);
2523 	if (button == 0)
2524 		return;
2525 
2526 	if (down)
2527 		flags = MOUSE_FLAG_DOWN;
2528 
2529 	/* Stop moving window when button is released, regardless of cursor position */
2530 	if (g_moving_wnd && (xevent.type == ButtonRelease))
2531 		g_moving_wnd = False;
2532 
2533 	/* If win_button_size is nonzero, enable single app mode */
2534 	if (xevent.xbutton.y < g_win_button_size)
2535 	{
2536 		/*  Check from right to left: */
2537 		if (xevent.xbutton.x >= attr.width - g_win_button_size)
2538 		{
2539 			/* The close button, continue */
2540 			;
2541 		}
2542 		else if (xevent.xbutton.x >= attr.width - g_win_button_size * 2)
2543 		{
2544 			/* The maximize/restore button. Do not send to
2545 			   server.  It might be a good idea to change the
2546 			   cursor or give some other visible indication
2547 			   that rdesktop inhibited this click */
2548 			if (xevent.type == ButtonPress)
2549 				return;
2550 		}
2551 		else if (xevent.xbutton.x >= attr.width - g_win_button_size * 3)
2552 		{
2553 			/* The minimize button. Iconify window. */
2554 			if (xevent.type == ButtonRelease)
2555 			{
2556 				/* Release the mouse button outside the minimize button, to prevent the
2557 				   actual minimization to happen */
2558 				rdp_send_input(time(NULL), input_type, button, 1, 1);
2559 				XIconifyWindow(g_display, g_wnd, DefaultScreen(g_display));
2560 				return;
2561 			}
2562 		}
2563 		else if (xevent.xbutton.x <= g_win_button_size)
2564 		{
2565 			/* The system menu. Ignore. */
2566 			if (xevent.type == ButtonPress)
2567 				return;
2568 		}
2569 		else
2570 		{
2571 			/* The title bar. */
2572 			if (xevent.type == ButtonPress)
2573 			{
2574 				if (!g_fullscreen && g_hide_decorations && !g_using_full_workarea)
2575 				{
2576 					g_moving_wnd = True;
2577 					g_move_x_offset = xevent.xbutton.x;
2578 					g_move_y_offset = xevent.xbutton.y;
2579 				}
2580 				return;
2581 			}
2582 		}
2583 	}
2584 
2585 	/* Ignore mouse scroll button release event which will be handled as an additional
2586 	 * scrolldown event on the Windows side.
2587 	 */
2588 	if (!down && (button == MOUSE_FLAG_BUTTON4 || button == MOUSE_FLAG_BUTTON5))
2589 	{
2590 		return;
2591 	}
2592 
2593 	if (xevent.xmotion.window == g_wnd)
2594 	{
2595 		rdp_send_input(time(NULL), input_type,
2596 			       flags | button, xevent.xbutton.x, xevent.xbutton.y);
2597 	}
2598 	else
2599 	{
2600 		/* SeamlessRDP */
2601 		rdp_send_input(time(NULL), input_type,
2602 			       flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root);
2603 	}
2604 }
2605 
2606 /* Process events in Xlib queue
2607    Returns 0 after user quit, 1 otherwise */
2608 static int
xwin_process_events(void)2609 xwin_process_events(void)
2610 {
2611 	XEvent xevent;
2612 	KeySym keysym;
2613 	uint32 ev_time;
2614 	char str[256];
2615 	Status status;
2616 	int events = 0;
2617 	seamless_window *sw;
2618 	static RD_BOOL is_g_wnd_mapped = False;
2619 
2620 	while ((XPending(g_display) > 0) && events++ < 20)
2621 	{
2622 		XNextEvent(g_display, &xevent);
2623 
2624 		if (!g_wnd)
2625 			/* Ignore events between ui_destroy_window and ui_create_window */
2626 			continue;
2627 
2628 		/* Also ignore root window events except ConfigureNotify */
2629 		if (xevent.type != ConfigureNotify
2630 		    && xevent.xany.window == DefaultRootWindow(g_display))
2631 			continue;
2632 
2633 		if ((g_IC != NULL) && (XFilterEvent(&xevent, None) == True))
2634 		{
2635 			logger(GUI, Debug, "xwin_process_events(), filtering event");
2636 			continue;
2637 		}
2638 
2639 		switch (xevent.type)
2640 		{
2641 			case VisibilityNotify:
2642 				if (xevent.xvisibility.window == g_wnd)
2643 					g_Unobscured =
2644 						xevent.xvisibility.state == VisibilityUnobscured;
2645 
2646 				break;
2647 			case ClientMessage:
2648 				if (xevent.xclient.message_type == g_protocol_atom)
2649 				{
2650 					if (xevent.xclient.data.l[0] == (long) g_kill_atom)
2651 					{
2652 						/* the window manager told us to quit */
2653 
2654 						/* When killing a seamless window, close the window on the
2655 						   serverside instead of terminating rdesktop */
2656 						sw = sw_get_window_by_wnd(xevent.xclient.window);
2657 						if (!sw)
2658 							/* Otherwise, quit */
2659 							return 0;
2660 						/* send seamless destroy process message */
2661 						seamless_send_destroy(sw->id);
2662 					}
2663 					else if (xevent.xclient.data.l[0] ==
2664 						 (long) g_net_wm_ping_atom)
2665 					{
2666 						/* pass ping message further to root window */
2667 						xevent.xclient.window =
2668 							RootWindowOfScreen(g_screen);
2669 						XSendEvent(g_display, xevent.xclient.window, False,
2670 							   SubstructureRedirectMask |
2671 							   SubstructureNotifyMask, &xevent);
2672 						break;
2673 					}
2674 				}
2675 				break;
2676 
2677 			case KeyPress:
2678 				g_last_gesturetime = xevent.xkey.time;
2679 				if (g_IC != NULL)
2680 					/* Multi_key compatible version */
2681 				{
2682 					XmbLookupString(g_IC,
2683 							&xevent.xkey, str, sizeof(str), &keysym,
2684 							&status);
2685 					if (!((status == XLookupKeySym) || (status == XLookupBoth)))
2686 					{
2687 						logger(GUI, Error,
2688 						       "XmbLookupString failed with status 0x%x\n",
2689 						       status);
2690 						break;
2691 					}
2692 				}
2693 				else
2694 				{
2695 					/* Plain old XLookupString */
2696 					logger(Keyboard, Debug,
2697 					       "No input context, using fallback XLookupString");
2698 					XLookupString((XKeyEvent *) & xevent, str, sizeof(str),
2699 						      &keysym, NULL);
2700 				}
2701 
2702 				logger(Keyboard, Debug, "KeyPress for keysym (0x%lx, %s)", keysym,
2703 				       get_ksname(keysym));
2704 
2705 				set_keypress_keysym(xevent.xkey.keycode, keysym);
2706 				ev_time = time(NULL);
2707 				if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
2708 					break;
2709 
2710 				xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2711 						  ev_time, True, 0);
2712 				break;
2713 
2714 			case KeyRelease:
2715 				g_last_gesturetime = xevent.xkey.time;
2716 				XLookupString((XKeyEvent *) & xevent, str,
2717 					      sizeof(str), &keysym, NULL);
2718 
2719 				logger(Keyboard, Debug, "KeyRelease for keysym (0x%lx, %s)", keysym,
2720 				       get_ksname(keysym));
2721 
2722 				keysym = reset_keypress_keysym(xevent.xkey.keycode, keysym);
2723 				ev_time = time(NULL);
2724 				if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
2725 					break;
2726 
2727 				xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2728 						  ev_time, False, 0);
2729 				break;
2730 
2731 			case ButtonPress:
2732 				handle_button_event(xevent, True);
2733 				break;
2734 
2735 			case ButtonRelease:
2736 				handle_button_event(xevent, False);
2737 				break;
2738 
2739 			case MotionNotify:
2740 				if (g_moving_wnd)
2741 				{
2742 					XMoveWindow(g_display, g_wnd,
2743 						    xevent.xmotion.x_root - g_move_x_offset,
2744 						    xevent.xmotion.y_root - g_move_y_offset);
2745 					break;
2746 				}
2747 
2748 				if (g_fullscreen && !g_focused)
2749 					XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
2750 						       CurrentTime);
2751 
2752 				if (xevent.xmotion.window == g_wnd)
2753 				{
2754 					rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2755 						       xevent.xmotion.x, xevent.xmotion.y);
2756 				}
2757 				else
2758 				{
2759 					/* SeamlessRDP */
2760 					rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2761 						       xevent.xmotion.x_root,
2762 						       xevent.xmotion.y_root);
2763 				}
2764 				break;
2765 
2766 			case FocusIn:
2767 				if (xevent.xfocus.mode == NotifyGrab)
2768 					break;
2769 				g_focused = True;
2770 				reset_modifier_keys();
2771 				if (g_grab_keyboard && g_mouse_in_wnd)
2772 					XGrabKeyboard(g_display, g_wnd, True,
2773 						      GrabModeAsync, GrabModeAsync, CurrentTime);
2774 
2775 				sw = sw_get_window_by_wnd(xevent.xfocus.window);
2776 				if (!sw)
2777 					break;
2778 
2779 				/* Menu windows are real X11 windows,
2780 				   with focus. When such a window is
2781 				   destroyed, focus is reverted to the
2782 				   main application window, which
2783 				   would cause us to send FOCUS. This
2784 				   breaks window switching in, say,
2785 				   Seamonkey. We shouldn't need to
2786 				   send FOCUS: Windows should also
2787 				   revert focus to some other window
2788 				   when the menu window is
2789 				   destroyed. So, we only send FOCUS
2790 				   if the previous focus window still
2791 				   exists. */
2792 				if (sw->id != g_seamless_focused)
2793 				{
2794 
2795 					if (sw_window_exists(g_seamless_focused))
2796 						seamless_send_focus(sw->id, 0);
2797 					g_seamless_focused = sw->id;
2798 				}
2799 				break;
2800 
2801 			case FocusOut:
2802 				if (xevent.xfocus.mode == NotifyUngrab)
2803 					break;
2804 				g_focused = False;
2805 				if (xevent.xfocus.mode == NotifyWhileGrabbed)
2806 					XUngrabKeyboard(g_display, CurrentTime);
2807 				break;
2808 
2809 			case EnterNotify:
2810 				/* we only register for this event when in fullscreen mode */
2811 				/* or grab_keyboard */
2812 				g_mouse_in_wnd = True;
2813 				if (g_fullscreen)
2814 				{
2815 					XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
2816 						       CurrentTime);
2817 					break;
2818 				}
2819 				if (g_focused)
2820 					XGrabKeyboard(g_display, g_wnd, True,
2821 						      GrabModeAsync, GrabModeAsync, CurrentTime);
2822 				break;
2823 
2824 			case LeaveNotify:
2825 				/* we only register for this event when grab_keyboard */
2826 				g_mouse_in_wnd = False;
2827 				XUngrabKeyboard(g_display, CurrentTime);
2828 				break;
2829 
2830 			case Expose:
2831 				if (xevent.xexpose.window == g_wnd)
2832 				{
2833 					XCopyArea(g_display, g_backstore, xevent.xexpose.window,
2834 						  g_gc,
2835 						  xevent.xexpose.x, xevent.xexpose.y,
2836 						  xevent.xexpose.width, xevent.xexpose.height,
2837 						  xevent.xexpose.x, xevent.xexpose.y);
2838 				}
2839 				else
2840 				{
2841 					sw = sw_get_window_by_wnd(xevent.xexpose.window);
2842 					if (!sw)
2843 						break;
2844 					XCopyArea(g_display, g_backstore,
2845 						  xevent.xexpose.window, g_gc,
2846 						  xevent.xexpose.x + sw->xoffset,
2847 						  xevent.xexpose.y + sw->yoffset,
2848 						  xevent.xexpose.width,
2849 						  xevent.xexpose.height, xevent.xexpose.x,
2850 						  xevent.xexpose.y);
2851 				}
2852 
2853 				break;
2854 
2855 			case MappingNotify:
2856 				/* Refresh keyboard mapping if it has changed. This is important for
2857 				   Xvnc, since it allocates keycodes dynamically */
2858 				if (xevent.xmapping.request == MappingKeyboard
2859 				    || xevent.xmapping.request == MappingModifier)
2860 					XRefreshKeyboardMapping(&xevent.xmapping);
2861 
2862 				if (xevent.xmapping.request == MappingModifier)
2863 				{
2864 					XFreeModifiermap(g_mod_map);
2865 					g_mod_map = XGetModifierMapping(g_display);
2866 				}
2867 
2868 				if (xevent.xmapping.request == MappingPointer)
2869 				{
2870 					xwin_refresh_pointer_map();
2871 				}
2872 
2873 				break;
2874 
2875 				/* clipboard stuff */
2876 			case SelectionNotify:
2877 				xclip_handle_SelectionNotify(&xevent.xselection);
2878 				break;
2879 			case SelectionRequest:
2880 				xclip_handle_SelectionRequest(&xevent.xselectionrequest);
2881 				break;
2882 			case SelectionClear:
2883 				xclip_handle_SelectionClear();
2884 				break;
2885 			case PropertyNotify:
2886 				xclip_handle_PropertyNotify(&xevent.xproperty);
2887 				if (xevent.xproperty.window == g_wnd)
2888 					break;
2889 				if (xevent.xproperty.window == DefaultRootWindow(g_display))
2890 					break;
2891 
2892 				/* seamless */
2893 				sw = sw_get_window_by_wnd(xevent.xproperty.window);
2894 				if (!sw)
2895 					break;
2896 
2897 				if ((xevent.xproperty.atom == g_net_wm_state_atom)
2898 				    && (xevent.xproperty.state == PropertyNewValue))
2899 				{
2900 					sw->state = ewmh_get_window_state(sw->wnd);
2901 					seamless_send_state(sw->id, sw->state, 0);
2902 				}
2903 
2904 				if ((xevent.xproperty.atom == g_net_wm_desktop_atom)
2905 				    && (xevent.xproperty.state == PropertyNewValue))
2906 				{
2907 					sw->desktop = ewmh_get_window_desktop(sw->wnd);
2908 					sw_all_to_desktop(sw->wnd, sw->desktop);
2909 				}
2910 
2911 				break;
2912 			case MapNotify:
2913 				if (xevent.xconfigure.window == g_wnd)
2914 				{
2915 					XWindowAttributes attr;
2916 					XGetWindowAttributes(g_display, g_wnd, &attr);
2917 					g_window_width = attr.width;
2918 					g_window_height = attr.height;
2919 
2920 					logger(GUI, Debug,
2921 					       "xwin_process_events(), Window mapped with size %dx%d",
2922 					       g_window_width, g_window_height);
2923 
2924 					is_g_wnd_mapped = True;
2925 				}
2926 
2927 				if (!g_seamless_active)
2928 				{
2929 					rdp_send_suppress_output_pdu(ALLOW_DISPLAY_UPDATES);
2930 				}
2931 				break;
2932 			case UnmapNotify:
2933 				if (xevent.xconfigure.window == g_wnd)
2934 				{
2935 					is_g_wnd_mapped = False;
2936 				}
2937 
2938 				if (!g_seamless_active)
2939 				{
2940 					rdp_send_suppress_output_pdu(SUPPRESS_DISPLAY_UPDATES);
2941 				}
2942 				break;
2943 			case ConfigureNotify:
2944 #ifdef HAVE_XRANDR
2945 				/* Resize on root window size change */
2946 				if (xevent.xconfigure.window == DefaultRootWindow(g_display))
2947 				{
2948 					/* only for fullscreen or x%-of-screen-sized windows */
2949 					if (g_window_size_type == PercentageOfScreen
2950 					    || g_window_size_type == Fullscreen || g_fullscreen)
2951 					{
2952 						if (xevent.xconfigure.width !=
2953 						    WidthOfScreen(g_screen)
2954 						    || xevent.xconfigure.height !=
2955 						    HeightOfScreen(g_screen))
2956 						{
2957 
2958 							logger(GUI, Debug,
2959 							       "xwin_process_events(), ConfigureNotify: Root window changed to %dx%d",
2960 							       xevent.xconfigure.width,
2961 							       xevent.xconfigure.height);
2962 
2963 							gettimeofday(&g_resize_timer, NULL);
2964 
2965 							/* Resize fullscreen window to match root window size */
2966 							/* TODO: Handle percentage of screen */
2967 							if (g_fullscreen)
2968 								ui_resize_window(xevent.xconfigure.
2969 										 width,
2970 										 xevent.xconfigure.
2971 										 height);
2972 							g_pending_resize = True;
2973 						}
2974 					}
2975 
2976 					XRRUpdateConfiguration(&xevent);
2977 					XSync(g_display, False);
2978 
2979 				}
2980 				else
2981 #endif
2982 				if (xevent.xconfigure.window == g_wnd && !g_seamless_rdp
2983 					    && is_g_wnd_mapped)
2984 				{
2985 
2986 					/* Update window size */
2987 					g_window_width = xevent.xconfigure.width;
2988 					g_window_height = xevent.xconfigure.height;
2989 
2990 					uint32 w, h;
2991 					w = g_window_width;
2992 					h = g_window_height;
2993 
2994 					utils_apply_session_size_limitations(&w, &h);
2995 
2996 					logger(GUI, Debug,
2997 					       "xwin_process_events(), ConfigureNotify: session: %dx%d, new window: %dx%d (adj: %dx%d)",
2998 					       g_session_width, g_session_height, g_window_width,
2999 					       g_window_height, w, h);
3000 
3001 					if (g_session_width != w || g_session_height != h)
3002 					{
3003 						logger(GUI, Debug,
3004 						       "xwin_process_events(), ConfigureNotify: session: %dx%d, new window: %dx%d",
3005 						       g_session_width, g_session_height,
3006 						       g_window_width, g_window_height);
3007 
3008 						/* perform a resize */
3009 						gettimeofday(&g_resize_timer, NULL);
3010 						g_pending_resize = True;
3011 					}
3012 					else
3013 					{
3014 						g_pending_resize = False;
3015 					}
3016 				}
3017 
3018 				if (!g_seamless_active)
3019 					break;
3020 
3021 				sw = sw_get_window_by_wnd(xevent.xconfigure.window);
3022 				if (!sw)
3023 					break;
3024 
3025 				gettimeofday(sw->position_timer, NULL);
3026 				if (sw->position_timer->tv_usec + SEAMLESSRDP_POSITION_TIMER >=
3027 				    1000000)
3028 				{
3029 					sw->position_timer->tv_usec +=
3030 						SEAMLESSRDP_POSITION_TIMER - 1000000;
3031 					sw->position_timer->tv_sec += 1;
3032 				}
3033 				else
3034 				{
3035 					sw->position_timer->tv_usec += SEAMLESSRDP_POSITION_TIMER;
3036 				}
3037 
3038 				sw_handle_restack(sw);
3039 				break;
3040 		}
3041 	}
3042 	/* Keep going */
3043 	return 1;
3044 }
3045 
3046 static inline uint32
time_difference_in_ms(struct timeval then,struct timeval now)3047 time_difference_in_ms(struct timeval then, struct timeval now)
3048 {
3049 	uint32 ms;
3050 	ms = 0;
3051 	ms += (now.tv_sec - then.tv_sec) * 1000;
3052 	ms += (now.tv_usec - then.tv_usec) / 1000;
3053 	return ms;
3054 }
3055 
3056 time_t g_wait_for_deactivate_ts = 0;
3057 
3058 static RD_BOOL
process_fds(int rdp_socket,int ms)3059 process_fds(int rdp_socket, int ms)
3060 {
3061 	int n, ret;
3062 	fd_set rfds, wfds;
3063 	struct timeval tv;
3064 	RD_BOOL s_timeout = False;
3065 
3066 	n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
3067 
3068 	FD_ZERO(&rfds);
3069 	FD_ZERO(&wfds);
3070 	FD_SET(rdp_socket, &rfds);
3071 	FD_SET(g_x_socket, &rfds);
3072 
3073 	/* default timeout */
3074 	tv.tv_sec = ms / 1000;
3075 	tv.tv_usec = (ms - (tv.tv_sec * 1000)) * 1000;
3076 
3077 #ifdef WITH_RDPSND
3078 	rdpsnd_add_fds(&n, &rfds, &wfds, &tv);
3079 #endif
3080 
3081 	/* add redirection handles */
3082 	rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
3083 	seamless_select_timeout(&tv);
3084 
3085 	/* add ctrl slaves handles */
3086 	ctrl_add_fds(&n, &rfds);
3087 
3088 	n++;
3089 
3090 	ret = select(n, &rfds, &wfds, NULL, &tv);
3091 	if (ret <= 0)
3092 	{
3093 		if (ret == -1)
3094 		{
3095 			logger(GUI, Error, "process_fds(), select failed: %s", strerror(errno));
3096 		}
3097 #ifdef WITH_RDPSND
3098 		rdpsnd_check_fds(&rfds, &wfds);
3099 #endif
3100 
3101 		/* Abort serial read calls */
3102 		if (s_timeout)
3103 			rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True);
3104 		return False;
3105 	}
3106 
3107 #ifdef WITH_RDPSND
3108 	rdpsnd_check_fds(&rfds, &wfds);
3109 #endif
3110 
3111 	rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
3112 
3113 	ctrl_check_fds(&rfds, &wfds);
3114 
3115 	if (FD_ISSET(rdp_socket, &rfds))
3116 		return True;
3117 
3118 	return False;
3119 }
3120 
3121 static RD_BOOL
timeval_is_set(struct timeval * time)3122 timeval_is_set(struct timeval *time)
3123 {
3124 	return (time->tv_sec == 0 && time->tv_usec == 0) ? False : True;
3125 }
3126 
3127 /* Handle a pending resize. Resize is handled by either a disconnect/reconnect
3128    sequence or online using RDPEDISP messages. Windows 2008 requires the use of
3129    disconnect/reconnect and to do that without user login credentials the
3130    auto-reconnect cookie is used. Windows 2008 seems sensitive to disconnects
3131    to early in the login sequence so we defer to resize until we get the cookie.
3132 
3133    Windows 2016 on the other hand does not seem to send cookies but uses
3134    RDPEDISP so in this case we defer until the RDPEDISP channel is established.
3135  */
3136 static RD_BOOL
process_pending_resize()3137 process_pending_resize()
3138 {
3139 	time_t now_ts;
3140 	struct timeval now;
3141 
3142 	/* Rate limit ConfigureNotify events before performing a
3143 	   resize - enough time has to pass after the last event
3144 	 */
3145 	gettimeofday(&now, NULL);
3146 	if (time_difference_in_ms(g_resize_timer, now) <= 500)
3147 		return False;
3148 
3149 	/* There is a race problem when using disconnect / reconnect
3150 	   sequence were one sometimes would be presented with
3151 	   unexpected login window. Waiting a little bit extra after
3152 	   getting the reconnect cookie solves this problem.
3153 
3154 	   In addition to that delay, we also want to wait for
3155 	   RDPEDISP to become available. In scenarios where we can use
3156 	   both online and reconnect-based resizes, we prefer
3157 	   online. Our brief investigation shows that RDPEDISP support
3158 	   is established about 100-300 ms after the login info packet
3159 	   was received. Thus, we want to wait a bit so we can avoid
3160 	   resizes using reconnect. Once RDPEDISP is established, the
3161 	   defer timer is cleared, so there will be no delay before
3162 	   the first resize for servers that support RDPEDISP. Other
3163 	   servers will get the initial resize delayed with 2 seconds.
3164 	 */
3165 
3166 	if (timeval_is_set(&g_pending_resize_defer_timer) &&
3167 	    time_difference_in_ms(g_pending_resize_defer_timer, now) >= 2000)
3168 	{
3169 		g_pending_resize_defer_timer.tv_sec = g_pending_resize_defer_timer.tv_usec = 0;
3170 		g_pending_resize_defer = False;
3171 	}
3172 
3173 	if (g_pending_resize_defer == True)
3174 		return False;
3175 
3176 	/* Set up width and height for new session */
3177 	if (g_fullscreen || g_seamless_rdp)
3178 	{
3179 		/* follow root window size */
3180 		g_requested_session_width = WidthOfScreen(g_screen);
3181 		g_requested_session_height = HeightOfScreen(g_screen);
3182 		if (g_window_size_type == PercentageOfScreen)
3183 		{
3184 			/* TODO: Implement percentage of screen */
3185 		}
3186 	}
3187 	else
3188 	{
3189 		/* Follow window size */
3190 		g_requested_session_width = g_window_width;
3191 		g_requested_session_height = g_window_height;
3192 	}
3193 
3194 
3195 	/* Carry out a resize to desired size */
3196 	if (rdpedisp_is_available() == False)
3197 	{
3198 		/* resize session using disconnect reconnect
3199 		 * sequence when RDPEDISP is not supported by
3200 		 * server by returning to outer loop.
3201 		 */
3202 
3203 		logger(GUI, Verbose, "Window resize detected, reconnecting to new size %dx%d",
3204 		       g_requested_session_width, g_requested_session_height);
3205 
3206 		return True;
3207 	}
3208 	else
3209 	{
3210 		now_ts = time(NULL);
3211 		if (now_ts - g_wait_for_deactivate_ts <= 5)
3212 			return False;
3213 
3214 		logger(GUI, Verbose,
3215 		       "Window resize detected, requesting matching session size %dx%d",
3216 		       g_requested_session_width, g_requested_session_height);
3217 
3218 		rdpedisp_set_session_size(g_requested_session_width, g_requested_session_height);
3219 
3220 		g_pending_resize = False;
3221 		g_wait_for_deactivate_ts = now_ts;
3222 	}
3223 
3224 	return False;
3225 }
3226 
3227 /* This function is the heart of the mainloop pump in
3228    rdekstop. Handles processing of pending X11 events and data on all
3229    file descriptors used by rdesktop except for rdp_socket.
3230    Processing of data on rdp_socket is done by returning from this
3231    function to the calling tcp_recv().
3232 
3233    This function will return if there is data available for reading on
3234    rdp_socket or if g_exit_mainloop flag is set.
3235 */
3236 void
ui_select(int rdp_socket)3237 ui_select(int rdp_socket)
3238 {
3239 	int timeout;
3240 	RD_BOOL rdp_socket_has_data = False;
3241 
3242 	while (g_exit_mainloop == False && rdp_socket_has_data == False)
3243 	{
3244 		/* Process a limited amount of pending x11 events */
3245 		if (!xwin_process_events())
3246 		{
3247 			/* User quit */
3248 			g_user_quit = True;
3249 			g_exit_mainloop = True;
3250 			g_pending_resize = False;
3251 			continue;
3252 		}
3253 
3254 		if (g_pending_resize == True && g_dynamic_session_resize)
3255 		{
3256 			/* returns True on disconnect-reconnect resize */
3257 			if (process_pending_resize() == True)
3258 			{
3259 				g_exit_mainloop = True;
3260 				continue;
3261 			}
3262 		}
3263 
3264 		if (g_seamless_active)
3265 			sw_check_timers();
3266 
3267 		/* process_fds() is a little special, it does two
3268 		   things in one. It will perform a select() on all
3269 		   filedescriptors; rdpsnd / rdpdr / ctrl and
3270 		   rdp_socket passed as argument. If data is available
3271 		   on any filedescriptor except rdp_socket, it will be processed.
3272 
3273 		   If data is available on rdp_socket, the call return
3274 		   true and we exit from ui_select() to let tcp_recv()
3275 		   read data from rdp_socket.
3276 
3277 		   Use 60 seconds as default timeout for select. If
3278 		   there is more X11 events on queue or g_pend is set,
3279 		   use a low timeout.
3280 		 */
3281 
3282 		timeout = 60000;
3283 
3284 		if (XPending(g_display) > 0)
3285 			timeout = 0;
3286 		else if (g_pending_resize == True)
3287 			timeout = 100;
3288 
3289 		rdp_socket_has_data = process_fds(rdp_socket, timeout);
3290 	}
3291 }
3292 
3293 void
ui_move_pointer(int x,int y)3294 ui_move_pointer(int x, int y)
3295 {
3296 	XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
3297 }
3298 
3299 RD_HBITMAP
ui_create_bitmap(int width,int height,uint8 * data)3300 ui_create_bitmap(int width, int height, uint8 * data)
3301 {
3302 	XImage *image;
3303 	Pixmap bitmap;
3304 	uint8 *tdata;
3305 	int bitmap_pad;
3306 
3307 	if (g_server_depth == 8)
3308 	{
3309 		bitmap_pad = 8;
3310 	}
3311 	else
3312 	{
3313 		bitmap_pad = g_bpp;
3314 
3315 		if (g_bpp == 24)
3316 			bitmap_pad = 32;
3317 	}
3318 
3319 	tdata = (g_owncolmap ? data : translate_image(width, height, data));
3320 	bitmap = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
3321 	image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
3322 			     (char *) tdata, width, height, bitmap_pad, 0);
3323 
3324 	XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height);
3325 
3326 	XFree(image);
3327 	if (tdata != data)
3328 		xfree(tdata);
3329 	return (RD_HBITMAP) bitmap;
3330 }
3331 
3332 void
ui_paint_bitmap(int x,int y,int cx,int cy,int width,int height,uint8 * data)3333 ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
3334 {
3335 	XImage *image;
3336 	uint8 *tdata;
3337 	int bitmap_pad;
3338 
3339 	if (g_server_depth == 8)
3340 	{
3341 		bitmap_pad = 8;
3342 	}
3343 	else
3344 	{
3345 		bitmap_pad = g_bpp;
3346 
3347 		if (g_bpp == 24)
3348 			bitmap_pad = 32;
3349 	}
3350 
3351 	tdata = (g_owncolmap ? data : translate_image(width, height, data));
3352 	image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
3353 			     (char *) tdata, width, height, bitmap_pad, 0);
3354 
3355 	if (g_ownbackstore)
3356 	{
3357 		XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
3358 		XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3359 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3360 					(g_display, g_backstore, sw->wnd, g_gc, x, y, cx, cy,
3361 					 x - sw->xoffset, y - sw->yoffset));
3362 	}
3363 	else
3364 	{
3365 		XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
3366 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3367 					(g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
3368 					 x - sw->xoffset, y - sw->yoffset));
3369 	}
3370 
3371 	XFree(image);
3372 	if (tdata != data)
3373 		xfree(tdata);
3374 }
3375 
3376 void
ui_destroy_bitmap(RD_HBITMAP bmp)3377 ui_destroy_bitmap(RD_HBITMAP bmp)
3378 {
3379 	XFreePixmap(g_display, (Pixmap) bmp);
3380 }
3381 
3382 RD_HGLYPH
ui_create_glyph(int width,int height,uint8 * data)3383 ui_create_glyph(int width, int height, uint8 * data)
3384 {
3385 	XImage *image;
3386 	Pixmap bitmap;
3387 	int scanline;
3388 
3389 	scanline = (width + 7) / 8;
3390 
3391 	bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1);
3392 	if (g_create_glyph_gc == 0)
3393 		g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL);
3394 
3395 	image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data,
3396 			     width, height, 8, scanline);
3397 	image->byte_order = MSBFirst;
3398 	image->bitmap_bit_order = MSBFirst;
3399 	XInitImage(image);
3400 
3401 	XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
3402 
3403 	XFree(image);
3404 	return (RD_HGLYPH) bitmap;
3405 }
3406 
3407 void
ui_destroy_glyph(RD_HGLYPH glyph)3408 ui_destroy_glyph(RD_HGLYPH glyph)
3409 {
3410 	XFreePixmap(g_display, (Pixmap) glyph);
3411 }
3412 
3413 #define GET_BIT(ptr, bit) (*(ptr + bit / 8) & (1 << (7 - (bit % 8))))
3414 
3415 static uint32
get_pixel(uint32 idx,uint8 * andmask,uint8 * xormask,int bpp,uint8 * xor_flag)3416 get_pixel(uint32 idx, uint8 * andmask, uint8 * xormask, int bpp, uint8 * xor_flag)
3417 {
3418 	uint32 offs;
3419 	uint32 argb;
3420 	uint8 alpha;
3421 	uint8 *pxor;
3422 	PixelColour pc;
3423 
3424 	*xor_flag = 0;
3425 
3426 	/* return a red pixel if bpp is not supported to signal failure */
3427 	argb = 0xffff0000;
3428 	switch (bpp)
3429 	{
3430 		case 1:
3431 			offs = idx;
3432 			argb = GET_BIT(xormask, idx);
3433 			alpha = GET_BIT(andmask, idx) ? 0x00 : 0xff;
3434 			if (!GET_BIT(andmask, idx) == 0x00 && argb)
3435 			{
3436 				// If we have an xor bit is high and
3437 				// andmask bit is low, we should
3438 				// render a black pixel due to we can
3439 				// not xor blit in X11.
3440 				argb = 0xff000000;
3441 				*xor_flag = 1;
3442 			}
3443 			else
3444 				argb = (alpha << 24) | (argb ? 0xffffff : 0x000000);
3445 			break;
3446 
3447 		case 16:
3448 			offs = idx * 2;
3449 			pxor = xormask + offs;
3450 			SPLITCOLOUR16(*((uint16 *) pxor), pc);
3451 			alpha = GET_BIT(andmask, idx) ? 0x00 : 0xff;
3452 			argb = (alpha << 24) | (pc.red << 16) | (pc.green << 8) | pc.blue;
3453 			break;
3454 
3455 		case 24:
3456 			offs = idx * 3;
3457 			pxor = xormask + offs;
3458 			alpha = GET_BIT(andmask, idx) ? 0x00 : 0xff;
3459 			argb = (alpha << 24) | (pxor[2] << 16) | (pxor[1] << 8) | pxor[0];
3460 			break;
3461 
3462 		case 32:
3463 			offs = idx * 4;
3464 			pxor = xormask + offs;
3465 			argb = (pxor[3] << 24) | (pxor[2] << 16) | (pxor[1] << 8) | pxor[0];
3466 			break;
3467 	}
3468 
3469 	return argb;
3470 }
3471 
3472 /* Copies the pixels from src to dest with given color and offset */
3473 static inline void
xcursor_stencil(XcursorImage * src,XcursorImage * dst,int dx,int dy,uint32 argb)3474 xcursor_stencil(XcursorImage * src, XcursorImage * dst, int dx, int dy, uint32 argb)
3475 {
3476 	int x, y, si, di;
3477 	assert(src->width == dst->width);
3478 	assert(src->height == dst->height);
3479 
3480 	for (y = 0; y < (int) src->height; y++)
3481 	{
3482 		for (x = 0; x < (int) src->width; x++)
3483 		{
3484 			si = y * src->width + x;
3485 			if (!src->pixels[si])
3486 				continue;
3487 
3488 			if ((y + dy) < 0 || (y + dy) >= (int) dst->height)
3489 				continue;
3490 			if ((x + dx) < 0 || (x + dx) >= (int) dst->width)
3491 				continue;
3492 			di = (y + dy) * src->width + (x + dx);
3493 			dst->pixels[di] = argb;
3494 		}
3495 	}
3496 }
3497 
3498 static inline void
xcursor_merge(XcursorImage * src,XcursorImage * dst)3499 xcursor_merge(XcursorImage * src, XcursorImage * dst)
3500 {
3501 	uint32 i;
3502 	assert(src->width == dst->width);
3503 	assert(src->height == dst->height);
3504 	for (i = 0; i < src->width * src->height; i++)
3505 	{
3506 		if (!src->pixels[i])
3507 			continue;
3508 		dst->pixels[i] = src->pixels[i];
3509 	}
3510 }
3511 
3512 RD_HCURSOR
ui_create_cursor(unsigned int xhot,unsigned int yhot,uint32 width,uint32 height,uint8 * andmask,uint8 * xormask,int bpp)3513 ui_create_cursor(unsigned int xhot, unsigned int yhot, uint32 width,
3514 		 uint32 height, uint8 * andmask, uint8 * xormask, int bpp)
3515 {
3516 	Cursor cursor;
3517 	XcursorPixel *out;
3518 	XcursorImage *cimg, *tmp;
3519 	uint32 x, y, oidx, idx, argb;
3520 	uint8 outline, xor;
3521 
3522 	logger(GUI, Debug, "ui_create_cursor(): xhot=%d, yhot=%d, width=%d, height=%d, bpp=%d",
3523 	       xhot, yhot, width, height, bpp);
3524 
3525 	if (bpp != 1 && bpp != 16 && bpp != 24 && bpp != 32)
3526 	{
3527 		logger(GUI, Warning, "ui_create_xcursor_cursor(): Unhandled cursor bit depth %d",
3528 		       bpp);
3529 		return g_null_cursor;
3530 	}
3531 
3532 	cimg = XcursorImageCreate(width, height);
3533 	if (!cimg)
3534 	{
3535 		logger(GUI, Error, "ui_create_xcursor_cursor(): XcursorImageCreate() failed");
3536 		return g_null_cursor;
3537 	}
3538 
3539 	cimg->xhot = xhot;
3540 	cimg->yhot = yhot;
3541 
3542 	out = cimg->pixels;
3543 	xor = 0;
3544 	outline = 0;
3545 	for (y = 0; y < height; y++)
3546 	{
3547 		for (x = 0; x < width; x++)
3548 		{
3549 			oidx = y * width + x;
3550 
3551 			// Flip cursor on Y axis if color pointer
3552 			if (bpp != 1)
3553 			{
3554 				oidx = (height - 1 - y) * width + x;
3555 			}
3556 
3557 			idx = y * width + x;
3558 			argb = get_pixel(idx, andmask, xormask, bpp, &xor);
3559 			out[oidx] = argb;
3560 			if (xor)
3561 				outline = 1;
3562 		}
3563 	}
3564 
3565 
3566 	// Render a white outline of cursor shape when xor
3567 	// pixels are identified in cursor
3568 	if (outline)
3569 	{
3570 		tmp = XcursorImageCreate(width, height);
3571 		memset(tmp->pixels, 0, tmp->width * tmp->height * 4);
3572 		xcursor_stencil(cimg, tmp, -1, 0, 0xffffffff);
3573 		xcursor_stencil(cimg, tmp, 1, 0, 0xffffffff);
3574 		xcursor_stencil(cimg, tmp, 0, -1, 0xffffffff);
3575 		xcursor_stencil(cimg, tmp, 0, 1, 0xffffffff);
3576 		xcursor_merge(cimg, tmp);
3577 		xcursor_merge(tmp, cimg);
3578 		XcursorImageDestroy(tmp);
3579 	}
3580 
3581 	cursor = XcursorImageLoadCursor(g_display, cimg);
3582 	XcursorImageDestroy(cimg);
3583 	if (!cursor)
3584 	{
3585 		logger(GUI, Error, "ui_create_cursor(): XcursorImageLoadCursor() failed");
3586 		return g_null_cursor;
3587 	}
3588 
3589 	return (RD_HCURSOR) cursor;
3590 }
3591 
3592 void
ui_set_cursor(RD_HCURSOR cursor)3593 ui_set_cursor(RD_HCURSOR cursor)
3594 {
3595 	extern RD_BOOL g_local_cursor;
3596 	if (g_local_cursor)
3597 		return;
3598 	logger(GUI, Debug, "ui_set_cursor(): g_current_cursor = %p, new = %p",
3599 	       g_current_cursor, cursor);
3600 
3601 	g_current_cursor = (Cursor) cursor;
3602 	XDefineCursor(g_display, g_wnd, g_current_cursor);
3603 	ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor));
3604 }
3605 
3606 void
ui_destroy_cursor(RD_HCURSOR cursor)3607 ui_destroy_cursor(RD_HCURSOR cursor)
3608 {
3609 	// Do not destroy fallback null cursor
3610 	if (cursor == g_null_cursor)
3611 		return;
3612 
3613 	XFreeCursor(g_display, (Cursor) cursor);
3614 }
3615 
3616 void
ui_set_null_cursor(void)3617 ui_set_null_cursor(void)
3618 {
3619 	ui_set_cursor(g_null_cursor);
3620 }
3621 
3622 void
ui_set_standard_cursor(void)3623 ui_set_standard_cursor(void)
3624 {
3625 	XUndefineCursor(g_display, g_wnd);
3626 }
3627 
3628 #define MAKE_XCOLOR(xc,c) \
3629 		(xc)->red   = ((c)->red   << 8) | (c)->red; \
3630 		(xc)->green = ((c)->green << 8) | (c)->green; \
3631 		(xc)->blue  = ((c)->blue  << 8) | (c)->blue; \
3632 		(xc)->flags = DoRed | DoGreen | DoBlue;
3633 
3634 
3635 RD_HCOLOURMAP
ui_create_colourmap(COLOURMAP * colours)3636 ui_create_colourmap(COLOURMAP * colours)
3637 {
3638 	COLOURENTRY *entry;
3639 	int i, ncolours = colours->ncolours;
3640 	if (!g_owncolmap)
3641 	{
3642 		uint32 *map = (uint32 *) xmalloc(sizeof(*g_colmap) * ncolours);
3643 		XColor xentry;
3644 		XColor xc_cache[256];
3645 		uint32 colour;
3646 		int colLookup = 256;
3647 		for (i = 0; i < ncolours; i++)
3648 		{
3649 			entry = &colours->colours[i];
3650 			MAKE_XCOLOR(&xentry, entry);
3651 
3652 			if (XAllocColor(g_display, g_xcolmap, &xentry) == 0)
3653 			{
3654 				/* Allocation failed, find closest match. */
3655 				int j = 256;
3656 				int nMinDist = 3 * 256 * 256;
3657 				long nDist = nMinDist;
3658 
3659 				/* only get the colors once */
3660 				while (colLookup--)
3661 				{
3662 					xc_cache[colLookup].pixel = colLookup;
3663 					xc_cache[colLookup].red = xc_cache[colLookup].green =
3664 						xc_cache[colLookup].blue = 0;
3665 					xc_cache[colLookup].flags = 0;
3666 					XQueryColor(g_display,
3667 						    DefaultColormap(g_display,
3668 								    DefaultScreen(g_display)),
3669 						    &xc_cache[colLookup]);
3670 				}
3671 				colLookup = 0;
3672 
3673 				/* approximate the pixel */
3674 				while (j--)
3675 				{
3676 					if (xc_cache[j].flags)
3677 					{
3678 						nDist = ((long) (xc_cache[j].red >> 8) -
3679 							 (long) (xentry.red >> 8)) *
3680 							((long) (xc_cache[j].red >> 8) -
3681 							 (long) (xentry.red >> 8)) +
3682 							((long) (xc_cache[j].green >> 8) -
3683 							 (long) (xentry.green >> 8)) *
3684 							((long) (xc_cache[j].green >> 8) -
3685 							 (long) (xentry.green >> 8)) +
3686 							((long) (xc_cache[j].blue >> 8) -
3687 							 (long) (xentry.blue >> 8)) *
3688 							((long) (xc_cache[j].blue >> 8) -
3689 							 (long) (xentry.blue >> 8));
3690 					}
3691 					if (nDist < nMinDist)
3692 					{
3693 						nMinDist = nDist;
3694 						xentry.pixel = j;
3695 					}
3696 				}
3697 			}
3698 			colour = xentry.pixel;
3699 
3700 			/* update our cache */
3701 			if (xentry.pixel < 256)
3702 			{
3703 				xc_cache[xentry.pixel].red = xentry.red;
3704 				xc_cache[xentry.pixel].green = xentry.green;
3705 				xc_cache[xentry.pixel].blue = xentry.blue;
3706 
3707 			}
3708 
3709 			map[i] = colour;
3710 		}
3711 		return map;
3712 	}
3713 	else
3714 	{
3715 		XColor *xcolours, *xentry;
3716 		Colormap map;
3717 
3718 		xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
3719 		for (i = 0; i < ncolours; i++)
3720 		{
3721 			entry = &colours->colours[i];
3722 			xentry = &xcolours[i];
3723 			xentry->pixel = i;
3724 			MAKE_XCOLOR(xentry, entry);
3725 		}
3726 
3727 		map = XCreateColormap(g_display, g_wnd, g_visual, AllocAll);
3728 		XStoreColors(g_display, map, xcolours, ncolours);
3729 
3730 		xfree(xcolours);
3731 		return (RD_HCOLOURMAP) map;
3732 	}
3733 }
3734 
3735 void
ui_destroy_colourmap(RD_HCOLOURMAP map)3736 ui_destroy_colourmap(RD_HCOLOURMAP map)
3737 {
3738 	if (!g_owncolmap)
3739 		xfree(map);
3740 	else
3741 		XFreeColormap(g_display, (Colormap) map);
3742 }
3743 
3744 void
ui_set_colourmap(RD_HCOLOURMAP map)3745 ui_set_colourmap(RD_HCOLOURMAP map)
3746 {
3747 	if (!g_owncolmap)
3748 	{
3749 		if (g_colmap)
3750 			xfree(g_colmap);
3751 
3752 		g_colmap = (uint32 *) map;
3753 	}
3754 	else
3755 	{
3756 		XSetWindowColormap(g_display, g_wnd, (Colormap) map);
3757 		ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
3758 	}
3759 }
3760 
3761 void
ui_set_clip(int x,int y,int cx,int cy)3762 ui_set_clip(int x, int y, int cx, int cy)
3763 {
3764 	g_clip_rectangle.x = x;
3765 	g_clip_rectangle.y = y;
3766 	g_clip_rectangle.width = cx;
3767 	g_clip_rectangle.height = cy;
3768 	XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
3769 }
3770 
3771 void
ui_reset_clip(void)3772 ui_reset_clip(void)
3773 {
3774 	XWindowAttributes attr;
3775 	XGetWindowAttributes(g_display, g_wnd, &attr);
3776 	ui_set_clip(0, 0, attr.width, attr.height);
3777 }
3778 
3779 void
ui_bell(void)3780 ui_bell(void)
3781 {
3782 	XBell(g_display, 0);
3783 }
3784 
3785 void
ui_destblt(uint8 opcode,int x,int y,int cx,int cy)3786 ui_destblt(uint8 opcode,
3787 	   /* dest */ int x, int y, int cx, int cy)
3788 {
3789 	SET_FUNCTION(opcode);
3790 	FILL_RECTANGLE(x, y, cx, cy);
3791 	RESET_FUNCTION(opcode);
3792 }
3793 
3794 static uint8 hatch_patterns[] = {
3795 	0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,	/* 0 - bsHorizontal */
3796 	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,	/* 1 - bsVertical */
3797 	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,	/* 2 - bsFDiagonal */
3798 	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,	/* 3 - bsBDiagonal */
3799 	0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08,	/* 4 - bsCross */
3800 	0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81	/* 5 - bsDiagCross */
3801 };
3802 
3803 void
ui_patblt(uint8 opcode,int x,int y,int cx,int cy,BRUSH * brush,uint32 bgcolour,uint32 fgcolour)3804 ui_patblt(uint8 opcode,
3805 	  /* dest */ int x, int y, int cx, int cy,
3806 	  /* brush */ BRUSH * brush, uint32 bgcolour, uint32 fgcolour)
3807 {
3808 	Pixmap fill;
3809 	uint8 i, ipattern[8];
3810 
3811 	SET_FUNCTION(opcode);
3812 
3813 	switch (brush->style)
3814 	{
3815 		case 0:	/* Solid */
3816 			SET_FOREGROUND(fgcolour);
3817 			FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3818 			break;
3819 
3820 		case 2:	/* Hatch */
3821 			fill = (Pixmap) ui_create_glyph(8, 8,
3822 							hatch_patterns + brush->pattern[0] * 8);
3823 			SET_FOREGROUND(fgcolour);
3824 			SET_BACKGROUND(bgcolour);
3825 			XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3826 			XSetStipple(g_display, g_gc, fill);
3827 			XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3828 			FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3829 			XSetFillStyle(g_display, g_gc, FillSolid);
3830 			XSetTSOrigin(g_display, g_gc, 0, 0);
3831 			ui_destroy_glyph((RD_HGLYPH) fill);
3832 			break;
3833 
3834 		case 3:	/* Pattern */
3835 			if (brush->bd == 0)	/* rdp4 brush */
3836 			{
3837 				for (i = 0; i != 8; i++)
3838 					ipattern[7 - i] = brush->pattern[i];
3839 				fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3840 				SET_FOREGROUND(bgcolour);
3841 				SET_BACKGROUND(fgcolour);
3842 				XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3843 				XSetStipple(g_display, g_gc, fill);
3844 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3845 				FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3846 				XSetFillStyle(g_display, g_gc, FillSolid);
3847 				XSetTSOrigin(g_display, g_gc, 0, 0);
3848 				ui_destroy_glyph((RD_HGLYPH) fill);
3849 			}
3850 			else if (brush->bd->colour_code > 1)	/* > 1 bpp */
3851 			{
3852 				fill = (Pixmap) ui_create_bitmap(8, 8, brush->bd->data);
3853 				XSetFillStyle(g_display, g_gc, FillTiled);
3854 				XSetTile(g_display, g_gc, fill);
3855 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3856 				FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3857 				XSetFillStyle(g_display, g_gc, FillSolid);
3858 				XSetTSOrigin(g_display, g_gc, 0, 0);
3859 				ui_destroy_bitmap((RD_HBITMAP) fill);
3860 			}
3861 			else
3862 			{
3863 				fill = (Pixmap) ui_create_glyph(8, 8, brush->bd->data);
3864 				SET_FOREGROUND(bgcolour);
3865 				SET_BACKGROUND(fgcolour);
3866 				XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3867 				XSetStipple(g_display, g_gc, fill);
3868 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3869 				FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3870 				XSetFillStyle(g_display, g_gc, FillSolid);
3871 				XSetTSOrigin(g_display, g_gc, 0, 0);
3872 				ui_destroy_glyph((RD_HGLYPH) fill);
3873 			}
3874 			break;
3875 
3876 		default:
3877 			logger(GUI, Warning, "Unimplemented support for brush type %d",
3878 			       brush->style);
3879 	}
3880 
3881 	RESET_FUNCTION(opcode);
3882 
3883 	if (g_ownbackstore)
3884 		XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3885 	ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3886 				(g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc,
3887 				 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3888 }
3889 
3890 void
ui_screenblt(uint8 opcode,int x,int y,int cx,int cy,int srcx,int srcy)3891 ui_screenblt(uint8 opcode,
3892 	     /* dest */ int x, int y, int cx, int cy,
3893 	     /* src */ int srcx, int srcy)
3894 {
3895 	SET_FUNCTION(opcode);
3896 	if (g_ownbackstore)
3897 	{
3898 		XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
3899 			  g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3900 		XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
3901 	}
3902 	else
3903 	{
3904 		XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3905 	}
3906 
3907 	ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3908 				(g_display, g_ownbackstore ? g_backstore : g_wnd,
3909 				 sw->wnd, g_gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3910 
3911 	RESET_FUNCTION(opcode);
3912 }
3913 
3914 void
ui_memblt(uint8 opcode,int x,int y,int cx,int cy,RD_HBITMAP src,int srcx,int srcy)3915 ui_memblt(uint8 opcode,
3916 	  /* dest */ int x, int y, int cx, int cy,
3917 	  /* src */ RD_HBITMAP src, int srcx, int srcy)
3918 {
3919 	SET_FUNCTION(opcode);
3920 	XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3921 	ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3922 				(g_display, (Pixmap) src, sw->wnd, g_gc,
3923 				 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
3924 	if (g_ownbackstore)
3925 		XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
3926 	RESET_FUNCTION(opcode);
3927 }
3928 
3929 void
ui_triblt(uint8 opcode,int x,int y,int cx,int cy,RD_HBITMAP src,int srcx,int srcy,BRUSH * brush,uint32 bgcolour,uint32 fgcolour)3930 ui_triblt(uint8 opcode,
3931 	  /* dest */ int x, int y, int cx, int cy,
3932 	  /* src */ RD_HBITMAP src, int srcx, int srcy,
3933 	  /* brush */ BRUSH * brush, uint32 bgcolour, uint32 fgcolour)
3934 {
3935 	/* This is potentially difficult to do in general. Until someone
3936 	   comes up with a more efficient way of doing it I am using cases. */
3937 
3938 	switch (opcode)
3939 	{
3940 		case 0x69:	/* PDSxxn */
3941 			ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
3942 			ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3943 			break;
3944 
3945 		case 0xb8:	/* PSDPxax */
3946 			ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3947 			ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
3948 			ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3949 			break;
3950 
3951 		case 0xc0:	/* PSa */
3952 			ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
3953 			ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
3954 			break;
3955 
3956 		default:
3957 			logger(GUI, Warning, "Unimplemented triblit opcode 0x%x", opcode);
3958 			ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
3959 	}
3960 }
3961 
3962 void
ui_line(uint8 opcode,int startx,int starty,int endx,int endy,PEN * pen)3963 ui_line(uint8 opcode,
3964 	/* dest */ int startx, int starty, int endx, int endy,
3965 	/* pen */ PEN * pen)
3966 {
3967 	SET_FUNCTION(opcode);
3968 	SET_FOREGROUND(pen->colour);
3969 	XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
3970 	ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc,
3971 					    startx - sw->xoffset, starty - sw->yoffset,
3972 					    endx - sw->xoffset, endy - sw->yoffset));
3973 	if (g_ownbackstore)
3974 		XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
3975 	RESET_FUNCTION(opcode);
3976 }
3977 
3978 void
ui_rect(int x,int y,int cx,int cy,uint32 colour)3979 ui_rect(
3980 	       /* dest */ int x, int y, int cx, int cy,
3981 	       /* brush */ uint32 colour)
3982 {
3983 	SET_FOREGROUND(colour);
3984 	FILL_RECTANGLE(x, y, cx, cy);
3985 }
3986 
3987 void
ui_polygon(uint8 opcode,uint8 fillmode,RD_POINT * point,int npoints,BRUSH * brush,uint32 bgcolour,uint32 fgcolour)3988 ui_polygon(uint8 opcode,
3989 	   /* mode */ uint8 fillmode,
3990 	   /* dest */ RD_POINT * point, int npoints,
3991 	   /* brush */ BRUSH * brush, uint32 bgcolour, uint32 fgcolour)
3992 {
3993 	uint8 style, i, ipattern[8];
3994 	Pixmap fill;
3995 
3996 	SET_FUNCTION(opcode);
3997 
3998 	switch (fillmode)
3999 	{
4000 		case ALTERNATE:
4001 			XSetFillRule(g_display, g_gc, EvenOddRule);
4002 			break;
4003 		case WINDING:
4004 			XSetFillRule(g_display, g_gc, WindingRule);
4005 			break;
4006 		default:
4007 			logger(GUI, Warning, "Unimplemented fill mode %d", fillmode);
4008 	}
4009 
4010 	if (brush)
4011 		style = brush->style;
4012 	else
4013 		style = 0;
4014 
4015 	switch (style)
4016 	{
4017 		case 0:	/* Solid */
4018 			SET_FOREGROUND(fgcolour);
4019 			FILL_POLYGON((XPoint *) point, npoints);
4020 			break;
4021 
4022 		case 2:	/* Hatch */
4023 			fill = (Pixmap) ui_create_glyph(8, 8,
4024 							hatch_patterns + brush->pattern[0] * 8);
4025 			SET_FOREGROUND(fgcolour);
4026 			SET_BACKGROUND(bgcolour);
4027 			XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
4028 			XSetStipple(g_display, g_gc, fill);
4029 			XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4030 			FILL_POLYGON((XPoint *) point, npoints);
4031 			XSetFillStyle(g_display, g_gc, FillSolid);
4032 			XSetTSOrigin(g_display, g_gc, 0, 0);
4033 			ui_destroy_glyph((RD_HGLYPH) fill);
4034 			break;
4035 
4036 		case 3:	/* Pattern */
4037 			if (brush->bd == 0)	/* rdp4 brush */
4038 			{
4039 				for (i = 0; i != 8; i++)
4040 					ipattern[7 - i] = brush->pattern[i];
4041 				fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
4042 				SET_FOREGROUND(bgcolour);
4043 				SET_BACKGROUND(fgcolour);
4044 				XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
4045 				XSetStipple(g_display, g_gc, fill);
4046 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4047 				FILL_POLYGON((XPoint *) point, npoints);
4048 				XSetFillStyle(g_display, g_gc, FillSolid);
4049 				XSetTSOrigin(g_display, g_gc, 0, 0);
4050 				ui_destroy_glyph((RD_HGLYPH) fill);
4051 			}
4052 			else if (brush->bd->colour_code > 1)	/* > 1 bpp */
4053 			{
4054 				fill = (Pixmap) ui_create_bitmap(8, 8, brush->bd->data);
4055 				XSetFillStyle(g_display, g_gc, FillTiled);
4056 				XSetTile(g_display, g_gc, fill);
4057 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4058 				FILL_POLYGON((XPoint *) point, npoints);
4059 				XSetFillStyle(g_display, g_gc, FillSolid);
4060 				XSetTSOrigin(g_display, g_gc, 0, 0);
4061 				ui_destroy_bitmap((RD_HBITMAP) fill);
4062 			}
4063 			else
4064 			{
4065 				fill = (Pixmap) ui_create_glyph(8, 8, brush->bd->data);
4066 				SET_FOREGROUND(bgcolour);
4067 				SET_BACKGROUND(fgcolour);
4068 				XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
4069 				XSetStipple(g_display, g_gc, fill);
4070 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4071 				FILL_POLYGON((XPoint *) point, npoints);
4072 				XSetFillStyle(g_display, g_gc, FillSolid);
4073 				XSetTSOrigin(g_display, g_gc, 0, 0);
4074 				ui_destroy_glyph((RD_HGLYPH) fill);
4075 			}
4076 			break;
4077 
4078 		default:
4079 			logger(GUI, Warning, "Unimplemented brush style %d", brush->style);
4080 	}
4081 
4082 	RESET_FUNCTION(opcode);
4083 }
4084 
4085 void
ui_polyline(uint8 opcode,RD_POINT * points,int npoints,PEN * pen)4086 ui_polyline(uint8 opcode,
4087 	    /* dest */ RD_POINT * points, int npoints,
4088 	    /* pen */ PEN * pen)
4089 {
4090 	/* TODO: set join style */
4091 	SET_FUNCTION(opcode);
4092 	SET_FOREGROUND(pen->colour);
4093 	XDrawLines(g_display, g_wnd, g_gc, (XPoint *) points, npoints, CoordModePrevious);
4094 	if (g_ownbackstore)
4095 		XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
4096 			   CoordModePrevious);
4097 
4098 	ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
4099 				(sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
4100 
4101 	RESET_FUNCTION(opcode);
4102 }
4103 
4104 void
ui_ellipse(uint8 opcode,uint8 fillmode,int x,int y,int cx,int cy,BRUSH * brush,uint32 bgcolour,uint32 fgcolour)4105 ui_ellipse(uint8 opcode,
4106 	   /* mode */ uint8 fillmode,
4107 	   /* dest */ int x, int y, int cx, int cy,
4108 	   /* brush */ BRUSH * brush, uint32 bgcolour, uint32 fgcolour)
4109 {
4110 	uint8 style, i, ipattern[8];
4111 	Pixmap fill;
4112 
4113 	SET_FUNCTION(opcode);
4114 
4115 	if (brush)
4116 		style = brush->style;
4117 	else
4118 		style = 0;
4119 
4120 	switch (style)
4121 	{
4122 		case 0:	/* Solid */
4123 			SET_FOREGROUND(fgcolour);
4124 			DRAW_ELLIPSE(x, y, cx, cy, fillmode);
4125 			break;
4126 
4127 		case 2:	/* Hatch */
4128 			fill = (Pixmap) ui_create_glyph(8, 8,
4129 							hatch_patterns + brush->pattern[0] * 8);
4130 			SET_FOREGROUND(fgcolour);
4131 			SET_BACKGROUND(bgcolour);
4132 			XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
4133 			XSetStipple(g_display, g_gc, fill);
4134 			XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4135 			DRAW_ELLIPSE(x, y, cx, cy, fillmode);
4136 			XSetFillStyle(g_display, g_gc, FillSolid);
4137 			XSetTSOrigin(g_display, g_gc, 0, 0);
4138 			ui_destroy_glyph((RD_HGLYPH) fill);
4139 			break;
4140 
4141 		case 3:	/* Pattern */
4142 			if (brush->bd == 0)	/* rdp4 brush */
4143 			{
4144 				for (i = 0; i != 8; i++)
4145 					ipattern[7 - i] = brush->pattern[i];
4146 				fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
4147 				SET_FOREGROUND(bgcolour);
4148 				SET_BACKGROUND(fgcolour);
4149 				XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
4150 				XSetStipple(g_display, g_gc, fill);
4151 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4152 				DRAW_ELLIPSE(x, y, cx, cy, fillmode);
4153 				XSetFillStyle(g_display, g_gc, FillSolid);
4154 				XSetTSOrigin(g_display, g_gc, 0, 0);
4155 				ui_destroy_glyph((RD_HGLYPH) fill);
4156 			}
4157 			else if (brush->bd->colour_code > 1)	/* > 1 bpp */
4158 			{
4159 				fill = (Pixmap) ui_create_bitmap(8, 8, brush->bd->data);
4160 				XSetFillStyle(g_display, g_gc, FillTiled);
4161 				XSetTile(g_display, g_gc, fill);
4162 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4163 				DRAW_ELLIPSE(x, y, cx, cy, fillmode);
4164 				XSetFillStyle(g_display, g_gc, FillSolid);
4165 				XSetTSOrigin(g_display, g_gc, 0, 0);
4166 				ui_destroy_bitmap((RD_HBITMAP) fill);
4167 			}
4168 			else
4169 			{
4170 				fill = (Pixmap) ui_create_glyph(8, 8, brush->bd->data);
4171 				SET_FOREGROUND(bgcolour);
4172 				SET_BACKGROUND(fgcolour);
4173 				XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
4174 				XSetStipple(g_display, g_gc, fill);
4175 				XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
4176 				DRAW_ELLIPSE(x, y, cx, cy, fillmode);
4177 				XSetFillStyle(g_display, g_gc, FillSolid);
4178 				XSetTSOrigin(g_display, g_gc, 0, 0);
4179 				ui_destroy_glyph((RD_HGLYPH) fill);
4180 			}
4181 			break;
4182 
4183 		default:
4184 			logger(GUI, Warning, "Unimplemented brush style %d", brush->style);
4185 	}
4186 
4187 	RESET_FUNCTION(opcode);
4188 }
4189 
4190 /* warning, this function only draws on wnd or backstore, not both */
4191 void
ui_draw_glyph(int mixmode,int x,int y,int cx,int cy,RD_HGLYPH glyph,int srcx,int srcy,uint32 bgcolour,uint32 fgcolour)4192 ui_draw_glyph(int mixmode,
4193 	      /* dest */ int x, int y, int cx, int cy,
4194 	      /* src */ RD_HGLYPH glyph, int srcx, int srcy,
4195 	      uint32 bgcolour, uint32 fgcolour)
4196 {
4197 	UNUSED(srcx);
4198 	UNUSED(srcy);
4199 
4200 	SET_FOREGROUND(fgcolour);
4201 	SET_BACKGROUND(bgcolour);
4202 
4203 	XSetFillStyle(g_display, g_gc,
4204 		      (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
4205 	XSetStipple(g_display, g_gc, (Pixmap) glyph);
4206 	XSetTSOrigin(g_display, g_gc, x, y);
4207 
4208 	FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
4209 
4210 	XSetFillStyle(g_display, g_gc, FillSolid);
4211 }
4212 
4213 #define DO_GLYPH(ttext,idx) \
4214 {\
4215   glyph = cache_get_font (font, ttext[idx]);\
4216   if (!(flags & TEXT2_IMPLICIT_X))\
4217   {\
4218     xyoffset = ttext[++idx];\
4219     if ((xyoffset & 0x80))\
4220     {\
4221       if (flags & TEXT2_VERTICAL)\
4222         y += ttext[idx+1] | (ttext[idx+2] << 8);\
4223       else\
4224         x += ttext[idx+1] | (ttext[idx+2] << 8);\
4225       idx += 2;\
4226     }\
4227     else\
4228     {\
4229       if (flags & TEXT2_VERTICAL)\
4230         y += xyoffset;\
4231       else\
4232         x += xyoffset;\
4233     }\
4234   }\
4235   if (glyph != NULL)\
4236   {\
4237     x1 = x + glyph->offset;\
4238     y1 = y + glyph->baseline;\
4239     XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
4240     XSetTSOrigin(g_display, g_gc, x1, y1);\
4241     FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
4242     if (flags & TEXT2_IMPLICIT_X)\
4243       x += glyph->width;\
4244   }\
4245 }
4246 
4247 void
ui_draw_text(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,uint32 bgcolour,uint32 fgcolour,uint8 * text,uint8 length)4248 ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
4249 	     int clipx, int clipy, int clipcx, int clipcy,
4250 	     int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
4251 	     uint32 bgcolour, uint32 fgcolour, uint8 * text, uint8 length)
4252 {
4253 	XWindowAttributes attr;
4254 	UNUSED(opcode);
4255 	UNUSED(brush);
4256 
4257 	XGetWindowAttributes(g_display, g_wnd, &attr);
4258 
4259 	/* TODO: use brush appropriately */
4260 
4261 	FONTGLYPH *glyph;
4262 	int i, j, xyoffset, x1, y1;
4263 	DATABLOB *entry;
4264 
4265 	SET_FOREGROUND(bgcolour);
4266 
4267 	/* Sometimes, the boxcx value is something really large, like
4268 	   32691. This makes XCopyArea fail with Xvnc. The code below
4269 	   is a quick fix. */
4270 	if (boxx + boxcx > attr.width)
4271 		boxcx = attr.width - boxx;
4272 
4273 	if (boxcx > 1)
4274 	{
4275 		FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
4276 	}
4277 	else if (mixmode == MIX_OPAQUE)
4278 	{
4279 		FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
4280 	}
4281 
4282 	SET_FOREGROUND(fgcolour);
4283 	SET_BACKGROUND(bgcolour);
4284 	XSetFillStyle(g_display, g_gc, FillStippled);
4285 
4286 	/* Paint text, character by character */
4287 	for (i = 0; i < length;)
4288 	{
4289 		switch (text[i])
4290 		{
4291 			case 0xff:
4292 				/* At least two bytes needs to follow */
4293 				if (i + 3 > length)
4294 				{
4295 					logger(GUI, Warning,
4296 					       "ui_draw_text(), skipping short 0xff command");
4297 					i = length = 0;
4298 					break;
4299 				}
4300 				cache_put_text(text[i + 1], text, text[i + 2]);
4301 				i += 3;
4302 				length -= i;
4303 				/* this will move pointer from start to first character after FF command */
4304 				text = &(text[i]);
4305 				i = 0;
4306 				break;
4307 
4308 			case 0xfe:
4309 				/* At least one byte needs to follow */
4310 				if (i + 2 > length)
4311 				{
4312 					logger(GUI, Warning,
4313 					       "ui_draw_text(), skipping short 0xfe command");
4314 					i = length = 0;
4315 					break;
4316 				}
4317 				entry = cache_get_text(text[i + 1]);
4318 				if (entry->data != NULL)
4319 				{
4320 					if ((((uint8 *) (entry->data))[1] == 0)
4321 					    && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
4322 					{
4323 						if (flags & TEXT2_VERTICAL)
4324 							y += text[i + 2];
4325 						else
4326 							x += text[i + 2];
4327 					}
4328 					for (j = 0; j < entry->size; j++)
4329 						DO_GLYPH(((uint8 *) (entry->data)), j);
4330 				}
4331 				if (i + 2 < length)
4332 					i += 3;
4333 				else
4334 					i += 2;
4335 				length -= i;
4336 				/* this will move pointer from start to first character after FE command */
4337 				text = &(text[i]);
4338 				i = 0;
4339 				break;
4340 
4341 			default:
4342 				DO_GLYPH(text, i);
4343 				i++;
4344 				break;
4345 		}
4346 	}
4347 
4348 	XSetFillStyle(g_display, g_gc, FillSolid);
4349 
4350 	if (g_ownbackstore)
4351 	{
4352 		if (boxcx > 1)
4353 		{
4354 			XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
4355 				  boxy, boxcx, boxcy, boxx, boxy);
4356 			ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
4357 						(g_display, g_backstore, sw->wnd, g_gc,
4358 						 boxx, boxy,
4359 						 boxcx, boxcy,
4360 						 boxx - sw->xoffset, boxy - sw->yoffset));
4361 		}
4362 		else
4363 		{
4364 			XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
4365 				  clipy, clipcx, clipcy, clipx, clipy);
4366 			ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
4367 						(g_display, g_backstore, sw->wnd, g_gc,
4368 						 clipx, clipy,
4369 						 clipcx, clipcy, clipx - sw->xoffset,
4370 						 clipy - sw->yoffset));
4371 		}
4372 	}
4373 }
4374 
4375 void
ui_desktop_save(uint32 offset,int x,int y,int cx,int cy)4376 ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
4377 {
4378 	Pixmap pix;
4379 	XImage *image;
4380 
4381 	if (g_ownbackstore)
4382 	{
4383 		image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
4384 		exit_if_null(image);
4385 	}
4386 	else
4387 	{
4388 		pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
4389 		XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
4390 		image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
4391 		exit_if_null(image);
4392 		XFreePixmap(g_display, pix);
4393 	}
4394 
4395 	offset *= g_bpp / 8;
4396 	cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
4397 
4398 	XDestroyImage(image);
4399 }
4400 
4401 void
ui_desktop_restore(uint32 offset,int x,int y,int cx,int cy)4402 ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
4403 {
4404 	XImage *image;
4405 	uint8 *data;
4406 
4407 	offset *= g_bpp / 8;
4408 	data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
4409 	if (data == NULL)
4410 		return;
4411 
4412 	image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
4413 			     (char *) data, cx, cy, g_bpp, 0);
4414 
4415 	if (g_ownbackstore)
4416 	{
4417 		XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
4418 		XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
4419 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
4420 					(g_display, g_backstore, sw->wnd, g_gc,
4421 					 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
4422 	}
4423 	else
4424 	{
4425 		XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
4426 		ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
4427 					(g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
4428 					 x - sw->xoffset, y - sw->yoffset));
4429 	}
4430 
4431 	XFree(image);
4432 }
4433 
4434 /* these do nothing here but are used in uiports */
4435 void
ui_begin_update(void)4436 ui_begin_update(void)
4437 {
4438 }
4439 
4440 void
ui_end_update(void)4441 ui_end_update(void)
4442 {
4443 	XFlush(g_display);
4444 }
4445 
4446 
4447 void
ui_seamless_begin(RD_BOOL hidden)4448 ui_seamless_begin(RD_BOOL hidden)
4449 {
4450 	if (!g_seamless_rdp)
4451 		return;
4452 
4453 	if (g_seamless_started)
4454 		return;
4455 
4456 	g_seamless_started = True;
4457 	g_seamless_hidden = hidden;
4458 
4459 	if (!hidden)
4460 		ui_seamless_toggle();
4461 
4462 	if (g_seamless_spawn_cmd[0])
4463 	{
4464 		seamless_send_spawn(g_seamless_spawn_cmd);
4465 		g_seamless_spawn_cmd[0] = 0;
4466 	}
4467 
4468 	seamless_send_persistent(g_seamless_persistent_mode);
4469 }
4470 
4471 
4472 void
ui_seamless_end()4473 ui_seamless_end()
4474 {
4475 	/* Destroy all seamless windows */
4476 	while (g_seamless_windows)
4477 	{
4478 		XDestroyWindow(g_display, g_seamless_windows->wnd);
4479 		sw_remove_window(g_seamless_windows);
4480 	}
4481 
4482 	g_seamless_started = False;
4483 	g_seamless_active = False;
4484 	g_seamless_hidden = False;
4485 }
4486 
4487 
4488 void
ui_seamless_hide_desktop()4489 ui_seamless_hide_desktop()
4490 {
4491 	if (!g_seamless_rdp)
4492 		return;
4493 
4494 	if (!g_seamless_started)
4495 		return;
4496 
4497 	if (g_seamless_active)
4498 		ui_seamless_toggle();
4499 
4500 	g_seamless_hidden = True;
4501 }
4502 
4503 
4504 void
ui_seamless_unhide_desktop()4505 ui_seamless_unhide_desktop()
4506 {
4507 	if (!g_seamless_rdp)
4508 		return;
4509 
4510 	if (!g_seamless_started)
4511 		return;
4512 
4513 	g_seamless_hidden = False;
4514 
4515 	ui_seamless_toggle();
4516 }
4517 
4518 
4519 void
ui_seamless_toggle()4520 ui_seamless_toggle()
4521 {
4522 	if (!g_seamless_rdp)
4523 		return;
4524 
4525 	if (!g_seamless_started)
4526 		return;
4527 
4528 	if (g_seamless_hidden)
4529 		return;
4530 
4531 	if (g_seamless_active)
4532 	{
4533 		/* Deactivate */
4534 		while (g_seamless_windows)
4535 		{
4536 			XDestroyWindow(g_display, g_seamless_windows->wnd);
4537 			sw_remove_window(g_seamless_windows);
4538 		}
4539 		XMapWindow(g_display, g_wnd);
4540 	}
4541 	else
4542 	{
4543 		/* Activate */
4544 		XUnmapWindow(g_display, g_wnd);
4545 		seamless_send_sync();
4546 	}
4547 
4548 	g_seamless_active = !g_seamless_active;
4549 }
4550 
4551 
4552 void
ui_seamless_create_window(unsigned long id,unsigned long group,unsigned long parent,unsigned long flags)4553 ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent,
4554 			  unsigned long flags)
4555 {
4556 	Window wnd;
4557 	XSetWindowAttributes attribs;
4558 	XClassHint *classhints;
4559 	XSizeHints *sizehints;
4560 	XWMHints *wmhints;
4561 	long input_mask;
4562 	unsigned long value_mask;
4563 	seamless_window *sw, *sw_parent;
4564 
4565 	if (!g_seamless_active)
4566 		return;
4567 
4568 	/* Ignore CREATEs for existing windows */
4569 	sw = sw_get_window_by_id(id);
4570 	if (sw)
4571 		return;
4572 
4573 	value_mask = get_window_attribs_seamless(&attribs);
4574 	wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
4575 			    InputOutput, g_visual, value_mask, &attribs);
4576 
4577 	ewmh_set_wm_pid(wnd, getpid());
4578 	set_wm_client_machine(g_display, wnd);
4579 
4580 	XStoreName(g_display, wnd, "SeamlessRDP");
4581 	ewmh_set_wm_name(wnd, "SeamlessRDP");
4582 
4583 	mwm_hide_decorations(wnd);
4584 
4585 	classhints = XAllocClassHint();
4586 	if (classhints != NULL)
4587 	{
4588 		classhints->res_name = "rdesktop";
4589 		classhints->res_class = "SeamlessRDP";
4590 		XSetClassHint(g_display, wnd, classhints);
4591 		XFree(classhints);
4592 	}
4593 
4594 	/* WM_NORMAL_HINTS */
4595 	sizehints = XAllocSizeHints();
4596 	if (sizehints != NULL)
4597 	{
4598 		sizehints->flags = USPosition;
4599 		XSetWMNormalHints(g_display, wnd, sizehints);
4600 		XFree(sizehints);
4601 	}
4602 
4603 	/* Parent-less transient windows */
4604 	if (parent == 0xFFFFFFFF)
4605 	{
4606 		XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
4607 		/* Some buggy WMs (kwin) do not handle the above, so fake it
4608 		   using some other hints. */
4609 		ewmh_set_window_popup(wnd);
4610 	}
4611 	/* Normal transient windows */
4612 	else if (parent != 0x00000000)
4613 	{
4614 		sw_parent = sw_get_window_by_id(parent);
4615 		if (sw_parent)
4616 			XSetTransientForHint(g_display, wnd, sw_parent->wnd);
4617 		else
4618 			logger(GUI, Warning,
4619 			       "ui_seamless_create_window(): no parent window 0x%lx\n", parent);
4620 	}
4621 
4622 	if (flags & SEAMLESSRDP_CREATE_MODAL)
4623 	{
4624 		/* We do this to support buggy WMs (*cough* Metacity *cough*)
4625 		   somewhat at least */
4626 		if (parent == 0x00000000)
4627 			XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
4628 		ewmh_set_window_modal(wnd);
4629 	}
4630 
4631 	if (flags & SEAMLESSRDP_CREATE_TOPMOST)
4632 	{
4633 		/* Make window always-on-top */
4634 		ewmh_set_window_above(wnd);
4635 	}
4636 
4637 	/* FIXME: Support for Input Context:s */
4638 
4639 	get_input_mask(&input_mask);
4640 	input_mask |= PropertyChangeMask;
4641 
4642 	XSelectInput(g_display, wnd, input_mask);
4643 
4644 	/* setup supported protocols. */
4645 	Atom supported[] = {
4646 		g_net_wm_ping_atom,
4647 		g_kill_atom
4648 	};
4649 
4650 	XSetWMProtocols(g_display, wnd, supported, 2);
4651 
4652 	sw = xmalloc(sizeof(seamless_window));
4653 
4654 	memset(sw, 0, sizeof(seamless_window));
4655 
4656 	sw->wnd = wnd;
4657 	sw->id = id;
4658 	sw->group = sw_find_group(group, False);
4659 	sw->group->refcnt++;
4660 	sw->state = SEAMLESSRDP_NOTYETMAPPED;
4661 	sw->desktop = 0;
4662 	sw->position_timer = xmalloc(sizeof(struct timeval));
4663 	timerclear(sw->position_timer);
4664 
4665 	sw->outstanding_position = False;
4666 	sw->outpos_serial = 0;
4667 	sw->outpos_xoffset = sw->outpos_yoffset = 0;
4668 	sw->outpos_width = sw->outpos_height = 0;
4669 
4670 	sw->next = g_seamless_windows;
4671 	g_seamless_windows = sw;
4672 
4673 	/* WM_HINTS */
4674 	wmhints = XAllocWMHints();
4675 	if (wmhints)
4676 	{
4677 		wmhints->flags = WindowGroupHint;
4678 		wmhints->window_group = sw->group->wnd;
4679 		XSetWMHints(g_display, sw->wnd, wmhints);
4680 		XFree(wmhints);
4681 	}
4682 }
4683 
4684 
4685 void
ui_seamless_destroy_window(unsigned long id,unsigned long flags)4686 ui_seamless_destroy_window(unsigned long id, unsigned long flags)
4687 {
4688 	UNUSED(flags);
4689 
4690 	seamless_window *sw;
4691 
4692 	if (!g_seamless_active)
4693 		return;
4694 
4695 	sw = sw_get_window_by_id(id);
4696 	if (!sw)
4697 	{
4698 		logger(GUI, Warning,
4699 		       "ui_seamless_destroy_window(), no information for window 0x%lx", id);
4700 		return;
4701 	}
4702 
4703 	XDestroyWindow(g_display, sw->wnd);
4704 	sw_remove_window(sw);
4705 }
4706 
4707 
4708 void
ui_seamless_destroy_group(unsigned long id,unsigned long flags)4709 ui_seamless_destroy_group(unsigned long id, unsigned long flags)
4710 {
4711 	UNUSED(flags);
4712 
4713 	seamless_window *sw, *sw_next;
4714 
4715 	if (!g_seamless_active)
4716 		return;
4717 
4718 	for (sw = g_seamless_windows; sw; sw = sw_next)
4719 	{
4720 		sw_next = sw->next;
4721 
4722 		if (sw->group->id == id)
4723 		{
4724 			XDestroyWindow(g_display, sw->wnd);
4725 			sw_remove_window(sw);
4726 		}
4727 	}
4728 }
4729 
4730 
4731 void
ui_seamless_seticon(unsigned long id,const char * format,int width,int height,int chunk,const char * data,size_t chunk_len)4732 ui_seamless_seticon(unsigned long id, const char *format, int width, int height, int chunk,
4733 		    const char *data, size_t chunk_len)
4734 {
4735 	seamless_window *sw;
4736 
4737 	if (!g_seamless_active)
4738 		return;
4739 
4740 	sw = sw_get_window_by_id(id);
4741 	if (!sw)
4742 	{
4743 		logger(GUI, Warning, "ui_seamless_seticon(): No information for window 0x%lx", id);
4744 		return;
4745 	}
4746 
4747 	if (chunk == 0)
4748 	{
4749 		if (sw->icon_size)
4750 			logger(GUI, Warning,
4751 			       "ui_seamless_seticon(), new icon started before previous completed");
4752 
4753 		if (strcmp(format, "RGBA") != 0)
4754 		{
4755 			logger(GUI, Warning, "ui_seamless_seticon(), unknown icon format \"%s\"",
4756 			       format);
4757 			return;
4758 		}
4759 
4760 		sw->icon_size = width * height * 4;
4761 		if (sw->icon_size > 32 * 32 * 4)
4762 		{
4763 			logger(GUI, Warning, "ui_seamless_seticon(), icon too large (%d bytes)",
4764 			       sw->icon_size);
4765 			sw->icon_size = 0;
4766 			return;
4767 		}
4768 
4769 		sw->icon_offset = 0;
4770 	}
4771 	else
4772 	{
4773 		if (!sw->icon_size)
4774 			return;
4775 	}
4776 
4777 	if (chunk_len > (sw->icon_size - sw->icon_offset))
4778 	{
4779 		logger(GUI, Warning,
4780 		       "ui_seamless_seticon(),  too large chunk received (%d bytes > %d bytes)",
4781 		       chunk_len, sw->icon_size - sw->icon_offset);
4782 		sw->icon_size = 0;
4783 		return;
4784 	}
4785 
4786 	memcpy(sw->icon_buffer + sw->icon_offset, data, chunk_len);
4787 	sw->icon_offset += chunk_len;
4788 
4789 	if (sw->icon_offset == sw->icon_size)
4790 	{
4791 		ewmh_set_icon(sw->wnd, width, height, sw->icon_buffer);
4792 		sw->icon_size = 0;
4793 	}
4794 }
4795 
4796 
4797 void
ui_seamless_delicon(unsigned long id,const char * format,int width,int height)4798 ui_seamless_delicon(unsigned long id, const char *format, int width, int height)
4799 {
4800 	seamless_window *sw;
4801 
4802 	if (!g_seamless_active)
4803 		return;
4804 
4805 	sw = sw_get_window_by_id(id);
4806 	if (!sw)
4807 	{
4808 		logger(GUI, Warning, "ui_seamless_seticon(), no information for window 0x%lx", id);
4809 		return;
4810 	}
4811 
4812 	if (strcmp(format, "RGBA") != 0)
4813 	{
4814 		logger(GUI, Warning, "ui_seamless_seticon(), unknown icon format \"%s\"", format);
4815 		return;
4816 	}
4817 
4818 	ewmh_del_icon(sw->wnd, width, height);
4819 }
4820 
4821 
4822 void
ui_seamless_move_window(unsigned long id,int x,int y,int width,int height,unsigned long flags)4823 ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags)
4824 {
4825 	UNUSED(flags);
4826 
4827 	seamless_window *sw;
4828 
4829 	if (!g_seamless_active)
4830 		return;
4831 
4832 	sw = sw_get_window_by_id(id);
4833 	if (!sw)
4834 	{
4835 		logger(GUI, Warning, "ui_seamless_move_window(), no information for window 0x%lx",
4836 		       id);
4837 		return;
4838 	}
4839 
4840 	/* We ignore server updates until it has handled our request. */
4841 	if (sw->outstanding_position)
4842 		return;
4843 
4844 	if (!width || !height)
4845 		/* X11 windows must be at least 1x1 */
4846 		return;
4847 
4848 	/* If we move the window in a maximized state, then KDE won't
4849 	   accept restoration */
4850 	switch (sw->state)
4851 	{
4852 		case SEAMLESSRDP_MINIMIZED:
4853 		case SEAMLESSRDP_MAXIMIZED:
4854 			sw_update_position(sw);
4855 			return;
4856 	}
4857 
4858 	sw->xoffset = x;
4859 	sw->yoffset = y;
4860 	sw->width = width;
4861 	sw->height = height;
4862 
4863 	/* FIXME: Perhaps use ewmh_net_moveresize_window instead */
4864 	XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
4865 }
4866 
4867 
4868 void
ui_seamless_restack_window(unsigned long id,unsigned long behind,unsigned long flags)4869 ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags)
4870 {
4871 	seamless_window *sw;
4872 	XWindowChanges values;
4873 	unsigned long restack_serial;
4874 	unsigned int value_mask;
4875 
4876 	if (!g_seamless_active)
4877 		return;
4878 
4879 	sw = sw_get_window_by_id(id);
4880 	if (!sw)
4881 	{
4882 		logger(GUI, Warning,
4883 		       "ui_seamless_restack_window(), no information for window 0x%lx", id);
4884 		return;
4885 	}
4886 
4887 	if (behind)
4888 	{
4889 		seamless_window *sw_behind;
4890 
4891 		sw_behind = sw_get_window_by_id(behind);
4892 		if (!sw_behind)
4893 		{
4894 			logger(GUI, Warning,
4895 			       "ui_seamless_restack_window(), no information for behind window 0x%lx",
4896 			       behind);
4897 			return;
4898 		}
4899 
4900 		values.stack_mode = Below;
4901 		value_mask = CWStackMode | CWSibling;
4902 		values.sibling = sw_behind->wnd;
4903 
4904 		/* Avoid that topmost windows references non-topmost
4905 		   windows, and vice versa. */
4906 		if (ewmh_is_window_above(sw->wnd))
4907 		{
4908 			if (!ewmh_is_window_above(sw_behind->wnd))
4909 			{
4910 				/* Disallow, move to bottom of the
4911 				   topmost stack. */
4912 				values.stack_mode = Below;
4913 				value_mask = CWStackMode;	/* Not sibling */
4914 			}
4915 		}
4916 		else
4917 		{
4918 			if (ewmh_is_window_above(sw_behind->wnd))
4919 			{
4920 				/* Move to top of non-topmost
4921 				   stack. */
4922 				values.stack_mode = Above;
4923 				value_mask = CWStackMode;	/* Not sibling */
4924 			}
4925 		}
4926 	}
4927 	else
4928 	{
4929 		values.stack_mode = Above;
4930 		value_mask = CWStackMode;
4931 	}
4932 
4933 	restack_serial = XNextRequest(g_display);
4934 	XReconfigureWMWindow(g_display, sw->wnd, DefaultScreen(g_display), value_mask, &values);
4935 	sw_wait_configurenotify(sw->wnd, restack_serial);
4936 
4937 	sw_restack_window(sw, behind);
4938 
4939 	if (flags & SEAMLESSRDP_CREATE_TOPMOST)
4940 	{
4941 		/* Make window always-on-top */
4942 		ewmh_set_window_above(sw->wnd);
4943 	}
4944 }
4945 
4946 
4947 void
ui_seamless_settitle(unsigned long id,const char * title,unsigned long flags)4948 ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags)
4949 {
4950 	UNUSED(flags);
4951 
4952 	seamless_window *sw;
4953 
4954 	if (!g_seamless_active)
4955 		return;
4956 
4957 	sw = sw_get_window_by_id(id);
4958 	if (!sw)
4959 	{
4960 		logger(GUI, Warning, "ui_seamless_settitle(), no information for window 0x%lx", id);
4961 		return;
4962 	}
4963 
4964 	/* FIXME: Might want to convert the name for non-EWMH WMs */
4965 	XStoreName(g_display, sw->wnd, title);
4966 	ewmh_set_wm_name(sw->wnd, title);
4967 }
4968 
4969 
4970 void
ui_seamless_setstate(unsigned long id,unsigned int state,unsigned long flags)4971 ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags)
4972 {
4973 	UNUSED(flags);
4974 
4975 	seamless_window *sw;
4976 
4977 	if (!g_seamless_active)
4978 		return;
4979 
4980 	sw = sw_get_window_by_id(id);
4981 	if (!sw)
4982 	{
4983 		logger(GUI, Warning, "ui_seamless_setstate(), no information for window 0x%lx", id);
4984 		return;
4985 	}
4986 
4987 	switch (state)
4988 	{
4989 		case SEAMLESSRDP_NORMAL:
4990 		case SEAMLESSRDP_MAXIMIZED:
4991 			ewmh_change_state(sw->wnd, state);
4992 			XMapWindow(g_display, sw->wnd);
4993 			break;
4994 		case SEAMLESSRDP_MINIMIZED:
4995 			/* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
4996 			   the Window Manager should probably just ignore the request, since
4997 			   _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
4998 			   such as minimization, rather than an independent state." Besides,
4999 			   XIconifyWindow is easier. */
5000 			if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
5001 			{
5002 				XWMHints *hints;
5003 				hints = XGetWMHints(g_display, sw->wnd);
5004 				if (hints)
5005 				{
5006 					hints->flags |= StateHint;
5007 					hints->initial_state = IconicState;
5008 					XSetWMHints(g_display, sw->wnd, hints);
5009 					XFree(hints);
5010 				}
5011 				XMapWindow(g_display, sw->wnd);
5012 			}
5013 			else
5014 				XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display));
5015 			break;
5016 		default:
5017 			logger(GUI, Warning, "ui_seamless_setstate(), invalid state %d", state);
5018 			break;
5019 	}
5020 
5021 	sw->state = state;
5022 }
5023 
5024 
5025 void
ui_seamless_syncbegin(unsigned long flags)5026 ui_seamless_syncbegin(unsigned long flags)
5027 {
5028 	UNUSED(flags);
5029 
5030 	if (!g_seamless_active)
5031 		return;
5032 
5033 	/* Destroy all seamless windows */
5034 	while (g_seamless_windows)
5035 	{
5036 		XDestroyWindow(g_display, g_seamless_windows->wnd);
5037 		sw_remove_window(g_seamless_windows);
5038 	}
5039 }
5040 
5041 
5042 void
ui_seamless_ack(unsigned int serial)5043 ui_seamless_ack(unsigned int serial)
5044 {
5045 	seamless_window *sw;
5046 	for (sw = g_seamless_windows; sw; sw = sw->next)
5047 	{
5048 		if (sw->outstanding_position && (sw->outpos_serial == serial))
5049 		{
5050 			sw->xoffset = sw->outpos_xoffset;
5051 			sw->yoffset = sw->outpos_yoffset;
5052 			sw->width = sw->outpos_width;
5053 			sw->height = sw->outpos_height;
5054 			sw->outstanding_position = False;
5055 
5056 			/* Do a complete redraw of the window as part of the
5057 			   completion of the move. This is to remove any
5058 			   artifacts caused by our lack of synchronization. */
5059 			XCopyArea(g_display, g_backstore,
5060 				  sw->wnd, g_gc,
5061 				  sw->xoffset, sw->yoffset, sw->width, sw->height, 0, 0);
5062 
5063 			break;
5064 		}
5065 	}
5066 }
5067