/[rdesktop]/sourceforge.net/trunk/rdesktop/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 /sourceforge.net/trunk/rdesktop/xwin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1463 - (show annotations)
Fri Mar 28 11:36:15 2008 UTC (16 years, 2 months ago) by astrand
File MIME type: text/plain
File size: 95531 byte(s)
Moved call to seamless_restack_test from ui_init to end of
ui_create_window. This seems to fix problems the WTS disconnecting the
session due to "DATA ENCRYPTION" errors. At this point, it's unknown
why this patch fixes the problem, but ui_create_window is a good place
for seamless_restack_test() in any case.

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

  ViewVC Help
Powered by ViewVC 1.1.26