/[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 1306 - (show annotations)
Fri Oct 27 12:59:38 2006 UTC (17 years, 6 months ago) by astrand
File MIME type: text/plain
File size: 84126 byte(s)
Prevent segfaults in out of memory conditions by checking the pointer returned from XGetImage.

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

  ViewVC Help
Powered by ViewVC 1.1.26