/[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 1413 - (show annotations)
Mon Jun 18 12:00:34 2007 UTC (16 years, 11 months ago) by ossman_
File MIME type: text/plain
File size: 87100 byte(s)
Implement support for icons in SeamlessRDP.

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

  ViewVC Help
Powered by ViewVC 1.1.26