/[rdesktop]/jpeg/rdesktop/trunk/xwin.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /jpeg/rdesktop/trunk/xwin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1485 - (show annotations)
Tue Nov 25 08:05:25 2008 UTC (15 years, 5 months ago) by astrand
Original Path: sourceforge.net/trunk/rdesktop/xwin.c
File MIME type: text/plain
File size: 98775 byte(s)
Ran indent-all

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

  ViewVC Help
Powered by ViewVC 1.1.26