/[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 1417 - (show annotations)
Thu Aug 30 04:47:36 2007 UTC (16 years, 9 months ago) by jsorg71
File MIME type: text/plain
File size: 87190 byte(s)
32 bit color

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

  ViewVC Help
Powered by ViewVC 1.1.26