/[rdesktop]/sourceforge.net/branches/seamlessrdp-branch/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/branches/seamlessrdp-branch/rdesktop/xwin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1182 - (show annotations)
Tue Mar 21 15:31:48 2006 UTC (18 years, 1 month ago) by ossman_
File MIME type: text/plain
File size: 83146 byte(s)
Must check outstanding_position since outpos_serial isn't valid otherwise.

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

  ViewVC Help
Powered by ViewVC 1.1.26