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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1508 - (show annotations)
Mon Jul 20 16:47:49 2009 UTC (14 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 100992 byte(s)
Date: Sun, 19 Jul 2009 14:00:30 -0400
From: Daniel Jarboe <daniel.jarboe(at)gmail.com>
To: Dobrica Pavlinusic <dpavlin(at)rot13.org>
Subject: Re: rdesktop diff

On Sun, Jul 19, 2009 at 8:01 AM, Dobrica Pavlinusic <dpavlin(at)rot13.org>wrote:
>
> I'm looking forward for your patch, and will keep you informed what I
> did with it :-)

This diff was an earlier one with some debug printfs in so you can see how
the tuning knobs work.  I was only keeping an eye on a young computer user's
activities so ignored small screen changes and wrote out the stills to lossy
jpegs with libjpeg.  If you plan on saving screens no matter if there are
changes or not then I'd throw away all the g_bitmap_data_last_write stuff.

diff is against rdesktop-1.6 with whatever patches are in ubuntu 9.04
jaunty.  Added libjpeg62-dev in addition to the regular rdesktop build
dependencies.  Have fun and thanks for letting me know what you end up with.

~ Daniel

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

  ViewVC Help
Powered by ViewVC 1.1.26