/[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 1223 - (show annotations)
Sun Apr 9 20:11:42 2006 UTC (18 years, 1 month ago) by astrand
File MIME type: text/plain
File size: 83712 byte(s)
Compile fixes for GCC 2.96. Fixes 1444824.

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 BOOL can_translate_to_bpp = False;
1348 int j;
1349
1350 /* Try to find a no-translation visual that'll
1351 allow us to use RDP bitmaps directly as ZPixmaps. */
1352 if (!g_xserver_be && (((visual_info->depth == 15) &&
1353 /* R5G5B5 */
1354 (visual_info->red_mask == 0x7c00) &&
1355 (visual_info->green_mask == 0x3e0) &&
1356 (visual_info->blue_mask == 0x1f)) ||
1357 ((visual_info->depth == 16) &&
1358 /* R5G6B5 */
1359 (visual_info->red_mask == 0xf800) &&
1360 (visual_info->green_mask == 0x7e0) &&
1361 (visual_info->blue_mask == 0x1f)) ||
1362 ((visual_info->depth == 24) &&
1363 /* R8G8B8 */
1364 (visual_info->red_mask == 0xff0000) &&
1365 (visual_info->green_mask == 0xff00) &&
1366 (visual_info->blue_mask == 0xff))))
1367 {
1368 g_visual = visual_info->visual;
1369 g_depth = visual_info->depth;
1370 g_compatible_arch = !g_host_be;
1371 g_no_translate_image = (visual_info->depth == g_server_depth);
1372 if (g_no_translate_image)
1373 /* We found the best visual */
1374 break;
1375 }
1376 else
1377 {
1378 g_compatible_arch = False;
1379 }
1380
1381 if (visual_info->depth > 24)
1382 {
1383 /* Avoid 32-bit visuals and likes like the plague.
1384 They're either untested or proven to work bad
1385 (e.g. nvidia's Composite 32-bit visual).
1386 Most implementation offer a 24-bit visual anyway. */
1387 continue;
1388 }
1389
1390 /* Only care for visuals, for whose BPPs (not depths!)
1391 we have a translateXtoY function. */
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 xclip_deinit();
1640
1641 if (g_IM != NULL)
1642 XCloseIM(g_IM);
1643
1644 if (g_null_cursor != NULL)
1645 ui_destroy_cursor(g_null_cursor);
1646
1647 XFreeModifiermap(g_mod_map);
1648
1649 if (g_ownbackstore)
1650 XFreePixmap(g_display, g_backstore);
1651
1652 XFreeGC(g_display, g_gc);
1653 XCloseDisplay(g_display);
1654 g_display = NULL;
1655 }
1656
1657
1658 static void
1659 get_window_attribs(XSetWindowAttributes * attribs)
1660 {
1661 attribs->background_pixel = BlackPixelOfScreen(g_screen);
1662 attribs->background_pixel = WhitePixelOfScreen(g_screen);
1663 attribs->border_pixel = WhitePixelOfScreen(g_screen);
1664 attribs->backing_store = g_ownbackstore ? NotUseful : Always;
1665 attribs->override_redirect = g_fullscreen;
1666 attribs->colormap = g_xcolmap;
1667 }
1668
1669 static void
1670 get_input_mask(long *input_mask)
1671 {
1672 *input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
1673 VisibilityChangeMask | FocusChangeMask | StructureNotifyMask;
1674
1675 if (g_sendmotion)
1676 *input_mask |= PointerMotionMask;
1677 if (g_ownbackstore)
1678 *input_mask |= ExposureMask;
1679 if (g_fullscreen || g_grab_keyboard)
1680 *input_mask |= EnterWindowMask;
1681 if (g_grab_keyboard)
1682 *input_mask |= LeaveWindowMask;
1683 }
1684
1685 BOOL
1686 ui_create_window(void)
1687 {
1688 uint8 null_pointer_mask[1] = { 0x80 };
1689 uint8 null_pointer_data[24] = { 0x00 };
1690
1691 XSetWindowAttributes attribs;
1692 XClassHint *classhints;
1693 XSizeHints *sizehints;
1694 int wndwidth, wndheight;
1695 long input_mask, ic_input_mask;
1696 XEvent xevent;
1697
1698 wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;
1699 wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;
1700
1701 /* Handle -x-y portion of geometry string */
1702 if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
1703 g_xpos = WidthOfScreen(g_screen) + g_xpos - g_width;
1704 if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4)))
1705 g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height;
1706
1707 get_window_attribs(&attribs);
1708
1709 g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
1710 wndheight, 0, g_depth, InputOutput, g_visual,
1711 CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
1712 CWBorderPixel, &attribs);
1713
1714 if (g_gc == NULL)
1715 {
1716 g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
1717 ui_reset_clip();
1718 }
1719
1720 if (g_create_bitmap_gc == NULL)
1721 g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL);
1722
1723 if ((g_ownbackstore) && (g_backstore == 0))
1724 {
1725 g_backstore = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1726
1727 /* clear to prevent rubbish being exposed at startup */
1728 XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
1729 XFillRectangle(g_display, g_backstore, g_gc, 0, 0, g_width, g_height);
1730 }
1731
1732 XStoreName(g_display, g_wnd, g_title);
1733
1734 if (g_hide_decorations)
1735 mwm_hide_decorations(g_wnd);
1736
1737 classhints = XAllocClassHint();
1738 if (classhints != NULL)
1739 {
1740 classhints->res_name = classhints->res_class = "rdesktop";
1741 XSetClassHint(g_display, g_wnd, classhints);
1742 XFree(classhints);
1743 }
1744
1745 sizehints = XAllocSizeHints();
1746 if (sizehints)
1747 {
1748 sizehints->flags = PMinSize | PMaxSize;
1749 if (g_pos)
1750 sizehints->flags |= PPosition;
1751 sizehints->min_width = sizehints->max_width = g_width;
1752 sizehints->min_height = sizehints->max_height = g_height;
1753 XSetWMNormalHints(g_display, g_wnd, sizehints);
1754 XFree(sizehints);
1755 }
1756
1757 if (g_embed_wnd)
1758 {
1759 XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);
1760 }
1761
1762 get_input_mask(&input_mask);
1763
1764 if (g_IM != NULL)
1765 {
1766 g_IC = XCreateIC(g_IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
1767 XNClientWindow, g_wnd, XNFocusWindow, g_wnd, NULL);
1768
1769 if ((g_IC != NULL)
1770 && (XGetICValues(g_IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
1771 input_mask |= ic_input_mask;
1772 }
1773
1774 XSelectInput(g_display, g_wnd, input_mask);
1775 XMapWindow(g_display, g_wnd);
1776
1777 /* wait for VisibilityNotify */
1778 do
1779 {
1780 XMaskEvent(g_display, VisibilityChangeMask, &xevent);
1781 }
1782 while (xevent.type != VisibilityNotify);
1783 g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1784
1785 g_focused = False;
1786 g_mouse_in_wnd = False;
1787
1788 /* handle the WM_DELETE_WINDOW protocol */
1789 g_protocol_atom = XInternAtom(g_display, "WM_PROTOCOLS", True);
1790 g_kill_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", True);
1791 XSetWMProtocols(g_display, g_wnd, &g_kill_atom, 1);
1792
1793 /* create invisible 1x1 cursor to be used as null cursor */
1794 if (g_null_cursor == NULL)
1795 g_null_cursor = ui_create_cursor(0, 0, 1, 1, null_pointer_mask, null_pointer_data);
1796
1797 return True;
1798 }
1799
1800 void
1801 ui_resize_window()
1802 {
1803 XSizeHints *sizehints;
1804 Pixmap bs;
1805
1806 sizehints = XAllocSizeHints();
1807 if (sizehints)
1808 {
1809 sizehints->flags = PMinSize | PMaxSize;
1810 sizehints->min_width = sizehints->max_width = g_width;
1811 sizehints->min_height = sizehints->max_height = g_height;
1812 XSetWMNormalHints(g_display, g_wnd, sizehints);
1813 XFree(sizehints);
1814 }
1815
1816 if (!(g_fullscreen || g_embed_wnd))
1817 {
1818 XResizeWindow(g_display, g_wnd, g_width, g_height);
1819 }
1820
1821 /* create new backstore pixmap */
1822 if (g_backstore != 0)
1823 {
1824 bs = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1825 XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
1826 XFillRectangle(g_display, bs, g_gc, 0, 0, g_width, g_height);
1827 XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, g_width, g_height, 0, 0);
1828 XFreePixmap(g_display, g_backstore);
1829 g_backstore = bs;
1830 }
1831 }
1832
1833 void
1834 ui_destroy_window(void)
1835 {
1836 if (g_IC != NULL)
1837 XDestroyIC(g_IC);
1838
1839 XDestroyWindow(g_display, g_wnd);
1840 }
1841
1842 void
1843 xwin_toggle_fullscreen(void)
1844 {
1845 Pixmap contents = 0;
1846
1847 if (g_seamless_active)
1848 /* Turn off SeamlessRDP mode */
1849 ui_seamless_toggle();
1850
1851 if (!g_ownbackstore)
1852 {
1853 /* need to save contents of window */
1854 contents = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1855 XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, g_width, g_height, 0, 0);
1856 }
1857
1858 ui_destroy_window();
1859 g_fullscreen = !g_fullscreen;
1860 ui_create_window();
1861
1862 XDefineCursor(g_display, g_wnd, g_current_cursor);
1863
1864 if (!g_ownbackstore)
1865 {
1866 XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, g_width, g_height, 0, 0);
1867 XFreePixmap(g_display, contents);
1868 }
1869 }
1870
1871 static void
1872 handle_button_event(XEvent xevent, BOOL down)
1873 {
1874 uint16 button, flags = 0;
1875 g_last_gesturetime = xevent.xbutton.time;
1876 button = xkeymap_translate_button(xevent.xbutton.button);
1877 if (button == 0)
1878 return;
1879
1880 if (down)
1881 flags = MOUSE_FLAG_DOWN;
1882
1883 /* Stop moving window when button is released, regardless of cursor position */
1884 if (g_moving_wnd && (xevent.type == ButtonRelease))
1885 g_moving_wnd = False;
1886
1887 /* If win_button_size is nonzero, enable single app mode */
1888 if (xevent.xbutton.y < g_win_button_size)
1889 {
1890 /* Check from right to left: */
1891 if (xevent.xbutton.x >= g_width - g_win_button_size)
1892 {
1893 /* The close button, continue */
1894 ;
1895 }
1896 else if (xevent.xbutton.x >= g_width - g_win_button_size * 2)
1897 {
1898 /* The maximize/restore button. Do not send to
1899 server. It might be a good idea to change the
1900 cursor or give some other visible indication
1901 that rdesktop inhibited this click */
1902 if (xevent.type == ButtonPress)
1903 return;
1904 }
1905 else if (xevent.xbutton.x >= g_width - g_win_button_size * 3)
1906 {
1907 /* The minimize button. Iconify window. */
1908 if (xevent.type == ButtonRelease)
1909 {
1910 /* Release the mouse button outside the minimize button, to prevent the
1911 actual minimazation to happen */
1912 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, button, 1, 1);
1913 XIconifyWindow(g_display, g_wnd, DefaultScreen(g_display));
1914 return;
1915 }
1916 }
1917 else if (xevent.xbutton.x <= g_win_button_size)
1918 {
1919 /* The system menu. Ignore. */
1920 if (xevent.type == ButtonPress)
1921 return;
1922 }
1923 else
1924 {
1925 /* The title bar. */
1926 if (xevent.type == ButtonPress)
1927 {
1928 if (!g_fullscreen && g_hide_decorations && !g_using_full_workarea)
1929 {
1930 g_moving_wnd = True;
1931 g_move_x_offset = xevent.xbutton.x;
1932 g_move_y_offset = xevent.xbutton.y;
1933 }
1934 return;
1935 }
1936 }
1937 }
1938
1939 if (xevent.xmotion.window == g_wnd)
1940 {
1941 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1942 flags | button, xevent.xbutton.x, xevent.xbutton.y);
1943 }
1944 else
1945 {
1946 /* SeamlessRDP */
1947 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1948 flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root);
1949 }
1950 }
1951
1952
1953 /* Process events in Xlib queue
1954 Returns 0 after user quit, 1 otherwise */
1955 static int
1956 xwin_process_events(void)
1957 {
1958 XEvent xevent;
1959 KeySym keysym;
1960 uint32 ev_time;
1961 char str[256];
1962 Status status;
1963 int events = 0;
1964 seamless_window *sw;
1965
1966 while ((XPending(g_display) > 0) && events++ < 20)
1967 {
1968 XNextEvent(g_display, &xevent);
1969
1970 if ((g_IC != NULL) && (XFilterEvent(&xevent, None) == True))
1971 {
1972 DEBUG_KBD(("Filtering event\n"));
1973 continue;
1974 }
1975
1976 switch (xevent.type)
1977 {
1978 case VisibilityNotify:
1979 if (xevent.xvisibility.window == g_wnd)
1980 g_Unobscured =
1981 xevent.xvisibility.state == VisibilityUnobscured;
1982
1983 break;
1984 case ClientMessage:
1985 /* the window manager told us to quit */
1986 if ((xevent.xclient.message_type == g_protocol_atom)
1987 && ((Atom) xevent.xclient.data.l[0] == g_kill_atom))
1988 /* Quit */
1989 return 0;
1990 break;
1991
1992 case KeyPress:
1993 g_last_gesturetime = xevent.xkey.time;
1994 if (g_IC != NULL)
1995 /* Multi_key compatible version */
1996 {
1997 XmbLookupString(g_IC,
1998 &xevent.xkey, str, sizeof(str), &keysym,
1999 &status);
2000 if (!((status == XLookupKeySym) || (status == XLookupBoth)))
2001 {
2002 error("XmbLookupString failed with status 0x%x\n",
2003 status);
2004 break;
2005 }
2006 }
2007 else
2008 {
2009 /* Plain old XLookupString */
2010 DEBUG_KBD(("\nNo input context, using XLookupString\n"));
2011 XLookupString((XKeyEvent *) & xevent,
2012 str, sizeof(str), &keysym, NULL);
2013 }
2014
2015 DEBUG_KBD(("KeyPress for keysym (0x%lx, %s)\n", keysym,
2016 get_ksname(keysym)));
2017
2018 ev_time = time(NULL);
2019 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
2020 break;
2021
2022 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2023 ev_time, True, 0);
2024 break;
2025
2026 case KeyRelease:
2027 g_last_gesturetime = xevent.xkey.time;
2028 XLookupString((XKeyEvent *) & xevent, str,
2029 sizeof(str), &keysym, NULL);
2030
2031 DEBUG_KBD(("\nKeyRelease for keysym (0x%lx, %s)\n", keysym,
2032 get_ksname(keysym)));
2033
2034 ev_time = time(NULL);
2035 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
2036 break;
2037
2038 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2039 ev_time, False, 0);
2040 break;
2041
2042 case ButtonPress:
2043 handle_button_event(xevent, True);
2044 break;
2045
2046 case ButtonRelease:
2047 handle_button_event(xevent, False);
2048 break;
2049
2050 case MotionNotify:
2051 if (g_moving_wnd)
2052 {
2053 XMoveWindow(g_display, g_wnd,
2054 xevent.xmotion.x_root - g_move_x_offset,
2055 xevent.xmotion.y_root - g_move_y_offset);
2056 break;
2057 }
2058
2059 if (g_fullscreen && !g_focused)
2060 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
2061 CurrentTime);
2062
2063 if (xevent.xmotion.window == g_wnd)
2064 {
2065 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2066 xevent.xmotion.x, xevent.xmotion.y);
2067 }
2068 else
2069 {
2070 /* SeamlessRDP */
2071 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2072 xevent.xmotion.x_root,
2073 xevent.xmotion.y_root);
2074 }
2075 break;
2076
2077 case FocusIn:
2078 if (xevent.xfocus.mode == NotifyGrab)
2079 break;
2080 g_focused = True;
2081 reset_modifier_keys();
2082 if (g_grab_keyboard && g_mouse_in_wnd)
2083 XGrabKeyboard(g_display, g_wnd, True,
2084 GrabModeAsync, GrabModeAsync, CurrentTime);
2085
2086 sw = sw_get_window_by_wnd(xevent.xfocus.window);
2087 if (!sw)
2088 break;
2089
2090 if (sw->id != g_seamless_focused)
2091 {
2092 seamless_send_focus(sw->id, 0);
2093 g_seamless_focused = sw->id;
2094 }
2095 break;
2096
2097 case FocusOut:
2098 if (xevent.xfocus.mode == NotifyUngrab)
2099 break;
2100 g_focused = False;
2101 if (xevent.xfocus.mode == NotifyWhileGrabbed)
2102 XUngrabKeyboard(g_display, CurrentTime);
2103 break;
2104
2105 case EnterNotify:
2106 /* we only register for this event when in fullscreen mode */
2107 /* or grab_keyboard */
2108 g_mouse_in_wnd = True;
2109 if (g_fullscreen)
2110 {
2111 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
2112 CurrentTime);
2113 break;
2114 }
2115 if (g_focused)
2116 XGrabKeyboard(g_display, g_wnd, True,
2117 GrabModeAsync, GrabModeAsync, CurrentTime);
2118 break;
2119
2120 case LeaveNotify:
2121 /* we only register for this event when grab_keyboard */
2122 g_mouse_in_wnd = False;
2123 XUngrabKeyboard(g_display, CurrentTime);
2124 break;
2125
2126 case Expose:
2127 if (xevent.xexpose.window == g_wnd)
2128 {
2129 XCopyArea(g_display, g_backstore, xevent.xexpose.window,
2130 g_gc,
2131 xevent.xexpose.x, xevent.xexpose.y,
2132 xevent.xexpose.width, xevent.xexpose.height,
2133 xevent.xexpose.x, xevent.xexpose.y);
2134 }
2135 else
2136 {
2137 sw = sw_get_window_by_wnd(xevent.xexpose.window);
2138 if (!sw)
2139 break;
2140 XCopyArea(g_display, g_backstore,
2141 xevent.xexpose.window, g_gc,
2142 xevent.xexpose.x + sw->xoffset,
2143 xevent.xexpose.y + sw->yoffset,
2144 xevent.xexpose.width,
2145 xevent.xexpose.height, xevent.xexpose.x,
2146 xevent.xexpose.y);
2147 }
2148
2149 break;
2150
2151 case MappingNotify:
2152 /* Refresh keyboard mapping if it has changed. This is important for
2153 Xvnc, since it allocates keycodes dynamically */
2154 if (xevent.xmapping.request == MappingKeyboard
2155 || xevent.xmapping.request == MappingModifier)
2156 XRefreshKeyboardMapping(&xevent.xmapping);
2157
2158 if (xevent.xmapping.request == MappingModifier)
2159 {
2160 XFreeModifiermap(g_mod_map);
2161 g_mod_map = XGetModifierMapping(g_display);
2162 }
2163 break;
2164
2165 /* clipboard stuff */
2166 case SelectionNotify:
2167 xclip_handle_SelectionNotify(&xevent.xselection);
2168 break;
2169 case SelectionRequest:
2170 xclip_handle_SelectionRequest(&xevent.xselectionrequest);
2171 break;
2172 case SelectionClear:
2173 xclip_handle_SelectionClear();
2174 break;
2175 case PropertyNotify:
2176 xclip_handle_PropertyNotify(&xevent.xproperty);
2177 if (xevent.xproperty.window == g_wnd)
2178 break;
2179 if (xevent.xproperty.window == DefaultRootWindow(g_display))
2180 break;
2181
2182 /* seamless */
2183 sw = sw_get_window_by_wnd(xevent.xproperty.window);
2184 if (!sw)
2185 break;
2186
2187 if ((xevent.xproperty.atom == g_net_wm_state_atom)
2188 && (xevent.xproperty.state == PropertyNewValue))
2189 {
2190 sw->state = ewmh_get_window_state(sw->wnd);
2191 seamless_send_state(sw->id, sw->state, 0);
2192 }
2193
2194 if ((xevent.xproperty.atom == g_net_wm_desktop_atom)
2195 && (xevent.xproperty.state == PropertyNewValue))
2196 {
2197 sw->desktop = ewmh_get_window_desktop(sw->wnd);
2198 sw_all_to_desktop(sw->wnd, sw->desktop);
2199 }
2200
2201 break;
2202 case MapNotify:
2203 if (!g_seamless_active)
2204 rdp_send_client_window_status(1);
2205 break;
2206 case UnmapNotify:
2207 if (!g_seamless_active)
2208 rdp_send_client_window_status(0);
2209 break;
2210 case ConfigureNotify:
2211 if (!g_seamless_active)
2212 break;
2213
2214 sw = sw_get_window_by_wnd(xevent.xconfigure.window);
2215 if (!sw)
2216 break;
2217
2218 gettimeofday(sw->position_timer, NULL);
2219 if (sw->position_timer->tv_usec + SEAMLESSRDP_POSITION_TIMER >=
2220 1000000)
2221 {
2222 sw->position_timer->tv_usec +=
2223 SEAMLESSRDP_POSITION_TIMER - 1000000;
2224 sw->position_timer->tv_sec += 1;
2225 }
2226 else
2227 {
2228 sw->position_timer->tv_usec += SEAMLESSRDP_POSITION_TIMER;
2229 }
2230
2231 sw_handle_restack(sw);
2232 break;
2233 }
2234 }
2235 /* Keep going */
2236 return 1;
2237 }
2238
2239 /* Returns 0 after user quit, 1 otherwise */
2240 int
2241 ui_select(int rdp_socket)
2242 {
2243 int n;
2244 fd_set rfds, wfds;
2245 struct timeval tv;
2246 BOOL s_timeout = False;
2247
2248 while (True)
2249 {
2250 n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
2251 /* Process any events already waiting */
2252 if (!xwin_process_events())
2253 /* User quit */
2254 return 0;
2255
2256 if (g_seamless_active)
2257 sw_check_timers();
2258
2259 FD_ZERO(&rfds);
2260 FD_ZERO(&wfds);
2261 FD_SET(rdp_socket, &rfds);
2262 FD_SET(g_x_socket, &rfds);
2263
2264 #ifdef WITH_RDPSND
2265 /* FIXME: there should be an API for registering fds */
2266 if (g_dsp_busy)
2267 {
2268 FD_SET(g_dsp_fd, &wfds);
2269 n = (g_dsp_fd > n) ? g_dsp_fd : n;
2270 }
2271 #endif
2272 /* default timeout */
2273 tv.tv_sec = 60;
2274 tv.tv_usec = 0;
2275
2276 /* add redirection handles */
2277 rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
2278 seamless_select_timeout(&tv);
2279
2280 n++;
2281
2282 switch (select(n, &rfds, &wfds, NULL, &tv))
2283 {
2284 case -1:
2285 error("select: %s\n", strerror(errno));
2286
2287 case 0:
2288 /* Abort serial read calls */
2289 if (s_timeout)
2290 rdpdr_check_fds(&rfds, &wfds, (BOOL) True);
2291 continue;
2292 }
2293
2294 rdpdr_check_fds(&rfds, &wfds, (BOOL) False);
2295
2296 if (FD_ISSET(rdp_socket, &rfds))
2297 return 1;
2298
2299 #ifdef WITH_RDPSND
2300 if (g_dsp_busy && FD_ISSET(g_dsp_fd, &wfds))
2301 wave_out_play();
2302 #endif
2303 }
2304 }
2305
2306 void
2307 ui_move_pointer(int x, int y)
2308 {
2309 XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
2310 }
2311
2312 HBITMAP
2313 ui_create_bitmap(int width, int height, uint8 * data)
2314 {
2315 XImage *image;
2316 Pixmap bitmap;
2317 uint8 *tdata;
2318 int bitmap_pad;
2319
2320 if (g_server_depth == 8)
2321 {
2322 bitmap_pad = 8;
2323 }
2324 else
2325 {
2326 bitmap_pad = g_bpp;
2327
2328 if (g_bpp == 24)
2329 bitmap_pad = 32;
2330 }
2331
2332 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2333 bitmap = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
2334 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2335 (char *) tdata, width, height, bitmap_pad, 0);
2336
2337 XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height);
2338
2339 XFree(image);
2340 if (tdata != data)
2341 xfree(tdata);
2342 return (HBITMAP) bitmap;
2343 }
2344
2345 void
2346 ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
2347 {
2348 XImage *image;
2349 uint8 *tdata;
2350 int bitmap_pad;
2351
2352 if (g_server_depth == 8)
2353 {
2354 bitmap_pad = 8;
2355 }
2356 else
2357 {
2358 bitmap_pad = g_bpp;
2359
2360 if (g_bpp == 24)
2361 bitmap_pad = 32;
2362 }
2363
2364 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2365 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2366 (char *) tdata, width, height, bitmap_pad, 0);
2367
2368 if (g_ownbackstore)
2369 {
2370 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2371 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2372 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2373 (g_display, g_backstore, sw->wnd, g_gc, x, y, cx, cy,
2374 x - sw->xoffset, y - sw->yoffset));
2375 }
2376 else
2377 {
2378 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2379 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2380 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
2381 x - sw->xoffset, y - sw->yoffset));
2382 }
2383
2384 XFree(image);
2385 if (tdata != data)
2386 xfree(tdata);
2387 }
2388
2389 void
2390 ui_destroy_bitmap(HBITMAP bmp)
2391 {
2392 XFreePixmap(g_display, (Pixmap) bmp);
2393 }
2394
2395 HGLYPH
2396 ui_create_glyph(int width, int height, uint8 * data)
2397 {
2398 XImage *image;
2399 Pixmap bitmap;
2400 int scanline;
2401
2402 scanline = (width + 7) / 8;
2403
2404 bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1);
2405 if (g_create_glyph_gc == 0)
2406 g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL);
2407
2408 image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data,
2409 width, height, 8, scanline);
2410 image->byte_order = MSBFirst;
2411 image->bitmap_bit_order = MSBFirst;
2412 XInitImage(image);
2413
2414 XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
2415
2416 XFree(image);
2417 return (HGLYPH) bitmap;
2418 }
2419
2420 void
2421 ui_destroy_glyph(HGLYPH glyph)
2422 {
2423 XFreePixmap(g_display, (Pixmap) glyph);
2424 }
2425
2426 HCURSOR
2427 ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
2428 uint8 * andmask, uint8 * xormask)
2429 {
2430 HGLYPH maskglyph, cursorglyph;
2431 XColor bg, fg;
2432 Cursor xcursor;
2433 uint8 *cursor, *pcursor;
2434 uint8 *mask, *pmask;
2435 uint8 nextbit;
2436 int scanline, offset;
2437 int i, j;
2438
2439 scanline = (width + 7) / 8;
2440 offset = scanline * height;
2441
2442 cursor = (uint8 *) xmalloc(offset);
2443 memset(cursor, 0, offset);
2444
2445 mask = (uint8 *) xmalloc(offset);
2446 memset(mask, 0, offset);
2447
2448 /* approximate AND and XOR masks with a monochrome X pointer */
2449 for (i = 0; i < height; i++)
2450 {
2451 offset -= scanline;
2452 pcursor = &cursor[offset];
2453 pmask = &mask[offset];
2454
2455 for (j = 0; j < scanline; j++)
2456 {
2457 for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
2458 {
2459 if (xormask[0] || xormask[1] || xormask[2])
2460 {
2461 *pcursor |= (~(*andmask) & nextbit);
2462 *pmask |= nextbit;
2463 }
2464 else
2465 {
2466 *pcursor |= ((*andmask) & nextbit);
2467 *pmask |= (~(*andmask) & nextbit);
2468 }
2469
2470 xormask += 3;
2471 }
2472
2473 andmask++;
2474 pcursor++;
2475 pmask++;
2476 }
2477 }
2478
2479 fg.red = fg.blue = fg.green = 0xffff;
2480 bg.red = bg.blue = bg.green = 0x0000;
2481 fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
2482
2483 cursorglyph = ui_create_glyph(width, height, cursor);
2484 maskglyph = ui_create_glyph(width, height, mask);
2485
2486 xcursor =
2487 XCreatePixmapCursor(g_display, (Pixmap) cursorglyph,
2488 (Pixmap) maskglyph, &fg, &bg, x, y);
2489
2490 ui_destroy_glyph(maskglyph);
2491 ui_destroy_glyph(cursorglyph);
2492 xfree(mask);
2493 xfree(cursor);
2494 return (HCURSOR) xcursor;
2495 }
2496
2497 void
2498 ui_set_cursor(HCURSOR cursor)
2499 {
2500 g_current_cursor = (Cursor) cursor;
2501 XDefineCursor(g_display, g_wnd, g_current_cursor);
2502 ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor));
2503 }
2504
2505 void
2506 ui_destroy_cursor(HCURSOR cursor)
2507 {
2508 XFreeCursor(g_display, (Cursor) cursor);
2509 }
2510
2511 void
2512 ui_set_null_cursor(void)
2513 {
2514 ui_set_cursor(g_null_cursor);
2515 }
2516
2517 #define MAKE_XCOLOR(xc,c) \
2518 (xc)->red = ((c)->red << 8) | (c)->red; \
2519 (xc)->green = ((c)->green << 8) | (c)->green; \
2520 (xc)->blue = ((c)->blue << 8) | (c)->blue; \
2521 (xc)->flags = DoRed | DoGreen | DoBlue;
2522
2523
2524 HCOLOURMAP
2525 ui_create_colourmap(COLOURMAP * colours)
2526 {
2527 COLOURENTRY *entry;
2528 int i, ncolours = colours->ncolours;
2529 if (!g_owncolmap)
2530 {
2531 uint32 *map = (uint32 *) xmalloc(sizeof(*g_colmap) * ncolours);
2532 XColor xentry;
2533 XColor xc_cache[256];
2534 uint32 colour;
2535 int colLookup = 256;
2536 for (i = 0; i < ncolours; i++)
2537 {
2538 entry = &colours->colours[i];
2539 MAKE_XCOLOR(&xentry, entry);
2540
2541 if (XAllocColor(g_display, g_xcolmap, &xentry) == 0)
2542 {
2543 /* Allocation failed, find closest match. */
2544 int j = 256;
2545 int nMinDist = 3 * 256 * 256;
2546 long nDist = nMinDist;
2547
2548 /* only get the colors once */
2549 while (colLookup--)
2550 {
2551 xc_cache[colLookup].pixel = colLookup;
2552 xc_cache[colLookup].red = xc_cache[colLookup].green =
2553 xc_cache[colLookup].blue = 0;
2554 xc_cache[colLookup].flags = 0;
2555 XQueryColor(g_display,
2556 DefaultColormap(g_display,
2557 DefaultScreen(g_display)),
2558 &xc_cache[colLookup]);
2559 }
2560 colLookup = 0;
2561
2562 /* approximate the pixel */
2563 while (j--)
2564 {
2565 if (xc_cache[j].flags)
2566 {
2567 nDist = ((long) (xc_cache[j].red >> 8) -
2568 (long) (xentry.red >> 8)) *
2569 ((long) (xc_cache[j].red >> 8) -
2570 (long) (xentry.red >> 8)) +
2571 ((long) (xc_cache[j].green >> 8) -
2572 (long) (xentry.green >> 8)) *
2573 ((long) (xc_cache[j].green >> 8) -
2574 (long) (xentry.green >> 8)) +
2575 ((long) (xc_cache[j].blue >> 8) -
2576 (long) (xentry.blue >> 8)) *
2577 ((long) (xc_cache[j].blue >> 8) -
2578 (long) (xentry.blue >> 8));
2579 }
2580 if (nDist < nMinDist)
2581 {
2582 nMinDist = nDist;
2583 xentry.pixel = j;
2584 }
2585 }
2586 }
2587 colour = xentry.pixel;
2588
2589 /* update our cache */
2590 if (xentry.pixel < 256)
2591 {
2592 xc_cache[xentry.pixel].red = xentry.red;
2593 xc_cache[xentry.pixel].green = xentry.green;
2594 xc_cache[xentry.pixel].blue = xentry.blue;
2595
2596 }
2597
2598 map[i] = colour;
2599 }
2600 return map;
2601 }
2602 else
2603 {
2604 XColor *xcolours, *xentry;
2605 Colormap map;
2606
2607 xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
2608 for (i = 0; i < ncolours; i++)
2609 {
2610 entry = &colours->colours[i];
2611 xentry = &xcolours[i];
2612 xentry->pixel = i;
2613 MAKE_XCOLOR(xentry, entry);
2614 }
2615
2616 map = XCreateColormap(g_display, g_wnd, g_visual, AllocAll);
2617 XStoreColors(g_display, map, xcolours, ncolours);
2618
2619 xfree(xcolours);
2620 return (HCOLOURMAP) map;
2621 }
2622 }
2623
2624 void
2625 ui_destroy_colourmap(HCOLOURMAP map)
2626 {
2627 if (!g_owncolmap)
2628 xfree(map);
2629 else
2630 XFreeColormap(g_display, (Colormap) map);
2631 }
2632
2633 void
2634 ui_set_colourmap(HCOLOURMAP map)
2635 {
2636 if (!g_owncolmap)
2637 {
2638 if (g_colmap)
2639 xfree(g_colmap);
2640
2641 g_colmap = (uint32 *) map;
2642 }
2643 else
2644 {
2645 XSetWindowColormap(g_display, g_wnd, (Colormap) map);
2646 ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
2647 }
2648 }
2649
2650 void
2651 ui_set_clip(int x, int y, int cx, int cy)
2652 {
2653 g_clip_rectangle.x = x;
2654 g_clip_rectangle.y = y;
2655 g_clip_rectangle.width = cx;
2656 g_clip_rectangle.height = cy;
2657 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
2658 }
2659
2660 void
2661 ui_reset_clip(void)
2662 {
2663 g_clip_rectangle.x = 0;
2664 g_clip_rectangle.y = 0;
2665 g_clip_rectangle.width = g_width;
2666 g_clip_rectangle.height = g_height;
2667 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
2668 }
2669
2670 void
2671 ui_bell(void)
2672 {
2673 XBell(g_display, 0);
2674 }
2675
2676 void
2677 ui_destblt(uint8 opcode,
2678 /* dest */ int x, int y, int cx, int cy)
2679 {
2680 SET_FUNCTION(opcode);
2681 FILL_RECTANGLE(x, y, cx, cy);
2682 RESET_FUNCTION(opcode);
2683 }
2684
2685 static uint8 hatch_patterns[] = {
2686 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0 - bsHorizontal */
2687 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /* 1 - bsVertical */
2688 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, /* 2 - bsFDiagonal */
2689 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /* 3 - bsBDiagonal */
2690 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, /* 4 - bsCross */
2691 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 /* 5 - bsDiagCross */
2692 };
2693
2694 void
2695 ui_patblt(uint8 opcode,
2696 /* dest */ int x, int y, int cx, int cy,
2697 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2698 {
2699 Pixmap fill;
2700 uint8 i, ipattern[8];
2701
2702 SET_FUNCTION(opcode);
2703
2704 switch (brush->style)
2705 {
2706 case 0: /* Solid */
2707 SET_FOREGROUND(fgcolour);
2708 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2709 break;
2710
2711 case 2: /* Hatch */
2712 fill = (Pixmap) ui_create_glyph(8, 8,
2713 hatch_patterns + brush->pattern[0] * 8);
2714 SET_FOREGROUND(fgcolour);
2715 SET_BACKGROUND(bgcolour);
2716 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2717 XSetStipple(g_display, g_gc, fill);
2718 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2719 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2720 XSetFillStyle(g_display, g_gc, FillSolid);
2721 XSetTSOrigin(g_display, g_gc, 0, 0);
2722 ui_destroy_glyph((HGLYPH) fill);
2723 break;
2724
2725 case 3: /* Pattern */
2726 for (i = 0; i != 8; i++)
2727 ipattern[7 - i] = brush->pattern[i];
2728 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2729 SET_FOREGROUND(bgcolour);
2730 SET_BACKGROUND(fgcolour);
2731 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2732 XSetStipple(g_display, g_gc, fill);
2733 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2734 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2735 XSetFillStyle(g_display, g_gc, FillSolid);
2736 XSetTSOrigin(g_display, g_gc, 0, 0);
2737 ui_destroy_glyph((HGLYPH) fill);
2738 break;
2739
2740 default:
2741 unimpl("brush %d\n", brush->style);
2742 }
2743
2744 RESET_FUNCTION(opcode);
2745
2746 if (g_ownbackstore)
2747 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2748 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2749 (g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc,
2750 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2751 }
2752
2753 void
2754 ui_screenblt(uint8 opcode,
2755 /* dest */ int x, int y, int cx, int cy,
2756 /* src */ int srcx, int srcy)
2757 {
2758 SET_FUNCTION(opcode);
2759 if (g_ownbackstore)
2760 {
2761 XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
2762 g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2763 XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2764 }
2765 else
2766 {
2767 XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2768 }
2769
2770 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2771 (g_display, g_ownbackstore ? g_backstore : g_wnd,
2772 sw->wnd, g_gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2773
2774 RESET_FUNCTION(opcode);
2775 }
2776
2777 void
2778 ui_memblt(uint8 opcode,
2779 /* dest */ int x, int y, int cx, int cy,
2780 /* src */ HBITMAP src, int srcx, int srcy)
2781 {
2782 SET_FUNCTION(opcode);
2783 XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2784 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2785 (g_display, (Pixmap) src, sw->wnd, g_gc,
2786 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
2787 if (g_ownbackstore)
2788 XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2789 RESET_FUNCTION(opcode);
2790 }
2791
2792 void
2793 ui_triblt(uint8 opcode,
2794 /* dest */ int x, int y, int cx, int cy,
2795 /* src */ HBITMAP src, int srcx, int srcy,
2796 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2797 {
2798 /* This is potentially difficult to do in general. Until someone
2799 comes up with a more efficient way of doing it I am using cases. */
2800
2801 switch (opcode)
2802 {
2803 case 0x69: /* PDSxxn */
2804 ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
2805 ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2806 break;
2807
2808 case 0xb8: /* PSDPxax */
2809 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2810 ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
2811 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2812 break;
2813
2814 case 0xc0: /* PSa */
2815 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2816 ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
2817 break;
2818
2819 default:
2820 unimpl("triblt 0x%x\n", opcode);
2821 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2822 }
2823 }
2824
2825 void
2826 ui_line(uint8 opcode,
2827 /* dest */ int startx, int starty, int endx, int endy,
2828 /* pen */ PEN * pen)
2829 {
2830 SET_FUNCTION(opcode);
2831 SET_FOREGROUND(pen->colour);
2832 XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
2833 ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc,
2834 startx - sw->xoffset, starty - sw->yoffset,
2835 endx - sw->xoffset, endy - sw->yoffset));
2836 if (g_ownbackstore)
2837 XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
2838 RESET_FUNCTION(opcode);
2839 }
2840
2841 void
2842 ui_rect(
2843 /* dest */ int x, int y, int cx, int cy,
2844 /* brush */ int colour)
2845 {
2846 SET_FOREGROUND(colour);
2847 FILL_RECTANGLE(x, y, cx, cy);
2848 }
2849
2850 void
2851 ui_polygon(uint8 opcode,
2852 /* mode */ uint8 fillmode,
2853 /* dest */ POINT * point, int npoints,
2854 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2855 {
2856 uint8 style, i, ipattern[8];
2857 Pixmap fill;
2858
2859 SET_FUNCTION(opcode);
2860
2861 switch (fillmode)
2862 {
2863 case ALTERNATE:
2864 XSetFillRule(g_display, g_gc, EvenOddRule);
2865 break;
2866 case WINDING:
2867 XSetFillRule(g_display, g_gc, WindingRule);
2868 break;
2869 default:
2870 unimpl("fill mode %d\n", fillmode);
2871 }
2872
2873 if (brush)
2874 style = brush->style;
2875 else
2876 style = 0;
2877
2878 switch (style)
2879 {
2880 case 0: /* Solid */
2881 SET_FOREGROUND(fgcolour);
2882 FILL_POLYGON((XPoint *) point, npoints);
2883 break;
2884
2885 case 2: /* Hatch */
2886 fill = (Pixmap) ui_create_glyph(8, 8,
2887 hatch_patterns + brush->pattern[0] * 8);
2888 SET_FOREGROUND(fgcolour);
2889 SET_BACKGROUND(bgcolour);
2890 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2891 XSetStipple(g_display, g_gc, fill);
2892 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2893 FILL_POLYGON((XPoint *) point, npoints);
2894 XSetFillStyle(g_display, g_gc, FillSolid);
2895 XSetTSOrigin(g_display, g_gc, 0, 0);
2896 ui_destroy_glyph((HGLYPH) fill);
2897 break;
2898
2899 case 3: /* Pattern */
2900 for (i = 0; i != 8; i++)
2901 ipattern[7 - i] = brush->pattern[i];
2902 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2903 SET_FOREGROUND(bgcolour);
2904 SET_BACKGROUND(fgcolour);
2905 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2906 XSetStipple(g_display, g_gc, fill);
2907 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2908 FILL_POLYGON((XPoint *) point, npoints);
2909 XSetFillStyle(g_display, g_gc, FillSolid);
2910 XSetTSOrigin(g_display, g_gc, 0, 0);
2911 ui_destroy_glyph((HGLYPH) fill);
2912 break;
2913
2914 default:
2915 unimpl("brush %d\n", brush->style);
2916 }
2917
2918 RESET_FUNCTION(opcode);
2919 }
2920
2921 void
2922 ui_polyline(uint8 opcode,
2923 /* dest */ POINT * points, int npoints,
2924 /* pen */ PEN * pen)
2925 {
2926 /* TODO: set join style */
2927 SET_FUNCTION(opcode);
2928 SET_FOREGROUND(pen->colour);
2929 XDrawLines(g_display, g_wnd, g_gc, (XPoint *) points, npoints, CoordModePrevious);
2930 if (g_ownbackstore)
2931 XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
2932 CoordModePrevious);
2933
2934 ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
2935 (sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
2936
2937 RESET_FUNCTION(opcode);
2938 }
2939
2940 void
2941 ui_ellipse(uint8 opcode,
2942 /* mode */ uint8 fillmode,
2943 /* dest */ int x, int y, int cx, int cy,
2944 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2945 {
2946 uint8 style, i, ipattern[8];
2947 Pixmap fill;
2948
2949 SET_FUNCTION(opcode);
2950
2951 if (brush)
2952 style = brush->style;
2953 else
2954 style = 0;
2955
2956 switch (style)
2957 {
2958 case 0: /* Solid */
2959 SET_FOREGROUND(fgcolour);
2960 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2961 break;
2962
2963 case 2: /* Hatch */
2964 fill = (Pixmap) ui_create_glyph(8, 8,
2965 hatch_patterns + brush->pattern[0] * 8);
2966 SET_FOREGROUND(fgcolour);
2967 SET_BACKGROUND(bgcolour);
2968 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2969 XSetStipple(g_display, g_gc, fill);
2970 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2971 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2972 XSetFillStyle(g_display, g_gc, FillSolid);
2973 XSetTSOrigin(g_display, g_gc, 0, 0);
2974 ui_destroy_glyph((HGLYPH) fill);
2975 break;
2976
2977 case 3: /* Pattern */
2978 for (i = 0; i != 8; i++)
2979 ipattern[7 - i] = brush->pattern[i];
2980 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2981 SET_FOREGROUND(bgcolour);
2982 SET_BACKGROUND(fgcolour);
2983 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2984 XSetStipple(g_display, g_gc, fill);
2985 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2986 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2987 XSetFillStyle(g_display, g_gc, FillSolid);
2988 XSetTSOrigin(g_display, g_gc, 0, 0);
2989 ui_destroy_glyph((HGLYPH) fill);
2990 break;
2991
2992 default:
2993 unimpl("brush %d\n", brush->style);
2994 }
2995
2996 RESET_FUNCTION(opcode);
2997 }
2998
2999 /* warning, this function only draws on wnd or backstore, not both */
3000 void
3001 ui_draw_glyph(int mixmode,
3002 /* dest */ int x, int y, int cx, int cy,
3003 /* src */ HGLYPH glyph, int srcx, int srcy,
3004 int bgcolour, int fgcolour)
3005 {
3006 SET_FOREGROUND(fgcolour);
3007 SET_BACKGROUND(bgcolour);
3008
3009 XSetFillStyle(g_display, g_gc,
3010 (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
3011 XSetStipple(g_display, g_gc, (Pixmap) glyph);
3012 XSetTSOrigin(g_display, g_gc, x, y);
3013
3014 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
3015
3016 XSetFillStyle(g_display, g_gc, FillSolid);
3017 }
3018
3019 #define DO_GLYPH(ttext,idx) \
3020 {\
3021 glyph = cache_get_font (font, ttext[idx]);\
3022 if (!(flags & TEXT2_IMPLICIT_X))\
3023 {\
3024 xyoffset = ttext[++idx];\
3025 if ((xyoffset & 0x80))\
3026 {\
3027 if (flags & TEXT2_VERTICAL)\
3028 y += ttext[idx+1] | (ttext[idx+2] << 8);\
3029 else\
3030 x += ttext[idx+1] | (ttext[idx+2] << 8);\
3031 idx += 2;\
3032 }\
3033 else\
3034 {\
3035 if (flags & TEXT2_VERTICAL)\
3036 y += xyoffset;\
3037 else\
3038 x += xyoffset;\
3039 }\
3040 }\
3041 if (glyph != NULL)\
3042 {\
3043 x1 = x + glyph->offset;\
3044 y1 = y + glyph->baseline;\
3045 XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
3046 XSetTSOrigin(g_display, g_gc, x1, y1);\
3047 FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
3048 if (flags & TEXT2_IMPLICIT_X)\
3049 x += glyph->width;\
3050 }\
3051 }
3052
3053 void
3054 ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
3055 int clipx, int clipy, int clipcx, int clipcy,
3056 int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
3057 int bgcolour, int fgcolour, uint8 * text, uint8 length)
3058 {
3059 /* TODO: use brush appropriately */
3060
3061 FONTGLYPH *glyph;
3062 int i, j, xyoffset, x1, y1;
3063 DATABLOB *entry;
3064
3065 SET_FOREGROUND(bgcolour);
3066
3067 /* Sometimes, the boxcx value is something really large, like
3068 32691. This makes XCopyArea fail with Xvnc. The code below
3069 is a quick fix. */
3070 if (boxx + boxcx > g_width)
3071 boxcx = g_width - boxx;
3072
3073 if (boxcx > 1)
3074 {
3075 FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
3076 }
3077 else if (mixmode == MIX_OPAQUE)
3078 {
3079 FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
3080 }
3081
3082 SET_FOREGROUND(fgcolour);
3083 SET_BACKGROUND(bgcolour);
3084 XSetFillStyle(g_display, g_gc, FillStippled);
3085
3086 /* Paint text, character by character */
3087 for (i = 0; i < length;)
3088 {
3089 switch (text[i])
3090 {
3091 case 0xff:
3092 /* At least two bytes needs to follow */
3093 if (i + 3 > length)
3094 {
3095 warning("Skipping short 0xff command:");
3096 for (j = 0; j < length; j++)
3097 fprintf(stderr, "%02x ", text[j]);
3098 fprintf(stderr, "\n");
3099 i = length = 0;
3100 break;
3101 }
3102 cache_put_text(text[i + 1], text, text[i + 2]);
3103 i += 3;
3104 length -= i;
3105 /* this will move pointer from start to first character after FF command */
3106 text = &(text[i]);
3107 i = 0;
3108 break;
3109
3110 case 0xfe:
3111 /* At least one byte needs to follow */
3112 if (i + 2 > length)
3113 {
3114 warning("Skipping short 0xfe command:");
3115 for (j = 0; j < length; j++)
3116 fprintf(stderr, "%02x ", text[j]);
3117 fprintf(stderr, "\n");
3118 i = length = 0;
3119 break;
3120 }
3121 entry = cache_get_text(text[i + 1]);
3122 if (entry->data != NULL)
3123 {
3124 if ((((uint8 *) (entry->data))[1] == 0)
3125 && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
3126 {
3127 if (flags & TEXT2_VERTICAL)
3128 y += text[i + 2];
3129 else
3130 x += text[i + 2];
3131 }
3132 for (j = 0; j < entry->size; j++)
3133 DO_GLYPH(((uint8 *) (entry->data)), j);
3134 }
3135 if (i + 2 < length)
3136 i += 3;
3137 else
3138 i += 2;
3139 length -= i;
3140 /* this will move pointer from start to first character after FE command */
3141 text = &(text[i]);
3142 i = 0;
3143 break;
3144
3145 default:
3146 DO_GLYPH(text, i);
3147 i++;
3148 break;
3149 }
3150 }
3151
3152 XSetFillStyle(g_display, g_gc, FillSolid);
3153
3154 if (g_ownbackstore)
3155 {
3156 if (boxcx > 1)
3157 {
3158 XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
3159 boxy, boxcx, boxcy, boxx, boxy);
3160 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3161 (g_display, g_backstore, sw->wnd, g_gc,
3162 boxx, boxy,
3163 boxcx, boxcy,
3164 boxx - sw->xoffset, boxy - sw->yoffset));
3165 }
3166 else
3167 {
3168 XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
3169 clipy, clipcx, clipcy, clipx, clipy);
3170 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3171 (g_display, g_backstore, sw->wnd, g_gc,
3172 clipx, clipy,
3173 clipcx, clipcy, clipx - sw->xoffset,
3174 clipy - sw->yoffset));
3175 }
3176 }
3177 }
3178
3179 void
3180 ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
3181 {
3182 Pixmap pix;
3183 XImage *image;
3184
3185 if (g_ownbackstore)
3186 {
3187 image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
3188 }
3189 else
3190 {
3191 pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
3192 XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
3193 image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
3194 XFreePixmap(g_display, pix);
3195 }
3196
3197 offset *= g_bpp / 8;
3198 cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
3199
3200 XDestroyImage(image);
3201 }
3202
3203 void
3204 ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
3205 {
3206 XImage *image;
3207 uint8 *data;
3208
3209 offset *= g_bpp / 8;
3210 data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
3211 if (data == NULL)
3212 return;
3213
3214 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
3215 (char *) data, cx, cy, BitmapPad(g_display), cx * g_bpp / 8);
3216
3217 if (g_ownbackstore)
3218 {
3219 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
3220 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3221 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3222 (g_display, g_backstore, sw->wnd, g_gc,
3223 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3224 }
3225 else
3226 {
3227 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
3228 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3229 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
3230 x - sw->xoffset, y - sw->yoffset));
3231 }
3232
3233 XFree(image);
3234 }
3235
3236 /* these do nothing here but are used in uiports */
3237 void
3238 ui_begin_update(void)
3239 {
3240 }
3241
3242 void
3243 ui_end_update(void)
3244 {
3245 }
3246
3247
3248 void
3249 ui_seamless_begin(BOOL hidden)
3250 {
3251 if (!g_seamless_rdp)
3252 return;
3253
3254 if (g_seamless_started)
3255 return;
3256
3257 g_seamless_started = True;
3258 g_seamless_hidden = hidden;
3259
3260 if (!hidden)
3261 ui_seamless_toggle();
3262 }
3263
3264
3265 void
3266 ui_seamless_hide_desktop()
3267 {
3268 if (!g_seamless_rdp)
3269 return;
3270
3271 if (!g_seamless_started)
3272 return;
3273
3274 if (g_seamless_active)
3275 ui_seamless_toggle();
3276
3277 g_seamless_hidden = True;
3278 }
3279
3280
3281 void
3282 ui_seamless_unhide_desktop()
3283 {
3284 if (!g_seamless_rdp)
3285 return;
3286
3287 if (!g_seamless_started)
3288 return;
3289
3290 g_seamless_hidden = False;
3291
3292 ui_seamless_toggle();
3293 }
3294
3295
3296 void
3297 ui_seamless_toggle()
3298 {
3299 if (!g_seamless_rdp)
3300 return;
3301
3302 if (!g_seamless_started)
3303 return;
3304
3305 if (g_seamless_hidden)
3306 return;
3307
3308 if (g_seamless_active)
3309 {
3310 /* Deactivate */
3311 while (g_seamless_windows)
3312 {
3313 XDestroyWindow(g_display, g_seamless_windows->wnd);
3314 sw_remove_window(g_seamless_windows);
3315 }
3316 XMapWindow(g_display, g_wnd);
3317 }
3318 else
3319 {
3320 /* Activate */
3321 XUnmapWindow(g_display, g_wnd);
3322 seamless_send_sync();
3323 }
3324
3325 g_seamless_active = !g_seamless_active;
3326 }
3327
3328
3329 void
3330 ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent,
3331 unsigned long flags)
3332 {
3333 Window wnd;
3334 XSetWindowAttributes attribs;
3335 XClassHint *classhints;
3336 XSizeHints *sizehints;
3337 XWMHints *wmhints;
3338 long input_mask;
3339 seamless_window *sw, *sw_parent;
3340
3341 if (!g_seamless_active)
3342 return;
3343
3344 /* Ignore CREATEs for existing windows */
3345 sw = sw_get_window_by_id(id);
3346 if (sw)
3347 return;
3348
3349 get_window_attribs(&attribs);
3350 wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
3351 InputOutput, g_visual,
3352 CWBackPixel | CWBackingStore | CWColormap | CWBorderPixel, &attribs);
3353
3354 XStoreName(g_display, wnd, "SeamlessRDP");
3355 ewmh_set_wm_name(wnd, "SeamlessRDP");
3356
3357 mwm_hide_decorations(wnd);
3358
3359 classhints = XAllocClassHint();
3360 if (classhints != NULL)
3361 {
3362 classhints->res_name = "rdesktop";
3363 classhints->res_class = "SeamlessRDP";
3364 XSetClassHint(g_display, wnd, classhints);
3365 XFree(classhints);
3366 }
3367
3368 /* WM_NORMAL_HINTS */
3369 sizehints = XAllocSizeHints();
3370 if (sizehints != NULL)
3371 {
3372 sizehints->flags = USPosition;
3373 XSetWMNormalHints(g_display, wnd, sizehints);
3374 XFree(sizehints);
3375 }
3376
3377 /* Parent-less transient windows */
3378 if (parent == 0xFFFFFFFF)
3379 {
3380 XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3381 /* Some buggy wm:s (kwin) do not handle the above, so fake it
3382 using some other hints. */
3383 ewmh_set_window_popup(wnd);
3384 }
3385 /* Normal transient windows */
3386 else if (parent != 0x00000000)
3387 {
3388 sw_parent = sw_get_window_by_id(parent);
3389 if (sw_parent)
3390 XSetTransientForHint(g_display, wnd, sw_parent->wnd);
3391 else
3392 warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3393 }
3394
3395 if (flags & SEAMLESSRDP_CREATE_MODAL)
3396 {
3397 /* We do this to support buggy wm:s (*cough* metacity *cough*)
3398 somewhat at least */
3399 if (parent == 0x00000000)
3400 XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3401 ewmh_set_window_modal(wnd);
3402 }
3403
3404 /* FIXME: Support for Input Context:s */
3405
3406 get_input_mask(&input_mask);
3407 input_mask |= PropertyChangeMask;
3408
3409 XSelectInput(g_display, wnd, input_mask);
3410
3411 /* handle the WM_DELETE_WINDOW protocol. FIXME: When killing a
3412 seamless window, we could try to close the window on the
3413 serverside, instead of terminating rdesktop */
3414 XSetWMProtocols(g_display, wnd, &g_kill_atom, 1);
3415
3416 sw = xmalloc(sizeof(seamless_window));
3417 sw->wnd = wnd;
3418 sw->id = id;
3419 sw->behind = 0;
3420 sw->group = sw_find_group(group, False);
3421 sw->group->refcnt++;
3422 sw->xoffset = 0;
3423 sw->yoffset = 0;
3424 sw->width = 0;
3425 sw->height = 0;
3426 sw->state = SEAMLESSRDP_NOTYETMAPPED;
3427 sw->desktop = 0;
3428 sw->position_timer = xmalloc(sizeof(struct timeval));
3429 timerclear(sw->position_timer);
3430
3431 sw->outstanding_position = False;
3432 sw->outpos_serial = 0;
3433 sw->outpos_xoffset = sw->outpos_yoffset = 0;
3434 sw->outpos_width = sw->outpos_height = 0;
3435
3436 sw->next = g_seamless_windows;
3437 g_seamless_windows = sw;
3438
3439 /* WM_HINTS */
3440 wmhints = XAllocWMHints();
3441 if (wmhints)
3442 {
3443 wmhints->flags = WindowGroupHint;
3444 wmhints->window_group = sw->group->wnd;
3445 XSetWMHints(g_display, sw->wnd, wmhints);
3446 XFree(wmhints);
3447 }
3448 }
3449
3450
3451 void
3452 ui_seamless_destroy_window(unsigned long id, unsigned long flags)
3453 {
3454 seamless_window *sw;
3455
3456 if (!g_seamless_active)
3457 return;
3458
3459 sw = sw_get_window_by_id(id);
3460 if (!sw)
3461 {
3462 warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3463 return;
3464 }
3465
3466 XDestroyWindow(g_display, sw->wnd);
3467 sw_remove_window(sw);
3468 }
3469
3470
3471 void
3472 ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags)
3473 {
3474 seamless_window *sw;
3475
3476 if (!g_seamless_active)
3477 return;
3478
3479 sw = sw_get_window_by_id(id);
3480 if (!sw)
3481 {
3482 warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3483 return;
3484 }
3485
3486 /* We ignore server updates until it has handled our request. */
3487 if (sw->outstanding_position)
3488 return;
3489
3490 if (!width || !height)
3491 /* X11 windows must be at least 1x1 */
3492 return;
3493
3494 sw->xoffset = x;
3495 sw->yoffset = y;
3496 sw->width = width;
3497 sw->height = height;
3498
3499 /* If we move the window in a maximized state, then KDE won't
3500 accept restoration */
3501 switch (sw->state)
3502 {
3503 case SEAMLESSRDP_MINIMIZED:
3504 case SEAMLESSRDP_MAXIMIZED:
3505 return;
3506 }
3507
3508 /* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3509 XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3510 }
3511
3512
3513 void
3514 ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags)
3515 {
3516 seamless_window *sw;
3517
3518 if (!g_seamless_active)
3519 return;
3520
3521 sw = sw_get_window_by_id(id);
3522 if (!sw)
3523 {
3524 warning("ui_seamless_restack_window: No information for window 0x%lx\n", id);
3525 return;
3526 }
3527
3528 if (behind)
3529 {
3530 seamless_window *sw_behind;
3531 Window wnds[2];
3532
3533 sw_behind = sw_get_window_by_id(behind);
3534 if (!sw_behind)
3535 {
3536 warning("ui_seamless_restack_window: No information for window 0x%lx\n",
3537 behind);
3538 return;
3539 }
3540
3541 wnds[1] = sw_behind->wnd;
3542 wnds[0] = sw->wnd;
3543
3544 XRestackWindows(g_display, wnds, 2);
3545 }
3546 else
3547 {
3548 XRaiseWindow(g_display, sw->wnd);
3549 }
3550
3551 sw_restack_window(sw, behind);
3552 }
3553
3554
3555 void
3556 ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags)
3557 {
3558 seamless_window *sw;
3559
3560 if (!g_seamless_active)
3561 return;
3562
3563 sw = sw_get_window_by_id(id);
3564 if (!sw)
3565 {
3566 warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
3567 return;
3568 }
3569
3570 /* FIXME: Might want to convert the name for non-EWMH WMs */
3571 XStoreName(g_display, sw->wnd, title);
3572 ewmh_set_wm_name(sw->wnd, title);
3573 }
3574
3575
3576 void
3577 ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags)
3578 {
3579 seamless_window *sw;
3580
3581 if (!g_seamless_active)
3582 return;
3583
3584 sw = sw_get_window_by_id(id);
3585 if (!sw)
3586 {
3587 warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
3588 return;
3589 }
3590
3591 switch (state)
3592 {
3593 case SEAMLESSRDP_NORMAL:
3594 case SEAMLESSRDP_MAXIMIZED:
3595 ewmh_change_state(sw->wnd, state);
3596 XMapWindow(g_display, sw->wnd);
3597 break;
3598 case SEAMLESSRDP_MINIMIZED:
3599 /* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
3600 the Window Manager should probably just ignore the request, since
3601 _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
3602 such as minimization, rather than an independent state." Besides,
3603 XIconifyWindow is easier. */
3604 if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
3605 {
3606 XWMHints *hints;
3607 hints = XGetWMHints(g_display, sw->wnd);
3608 if (hints)
3609 {
3610 hints->flags |= StateHint;
3611 hints->initial_state = IconicState;
3612 XSetWMHints(g_display, sw->wnd, hints);
3613 XFree(hints);
3614 }
3615 XMapWindow(g_display, sw->wnd);
3616 }
3617 else
3618 XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display));
3619 break;
3620 default:
3621 warning("SeamlessRDP: Invalid state %d\n", state);
3622 break;
3623 }
3624
3625 sw->state = state;
3626 }
3627
3628
3629 void
3630 ui_seamless_syncbegin(unsigned long flags)
3631 {
3632 if (!g_seamless_active)
3633 return;
3634
3635 /* Destroy all seamless windows */
3636 while (g_seamless_windows)
3637 {
3638 XDestroyWindow(g_display, g_seamless_windows->wnd);
3639 sw_remove_window(g_seamless_windows);
3640 }
3641 }
3642
3643
3644 void
3645 ui_seamless_ack(unsigned int serial)
3646 {
3647 seamless_window *sw;
3648 for (sw = g_seamless_windows; sw; sw = sw->next)
3649 {
3650 if (sw->outstanding_position && (sw->outpos_serial == serial))
3651 {
3652 sw->xoffset = sw->outpos_xoffset;
3653 sw->yoffset = sw->outpos_yoffset;
3654 sw->width = sw->outpos_width;
3655 sw->height = sw->outpos_height;
3656 sw->outstanding_position = False;
3657
3658 /* Do a complete redraw of the window as part of the
3659 completion of the move. This is to remove any
3660 artifacts caused by our lack of synchronization. */
3661 XCopyArea(g_display, g_backstore,
3662 sw->wnd, g_gc,
3663 sw->xoffset, sw->yoffset, sw->width, sw->height, 0, 0);
3664
3665 break;
3666 }
3667 }
3668 }

  ViewVC Help
Powered by ViewVC 1.1.26