VirtualBox

source: vbox/trunk/src/VBox/RDP/client/xwin.c@ 53007

最後變更 在這個檔案從53007是 37224,由 vboxsync 提交於 14 年 前

RDP/client: fix OSE

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

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