/[rdesktop]/sourceforge.net/trunk/rdesktop/xwin.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /sourceforge.net/trunk/rdesktop/xwin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1461 - (hide annotations)
Wed Mar 26 17:16:32 2008 UTC (16 years, 1 month ago) by astrand
File MIME type: text/plain
File size: 95503 byte(s)
Decreased timeout in sw_wait_configurenotify to one second, to reduce
frusrtation with old metacity versions. Need to use gettimeofday.

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

  ViewVC Help
Powered by ViewVC 1.1.26