/[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 1469 - (show annotations)
Thu Apr 24 14:13:28 2008 UTC (16 years, 1 month ago) by astrand
Original Path: sourceforge.net/trunk/rdesktop/xwin.c
File MIME type: text/plain
File size: 95817 byte(s)
Indented

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 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 for (i = 0; i != 8; i++)
3074 ipattern[7 - i] = brush->pattern[i];
3075 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3076 SET_FOREGROUND(bgcolour);
3077 SET_BACKGROUND(fgcolour);
3078 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3079 XSetStipple(g_display, g_gc, fill);
3080 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3081 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3082 XSetFillStyle(g_display, g_gc, FillSolid);
3083 XSetTSOrigin(g_display, g_gc, 0, 0);
3084 ui_destroy_glyph((RD_HGLYPH) fill);
3085 break;
3086
3087 default:
3088 unimpl("brush %d\n", brush->style);
3089 }
3090
3091 RESET_FUNCTION(opcode);
3092
3093 if (g_ownbackstore)
3094 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3095 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3096 (g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc,
3097 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3098 }
3099
3100 void
3101 ui_screenblt(uint8 opcode,
3102 /* dest */ int x, int y, int cx, int cy,
3103 /* src */ int srcx, int srcy)
3104 {
3105 SET_FUNCTION(opcode);
3106 if (g_ownbackstore)
3107 {
3108 XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
3109 g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3110 XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
3111 }
3112 else
3113 {
3114 XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3115 }
3116
3117 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3118 (g_display, g_ownbackstore ? g_backstore : g_wnd,
3119 sw->wnd, g_gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3120
3121 RESET_FUNCTION(opcode);
3122 }
3123
3124 void
3125 ui_memblt(uint8 opcode,
3126 /* dest */ int x, int y, int cx, int cy,
3127 /* src */ RD_HBITMAP src, int srcx, int srcy)
3128 {
3129 SET_FUNCTION(opcode);
3130 XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3131 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3132 (g_display, (Pixmap) src, sw->wnd, g_gc,
3133 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
3134 if (g_ownbackstore)
3135 XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
3136 RESET_FUNCTION(opcode);
3137 }
3138
3139 void
3140 ui_triblt(uint8 opcode,
3141 /* dest */ int x, int y, int cx, int cy,
3142 /* src */ RD_HBITMAP src, int srcx, int srcy,
3143 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
3144 {
3145 /* This is potentially difficult to do in general. Until someone
3146 comes up with a more efficient way of doing it I am using cases. */
3147
3148 switch (opcode)
3149 {
3150 case 0x69: /* PDSxxn */
3151 ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
3152 ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3153 break;
3154
3155 case 0xb8: /* PSDPxax */
3156 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3157 ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
3158 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3159 break;
3160
3161 case 0xc0: /* PSa */
3162 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
3163 ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
3164 break;
3165
3166 default:
3167 unimpl("triblt 0x%x\n", opcode);
3168 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
3169 }
3170 }
3171
3172 void
3173 ui_line(uint8 opcode,
3174 /* dest */ int startx, int starty, int endx, int endy,
3175 /* pen */ PEN * pen)
3176 {
3177 SET_FUNCTION(opcode);
3178 SET_FOREGROUND(pen->colour);
3179 XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
3180 ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc,
3181 startx - sw->xoffset, starty - sw->yoffset,
3182 endx - sw->xoffset, endy - sw->yoffset));
3183 if (g_ownbackstore)
3184 XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
3185 RESET_FUNCTION(opcode);
3186 }
3187
3188 void
3189 ui_rect(
3190 /* dest */ int x, int y, int cx, int cy,
3191 /* brush */ int colour)
3192 {
3193 SET_FOREGROUND(colour);
3194 FILL_RECTANGLE(x, y, cx, cy);
3195 }
3196
3197 void
3198 ui_polygon(uint8 opcode,
3199 /* mode */ uint8 fillmode,
3200 /* dest */ RD_POINT * point, int npoints,
3201 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
3202 {
3203 uint8 style, i, ipattern[8];
3204 Pixmap fill;
3205
3206 SET_FUNCTION(opcode);
3207
3208 switch (fillmode)
3209 {
3210 case ALTERNATE:
3211 XSetFillRule(g_display, g_gc, EvenOddRule);
3212 break;
3213 case WINDING:
3214 XSetFillRule(g_display, g_gc, WindingRule);
3215 break;
3216 default:
3217 unimpl("fill mode %d\n", fillmode);
3218 }
3219
3220 if (brush)
3221 style = brush->style;
3222 else
3223 style = 0;
3224
3225 switch (style)
3226 {
3227 case 0: /* Solid */
3228 SET_FOREGROUND(fgcolour);
3229 FILL_POLYGON((XPoint *) point, npoints);
3230 break;
3231
3232 case 2: /* Hatch */
3233 fill = (Pixmap) ui_create_glyph(8, 8,
3234 hatch_patterns + brush->pattern[0] * 8);
3235 SET_FOREGROUND(fgcolour);
3236 SET_BACKGROUND(bgcolour);
3237 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3238 XSetStipple(g_display, g_gc, fill);
3239 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3240 FILL_POLYGON((XPoint *) point, npoints);
3241 XSetFillStyle(g_display, g_gc, FillSolid);
3242 XSetTSOrigin(g_display, g_gc, 0, 0);
3243 ui_destroy_glyph((RD_HGLYPH) fill);
3244 break;
3245
3246 case 3: /* Pattern */
3247 for (i = 0; i != 8; i++)
3248 ipattern[7 - i] = brush->pattern[i];
3249 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3250 SET_FOREGROUND(bgcolour);
3251 SET_BACKGROUND(fgcolour);
3252 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3253 XSetStipple(g_display, g_gc, fill);
3254 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3255 FILL_POLYGON((XPoint *) point, npoints);
3256 XSetFillStyle(g_display, g_gc, FillSolid);
3257 XSetTSOrigin(g_display, g_gc, 0, 0);
3258 ui_destroy_glyph((RD_HGLYPH) fill);
3259 break;
3260
3261 default:
3262 unimpl("brush %d\n", brush->style);
3263 }
3264
3265 RESET_FUNCTION(opcode);
3266 }
3267
3268 void
3269 ui_polyline(uint8 opcode,
3270 /* dest */ RD_POINT * points, int npoints,
3271 /* pen */ PEN * pen)
3272 {
3273 /* TODO: set join style */
3274 SET_FUNCTION(opcode);
3275 SET_FOREGROUND(pen->colour);
3276 XDrawLines(g_display, g_wnd, g_gc, (XPoint *) points, npoints, CoordModePrevious);
3277 if (g_ownbackstore)
3278 XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
3279 CoordModePrevious);
3280
3281 ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
3282 (sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
3283
3284 RESET_FUNCTION(opcode);
3285 }
3286
3287 void
3288 ui_ellipse(uint8 opcode,
3289 /* mode */ uint8 fillmode,
3290 /* dest */ int x, int y, int cx, int cy,
3291 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
3292 {
3293 uint8 style, i, ipattern[8];
3294 Pixmap fill;
3295
3296 SET_FUNCTION(opcode);
3297
3298 if (brush)
3299 style = brush->style;
3300 else
3301 style = 0;
3302
3303 switch (style)
3304 {
3305 case 0: /* Solid */
3306 SET_FOREGROUND(fgcolour);
3307 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3308 break;
3309
3310 case 2: /* Hatch */
3311 fill = (Pixmap) ui_create_glyph(8, 8,
3312 hatch_patterns + brush->pattern[0] * 8);
3313 SET_FOREGROUND(fgcolour);
3314 SET_BACKGROUND(bgcolour);
3315 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3316 XSetStipple(g_display, g_gc, fill);
3317 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3318 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3319 XSetFillStyle(g_display, g_gc, FillSolid);
3320 XSetTSOrigin(g_display, g_gc, 0, 0);
3321 ui_destroy_glyph((RD_HGLYPH) fill);
3322 break;
3323
3324 case 3: /* Pattern */
3325 for (i = 0; i != 8; i++)
3326 ipattern[7 - i] = brush->pattern[i];
3327 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3328 SET_FOREGROUND(bgcolour);
3329 SET_BACKGROUND(fgcolour);
3330 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3331 XSetStipple(g_display, g_gc, fill);
3332 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3333 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3334 XSetFillStyle(g_display, g_gc, FillSolid);
3335 XSetTSOrigin(g_display, g_gc, 0, 0);
3336 ui_destroy_glyph((RD_HGLYPH) fill);
3337 break;
3338
3339 default:
3340 unimpl("brush %d\n", brush->style);
3341 }
3342
3343 RESET_FUNCTION(opcode);
3344 }
3345
3346 /* warning, this function only draws on wnd or backstore, not both */
3347 void
3348 ui_draw_glyph(int mixmode,
3349 /* dest */ int x, int y, int cx, int cy,
3350 /* src */ RD_HGLYPH glyph, int srcx, int srcy,
3351 int bgcolour, int fgcolour)
3352 {
3353 SET_FOREGROUND(fgcolour);
3354 SET_BACKGROUND(bgcolour);
3355
3356 XSetFillStyle(g_display, g_gc,
3357 (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
3358 XSetStipple(g_display, g_gc, (Pixmap) glyph);
3359 XSetTSOrigin(g_display, g_gc, x, y);
3360
3361 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3362
3363 XSetFillStyle(g_display, g_gc, FillSolid);
3364 }
3365
3366 #define DO_GLYPH(ttext,idx) \
3367 {\
3368 glyph = cache_get_font (font, ttext[idx]);\
3369 if (!(flags & TEXT2_IMPLICIT_X))\
3370 {\
3371 xyoffset = ttext[++idx];\
3372 if ((xyoffset & 0x80))\
3373 {\
3374 if (flags & TEXT2_VERTICAL)\
3375 y += ttext[idx+1] | (ttext[idx+2] << 8);\
3376 else\
3377 x += ttext[idx+1] | (ttext[idx+2] << 8);\
3378 idx += 2;\
3379 }\
3380 else\
3381 {\
3382 if (flags & TEXT2_VERTICAL)\
3383 y += xyoffset;\
3384 else\
3385 x += xyoffset;\
3386 }\
3387 }\
3388 if (glyph != NULL)\
3389 {\
3390 x1 = x + glyph->offset;\
3391 y1 = y + glyph->baseline;\
3392 XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
3393 XSetTSOrigin(g_display, g_gc, x1, y1);\
3394 FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
3395 if (flags & TEXT2_IMPLICIT_X)\
3396 x += glyph->width;\
3397 }\
3398 }
3399
3400 void
3401 ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
3402 int clipx, int clipy, int clipcx, int clipcy,
3403 int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
3404 int bgcolour, int fgcolour, uint8 * text, uint8 length)
3405 {
3406 /* TODO: use brush appropriately */
3407
3408 FONTGLYPH *glyph;
3409 int i, j, xyoffset, x1, y1;
3410 DATABLOB *entry;
3411
3412 SET_FOREGROUND(bgcolour);
3413
3414 /* Sometimes, the boxcx value is something really large, like
3415 32691. This makes XCopyArea fail with Xvnc. The code below
3416 is a quick fix. */
3417 if (boxx + boxcx > g_width)
3418 boxcx = g_width - boxx;
3419
3420 if (boxcx > 1)
3421 {
3422 FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
3423 }
3424 else if (mixmode == MIX_OPAQUE)
3425 {
3426 FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
3427 }
3428
3429 SET_FOREGROUND(fgcolour);
3430 SET_BACKGROUND(bgcolour);
3431 XSetFillStyle(g_display, g_gc, FillStippled);
3432
3433 /* Paint text, character by character */
3434 for (i = 0; i < length;)
3435 {
3436 switch (text[i])
3437 {
3438 case 0xff:
3439 /* At least two bytes needs to follow */
3440 if (i + 3 > length)
3441 {
3442 warning("Skipping short 0xff command:");
3443 for (j = 0; j < length; j++)
3444 fprintf(stderr, "%02x ", text[j]);
3445 fprintf(stderr, "\n");
3446 i = length = 0;
3447 break;
3448 }
3449 cache_put_text(text[i + 1], text, text[i + 2]);
3450 i += 3;
3451 length -= i;
3452 /* this will move pointer from start to first character after FF command */
3453 text = &(text[i]);
3454 i = 0;
3455 break;
3456
3457 case 0xfe:
3458 /* At least one byte needs to follow */
3459 if (i + 2 > length)
3460 {
3461 warning("Skipping short 0xfe command:");
3462 for (j = 0; j < length; j++)
3463 fprintf(stderr, "%02x ", text[j]);
3464 fprintf(stderr, "\n");
3465 i = length = 0;
3466 break;
3467 }
3468 entry = cache_get_text(text[i + 1]);
3469 if (entry->data != NULL)
3470 {
3471 if ((((uint8 *) (entry->data))[1] == 0)
3472 && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
3473 {
3474 if (flags & TEXT2_VERTICAL)
3475 y += text[i + 2];
3476 else
3477 x += text[i + 2];
3478 }
3479 for (j = 0; j < entry->size; j++)
3480 DO_GLYPH(((uint8 *) (entry->data)), j);
3481 }
3482 if (i + 2 < length)
3483 i += 3;
3484 else
3485 i += 2;
3486 length -= i;
3487 /* this will move pointer from start to first character after FE command */
3488 text = &(text[i]);
3489 i = 0;
3490 break;
3491
3492 default:
3493 DO_GLYPH(text, i);
3494 i++;
3495 break;
3496 }
3497 }
3498
3499 XSetFillStyle(g_display, g_gc, FillSolid);
3500
3501 if (g_ownbackstore)
3502 {
3503 if (boxcx > 1)
3504 {
3505 XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
3506 boxy, boxcx, boxcy, boxx, boxy);
3507 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3508 (g_display, g_backstore, sw->wnd, g_gc,
3509 boxx, boxy,
3510 boxcx, boxcy,
3511 boxx - sw->xoffset, boxy - sw->yoffset));
3512 }
3513 else
3514 {
3515 XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
3516 clipy, clipcx, clipcy, clipx, clipy);
3517 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3518 (g_display, g_backstore, sw->wnd, g_gc,
3519 clipx, clipy,
3520 clipcx, clipcy, clipx - sw->xoffset,
3521 clipy - sw->yoffset));
3522 }
3523 }
3524 }
3525
3526 void
3527 ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
3528 {
3529 Pixmap pix;
3530 XImage *image;
3531
3532 if (g_ownbackstore)
3533 {
3534 image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
3535 exit_if_null(image);
3536 }
3537 else
3538 {
3539 pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
3540 XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
3541 image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
3542 exit_if_null(image);
3543 XFreePixmap(g_display, pix);
3544 }
3545
3546 offset *= g_bpp / 8;
3547 cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
3548
3549 XDestroyImage(image);
3550 }
3551
3552 void
3553 ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
3554 {
3555 XImage *image;
3556 uint8 *data;
3557
3558 offset *= g_bpp / 8;
3559 data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
3560 if (data == NULL)
3561 return;
3562
3563 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
3564 (char *) data, cx, cy, g_bpp, 0);
3565
3566 if (g_ownbackstore)
3567 {
3568 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
3569 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3570 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3571 (g_display, g_backstore, sw->wnd, g_gc,
3572 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3573 }
3574 else
3575 {
3576 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
3577 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3578 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
3579 x - sw->xoffset, y - sw->yoffset));
3580 }
3581
3582 XFree(image);
3583 }
3584
3585 /* these do nothing here but are used in uiports */
3586 void
3587 ui_begin_update(void)
3588 {
3589 }
3590
3591 void
3592 ui_end_update(void)
3593 {
3594 }
3595
3596
3597 void
3598 ui_seamless_begin(RD_BOOL hidden)
3599 {
3600 if (!g_seamless_rdp)
3601 return;
3602
3603 if (g_seamless_started)
3604 return;
3605
3606 g_seamless_started = True;
3607 g_seamless_hidden = hidden;
3608
3609 if (!hidden)
3610 ui_seamless_toggle();
3611 }
3612
3613
3614 void
3615 ui_seamless_hide_desktop()
3616 {
3617 if (!g_seamless_rdp)
3618 return;
3619
3620 if (!g_seamless_started)
3621 return;
3622
3623 if (g_seamless_active)
3624 ui_seamless_toggle();
3625
3626 g_seamless_hidden = True;
3627 }
3628
3629
3630 void
3631 ui_seamless_unhide_desktop()
3632 {
3633 if (!g_seamless_rdp)
3634 return;
3635
3636 if (!g_seamless_started)
3637 return;
3638
3639 g_seamless_hidden = False;
3640
3641 ui_seamless_toggle();
3642 }
3643
3644
3645 void
3646 ui_seamless_toggle()
3647 {
3648 if (!g_seamless_rdp)
3649 return;
3650
3651 if (!g_seamless_started)
3652 return;
3653
3654 if (g_seamless_hidden)
3655 return;
3656
3657 if (g_seamless_active)
3658 {
3659 /* Deactivate */
3660 while (g_seamless_windows)
3661 {
3662 XDestroyWindow(g_display, g_seamless_windows->wnd);
3663 sw_remove_window(g_seamless_windows);
3664 }
3665 XMapWindow(g_display, g_wnd);
3666 }
3667 else
3668 {
3669 /* Activate */
3670 XUnmapWindow(g_display, g_wnd);
3671 seamless_send_sync();
3672 }
3673
3674 g_seamless_active = !g_seamless_active;
3675 }
3676
3677
3678 void
3679 ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent,
3680 unsigned long flags)
3681 {
3682 Window wnd;
3683 XSetWindowAttributes attribs;
3684 XClassHint *classhints;
3685 XSizeHints *sizehints;
3686 XWMHints *wmhints;
3687 long input_mask;
3688 seamless_window *sw, *sw_parent;
3689
3690 if (!g_seamless_active)
3691 return;
3692
3693 /* Ignore CREATEs for existing windows */
3694 sw = sw_get_window_by_id(id);
3695 if (sw)
3696 return;
3697
3698 get_window_attribs(&attribs);
3699 wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
3700 InputOutput, g_visual,
3701 CWBackPixel | CWBackingStore | CWColormap | CWBorderPixel, &attribs);
3702
3703 XStoreName(g_display, wnd, "SeamlessRDP");
3704 ewmh_set_wm_name(wnd, "SeamlessRDP");
3705
3706 mwm_hide_decorations(wnd);
3707
3708 classhints = XAllocClassHint();
3709 if (classhints != NULL)
3710 {
3711 classhints->res_name = "rdesktop";
3712 classhints->res_class = "SeamlessRDP";
3713 XSetClassHint(g_display, wnd, classhints);
3714 XFree(classhints);
3715 }
3716
3717 /* WM_NORMAL_HINTS */
3718 sizehints = XAllocSizeHints();
3719 if (sizehints != NULL)
3720 {
3721 sizehints->flags = USPosition;
3722 XSetWMNormalHints(g_display, wnd, sizehints);
3723 XFree(sizehints);
3724 }
3725
3726 /* Parent-less transient windows */
3727 if (parent == 0xFFFFFFFF)
3728 {
3729 XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3730 /* Some buggy wm:s (kwin) do not handle the above, so fake it
3731 using some other hints. */
3732 ewmh_set_window_popup(wnd);
3733 }
3734 /* Normal transient windows */
3735 else if (parent != 0x00000000)
3736 {
3737 sw_parent = sw_get_window_by_id(parent);
3738 if (sw_parent)
3739 XSetTransientForHint(g_display, wnd, sw_parent->wnd);
3740 else
3741 warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3742 }
3743
3744 if (flags & SEAMLESSRDP_CREATE_MODAL)
3745 {
3746 /* We do this to support buggy wm:s (*cough* metacity *cough*)
3747 somewhat at least */
3748 if (parent == 0x00000000)
3749 XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3750 ewmh_set_window_modal(wnd);
3751 }
3752
3753 if (flags & SEAMLESSRDP_CREATE_TOPMOST)
3754 {
3755 /* Make window always-on-top */
3756 ewmh_set_window_above(wnd);
3757 }
3758
3759 /* FIXME: Support for Input Context:s */
3760
3761 get_input_mask(&input_mask);
3762 input_mask |= PropertyChangeMask;
3763
3764 XSelectInput(g_display, wnd, input_mask);
3765
3766 /* handle the WM_DELETE_WINDOW protocol. */
3767 XSetWMProtocols(g_display, wnd, &g_kill_atom, 1);
3768
3769 sw = xmalloc(sizeof(seamless_window));
3770
3771 memset(sw, 0, sizeof(seamless_window));
3772
3773 sw->wnd = wnd;
3774 sw->id = id;
3775 sw->group = sw_find_group(group, False);
3776 sw->group->refcnt++;
3777 sw->state = SEAMLESSRDP_NOTYETMAPPED;
3778 sw->desktop = 0;
3779 sw->position_timer = xmalloc(sizeof(struct timeval));
3780 timerclear(sw->position_timer);
3781
3782 sw->outstanding_position = False;
3783 sw->outpos_serial = 0;
3784 sw->outpos_xoffset = sw->outpos_yoffset = 0;
3785 sw->outpos_width = sw->outpos_height = 0;
3786
3787 sw->next = g_seamless_windows;
3788 g_seamless_windows = sw;
3789
3790 /* WM_HINTS */
3791 wmhints = XAllocWMHints();
3792 if (wmhints)
3793 {
3794 wmhints->flags = WindowGroupHint;
3795 wmhints->window_group = sw->group->wnd;
3796 XSetWMHints(g_display, sw->wnd, wmhints);
3797 XFree(wmhints);
3798 }
3799 }
3800
3801
3802 void
3803 ui_seamless_destroy_window(unsigned long id, unsigned long flags)
3804 {
3805 seamless_window *sw;
3806
3807 if (!g_seamless_active)
3808 return;
3809
3810 sw = sw_get_window_by_id(id);
3811 if (!sw)
3812 {
3813 warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3814 return;
3815 }
3816
3817 XDestroyWindow(g_display, sw->wnd);
3818 sw_remove_window(sw);
3819 }
3820
3821
3822 void
3823 ui_seamless_destroy_group(unsigned long id, unsigned long flags)
3824 {
3825 seamless_window *sw, *sw_next;
3826
3827 if (!g_seamless_active)
3828 return;
3829
3830 for (sw = g_seamless_windows; sw; sw = sw_next)
3831 {
3832 sw_next = sw->next;
3833
3834 if (sw->group->id == id)
3835 {
3836 XDestroyWindow(g_display, sw->wnd);
3837 sw_remove_window(sw);
3838 }
3839 }
3840 }
3841
3842
3843 void
3844 ui_seamless_seticon(unsigned long id, const char *format, int width, int height, int chunk,
3845 const char *data, int chunk_len)
3846 {
3847 seamless_window *sw;
3848
3849 if (!g_seamless_active)
3850 return;
3851
3852 sw = sw_get_window_by_id(id);
3853 if (!sw)
3854 {
3855 warning("ui_seamless_seticon: No information for window 0x%lx\n", id);
3856 return;
3857 }
3858
3859 if (chunk == 0)
3860 {
3861 if (sw->icon_size)
3862 warning("ui_seamless_seticon: New icon started before previous completed\n");
3863
3864 if (strcmp(format, "RGBA") != 0)
3865 {
3866 warning("ui_seamless_seticon: Uknown icon format \"%s\"\n", format);
3867 return;
3868 }
3869
3870 sw->icon_size = width * height * 4;
3871 if (sw->icon_size > 32 * 32 * 4)
3872 {
3873 warning("ui_seamless_seticon: Icon too large (%d bytes)\n", sw->icon_size);
3874 sw->icon_size = 0;
3875 return;
3876 }
3877
3878 sw->icon_offset = 0;
3879 }
3880 else
3881 {
3882 if (!sw->icon_size)
3883 return;
3884 }
3885
3886 if (chunk_len > (sw->icon_size - sw->icon_offset))
3887 {
3888 warning("ui_seamless_seticon: Too large chunk received (%d bytes > %d bytes)\n",
3889 chunk_len, sw->icon_size - sw->icon_offset);
3890 sw->icon_size = 0;
3891 return;
3892 }
3893
3894 memcpy(sw->icon_buffer + sw->icon_offset, data, chunk_len);
3895 sw->icon_offset += chunk_len;
3896
3897 if (sw->icon_offset == sw->icon_size)
3898 {
3899 ewmh_set_icon(sw->wnd, width, height, sw->icon_buffer);
3900 sw->icon_size = 0;
3901 }
3902 }
3903
3904
3905 void
3906 ui_seamless_delicon(unsigned long id, const char *format, int width, int height)
3907 {
3908 seamless_window *sw;
3909
3910 if (!g_seamless_active)
3911 return;
3912
3913 sw = sw_get_window_by_id(id);
3914 if (!sw)
3915 {
3916 warning("ui_seamless_seticon: No information for window 0x%lx\n", id);
3917 return;
3918 }
3919
3920 if (strcmp(format, "RGBA") != 0)
3921 {
3922 warning("ui_seamless_seticon: Uknown icon format \"%s\"\n", format);
3923 return;
3924 }
3925
3926 ewmh_del_icon(sw->wnd, width, height);
3927 }
3928
3929
3930 void
3931 ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags)
3932 {
3933 seamless_window *sw;
3934
3935 if (!g_seamless_active)
3936 return;
3937
3938 sw = sw_get_window_by_id(id);
3939 if (!sw)
3940 {
3941 warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3942 return;
3943 }
3944
3945 /* We ignore server updates until it has handled our request. */
3946 if (sw->outstanding_position)
3947 return;
3948
3949 if (!width || !height)
3950 /* X11 windows must be at least 1x1 */
3951 return;
3952
3953 sw->xoffset = x;
3954 sw->yoffset = y;
3955 sw->width = width;
3956 sw->height = height;
3957
3958 /* If we move the window in a maximized state, then KDE won't
3959 accept restoration */
3960 switch (sw->state)
3961 {
3962 case SEAMLESSRDP_MINIMIZED:
3963 case SEAMLESSRDP_MAXIMIZED:
3964 return;
3965 }
3966
3967 /* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3968 XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3969 }
3970
3971
3972 void
3973 ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags)
3974 {
3975 seamless_window *sw;
3976 XWindowChanges values;
3977 unsigned long restack_serial;
3978
3979 if (!g_seamless_active)
3980 return;
3981
3982 sw = sw_get_window_by_id(id);
3983 if (!sw)
3984 {
3985 warning("ui_seamless_restack_window: No information for window 0x%lx\n", id);
3986 return;
3987 }
3988
3989 if (behind)
3990 {
3991 seamless_window *sw_behind;
3992
3993 sw_behind = sw_get_window_by_id(behind);
3994 if (!sw_behind)
3995 {
3996 warning("ui_seamless_restack_window: No information for behind window 0x%lx\n", behind);
3997 return;
3998 }
3999
4000 if (!g_seamless_broken_restack)
4001 {
4002 values.stack_mode = Below;
4003 values.sibling = sw_behind->wnd;
4004 restack_serial = XNextRequest(g_display);
4005 XReconfigureWMWindow(g_display, sw->wnd, DefaultScreen(g_display),
4006 CWStackMode | CWSibling, &values);
4007 sw_wait_configurenotify(sw->wnd, restack_serial);
4008 }
4009 }
4010 else
4011 {
4012 values.stack_mode = Above;
4013 restack_serial = XNextRequest(g_display);
4014 XReconfigureWMWindow(g_display, sw->wnd, DefaultScreen(g_display), CWStackMode,
4015 &values);
4016 sw_wait_configurenotify(sw->wnd, restack_serial);
4017 }
4018
4019 sw_restack_window(sw, behind);
4020
4021 if (flags & SEAMLESSRDP_CREATE_TOPMOST)
4022 {
4023 /* Make window always-on-top */
4024 ewmh_set_window_above(sw->wnd);
4025 }
4026 }
4027
4028
4029 void
4030 ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags)
4031 {
4032 seamless_window *sw;
4033
4034 if (!g_seamless_active)
4035 return;
4036
4037 sw = sw_get_window_by_id(id);
4038 if (!sw)
4039 {
4040 warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
4041 return;
4042 }
4043
4044 /* FIXME: Might want to convert the name for non-EWMH WMs */
4045 XStoreName(g_display, sw->wnd, title);
4046 ewmh_set_wm_name(sw->wnd, title);
4047 }
4048
4049
4050 void
4051 ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags)
4052 {
4053 seamless_window *sw;
4054
4055 if (!g_seamless_active)
4056 return;
4057
4058 sw = sw_get_window_by_id(id);
4059 if (!sw)
4060 {
4061 warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
4062 return;
4063 }
4064
4065 switch (state)
4066 {
4067 case SEAMLESSRDP_NORMAL:
4068 case SEAMLESSRDP_MAXIMIZED:
4069 ewmh_change_state(sw->wnd, state);
4070 XMapWindow(g_display, sw->wnd);
4071 break;
4072 case SEAMLESSRDP_MINIMIZED:
4073 /* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
4074 the Window Manager should probably just ignore the request, since
4075 _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
4076 such as minimization, rather than an independent state." Besides,
4077 XIconifyWindow is easier. */
4078 if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
4079 {
4080 XWMHints *hints;
4081 hints = XGetWMHints(g_display, sw->wnd);
4082 if (hints)
4083 {
4084 hints->flags |= StateHint;
4085 hints->initial_state = IconicState;
4086 XSetWMHints(g_display, sw->wnd, hints);
4087 XFree(hints);
4088 }
4089 XMapWindow(g_display, sw->wnd);
4090 }
4091 else
4092 XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display));
4093 break;
4094 default:
4095 warning("SeamlessRDP: Invalid state %d\n", state);
4096 break;
4097 }
4098
4099 sw->state = state;
4100 }
4101
4102
4103 void
4104 ui_seamless_syncbegin(unsigned long flags)
4105 {
4106 if (!g_seamless_active)
4107 return;
4108
4109 /* Destroy all seamless windows */
4110 while (g_seamless_windows)
4111 {
4112 XDestroyWindow(g_display, g_seamless_windows->wnd);
4113 sw_remove_window(g_seamless_windows);
4114 }
4115 }
4116
4117
4118 void
4119 ui_seamless_ack(unsigned int serial)
4120 {
4121 seamless_window *sw;
4122 for (sw = g_seamless_windows; sw; sw = sw->next)
4123 {
4124 if (sw->outstanding_position && (sw->outpos_serial == serial))
4125 {
4126 sw->xoffset = sw->outpos_xoffset;
4127 sw->yoffset = sw->outpos_yoffset;
4128 sw->width = sw->outpos_width;
4129 sw->height = sw->outpos_height;
4130 sw->outstanding_position = False;
4131
4132 /* Do a complete redraw of the window as part of the
4133 completion of the move. This is to remove any
4134 artifacts caused by our lack of synchronization. */
4135 XCopyArea(g_display, g_backstore,
4136 sw->wnd, g_gc,
4137 sw->xoffset, sw->yoffset, sw->width, sw->height, 0, 0);
4138
4139 break;
4140 }
4141 }
4142 }

  ViewVC Help
Powered by ViewVC 1.1.26