/[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 1453 - (show annotations)
Fri Mar 14 07:39:38 2008 UTC (16 years, 2 months ago) by astrand
File MIME type: text/plain
File size: 87587 byte(s)
SeamlessRDP bugfix: The first window to XRestackWindows should be the
reference window (the "behind" window); not the window to restack.

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

  ViewVC Help
Powered by ViewVC 1.1.26