VirtualBox

source: vbox/trunk/src/VBox/RDP/client-1.8.3/xwin.c@ 55121

Last change on this file since 55121 was 55121, checked in by vboxsync, 10 years ago

rdesktop 1.8.3 unmodified

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette