/[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 1407 - (show annotations)
Mon May 14 12:11:15 2007 UTC (17 years, 1 month ago) by astrand
File MIME type: text/plain
File size: 85263 byte(s)
Send physical mouse buttons over RDP rather than logical ones. Fixes bug 1693905. Patch from Ilya Konstantinov (slightly modified)

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

  ViewVC Help
Powered by ViewVC 1.1.26