VirtualBox

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

Last change on this file since 34346 was 33656, checked in by vboxsync, 14 years ago

*: rebrand Sun (L)GPL disclaimers

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