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

Contents of /sourceforge.net/trunk/rdesktop/xwin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1199 - (show annotations)
Mon Mar 27 08:17:34 2006 UTC (18 years, 1 month ago) by astrand
File MIME type: text/plain
File size: 83694 byte(s)
Added SeamlessRDP support: Merged seamlessrdp-branch

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

  ViewVC Help
Powered by ViewVC 1.1.26