/[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 1372 - (show annotations)
Mon Jan 8 04:47:06 2007 UTC (17 years, 4 months ago) by jsorg71
File MIME type: text/plain
File size: 84173 byte(s)
prefix BOOL with RD_

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

  ViewVC Help
Powered by ViewVC 1.1.26