VirtualBox

source: vbox/trunk/src/VBox/RDP/client-1.8.3/xwin.c@ 60190

Last change on this file since 60190 was 55123, checked in by vboxsync, 10 years ago

rdesktop 1.8.3 modified for VBox

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