VirtualBox

source: vbox/trunk/src/VBox/RDP/client/xwin.c@ 10809

Last change on this file since 10809 was 9902, checked in by vboxsync, 17 years ago

Added rdesktop 1.6.0.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette