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