/[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 1461 - (show annotations)
Wed Mar 26 17:16:32 2008 UTC (16 years, 2 months ago) by astrand
Original Path: sourceforge.net/trunk/rdesktop/xwin.c
File MIME type: text/plain
File size: 95503 byte(s)
Decreased timeout in sw_wait_configurenotify to one second, to reduce
frusrtation with old metacity versions. Need to use gettimeofday.

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-2007
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[16];
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 for (i = 0; i < pointer_buttons; ++i)
1529 {
1530 /* This might produce multiple logical buttons mapping
1531 to a single physical one, but hey, that's
1532 life... */
1533 g_pointer_log_to_phys_map[phys_to_log_map[i] - 1] = i + 1;
1534 }
1535 }
1536
1537 RD_BOOL
1538 get_key_state(unsigned int state, uint32 keysym)
1539 {
1540 int modifierpos, key, keysymMask = 0;
1541 int offset;
1542
1543 KeyCode keycode = XKeysymToKeycode(g_display, keysym);
1544
1545 if (keycode == NoSymbol)
1546 return False;
1547
1548 for (modifierpos = 0; modifierpos < 8; modifierpos++)
1549 {
1550 offset = g_mod_map->max_keypermod * modifierpos;
1551
1552 for (key = 0; key < g_mod_map->max_keypermod; key++)
1553 {
1554 if (g_mod_map->modifiermap[offset + key] == keycode)
1555 keysymMask |= 1 << modifierpos;
1556 }
1557 }
1558
1559 return (state & keysymMask) ? True : False;
1560 }
1561
1562 static void
1563 calculate_shifts(uint32 mask, int *shift_r, int *shift_l)
1564 {
1565 *shift_l = ffs(mask) - 1;
1566 mask >>= *shift_l;
1567 *shift_r = 8 - ffs(mask & ~(mask >> 1));
1568 }
1569
1570 /* Given a mask of a colour channel (e.g. XVisualInfo.red_mask),
1571 calculates the bits-per-pixel of this channel (a.k.a. colour weight).
1572 */
1573 static unsigned
1574 calculate_mask_weight(uint32 mask)
1575 {
1576 unsigned weight = 0;
1577 do
1578 {
1579 weight += (mask & 1);
1580 }
1581 while (mask >>= 1);
1582 return weight;
1583 }
1584
1585 static RD_BOOL
1586 select_visual(int screen_num)
1587 {
1588 XPixmapFormatValues *pfm;
1589 int pixmap_formats_count, visuals_count;
1590 XVisualInfo *vmatches = NULL;
1591 XVisualInfo template;
1592 int i;
1593 unsigned red_weight, blue_weight, green_weight;
1594
1595 red_weight = blue_weight = green_weight = 0;
1596
1597 if (g_server_depth == -1)
1598 {
1599 g_server_depth = DisplayPlanes(g_display, DefaultScreen(g_display));
1600 }
1601
1602 pfm = XListPixmapFormats(g_display, &pixmap_formats_count);
1603 if (pfm == NULL)
1604 {
1605 error("Unable to get list of pixmap formats from display.\n");
1606 XCloseDisplay(g_display);
1607 return False;
1608 }
1609
1610 /* Search for best TrueColor visual */
1611 template.class = TrueColor;
1612 template.screen = screen_num;
1613 vmatches =
1614 XGetVisualInfo(g_display, VisualClassMask | VisualScreenMask, &template,
1615 &visuals_count);
1616 g_visual = NULL;
1617 g_no_translate_image = False;
1618 g_compatible_arch = False;
1619 if (vmatches != NULL)
1620 {
1621 for (i = 0; i < visuals_count; ++i)
1622 {
1623 XVisualInfo *visual_info = &vmatches[i];
1624 RD_BOOL can_translate_to_bpp = False;
1625 int j;
1626
1627 /* Try to find a no-translation visual that'll
1628 allow us to use RDP bitmaps directly as ZPixmaps. */
1629 if (!g_xserver_be && (((visual_info->depth == 15) &&
1630 /* R5G5B5 */
1631 (visual_info->red_mask == 0x7c00) &&
1632 (visual_info->green_mask == 0x3e0) &&
1633 (visual_info->blue_mask == 0x1f)) ||
1634 ((visual_info->depth == 16) &&
1635 /* R5G6B5 */
1636 (visual_info->red_mask == 0xf800) &&
1637 (visual_info->green_mask == 0x7e0) &&
1638 (visual_info->blue_mask == 0x1f)) ||
1639 ((visual_info->depth == 24) &&
1640 /* R8G8B8 */
1641 (visual_info->red_mask == 0xff0000) &&
1642 (visual_info->green_mask == 0xff00) &&
1643 (visual_info->blue_mask == 0xff))))
1644 {
1645 g_visual = visual_info->visual;
1646 g_depth = visual_info->depth;
1647 g_compatible_arch = !g_host_be;
1648 g_no_translate_image = (visual_info->depth == g_server_depth);
1649 if (g_no_translate_image)
1650 /* We found the best visual */
1651 break;
1652 }
1653 else
1654 {
1655 g_compatible_arch = False;
1656 }
1657
1658 if (visual_info->depth > 24)
1659 {
1660 /* Avoid 32-bit visuals and likes like the plague.
1661 They're either untested or proven to work bad
1662 (e.g. nvidia's Composite 32-bit visual).
1663 Most implementation offer a 24-bit visual anyway. */
1664 continue;
1665 }
1666
1667 /* Only care for visuals, for whose BPPs (not depths!)
1668 we have a translateXtoY function. */
1669 for (j = 0; j < pixmap_formats_count; ++j)
1670 {
1671 if (pfm[j].depth == visual_info->depth)
1672 {
1673 if ((pfm[j].bits_per_pixel == 16) ||
1674 (pfm[j].bits_per_pixel == 24) ||
1675 (pfm[j].bits_per_pixel == 32))
1676 {
1677 can_translate_to_bpp = True;
1678 }
1679 break;
1680 }
1681 }
1682
1683 /* Prefer formats which have the most colour depth.
1684 We're being truly aristocratic here, minding each
1685 weight on its own. */
1686 if (can_translate_to_bpp)
1687 {
1688 unsigned vis_red_weight =
1689 calculate_mask_weight(visual_info->red_mask);
1690 unsigned vis_green_weight =
1691 calculate_mask_weight(visual_info->green_mask);
1692 unsigned vis_blue_weight =
1693 calculate_mask_weight(visual_info->blue_mask);
1694 if ((vis_red_weight >= red_weight)
1695 && (vis_green_weight >= green_weight)
1696 && (vis_blue_weight >= blue_weight))
1697 {
1698 red_weight = vis_red_weight;
1699 green_weight = vis_green_weight;
1700 blue_weight = vis_blue_weight;
1701 g_visual = visual_info->visual;
1702 g_depth = visual_info->depth;
1703 }
1704 }
1705 }
1706 XFree(vmatches);
1707 }
1708
1709 if (g_visual != NULL)
1710 {
1711 g_owncolmap = False;
1712 calculate_shifts(g_visual->red_mask, &g_red_shift_r, &g_red_shift_l);
1713 calculate_shifts(g_visual->green_mask, &g_green_shift_r, &g_green_shift_l);
1714 calculate_shifts(g_visual->blue_mask, &g_blue_shift_r, &g_blue_shift_l);
1715 }
1716 else
1717 {
1718 template.class = PseudoColor;
1719 template.depth = 8;
1720 template.colormap_size = 256;
1721 vmatches =
1722 XGetVisualInfo(g_display,
1723 VisualClassMask | VisualDepthMask | VisualColormapSizeMask,
1724 &template, &visuals_count);
1725 if (vmatches == NULL)
1726 {
1727 error("No usable TrueColor or PseudoColor visuals on this display.\n");
1728 XCloseDisplay(g_display);
1729 XFree(pfm);
1730 return False;
1731 }
1732
1733 /* we use a colourmap, so the default visual should do */
1734 g_owncolmap = True;
1735 g_visual = vmatches[0].visual;
1736 g_depth = vmatches[0].depth;
1737 }
1738
1739 g_bpp = 0;
1740 for (i = 0; i < pixmap_formats_count; ++i)
1741 {
1742 XPixmapFormatValues *pf = &pfm[i];
1743 if (pf->depth == g_depth)
1744 {
1745 g_bpp = pf->bits_per_pixel;
1746
1747 if (g_no_translate_image)
1748 {
1749 switch (g_server_depth)
1750 {
1751 case 15:
1752 case 16:
1753 if (g_bpp != 16)
1754 g_no_translate_image = False;
1755 break;
1756 case 24:
1757 /* Yes, this will force image translation
1758 on most modern servers which use 32 bits
1759 for R8G8B8. */
1760 if (g_bpp != 24)
1761 g_no_translate_image = False;
1762 break;
1763 default:
1764 g_no_translate_image = False;
1765 break;
1766 }
1767 }
1768
1769 /* Pixmap formats list is a depth-to-bpp mapping --
1770 there's just a single entry for every depth,
1771 so we can safely break here */
1772 break;
1773 }
1774 }
1775 XFree(pfm);
1776 pfm = NULL;
1777 return True;
1778 }
1779
1780 static XErrorHandler g_old_error_handler;
1781 static RD_BOOL g_error_expected = False;
1782
1783 /* Check if the X11 window corresponding to a seamless window with
1784 specified id exists. */
1785 RD_BOOL
1786 sw_window_exists(unsigned long id)
1787 {
1788 seamless_window *sw;
1789 char *name;
1790 Status sts = 0;
1791
1792 sw = sw_get_window_by_id(id);
1793 if (!sw)
1794 return False;
1795
1796 g_error_expected = True;
1797 sts = XFetchName(g_display, sw->wnd, &name);
1798 g_error_expected = False;
1799 if (sts)
1800 {
1801 XFree(name);
1802 }
1803
1804 return sts;
1805 }
1806
1807 static int
1808 error_handler(Display * dpy, XErrorEvent * eev)
1809 {
1810 if (g_error_expected)
1811 return 0;
1812
1813 return g_old_error_handler(dpy, eev);
1814 }
1815
1816 RD_BOOL
1817 ui_init(void)
1818 {
1819 int screen_num;
1820
1821 g_display = XOpenDisplay(NULL);
1822 if (g_display == NULL)
1823 {
1824 error("Failed to open display: %s\n", XDisplayName(NULL));
1825 return False;
1826 }
1827
1828 {
1829 uint16 endianess_test = 1;
1830 g_host_be = !(RD_BOOL) (*(uint8 *) (&endianess_test));
1831 }
1832
1833 g_old_error_handler = XSetErrorHandler(error_handler);
1834 g_xserver_be = (ImageByteOrder(g_display) == MSBFirst);
1835 screen_num = DefaultScreen(g_display);
1836 g_x_socket = ConnectionNumber(g_display);
1837 g_screen = ScreenOfDisplay(g_display, screen_num);
1838 g_depth = DefaultDepthOfScreen(g_screen);
1839
1840 if (!select_visual(screen_num))
1841 return False;
1842
1843 if (g_no_translate_image)
1844 {
1845 DEBUG(("Performance optimization possible: avoiding image translation (colour depth conversion).\n"));
1846 }
1847
1848 if (g_server_depth > g_bpp)
1849 {
1850 warning("Remote desktop colour depth %d higher than display colour depth %d.\n",
1851 g_server_depth, g_bpp);
1852 }
1853
1854 DEBUG(("RDP depth: %d, display depth: %d, display bpp: %d, X server BE: %d, host BE: %d\n",
1855 g_server_depth, g_depth, g_bpp, g_xserver_be, g_host_be));
1856
1857 if (!g_owncolmap)
1858 {
1859 g_xcolmap =
1860 XCreateColormap(g_display, RootWindowOfScreen(g_screen), g_visual,
1861 AllocNone);
1862 if (g_depth <= 8)
1863 warning("Display colour depth is %d bit: you may want to use -C for a private colourmap.\n", g_depth);
1864 }
1865
1866 if ((!g_ownbackstore) && (DoesBackingStore(g_screen) != Always))
1867 {
1868 warning("External BackingStore not available. Using internal.\n");
1869 g_ownbackstore = True;
1870 }
1871
1872 /*
1873 * Determine desktop size
1874 */
1875 if (g_fullscreen)
1876 {
1877 g_width = WidthOfScreen(g_screen);
1878 g_height = HeightOfScreen(g_screen);
1879 g_using_full_workarea = True;
1880 }
1881 else if (g_width < 0)
1882 {
1883 /* Percent of screen */
1884 if (-g_width >= 100)
1885 g_using_full_workarea = True;
1886 g_height = HeightOfScreen(g_screen) * (-g_width) / 100;
1887 g_width = WidthOfScreen(g_screen) * (-g_width) / 100;
1888 }
1889 else if (g_width == 0)
1890 {
1891 /* Fetch geometry from _NET_WORKAREA */
1892 uint32 x, y, cx, cy;
1893 if (get_current_workarea(&x, &y, &cx, &cy) == 0)
1894 {
1895 g_width = cx;
1896 g_height = cy;
1897 g_using_full_workarea = True;
1898 }
1899 else
1900 {
1901 warning("Failed to get workarea: probably your window manager does not support extended hints\n");
1902 g_width = WidthOfScreen(g_screen);
1903 g_height = HeightOfScreen(g_screen);
1904 }
1905 }
1906
1907 /* make sure width is a multiple of 4 */
1908 g_width = (g_width + 3) & ~3;
1909
1910 g_mod_map = XGetModifierMapping(g_display);
1911 xwin_refresh_pointer_map();
1912
1913 xkeymap_init();
1914
1915 if (g_enable_compose)
1916 g_IM = XOpenIM(g_display, NULL, NULL, NULL);
1917
1918 xclip_init();
1919 ewmh_init();
1920 if (g_seamless_rdp)
1921 {
1922 seamless_restack_test();
1923 seamless_init();
1924 }
1925
1926 DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", g_server_depth, g_bpp, g_depth));
1927
1928 return True;
1929 }
1930
1931 void
1932 ui_deinit(void)
1933 {
1934 while (g_seamless_windows)
1935 {
1936 XDestroyWindow(g_display, g_seamless_windows->wnd);
1937 sw_remove_window(g_seamless_windows);
1938 }
1939
1940 xclip_deinit();
1941
1942 if (g_IM != NULL)
1943 XCloseIM(g_IM);
1944
1945 if (g_null_cursor != NULL)
1946 ui_destroy_cursor(g_null_cursor);
1947
1948 XFreeModifiermap(g_mod_map);
1949
1950 if (g_ownbackstore)
1951 XFreePixmap(g_display, g_backstore);
1952
1953 XFreeGC(g_display, g_gc);
1954 XCloseDisplay(g_display);
1955 g_display = NULL;
1956 }
1957
1958
1959 static void
1960 get_window_attribs(XSetWindowAttributes * attribs)
1961 {
1962 attribs->background_pixel = BlackPixelOfScreen(g_screen);
1963 attribs->background_pixel = WhitePixelOfScreen(g_screen);
1964 attribs->border_pixel = WhitePixelOfScreen(g_screen);
1965 attribs->backing_store = g_ownbackstore ? NotUseful : Always;
1966 attribs->override_redirect = g_fullscreen;
1967 attribs->colormap = g_xcolmap;
1968 }
1969
1970 static void
1971 get_input_mask(long *input_mask)
1972 {
1973 *input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
1974 VisibilityChangeMask | FocusChangeMask | StructureNotifyMask;
1975
1976 if (g_sendmotion)
1977 *input_mask |= PointerMotionMask;
1978 if (g_ownbackstore)
1979 *input_mask |= ExposureMask;
1980 if (g_fullscreen || g_grab_keyboard)
1981 *input_mask |= EnterWindowMask;
1982 if (g_grab_keyboard)
1983 *input_mask |= LeaveWindowMask;
1984 }
1985
1986 RD_BOOL
1987 ui_create_window(void)
1988 {
1989 uint8 null_pointer_mask[1] = { 0x80 };
1990 uint8 null_pointer_data[24] = { 0x00 };
1991
1992 XSetWindowAttributes attribs;
1993 XClassHint *classhints;
1994 XSizeHints *sizehints;
1995 int wndwidth, wndheight;
1996 long input_mask, ic_input_mask;
1997 XEvent xevent;
1998
1999 wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;
2000 wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;
2001
2002 /* Handle -x-y portion of geometry string */
2003 if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
2004 g_xpos = WidthOfScreen(g_screen) + g_xpos - g_width;
2005 if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4)))
2006 g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height;
2007
2008 get_window_attribs(&attribs);
2009
2010 g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
2011 wndheight, 0, g_depth, InputOutput, g_visual,
2012 CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
2013 CWBorderPixel, &attribs);
2014
2015 if (g_gc == NULL)
2016 {
2017 g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
2018 ui_reset_clip();
2019 }
2020
2021 if (g_create_bitmap_gc == NULL)
2022 g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL);
2023
2024 if ((g_ownbackstore) && (g_backstore == 0))
2025 {
2026 g_backstore = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
2027
2028 /* clear to prevent rubbish being exposed at startup */
2029 XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
2030 XFillRectangle(g_display, g_backstore, g_gc, 0, 0, g_width, g_height);
2031 }
2032
2033 XStoreName(g_display, g_wnd, g_title);
2034 ewmh_set_wm_name(g_wnd, g_title);
2035
2036 if (g_hide_decorations)
2037 mwm_hide_decorations(g_wnd);
2038
2039 classhints = XAllocClassHint();
2040 if (classhints != NULL)
2041 {
2042 classhints->res_name = classhints->res_class = "rdesktop";
2043 XSetClassHint(g_display, g_wnd, classhints);
2044 XFree(classhints);
2045 }
2046
2047 sizehints = XAllocSizeHints();
2048 if (sizehints)
2049 {
2050 sizehints->flags = PMinSize | PMaxSize;
2051 if (g_pos)
2052 sizehints->flags |= PPosition;
2053 sizehints->min_width = sizehints->max_width = g_width;
2054 sizehints->min_height = sizehints->max_height = g_height;
2055 XSetWMNormalHints(g_display, g_wnd, sizehints);
2056 XFree(sizehints);
2057 }
2058
2059 if (g_embed_wnd)
2060 {
2061 XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);
2062 }
2063
2064 get_input_mask(&input_mask);
2065
2066 if (g_IM != NULL)
2067 {
2068 g_IC = XCreateIC(g_IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
2069 XNClientWindow, g_wnd, XNFocusWindow, g_wnd, NULL);
2070
2071 if ((g_IC != NULL)
2072 && (XGetICValues(g_IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
2073 input_mask |= ic_input_mask;
2074 }
2075
2076 XSelectInput(g_display, g_wnd, input_mask);
2077 XMapWindow(g_display, g_wnd);
2078
2079 /* wait for VisibilityNotify */
2080 do
2081 {
2082 XMaskEvent(g_display, VisibilityChangeMask, &xevent);
2083 }
2084 while (xevent.type != VisibilityNotify);
2085 g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
2086
2087 g_focused = False;
2088 g_mouse_in_wnd = False;
2089
2090 /* handle the WM_DELETE_WINDOW protocol */
2091 g_protocol_atom = XInternAtom(g_display, "WM_PROTOCOLS", True);
2092 g_kill_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", True);
2093 XSetWMProtocols(g_display, g_wnd, &g_kill_atom, 1);
2094
2095 /* create invisible 1x1 cursor to be used as null cursor */
2096 if (g_null_cursor == NULL)
2097 g_null_cursor = ui_create_cursor(0, 0, 1, 1, null_pointer_mask, null_pointer_data);
2098
2099 return True;
2100 }
2101
2102 void
2103 ui_resize_window()
2104 {
2105 XSizeHints *sizehints;
2106 Pixmap bs;
2107
2108 sizehints = XAllocSizeHints();
2109 if (sizehints)
2110 {
2111 sizehints->flags = PMinSize | PMaxSize;
2112 sizehints->min_width = sizehints->max_width = g_width;
2113 sizehints->min_height = sizehints->max_height = g_height;
2114 XSetWMNormalHints(g_display, g_wnd, sizehints);
2115 XFree(sizehints);
2116 }
2117
2118 if (!(g_fullscreen || g_embed_wnd))
2119 {
2120 XResizeWindow(g_display, g_wnd, g_width, g_height);
2121 }
2122
2123 /* create new backstore pixmap */
2124 if (g_backstore != 0)
2125 {
2126 bs = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
2127 XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
2128 XFillRectangle(g_display, bs, g_gc, 0, 0, g_width, g_height);
2129 XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, g_width, g_height, 0, 0);
2130 XFreePixmap(g_display, g_backstore);
2131 g_backstore = bs;
2132 }
2133 }
2134
2135 void
2136 ui_destroy_window(void)
2137 {
2138 if (g_IC != NULL)
2139 XDestroyIC(g_IC);
2140
2141 XDestroyWindow(g_display, g_wnd);
2142 }
2143
2144 void
2145 xwin_toggle_fullscreen(void)
2146 {
2147 Pixmap contents = 0;
2148
2149 if (g_seamless_active)
2150 /* Turn off SeamlessRDP mode */
2151 ui_seamless_toggle();
2152
2153 if (!g_ownbackstore)
2154 {
2155 /* need to save contents of window */
2156 contents = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
2157 XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, g_width, g_height, 0, 0);
2158 }
2159
2160 ui_destroy_window();
2161 g_fullscreen = !g_fullscreen;
2162 ui_create_window();
2163
2164 XDefineCursor(g_display, g_wnd, g_current_cursor);
2165
2166 if (!g_ownbackstore)
2167 {
2168 XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, g_width, g_height, 0, 0);
2169 XFreePixmap(g_display, contents);
2170 }
2171 }
2172
2173 static void
2174 handle_button_event(XEvent xevent, RD_BOOL down)
2175 {
2176 uint16 button, flags = 0;
2177 g_last_gesturetime = xevent.xbutton.time;
2178 /* Reverse the pointer button mapping, e.g. in the case of
2179 "left-handed mouse mode"; the RDP session expects to
2180 receive physical buttons (true in mstsc as well) and
2181 logical button behavior depends on the remote desktop's own
2182 mouse settings */
2183 xevent.xbutton.button = g_pointer_log_to_phys_map[xevent.xbutton.button - 1];
2184 button = xkeymap_translate_button(xevent.xbutton.button);
2185 if (button == 0)
2186 return;
2187
2188 if (down)
2189 flags = MOUSE_FLAG_DOWN;
2190
2191 /* Stop moving window when button is released, regardless of cursor position */
2192 if (g_moving_wnd && (xevent.type == ButtonRelease))
2193 g_moving_wnd = False;
2194
2195 /* If win_button_size is nonzero, enable single app mode */
2196 if (xevent.xbutton.y < g_win_button_size)
2197 {
2198 /* Check from right to left: */
2199 if (xevent.xbutton.x >= g_width - g_win_button_size)
2200 {
2201 /* The close button, continue */
2202 ;
2203 }
2204 else if (xevent.xbutton.x >= g_width - g_win_button_size * 2)
2205 {
2206 /* The maximize/restore button. Do not send to
2207 server. It might be a good idea to change the
2208 cursor or give some other visible indication
2209 that rdesktop inhibited this click */
2210 if (xevent.type == ButtonPress)
2211 return;
2212 }
2213 else if (xevent.xbutton.x >= g_width - g_win_button_size * 3)
2214 {
2215 /* The minimize button. Iconify window. */
2216 if (xevent.type == ButtonRelease)
2217 {
2218 /* Release the mouse button outside the minimize button, to prevent the
2219 actual minimazation to happen */
2220 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, button, 1, 1);
2221 XIconifyWindow(g_display, g_wnd, DefaultScreen(g_display));
2222 return;
2223 }
2224 }
2225 else if (xevent.xbutton.x <= g_win_button_size)
2226 {
2227 /* The system menu. Ignore. */
2228 if (xevent.type == ButtonPress)
2229 return;
2230 }
2231 else
2232 {
2233 /* The title bar. */
2234 if (xevent.type == ButtonPress)
2235 {
2236 if (!g_fullscreen && g_hide_decorations && !g_using_full_workarea)
2237 {
2238 g_moving_wnd = True;
2239 g_move_x_offset = xevent.xbutton.x;
2240 g_move_y_offset = xevent.xbutton.y;
2241 }
2242 return;
2243 }
2244 }
2245 }
2246
2247 if (xevent.xmotion.window == g_wnd)
2248 {
2249 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
2250 flags | button, xevent.xbutton.x, xevent.xbutton.y);
2251 }
2252 else
2253 {
2254 /* SeamlessRDP */
2255 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
2256 flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root);
2257 }
2258 }
2259
2260
2261 /* Process events in Xlib queue
2262 Returns 0 after user quit, 1 otherwise */
2263 static int
2264 xwin_process_events(void)
2265 {
2266 XEvent xevent;
2267 KeySym keysym;
2268 uint32 ev_time;
2269 char str[256];
2270 Status status;
2271 int events = 0;
2272 seamless_window *sw;
2273
2274 while ((XPending(g_display) > 0) && events++ < 20)
2275 {
2276 XNextEvent(g_display, &xevent);
2277
2278 if ((g_IC != NULL) && (XFilterEvent(&xevent, None) == True))
2279 {
2280 DEBUG_KBD(("Filtering event\n"));
2281 continue;
2282 }
2283
2284 switch (xevent.type)
2285 {
2286 case VisibilityNotify:
2287 if (xevent.xvisibility.window == g_wnd)
2288 g_Unobscured =
2289 xevent.xvisibility.state == VisibilityUnobscured;
2290
2291 break;
2292 case ClientMessage:
2293 /* the window manager told us to quit */
2294 if ((xevent.xclient.message_type == g_protocol_atom)
2295 && ((Atom) xevent.xclient.data.l[0] == g_kill_atom))
2296 {
2297 /* When killing a seamless window, close the window on the
2298 serverside instead of terminating rdesktop */
2299 sw = sw_get_window_by_wnd(xevent.xclient.window);
2300 if (!sw)
2301 /* Otherwise, quit */
2302 return 0;
2303 /* send seamless destroy process message */
2304 seamless_send_destroy(sw->id);
2305 }
2306 break;
2307
2308 case KeyPress:
2309 g_last_gesturetime = xevent.xkey.time;
2310 if (g_IC != NULL)
2311 /* Multi_key compatible version */
2312 {
2313 XmbLookupString(g_IC,
2314 &xevent.xkey, str, sizeof(str), &keysym,
2315 &status);
2316 if (!((status == XLookupKeySym) || (status == XLookupBoth)))
2317 {
2318 error("XmbLookupString failed with status 0x%x\n",
2319 status);
2320 break;
2321 }
2322 }
2323 else
2324 {
2325 /* Plain old XLookupString */
2326 DEBUG_KBD(("\nNo input context, using XLookupString\n"));
2327 XLookupString((XKeyEvent *) & xevent,
2328 str, sizeof(str), &keysym, NULL);
2329 }
2330
2331 DEBUG_KBD(("KeyPress for keysym (0x%lx, %s)\n", keysym,
2332 get_ksname(keysym)));
2333
2334 ev_time = time(NULL);
2335 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
2336 break;
2337
2338 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2339 ev_time, True, 0);
2340 break;
2341
2342 case KeyRelease:
2343 g_last_gesturetime = xevent.xkey.time;
2344 XLookupString((XKeyEvent *) & xevent, str,
2345 sizeof(str), &keysym, NULL);
2346
2347 DEBUG_KBD(("\nKeyRelease for keysym (0x%lx, %s)\n", keysym,
2348 get_ksname(keysym)));
2349
2350 ev_time = time(NULL);
2351 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
2352 break;
2353
2354 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2355 ev_time, False, 0);
2356 break;
2357
2358 case ButtonPress:
2359 handle_button_event(xevent, True);
2360 break;
2361
2362 case ButtonRelease:
2363 handle_button_event(xevent, False);
2364 break;
2365
2366 case MotionNotify:
2367 if (g_moving_wnd)
2368 {
2369 XMoveWindow(g_display, g_wnd,
2370 xevent.xmotion.x_root - g_move_x_offset,
2371 xevent.xmotion.y_root - g_move_y_offset);
2372 break;
2373 }
2374
2375 if (g_fullscreen && !g_focused)
2376 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
2377 CurrentTime);
2378
2379 if (xevent.xmotion.window == g_wnd)
2380 {
2381 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2382 xevent.xmotion.x, xevent.xmotion.y);
2383 }
2384 else
2385 {
2386 /* SeamlessRDP */
2387 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2388 xevent.xmotion.x_root,
2389 xevent.xmotion.y_root);
2390 }
2391 break;
2392
2393 case FocusIn:
2394 if (xevent.xfocus.mode == NotifyGrab)
2395 break;
2396 g_focused = True;
2397 reset_modifier_keys();
2398 if (g_grab_keyboard && g_mouse_in_wnd)
2399 XGrabKeyboard(g_display, g_wnd, True,
2400 GrabModeAsync, GrabModeAsync, CurrentTime);
2401
2402 sw = sw_get_window_by_wnd(xevent.xfocus.window);
2403 if (!sw)
2404 break;
2405
2406 /* Menu windows are real X11 windows,
2407 with focus. When such a window is
2408 destroyed, focus is reverted to the
2409 main application window, which
2410 would cause us to send FOCUS. This
2411 breaks window switching in, say,
2412 Seamonkey. We shouldn't need to
2413 send FOCUS: Windows should also
2414 revert focus to some other window
2415 when the menu window is
2416 destroyed. So, we only send FOCUS
2417 if the previous focus window still
2418 exists. */
2419 if (sw->id != g_seamless_focused)
2420 {
2421
2422 if (sw_window_exists(g_seamless_focused))
2423 seamless_send_focus(sw->id, 0);
2424 g_seamless_focused = sw->id;
2425 }
2426 break;
2427
2428 case FocusOut:
2429 if (xevent.xfocus.mode == NotifyUngrab)
2430 break;
2431 g_focused = False;
2432 if (xevent.xfocus.mode == NotifyWhileGrabbed)
2433 XUngrabKeyboard(g_display, CurrentTime);
2434 break;
2435
2436 case EnterNotify:
2437 /* we only register for this event when in fullscreen mode */
2438 /* or grab_keyboard */
2439 g_mouse_in_wnd = True;
2440 if (g_fullscreen)
2441 {
2442 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
2443 CurrentTime);
2444 break;
2445 }
2446 if (g_focused)
2447 XGrabKeyboard(g_display, g_wnd, True,
2448 GrabModeAsync, GrabModeAsync, CurrentTime);
2449 break;
2450
2451 case LeaveNotify:
2452 /* we only register for this event when grab_keyboard */
2453 g_mouse_in_wnd = False;
2454 XUngrabKeyboard(g_display, CurrentTime);
2455 break;
2456
2457 case Expose:
2458 if (xevent.xexpose.window == g_wnd)
2459 {
2460 XCopyArea(g_display, g_backstore, xevent.xexpose.window,
2461 g_gc,
2462 xevent.xexpose.x, xevent.xexpose.y,
2463 xevent.xexpose.width, xevent.xexpose.height,
2464 xevent.xexpose.x, xevent.xexpose.y);
2465 }
2466 else
2467 {
2468 sw = sw_get_window_by_wnd(xevent.xexpose.window);
2469 if (!sw)
2470 break;
2471 XCopyArea(g_display, g_backstore,
2472 xevent.xexpose.window, g_gc,
2473 xevent.xexpose.x + sw->xoffset,
2474 xevent.xexpose.y + sw->yoffset,
2475 xevent.xexpose.width,
2476 xevent.xexpose.height, xevent.xexpose.x,
2477 xevent.xexpose.y);
2478 }
2479
2480 break;
2481
2482 case MappingNotify:
2483 /* Refresh keyboard mapping if it has changed. This is important for
2484 Xvnc, since it allocates keycodes dynamically */
2485 if (xevent.xmapping.request == MappingKeyboard
2486 || xevent.xmapping.request == MappingModifier)
2487 XRefreshKeyboardMapping(&xevent.xmapping);
2488
2489 if (xevent.xmapping.request == MappingModifier)
2490 {
2491 XFreeModifiermap(g_mod_map);
2492 g_mod_map = XGetModifierMapping(g_display);
2493 }
2494
2495 if (xevent.xmapping.request == MappingPointer)
2496 {
2497 xwin_refresh_pointer_map();
2498 }
2499
2500 break;
2501
2502 /* clipboard stuff */
2503 case SelectionNotify:
2504 xclip_handle_SelectionNotify(&xevent.xselection);
2505 break;
2506 case SelectionRequest:
2507 xclip_handle_SelectionRequest(&xevent.xselectionrequest);
2508 break;
2509 case SelectionClear:
2510 xclip_handle_SelectionClear();
2511 break;
2512 case PropertyNotify:
2513 xclip_handle_PropertyNotify(&xevent.xproperty);
2514 if (xevent.xproperty.window == g_wnd)
2515 break;
2516 if (xevent.xproperty.window == DefaultRootWindow(g_display))
2517 break;
2518
2519 /* seamless */
2520 sw = sw_get_window_by_wnd(xevent.xproperty.window);
2521 if (!sw)
2522 break;
2523
2524 if ((xevent.xproperty.atom == g_net_wm_state_atom)
2525 && (xevent.xproperty.state == PropertyNewValue))
2526 {
2527 sw->state = ewmh_get_window_state(sw->wnd);
2528 seamless_send_state(sw->id, sw->state, 0);
2529 }
2530
2531 if ((xevent.xproperty.atom == g_net_wm_desktop_atom)
2532 && (xevent.xproperty.state == PropertyNewValue))
2533 {
2534 sw->desktop = ewmh_get_window_desktop(sw->wnd);
2535 sw_all_to_desktop(sw->wnd, sw->desktop);
2536 }
2537
2538 break;
2539 case MapNotify:
2540 if (!g_seamless_active)
2541 rdp_send_client_window_status(1);
2542 break;
2543 case UnmapNotify:
2544 if (!g_seamless_active)
2545 rdp_send_client_window_status(0);
2546 break;
2547 case ConfigureNotify:
2548 if (!g_seamless_active)
2549 break;
2550
2551 sw = sw_get_window_by_wnd(xevent.xconfigure.window);
2552 if (!sw)
2553 break;
2554
2555 gettimeofday(sw->position_timer, NULL);
2556 if (sw->position_timer->tv_usec + SEAMLESSRDP_POSITION_TIMER >=
2557 1000000)
2558 {
2559 sw->position_timer->tv_usec +=
2560 SEAMLESSRDP_POSITION_TIMER - 1000000;
2561 sw->position_timer->tv_sec += 1;
2562 }
2563 else
2564 {
2565 sw->position_timer->tv_usec += SEAMLESSRDP_POSITION_TIMER;
2566 }
2567
2568 sw_handle_restack(sw);
2569 break;
2570 }
2571 }
2572 /* Keep going */
2573 return 1;
2574 }
2575
2576 /* Returns 0 after user quit, 1 otherwise */
2577 int
2578 ui_select(int rdp_socket)
2579 {
2580 int n;
2581 fd_set rfds, wfds;
2582 struct timeval tv;
2583 RD_BOOL s_timeout = False;
2584
2585 while (True)
2586 {
2587 n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
2588 /* Process any events already waiting */
2589 if (!xwin_process_events())
2590 /* User quit */
2591 return 0;
2592
2593 if (g_seamless_active)
2594 sw_check_timers();
2595
2596 FD_ZERO(&rfds);
2597 FD_ZERO(&wfds);
2598 FD_SET(rdp_socket, &rfds);
2599 FD_SET(g_x_socket, &rfds);
2600
2601 /* default timeout */
2602 tv.tv_sec = 60;
2603 tv.tv_usec = 0;
2604
2605 #ifdef WITH_RDPSND
2606 rdpsnd_add_fds(&n, &rfds, &wfds, &tv);
2607 #endif
2608
2609 /* add redirection handles */
2610 rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
2611 seamless_select_timeout(&tv);
2612
2613 n++;
2614
2615 switch (select(n, &rfds, &wfds, NULL, &tv))
2616 {
2617 case -1:
2618 error("select: %s\n", strerror(errno));
2619
2620 case 0:
2621 #ifdef WITH_RDPSND
2622 rdpsnd_check_fds(&rfds, &wfds);
2623 #endif
2624
2625 /* Abort serial read calls */
2626 if (s_timeout)
2627 rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True);
2628 continue;
2629 }
2630
2631 #ifdef WITH_RDPSND
2632 rdpsnd_check_fds(&rfds, &wfds);
2633 #endif
2634
2635 rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
2636
2637 if (FD_ISSET(rdp_socket, &rfds))
2638 return 1;
2639
2640 }
2641 }
2642
2643 void
2644 ui_move_pointer(int x, int y)
2645 {
2646 XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
2647 }
2648
2649 RD_HBITMAP
2650 ui_create_bitmap(int width, int height, uint8 * data)
2651 {
2652 XImage *image;
2653 Pixmap bitmap;
2654 uint8 *tdata;
2655 int bitmap_pad;
2656
2657 if (g_server_depth == 8)
2658 {
2659 bitmap_pad = 8;
2660 }
2661 else
2662 {
2663 bitmap_pad = g_bpp;
2664
2665 if (g_bpp == 24)
2666 bitmap_pad = 32;
2667 }
2668
2669 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2670 bitmap = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
2671 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2672 (char *) tdata, width, height, bitmap_pad, 0);
2673
2674 XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height);
2675
2676 XFree(image);
2677 if (tdata != data)
2678 xfree(tdata);
2679 return (RD_HBITMAP) bitmap;
2680 }
2681
2682 void
2683 ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
2684 {
2685 XImage *image;
2686 uint8 *tdata;
2687 int bitmap_pad;
2688
2689 if (g_server_depth == 8)
2690 {
2691 bitmap_pad = 8;
2692 }
2693 else
2694 {
2695 bitmap_pad = g_bpp;
2696
2697 if (g_bpp == 24)
2698 bitmap_pad = 32;
2699 }
2700
2701 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2702 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2703 (char *) tdata, width, height, bitmap_pad, 0);
2704
2705 if (g_ownbackstore)
2706 {
2707 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2708 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2709 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2710 (g_display, g_backstore, sw->wnd, g_gc, x, y, cx, cy,
2711 x - sw->xoffset, y - sw->yoffset));
2712 }
2713 else
2714 {
2715 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2716 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2717 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
2718 x - sw->xoffset, y - sw->yoffset));
2719 }
2720
2721 XFree(image);
2722 if (tdata != data)
2723 xfree(tdata);
2724 }
2725
2726 void
2727 ui_destroy_bitmap(RD_HBITMAP bmp)
2728 {
2729 XFreePixmap(g_display, (Pixmap) bmp);
2730 }
2731
2732 RD_HGLYPH
2733 ui_create_glyph(int width, int height, uint8 * data)
2734 {
2735 XImage *image;
2736 Pixmap bitmap;
2737 int scanline;
2738
2739 scanline = (width + 7) / 8;
2740
2741 bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1);
2742 if (g_create_glyph_gc == 0)
2743 g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL);
2744
2745 image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data,
2746 width, height, 8, scanline);
2747 image->byte_order = MSBFirst;
2748 image->bitmap_bit_order = MSBFirst;
2749 XInitImage(image);
2750
2751 XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
2752
2753 XFree(image);
2754 return (RD_HGLYPH) bitmap;
2755 }
2756
2757 void
2758 ui_destroy_glyph(RD_HGLYPH glyph)
2759 {
2760 XFreePixmap(g_display, (Pixmap) glyph);
2761 }
2762
2763 RD_HCURSOR
2764 ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
2765 uint8 * andmask, uint8 * xormask)
2766 {
2767 RD_HGLYPH maskglyph, cursorglyph;
2768 XColor bg, fg;
2769 Cursor xcursor;
2770 uint8 *cursor, *pcursor;
2771 uint8 *mask, *pmask;
2772 uint8 nextbit;
2773 int scanline, offset;
2774 int i, j;
2775
2776 scanline = (width + 7) / 8;
2777 offset = scanline * height;
2778
2779 cursor = (uint8 *) xmalloc(offset);
2780 memset(cursor, 0, offset);
2781
2782 mask = (uint8 *) xmalloc(offset);
2783 memset(mask, 0, offset);
2784
2785 /* approximate AND and XOR masks with a monochrome X pointer */
2786 for (i = 0; i < height; i++)
2787 {
2788 offset -= scanline;
2789 pcursor = &cursor[offset];
2790 pmask = &mask[offset];
2791
2792 for (j = 0; j < scanline; j++)
2793 {
2794 for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
2795 {
2796 if (xormask[0] || xormask[1] || xormask[2])
2797 {
2798 *pcursor |= (~(*andmask) & nextbit);
2799 *pmask |= nextbit;
2800 }
2801 else
2802 {
2803 *pcursor |= ((*andmask) & nextbit);
2804 *pmask |= (~(*andmask) & nextbit);
2805 }
2806
2807 xormask += 3;
2808 }
2809
2810 andmask++;
2811 pcursor++;
2812 pmask++;
2813 }
2814 }
2815
2816 fg.red = fg.blue = fg.green = 0xffff;
2817 bg.red = bg.blue = bg.green = 0x0000;
2818 fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
2819
2820 cursorglyph = ui_create_glyph(width, height, cursor);
2821 maskglyph = ui_create_glyph(width, height, mask);
2822
2823 xcursor =
2824 XCreatePixmapCursor(g_display, (Pixmap) cursorglyph,
2825 (Pixmap) maskglyph, &fg, &bg, x, y);
2826
2827 ui_destroy_glyph(maskglyph);
2828 ui_destroy_glyph(cursorglyph);
2829 xfree(mask);
2830 xfree(cursor);
2831 return (RD_HCURSOR) xcursor;
2832 }
2833
2834 void
2835 ui_set_cursor(RD_HCURSOR cursor)
2836 {
2837 g_current_cursor = (Cursor) cursor;
2838 XDefineCursor(g_display, g_wnd, g_current_cursor);
2839 ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor));
2840 }
2841
2842 void
2843 ui_destroy_cursor(RD_HCURSOR cursor)
2844 {
2845 XFreeCursor(g_display, (Cursor) cursor);
2846 }
2847
2848 void
2849 ui_set_null_cursor(void)
2850 {
2851 ui_set_cursor(g_null_cursor);
2852 }
2853
2854 #define MAKE_XCOLOR(xc,c) \
2855 (xc)->red = ((c)->red << 8) | (c)->red; \
2856 (xc)->green = ((c)->green << 8) | (c)->green; \
2857 (xc)->blue = ((c)->blue << 8) | (c)->blue; \
2858 (xc)->flags = DoRed | DoGreen | DoBlue;
2859
2860
2861 RD_HCOLOURMAP
2862 ui_create_colourmap(COLOURMAP * colours)
2863 {
2864 COLOURENTRY *entry;
2865 int i, ncolours = colours->ncolours;
2866 if (!g_owncolmap)
2867 {
2868 uint32 *map = (uint32 *) xmalloc(sizeof(*g_colmap) * ncolours);
2869 XColor xentry;
2870 XColor xc_cache[256];
2871 uint32 colour;
2872 int colLookup = 256;
2873 for (i = 0; i < ncolours; i++)
2874 {
2875 entry = &colours->colours[i];
2876 MAKE_XCOLOR(&xentry, entry);
2877
2878 if (XAllocColor(g_display, g_xcolmap, &xentry) == 0)
2879 {
2880 /* Allocation failed, find closest match. */
2881 int j = 256;
2882 int nMinDist = 3 * 256 * 256;
2883 long nDist = nMinDist;
2884
2885 /* only get the colors once */
2886 while (colLookup--)
2887 {
2888 xc_cache[colLookup].pixel = colLookup;
2889 xc_cache[colLookup].red = xc_cache[colLookup].green =
2890 xc_cache[colLookup].blue = 0;
2891 xc_cache[colLookup].flags = 0;
2892 XQueryColor(g_display,
2893 DefaultColormap(g_display,
2894 DefaultScreen(g_display)),
2895 &xc_cache[colLookup]);
2896 }
2897 colLookup = 0;
2898
2899 /* approximate the pixel */
2900 while (j--)
2901 {
2902 if (xc_cache[j].flags)
2903 {
2904 nDist = ((long) (xc_cache[j].red >> 8) -
2905 (long) (xentry.red >> 8)) *
2906 ((long) (xc_cache[j].red >> 8) -
2907 (long) (xentry.red >> 8)) +
2908 ((long) (xc_cache[j].green >> 8) -
2909 (long) (xentry.green >> 8)) *
2910 ((long) (xc_cache[j].green >> 8) -
2911 (long) (xentry.green >> 8)) +
2912 ((long) (xc_cache[j].blue >> 8) -
2913 (long) (xentry.blue >> 8)) *
2914 ((long) (xc_cache[j].blue >> 8) -
2915 (long) (xentry.blue >> 8));
2916 }
2917 if (nDist < nMinDist)
2918 {
2919 nMinDist = nDist;
2920 xentry.pixel = j;
2921 }
2922 }
2923 }
2924 colour = xentry.pixel;
2925
2926 /* update our cache */
2927 if (xentry.pixel < 256)
2928 {
2929 xc_cache[xentry.pixel].red = xentry.red;
2930 xc_cache[xentry.pixel].green = xentry.green;
2931 xc_cache[xentry.pixel].blue = xentry.blue;
2932
2933 }
2934
2935 map[i] = colour;
2936 }
2937 return map;
2938 }
2939 else
2940 {
2941 XColor *xcolours, *xentry;
2942 Colormap map;
2943
2944 xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
2945 for (i = 0; i < ncolours; i++)
2946 {
2947 entry = &colours->colours[i];
2948 xentry = &xcolours[i];
2949 xentry->pixel = i;
2950 MAKE_XCOLOR(xentry, entry);
2951 }
2952
2953 map = XCreateColormap(g_display, g_wnd, g_visual, AllocAll);
2954 XStoreColors(g_display, map, xcolours, ncolours);
2955
2956 xfree(xcolours);
2957 return (RD_HCOLOURMAP) map;
2958 }
2959 }
2960
2961 void
2962 ui_destroy_colourmap(RD_HCOLOURMAP map)
2963 {
2964 if (!g_owncolmap)
2965 xfree(map);
2966 else
2967 XFreeColormap(g_display, (Colormap) map);
2968 }
2969
2970 void
2971 ui_set_colourmap(RD_HCOLOURMAP map)
2972 {
2973 if (!g_owncolmap)
2974 {
2975 if (g_colmap)
2976 xfree(g_colmap);
2977
2978 g_colmap = (uint32 *) map;
2979 }
2980 else
2981 {
2982 XSetWindowColormap(g_display, g_wnd, (Colormap) map);
2983 ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
2984 }
2985 }
2986
2987 void
2988 ui_set_clip(int x, int y, int cx, int cy)
2989 {
2990 g_clip_rectangle.x = x;
2991 g_clip_rectangle.y = y;
2992 g_clip_rectangle.width = cx;
2993 g_clip_rectangle.height = cy;
2994 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
2995 }
2996
2997 void
2998 ui_reset_clip(void)
2999 {
3000 g_clip_rectangle.x = 0;
3001 g_clip_rectangle.y = 0;
3002 g_clip_rectangle.width = g_width;
3003 g_clip_rectangle.height = g_height;
3004 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
3005 }
3006
3007 void
3008 ui_bell(void)
3009 {
3010 XBell(g_display, 0);
3011 }
3012
3013 void
3014 ui_destblt(uint8 opcode,
3015 /* dest */ int x, int y, int cx, int cy)
3016 {
3017 SET_FUNCTION(opcode);
3018 FILL_RECTANGLE(x, y, cx, cy);
3019 RESET_FUNCTION(opcode);
3020 }
3021
3022 static uint8 hatch_patterns[] = {
3023 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0 - bsHorizontal */
3024 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /* 1 - bsVertical */
3025 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, /* 2 - bsFDiagonal */
3026 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /* 3 - bsBDiagonal */
3027 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, /* 4 - bsCross */
3028 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 /* 5 - bsDiagCross */
3029 };
3030
3031 void
3032 ui_patblt(uint8 opcode,
3033 /* dest */ int x, int y, int cx, int cy,
3034 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
3035 {
3036 Pixmap fill;
3037 uint8 i, ipattern[8];
3038
3039 SET_FUNCTION(opcode);
3040
3041 switch (brush->style)
3042 {
3043 case 0: /* Solid */
3044 SET_FOREGROUND(fgcolour);
3045 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3046 break;
3047
3048 case 2: /* Hatch */
3049 fill = (Pixmap) ui_create_glyph(8, 8,
3050 hatch_patterns + brush->pattern[0] * 8);
3051 SET_FOREGROUND(fgcolour);
3052 SET_BACKGROUND(bgcolour);
3053 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3054 XSetStipple(g_display, g_gc, fill);
3055 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3056 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3057 XSetFillStyle(g_display, g_gc, FillSolid);
3058 XSetTSOrigin(g_display, g_gc, 0, 0);
3059 ui_destroy_glyph((RD_HGLYPH) fill);
3060 break;
3061
3062 case 3: /* Pattern */
3063 for (i = 0; i != 8; i++)
3064 ipattern[7 - i] = brush->pattern[i];
3065 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3066 SET_FOREGROUND(bgcolour);
3067 SET_BACKGROUND(fgcolour);
3068 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3069 XSetStipple(g_display, g_gc, fill);
3070 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3071 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3072 XSetFillStyle(g_display, g_gc, FillSolid);
3073 XSetTSOrigin(g_display, g_gc, 0, 0);
3074 ui_destroy_glyph((RD_HGLYPH) fill);
3075 break;
3076
3077 default:
3078 unimpl("brush %d\n", brush->style);
3079 }
3080
3081 RESET_FUNCTION(opcode);
3082
3083 if (g_ownbackstore)
3084 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3085 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3086 (g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc,
3087 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3088 }
3089
3090 void
3091 ui_screenblt(uint8 opcode,
3092 /* dest */ int x, int y, int cx, int cy,
3093 /* src */ int srcx, int srcy)
3094 {
3095 SET_FUNCTION(opcode);
3096 if (g_ownbackstore)
3097 {
3098 XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
3099 g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3100 XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
3101 }
3102 else
3103 {
3104 XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3105 }
3106
3107 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3108 (g_display, g_ownbackstore ? g_backstore : g_wnd,
3109 sw->wnd, g_gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3110
3111 RESET_FUNCTION(opcode);
3112 }
3113
3114 void
3115 ui_memblt(uint8 opcode,
3116 /* dest */ int x, int y, int cx, int cy,
3117 /* src */ RD_HBITMAP src, int srcx, int srcy)
3118 {
3119 SET_FUNCTION(opcode);
3120 XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
3121 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3122 (g_display, (Pixmap) src, sw->wnd, g_gc,
3123 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
3124 if (g_ownbackstore)
3125 XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
3126 RESET_FUNCTION(opcode);
3127 }
3128
3129 void
3130 ui_triblt(uint8 opcode,
3131 /* dest */ int x, int y, int cx, int cy,
3132 /* src */ RD_HBITMAP src, int srcx, int srcy,
3133 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
3134 {
3135 /* This is potentially difficult to do in general. Until someone
3136 comes up with a more efficient way of doing it I am using cases. */
3137
3138 switch (opcode)
3139 {
3140 case 0x69: /* PDSxxn */
3141 ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
3142 ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3143 break;
3144
3145 case 0xb8: /* PSDPxax */
3146 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3147 ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
3148 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
3149 break;
3150
3151 case 0xc0: /* PSa */
3152 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
3153 ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
3154 break;
3155
3156 default:
3157 unimpl("triblt 0x%x\n", opcode);
3158 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
3159 }
3160 }
3161
3162 void
3163 ui_line(uint8 opcode,
3164 /* dest */ int startx, int starty, int endx, int endy,
3165 /* pen */ PEN * pen)
3166 {
3167 SET_FUNCTION(opcode);
3168 SET_FOREGROUND(pen->colour);
3169 XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
3170 ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc,
3171 startx - sw->xoffset, starty - sw->yoffset,
3172 endx - sw->xoffset, endy - sw->yoffset));
3173 if (g_ownbackstore)
3174 XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
3175 RESET_FUNCTION(opcode);
3176 }
3177
3178 void
3179 ui_rect(
3180 /* dest */ int x, int y, int cx, int cy,
3181 /* brush */ int colour)
3182 {
3183 SET_FOREGROUND(colour);
3184 FILL_RECTANGLE(x, y, cx, cy);
3185 }
3186
3187 void
3188 ui_polygon(uint8 opcode,
3189 /* mode */ uint8 fillmode,
3190 /* dest */ RD_POINT * point, int npoints,
3191 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
3192 {
3193 uint8 style, i, ipattern[8];
3194 Pixmap fill;
3195
3196 SET_FUNCTION(opcode);
3197
3198 switch (fillmode)
3199 {
3200 case ALTERNATE:
3201 XSetFillRule(g_display, g_gc, EvenOddRule);
3202 break;
3203 case WINDING:
3204 XSetFillRule(g_display, g_gc, WindingRule);
3205 break;
3206 default:
3207 unimpl("fill mode %d\n", fillmode);
3208 }
3209
3210 if (brush)
3211 style = brush->style;
3212 else
3213 style = 0;
3214
3215 switch (style)
3216 {
3217 case 0: /* Solid */
3218 SET_FOREGROUND(fgcolour);
3219 FILL_POLYGON((XPoint *) point, npoints);
3220 break;
3221
3222 case 2: /* Hatch */
3223 fill = (Pixmap) ui_create_glyph(8, 8,
3224 hatch_patterns + brush->pattern[0] * 8);
3225 SET_FOREGROUND(fgcolour);
3226 SET_BACKGROUND(bgcolour);
3227 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3228 XSetStipple(g_display, g_gc, fill);
3229 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3230 FILL_POLYGON((XPoint *) point, npoints);
3231 XSetFillStyle(g_display, g_gc, FillSolid);
3232 XSetTSOrigin(g_display, g_gc, 0, 0);
3233 ui_destroy_glyph((RD_HGLYPH) fill);
3234 break;
3235
3236 case 3: /* Pattern */
3237 for (i = 0; i != 8; i++)
3238 ipattern[7 - i] = brush->pattern[i];
3239 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3240 SET_FOREGROUND(bgcolour);
3241 SET_BACKGROUND(fgcolour);
3242 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3243 XSetStipple(g_display, g_gc, fill);
3244 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3245 FILL_POLYGON((XPoint *) point, npoints);
3246 XSetFillStyle(g_display, g_gc, FillSolid);
3247 XSetTSOrigin(g_display, g_gc, 0, 0);
3248 ui_destroy_glyph((RD_HGLYPH) fill);
3249 break;
3250
3251 default:
3252 unimpl("brush %d\n", brush->style);
3253 }
3254
3255 RESET_FUNCTION(opcode);
3256 }
3257
3258 void
3259 ui_polyline(uint8 opcode,
3260 /* dest */ RD_POINT * points, int npoints,
3261 /* pen */ PEN * pen)
3262 {
3263 /* TODO: set join style */
3264 SET_FUNCTION(opcode);
3265 SET_FOREGROUND(pen->colour);
3266 XDrawLines(g_display, g_wnd, g_gc, (XPoint *) points, npoints, CoordModePrevious);
3267 if (g_ownbackstore)
3268 XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
3269 CoordModePrevious);
3270
3271 ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
3272 (sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
3273
3274 RESET_FUNCTION(opcode);
3275 }
3276
3277 void
3278 ui_ellipse(uint8 opcode,
3279 /* mode */ uint8 fillmode,
3280 /* dest */ int x, int y, int cx, int cy,
3281 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
3282 {
3283 uint8 style, i, ipattern[8];
3284 Pixmap fill;
3285
3286 SET_FUNCTION(opcode);
3287
3288 if (brush)
3289 style = brush->style;
3290 else
3291 style = 0;
3292
3293 switch (style)
3294 {
3295 case 0: /* Solid */
3296 SET_FOREGROUND(fgcolour);
3297 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3298 break;
3299
3300 case 2: /* Hatch */
3301 fill = (Pixmap) ui_create_glyph(8, 8,
3302 hatch_patterns + brush->pattern[0] * 8);
3303 SET_FOREGROUND(fgcolour);
3304 SET_BACKGROUND(bgcolour);
3305 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3306 XSetStipple(g_display, g_gc, fill);
3307 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3308 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3309 XSetFillStyle(g_display, g_gc, FillSolid);
3310 XSetTSOrigin(g_display, g_gc, 0, 0);
3311 ui_destroy_glyph((RD_HGLYPH) fill);
3312 break;
3313
3314 case 3: /* Pattern */
3315 for (i = 0; i != 8; i++)
3316 ipattern[7 - i] = brush->pattern[i];
3317 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3318 SET_FOREGROUND(bgcolour);
3319 SET_BACKGROUND(fgcolour);
3320 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3321 XSetStipple(g_display, g_gc, fill);
3322 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3323 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3324 XSetFillStyle(g_display, g_gc, FillSolid);
3325 XSetTSOrigin(g_display, g_gc, 0, 0);
3326 ui_destroy_glyph((RD_HGLYPH) fill);
3327 break;
3328
3329 default:
3330 unimpl("brush %d\n", brush->style);
3331 }
3332
3333 RESET_FUNCTION(opcode);
3334 }
3335
3336 /* warning, this function only draws on wnd or backstore, not both */
3337 void
3338 ui_draw_glyph(int mixmode,
3339 /* dest */ int x, int y, int cx, int cy,
3340 /* src */ RD_HGLYPH glyph, int srcx, int srcy,
3341 int bgcolour, int fgcolour)
3342 {
3343 SET_FOREGROUND(fgcolour);
3344 SET_BACKGROUND(bgcolour);
3345
3346 XSetFillStyle(g_display, g_gc,
3347 (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
3348 XSetStipple(g_display, g_gc, (Pixmap) glyph);
3349 XSetTSOrigin(g_display, g_gc, x, y);
3350
3351 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3352
3353 XSetFillStyle(g_display, g_gc, FillSolid);
3354 }
3355
3356 #define DO_GLYPH(ttext,idx) \
3357 {\
3358 glyph = cache_get_font (font, ttext[idx]);\
3359 if (!(flags & TEXT2_IMPLICIT_X))\
3360 {\
3361 xyoffset = ttext[++idx];\
3362 if ((xyoffset & 0x80))\
3363 {\
3364 if (flags & TEXT2_VERTICAL)\
3365 y += ttext[idx+1] | (ttext[idx+2] << 8);\
3366 else\
3367 x += ttext[idx+1] | (ttext[idx+2] << 8);\
3368 idx += 2;\
3369 }\
3370 else\
3371 {\
3372 if (flags & TEXT2_VERTICAL)\
3373 y += xyoffset;\
3374 else\
3375 x += xyoffset;\
3376 }\
3377 }\
3378 if (glyph != NULL)\
3379 {\
3380 x1 = x + glyph->offset;\
3381 y1 = y + glyph->baseline;\
3382 XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
3383 XSetTSOrigin(g_display, g_gc, x1, y1);\
3384 FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
3385 if (flags & TEXT2_IMPLICIT_X)\
3386 x += glyph->width;\
3387 }\
3388 }
3389
3390 void
3391 ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
3392 int clipx, int clipy, int clipcx, int clipcy,
3393 int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
3394 int bgcolour, int fgcolour, uint8 * text, uint8 length)
3395 {
3396 /* TODO: use brush appropriately */
3397
3398 FONTGLYPH *glyph;
3399 int i, j, xyoffset, x1, y1;
3400 DATABLOB *entry;
3401
3402 SET_FOREGROUND(bgcolour);
3403
3404 /* Sometimes, the boxcx value is something really large, like
3405 32691. This makes XCopyArea fail with Xvnc. The code below
3406 is a quick fix. */
3407 if (boxx + boxcx > g_width)
3408 boxcx = g_width - boxx;
3409
3410 if (boxcx > 1)
3411 {
3412 FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
3413 }
3414 else if (mixmode == MIX_OPAQUE)
3415 {
3416 FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
3417 }
3418
3419 SET_FOREGROUND(fgcolour);
3420 SET_BACKGROUND(bgcolour);
3421 XSetFillStyle(g_display, g_gc, FillStippled);
3422
3423 /* Paint text, character by character */
3424 for (i = 0; i < length;)
3425 {
3426 switch (text[i])
3427 {
3428 case 0xff:
3429 /* At least two bytes needs to follow */
3430 if (i + 3 > length)
3431 {
3432 warning("Skipping short 0xff command:");
3433 for (j = 0; j < length; j++)
3434 fprintf(stderr, "%02x ", text[j]);
3435 fprintf(stderr, "\n");
3436 i = length = 0;
3437 break;
3438 }
3439 cache_put_text(text[i + 1], text, text[i + 2]);
3440 i += 3;
3441 length -= i;
3442 /* this will move pointer from start to first character after FF command */
3443 text = &(text[i]);
3444 i = 0;
3445 break;
3446
3447 case 0xfe:
3448 /* At least one byte needs to follow */
3449 if (i + 2 > length)
3450 {
3451 warning("Skipping short 0xfe command:");
3452 for (j = 0; j < length; j++)
3453 fprintf(stderr, "%02x ", text[j]);
3454 fprintf(stderr, "\n");
3455 i = length = 0;
3456 break;
3457 }
3458 entry = cache_get_text(text[i + 1]);
3459 if (entry->data != NULL)
3460 {
3461 if ((((uint8 *) (entry->data))[1] == 0)
3462 && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
3463 {
3464 if (flags & TEXT2_VERTICAL)
3465 y += text[i + 2];
3466 else
3467 x += text[i + 2];
3468 }
3469 for (j = 0; j < entry->size; j++)
3470 DO_GLYPH(((uint8 *) (entry->data)), j);
3471 }
3472 if (i + 2 < length)
3473 i += 3;
3474 else
3475 i += 2;
3476 length -= i;
3477 /* this will move pointer from start to first character after FE command */
3478 text = &(text[i]);
3479 i = 0;
3480 break;
3481
3482 default:
3483 DO_GLYPH(text, i);
3484 i++;
3485 break;
3486 }
3487 }
3488
3489 XSetFillStyle(g_display, g_gc, FillSolid);
3490
3491 if (g_ownbackstore)
3492 {
3493 if (boxcx > 1)
3494 {
3495 XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
3496 boxy, boxcx, boxcy, boxx, boxy);
3497 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3498 (g_display, g_backstore, sw->wnd, g_gc,
3499 boxx, boxy,
3500 boxcx, boxcy,
3501 boxx - sw->xoffset, boxy - sw->yoffset));
3502 }
3503 else
3504 {
3505 XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
3506 clipy, clipcx, clipcy, clipx, clipy);
3507 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3508 (g_display, g_backstore, sw->wnd, g_gc,
3509 clipx, clipy,
3510 clipcx, clipcy, clipx - sw->xoffset,
3511 clipy - sw->yoffset));
3512 }
3513 }
3514 }
3515
3516 void
3517 ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
3518 {
3519 Pixmap pix;
3520 XImage *image;
3521
3522 if (g_ownbackstore)
3523 {
3524 image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
3525 exit_if_null(image);
3526 }
3527 else
3528 {
3529 pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
3530 XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
3531 image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
3532 exit_if_null(image);
3533 XFreePixmap(g_display, pix);
3534 }
3535
3536 offset *= g_bpp / 8;
3537 cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
3538
3539 XDestroyImage(image);
3540 }
3541
3542 void
3543 ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
3544 {
3545 XImage *image;
3546 uint8 *data;
3547
3548 offset *= g_bpp / 8;
3549 data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
3550 if (data == NULL)
3551 return;
3552
3553 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
3554 (char *) data, cx, cy, g_bpp, 0);
3555
3556 if (g_ownbackstore)
3557 {
3558 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
3559 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3560 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3561 (g_display, g_backstore, sw->wnd, g_gc,
3562 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3563 }
3564 else
3565 {
3566 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
3567 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3568 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
3569 x - sw->xoffset, y - sw->yoffset));
3570 }
3571
3572 XFree(image);
3573 }
3574
3575 /* these do nothing here but are used in uiports */
3576 void
3577 ui_begin_update(void)
3578 {
3579 }
3580
3581 void
3582 ui_end_update(void)
3583 {
3584 }
3585
3586
3587 void
3588 ui_seamless_begin(RD_BOOL hidden)
3589 {
3590 if (!g_seamless_rdp)
3591 return;
3592
3593 if (g_seamless_started)
3594 return;
3595
3596 g_seamless_started = True;
3597 g_seamless_hidden = hidden;
3598
3599 if (!hidden)
3600 ui_seamless_toggle();
3601 }
3602
3603
3604 void
3605 ui_seamless_hide_desktop()
3606 {
3607 if (!g_seamless_rdp)
3608 return;
3609
3610 if (!g_seamless_started)
3611 return;
3612
3613 if (g_seamless_active)
3614 ui_seamless_toggle();
3615
3616 g_seamless_hidden = True;
3617 }
3618
3619
3620 void
3621 ui_seamless_unhide_desktop()
3622 {
3623 if (!g_seamless_rdp)
3624 return;
3625
3626 if (!g_seamless_started)
3627 return;
3628
3629 g_seamless_hidden = False;
3630
3631 ui_seamless_toggle();
3632 }
3633
3634
3635 void
3636 ui_seamless_toggle()
3637 {
3638 if (!g_seamless_rdp)
3639 return;
3640
3641 if (!g_seamless_started)
3642 return;
3643
3644 if (g_seamless_hidden)
3645 return;
3646
3647 if (g_seamless_active)
3648 {
3649 /* Deactivate */
3650 while (g_seamless_windows)
3651 {
3652 XDestroyWindow(g_display, g_seamless_windows->wnd);
3653 sw_remove_window(g_seamless_windows);
3654 }
3655 XMapWindow(g_display, g_wnd);
3656 }
3657 else
3658 {
3659 /* Activate */
3660 XUnmapWindow(g_display, g_wnd);
3661 seamless_send_sync();
3662 }
3663
3664 g_seamless_active = !g_seamless_active;
3665 }
3666
3667
3668 void
3669 ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent,
3670 unsigned long flags)
3671 {
3672 Window wnd;
3673 XSetWindowAttributes attribs;
3674 XClassHint *classhints;
3675 XSizeHints *sizehints;
3676 XWMHints *wmhints;
3677 long input_mask;
3678 seamless_window *sw, *sw_parent;
3679
3680 if (!g_seamless_active)
3681 return;
3682
3683 /* Ignore CREATEs for existing windows */
3684 sw = sw_get_window_by_id(id);
3685 if (sw)
3686 return;
3687
3688 get_window_attribs(&attribs);
3689 wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
3690 InputOutput, g_visual,
3691 CWBackPixel | CWBackingStore | CWColormap | CWBorderPixel, &attribs);
3692
3693 XStoreName(g_display, wnd, "SeamlessRDP");
3694 ewmh_set_wm_name(wnd, "SeamlessRDP");
3695
3696 mwm_hide_decorations(wnd);
3697
3698 classhints = XAllocClassHint();
3699 if (classhints != NULL)
3700 {
3701 classhints->res_name = "rdesktop";
3702 classhints->res_class = "SeamlessRDP";
3703 XSetClassHint(g_display, wnd, classhints);
3704 XFree(classhints);
3705 }
3706
3707 /* WM_NORMAL_HINTS */
3708 sizehints = XAllocSizeHints();
3709 if (sizehints != NULL)
3710 {
3711 sizehints->flags = USPosition;
3712 XSetWMNormalHints(g_display, wnd, sizehints);
3713 XFree(sizehints);
3714 }
3715
3716 /* Parent-less transient windows */
3717 if (parent == 0xFFFFFFFF)
3718 {
3719 XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3720 /* Some buggy wm:s (kwin) do not handle the above, so fake it
3721 using some other hints. */
3722 ewmh_set_window_popup(wnd);
3723 }
3724 /* Normal transient windows */
3725 else if (parent != 0x00000000)
3726 {
3727 sw_parent = sw_get_window_by_id(parent);
3728 if (sw_parent)
3729 XSetTransientForHint(g_display, wnd, sw_parent->wnd);
3730 else
3731 warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3732 }
3733
3734 if (flags & SEAMLESSRDP_CREATE_MODAL)
3735 {
3736 /* We do this to support buggy wm:s (*cough* metacity *cough*)
3737 somewhat at least */
3738 if (parent == 0x00000000)
3739 XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3740 ewmh_set_window_modal(wnd);
3741 }
3742
3743 if (flags & SEAMLESSRDP_CREATE_TOPMOST)
3744 {
3745 /* Make window always-on-top */
3746 ewmh_set_window_above(wnd);
3747 }
3748
3749 /* FIXME: Support for Input Context:s */
3750
3751 get_input_mask(&input_mask);
3752 input_mask |= PropertyChangeMask;
3753
3754 XSelectInput(g_display, wnd, input_mask);
3755
3756 /* handle the WM_DELETE_WINDOW protocol. */
3757 XSetWMProtocols(g_display, wnd, &g_kill_atom, 1);
3758
3759 sw = xmalloc(sizeof(seamless_window));
3760
3761 memset(sw, 0, sizeof(seamless_window));
3762
3763 sw->wnd = wnd;
3764 sw->id = id;
3765 sw->group = sw_find_group(group, False);
3766 sw->group->refcnt++;
3767 sw->state = SEAMLESSRDP_NOTYETMAPPED;
3768 sw->desktop = 0;
3769 sw->position_timer = xmalloc(sizeof(struct timeval));
3770 timerclear(sw->position_timer);
3771
3772 sw->outstanding_position = False;
3773 sw->outpos_serial = 0;
3774 sw->outpos_xoffset = sw->outpos_yoffset = 0;
3775 sw->outpos_width = sw->outpos_height = 0;
3776
3777 sw->next = g_seamless_windows;
3778 g_seamless_windows = sw;
3779
3780 /* WM_HINTS */
3781 wmhints = XAllocWMHints();
3782 if (wmhints)
3783 {
3784 wmhints->flags = WindowGroupHint;
3785 wmhints->window_group = sw->group->wnd;
3786 XSetWMHints(g_display, sw->wnd, wmhints);
3787 XFree(wmhints);
3788 }
3789 }
3790
3791
3792 void
3793 ui_seamless_destroy_window(unsigned long id, unsigned long flags)
3794 {
3795 seamless_window *sw;
3796
3797 if (!g_seamless_active)
3798 return;
3799
3800 sw = sw_get_window_by_id(id);
3801 if (!sw)
3802 {
3803 warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3804 return;
3805 }
3806
3807 XDestroyWindow(g_display, sw->wnd);
3808 sw_remove_window(sw);
3809 }
3810
3811
3812 void
3813 ui_seamless_destroy_group(unsigned long id, unsigned long flags)
3814 {
3815 seamless_window *sw, *sw_next;
3816
3817 if (!g_seamless_active)
3818 return;
3819
3820 for (sw = g_seamless_windows; sw; sw = sw_next)
3821 {
3822 sw_next = sw->next;
3823
3824 if (sw->group->id == id)
3825 {
3826 XDestroyWindow(g_display, sw->wnd);
3827 sw_remove_window(sw);
3828 }
3829 }
3830 }
3831
3832
3833 void
3834 ui_seamless_seticon(unsigned long id, const char *format, int width, int height, int chunk,
3835 const char *data, int chunk_len)
3836 {
3837 seamless_window *sw;
3838
3839 if (!g_seamless_active)
3840 return;
3841
3842 sw = sw_get_window_by_id(id);
3843 if (!sw)
3844 {
3845 warning("ui_seamless_seticon: No information for window 0x%lx\n", id);
3846 return;
3847 }
3848
3849 if (chunk == 0)
3850 {
3851 if (sw->icon_size)
3852 warning("ui_seamless_seticon: New icon started before previous completed\n");
3853
3854 if (strcmp(format, "RGBA") != 0)
3855 {
3856 warning("ui_seamless_seticon: Uknown icon format \"%s\"\n", format);
3857 return;
3858 }
3859
3860 sw->icon_size = width * height * 4;
3861 if (sw->icon_size > 32 * 32 * 4)
3862 {
3863 warning("ui_seamless_seticon: Icon too large (%d bytes)\n", sw->icon_size);
3864 sw->icon_size = 0;
3865 return;
3866 }
3867
3868 sw->icon_offset = 0;
3869 }
3870 else
3871 {
3872 if (!sw->icon_size)
3873 return;
3874 }
3875
3876 if (chunk_len > (sw->icon_size - sw->icon_offset))
3877 {
3878 warning("ui_seamless_seticon: Too large chunk received (%d bytes > %d bytes)\n",
3879 chunk_len, sw->icon_size - sw->icon_offset);
3880 sw->icon_size = 0;
3881 return;
3882 }
3883
3884 memcpy(sw->icon_buffer + sw->icon_offset, data, chunk_len);
3885 sw->icon_offset += chunk_len;
3886
3887 if (sw->icon_offset == sw->icon_size)
3888 {
3889 ewmh_set_icon(sw->wnd, width, height, sw->icon_buffer);
3890 sw->icon_size = 0;
3891 }
3892 }
3893
3894
3895 void
3896 ui_seamless_delicon(unsigned long id, const char *format, int width, int height)
3897 {
3898 seamless_window *sw;
3899
3900 if (!g_seamless_active)
3901 return;
3902
3903 sw = sw_get_window_by_id(id);
3904 if (!sw)
3905 {
3906 warning("ui_seamless_seticon: No information for window 0x%lx\n", id);
3907 return;
3908 }
3909
3910 if (strcmp(format, "RGBA") != 0)
3911 {
3912 warning("ui_seamless_seticon: Uknown icon format \"%s\"\n", format);
3913 return;
3914 }
3915
3916 ewmh_del_icon(sw->wnd, width, height);
3917 }
3918
3919
3920 void
3921 ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags)
3922 {
3923 seamless_window *sw;
3924
3925 if (!g_seamless_active)
3926 return;
3927
3928 sw = sw_get_window_by_id(id);
3929 if (!sw)
3930 {
3931 warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3932 return;
3933 }
3934
3935 /* We ignore server updates until it has handled our request. */
3936 if (sw->outstanding_position)
3937 return;
3938
3939 if (!width || !height)
3940 /* X11 windows must be at least 1x1 */
3941 return;
3942
3943 sw->xoffset = x;
3944 sw->yoffset = y;
3945 sw->width = width;
3946 sw->height = height;
3947
3948 /* If we move the window in a maximized state, then KDE won't
3949 accept restoration */
3950 switch (sw->state)
3951 {
3952 case SEAMLESSRDP_MINIMIZED:
3953 case SEAMLESSRDP_MAXIMIZED:
3954 return;
3955 }
3956
3957 /* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3958 XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3959 }
3960
3961
3962 void
3963 ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags)
3964 {
3965 seamless_window *sw;
3966 XWindowChanges values;
3967 unsigned long restack_serial;
3968
3969 if (!g_seamless_active)
3970 return;
3971
3972 sw = sw_get_window_by_id(id);
3973 if (!sw)
3974 {
3975 warning("ui_seamless_restack_window: No information for window 0x%lx\n", id);
3976 return;
3977 }
3978
3979 if (behind)
3980 {
3981 seamless_window *sw_behind;
3982
3983 sw_behind = sw_get_window_by_id(behind);
3984 if (!sw_behind)
3985 {
3986 warning("ui_seamless_restack_window: No information for behind window 0x%lx\n", behind);
3987 return;
3988 }
3989
3990 if (!g_seamless_broken_restack)
3991 {
3992 values.stack_mode = Below;
3993 values.sibling = sw_behind->wnd;
3994 restack_serial = XNextRequest(g_display);
3995 XReconfigureWMWindow(g_display, sw->wnd, DefaultScreen(g_display),
3996 CWStackMode | CWSibling, &values);
3997 sw_wait_configurenotify(sw->wnd, restack_serial);
3998 }
3999 }
4000 else
4001 {
4002 values.stack_mode = Above;
4003 restack_serial = XNextRequest(g_display);
4004 XReconfigureWMWindow(g_display, sw->wnd, DefaultScreen(g_display), CWStackMode,
4005 &values);
4006 sw_wait_configurenotify(sw->wnd, restack_serial);
4007 }
4008
4009 sw_restack_window(sw, behind);
4010
4011 if (flags & SEAMLESSRDP_CREATE_TOPMOST)
4012 {
4013 /* Make window always-on-top */
4014 ewmh_set_window_above(sw->wnd);
4015 }
4016 }
4017
4018
4019 void
4020 ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags)
4021 {
4022 seamless_window *sw;
4023
4024 if (!g_seamless_active)
4025 return;
4026
4027 sw = sw_get_window_by_id(id);
4028 if (!sw)
4029 {
4030 warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
4031 return;
4032 }
4033
4034 /* FIXME: Might want to convert the name for non-EWMH WMs */
4035 XStoreName(g_display, sw->wnd, title);
4036 ewmh_set_wm_name(sw->wnd, title);
4037 }
4038
4039
4040 void
4041 ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags)
4042 {
4043 seamless_window *sw;
4044
4045 if (!g_seamless_active)
4046 return;
4047
4048 sw = sw_get_window_by_id(id);
4049 if (!sw)
4050 {
4051 warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
4052 return;
4053 }
4054
4055 switch (state)
4056 {
4057 case SEAMLESSRDP_NORMAL:
4058 case SEAMLESSRDP_MAXIMIZED:
4059 ewmh_change_state(sw->wnd, state);
4060 XMapWindow(g_display, sw->wnd);
4061 break;
4062 case SEAMLESSRDP_MINIMIZED:
4063 /* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
4064 the Window Manager should probably just ignore the request, since
4065 _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
4066 such as minimization, rather than an independent state." Besides,
4067 XIconifyWindow is easier. */
4068 if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
4069 {
4070 XWMHints *hints;
4071 hints = XGetWMHints(g_display, sw->wnd);
4072 if (hints)
4073 {
4074 hints->flags |= StateHint;
4075 hints->initial_state = IconicState;
4076 XSetWMHints(g_display, sw->wnd, hints);
4077 XFree(hints);
4078 }
4079 XMapWindow(g_display, sw->wnd);
4080 }
4081 else
4082 XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display));
4083 break;
4084 default:
4085 warning("SeamlessRDP: Invalid state %d\n", state);
4086 break;
4087 }
4088
4089 sw->state = state;
4090 }
4091
4092
4093 void
4094 ui_seamless_syncbegin(unsigned long flags)
4095 {
4096 if (!g_seamless_active)
4097 return;
4098
4099 /* Destroy all seamless windows */
4100 while (g_seamless_windows)
4101 {
4102 XDestroyWindow(g_display, g_seamless_windows->wnd);
4103 sw_remove_window(g_seamless_windows);
4104 }
4105 }
4106
4107
4108 void
4109 ui_seamless_ack(unsigned int serial)
4110 {
4111 seamless_window *sw;
4112 for (sw = g_seamless_windows; sw; sw = sw->next)
4113 {
4114 if (sw->outstanding_position && (sw->outpos_serial == serial))
4115 {
4116 sw->xoffset = sw->outpos_xoffset;
4117 sw->yoffset = sw->outpos_yoffset;
4118 sw->width = sw->outpos_width;
4119 sw->height = sw->outpos_height;
4120 sw->outstanding_position = False;
4121
4122 /* Do a complete redraw of the window as part of the
4123 completion of the move. This is to remove any
4124 artifacts caused by our lack of synchronization. */
4125 XCopyArea(g_display, g_backstore,
4126 sw->wnd, g_gc,
4127 sw->xoffset, sw->yoffset, sw->width, sw->height, 0, 0);
4128
4129 break;
4130 }
4131 }
4132 }

  ViewVC Help
Powered by ViewVC 1.1.26