/[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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 821 by jdmeijer, Mon Feb 28 01:15:45 2005 UTC revision 1407 by astrand, Mon May 14 12:11:15 2007 UTC
# Line 1  Line 1 
1  /* -*- c-basic-offset: 8 -*-  /* -*- c-basic-offset: 8 -*-
2     rdesktop: A Remote Desktop Protocol client.     rdesktop: A Remote Desktop Protocol client.
3     User interface services - X Window System     User interface services - X Window System
4     Copyright (C) Matthew Chapman 1999-2002     Copyright (C) Matthew Chapman 1999-2007
5    
6     This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
# Line 20  Line 20 
20    
21  #include <X11/Xlib.h>  #include <X11/Xlib.h>
22  #include <X11/Xutil.h>  #include <X11/Xutil.h>
23    #include <X11/Xproto.h>
24  #include <unistd.h>  #include <unistd.h>
25  #include <sys/time.h>  #include <sys/time.h>
26  #include <time.h>  #include <time.h>
# Line 32  extern int g_width; Line 33  extern int g_width;
33  extern int g_height;  extern int g_height;
34  extern int g_xpos;  extern int g_xpos;
35  extern int g_ypos;  extern int g_ypos;
36  extern BOOL g_sendmotion;  extern int g_pos;
37  extern BOOL g_fullscreen;  extern RD_BOOL g_sendmotion;
38  extern BOOL g_grab_keyboard;  extern RD_BOOL g_fullscreen;
39  extern BOOL g_hide_decorations;  extern RD_BOOL g_grab_keyboard;
40    extern RD_BOOL g_hide_decorations;
41  extern char g_title[];  extern char g_title[];
42  extern int g_server_bpp;  /* Color depth of the RDP session.
43       As of RDP 5.1, it may be 8, 15, 16 or 24. */
44    extern int g_server_depth;
45  extern int g_win_button_size;  extern int g_win_button_size;
46    
47  Display *g_display;  Display *g_display;
# Line 45  Time g_last_gesturetime; Line 49  Time g_last_gesturetime;
49  static int g_x_socket;  static int g_x_socket;
50  static Screen *g_screen;  static Screen *g_screen;
51  Window g_wnd;  Window g_wnd;
52    
53    /* SeamlessRDP support */
54    typedef struct _seamless_group
55    {
56            Window wnd;
57            unsigned long id;
58            unsigned int refcnt;
59    } seamless_group;
60    typedef struct _seamless_window
61    {
62            Window wnd;
63            unsigned long id;
64            unsigned long behind;
65            seamless_group *group;
66            int xoffset, yoffset;
67            int width, height;
68            int state;              /* normal/minimized/maximized. */
69            unsigned int desktop;
70            struct timeval *position_timer;
71    
72            RD_BOOL outstanding_position;
73            unsigned int outpos_serial;
74            int outpos_xoffset, outpos_yoffset;
75            int outpos_width, outpos_height;
76    
77            struct _seamless_window *next;
78    } seamless_window;
79    static seamless_window *g_seamless_windows = NULL;
80    static unsigned long g_seamless_focused = 0;
81    static RD_BOOL g_seamless_started = False;      /* Server end is up and running */
82    static RD_BOOL g_seamless_active = False;       /* We are currently in seamless mode */
83    static RD_BOOL g_seamless_hidden = False;       /* Desktop is hidden on server */
84    extern RD_BOOL g_seamless_rdp;
85    
86  extern uint32 g_embed_wnd;  extern uint32 g_embed_wnd;
87  BOOL g_enable_compose = False;  RD_BOOL g_enable_compose = False;
88  BOOL g_Unobscured;              /* used for screenblt */  RD_BOOL g_Unobscured;           /* used for screenblt */
89  static GC g_gc = NULL;  static GC g_gc = NULL;
90  static GC g_create_bitmap_gc = NULL;  static GC g_create_bitmap_gc = NULL;
91  static GC g_create_glyph_gc = NULL;  static GC g_create_glyph_gc = NULL;
92    static XRectangle g_clip_rectangle;
93  static Visual *g_visual;  static Visual *g_visual;
94    /* Color depth of the X11 visual of our window (e.g. 24 for True Color R8G8B visual).
95       This may be 32 for R8G8B8 visuals, and then the rest of the bits are undefined
96       as far as we're concerned. */
97  static int g_depth;  static int g_depth;
98    /* Bits-per-Pixel of the pixmaps we'll be using to draw on our window.
99       This may be larger than g_depth, in which case some of the bits would
100       be kept solely for alignment (e.g. 32bpp pixmaps on a 24bpp visual). */
101  static int g_bpp;  static int g_bpp;
102  static XIM g_IM;  static XIM g_IM;
103  static XIC g_IC;  static XIC g_IC;
104  static XModifierKeymap *g_mod_map;  static XModifierKeymap *g_mod_map;
105    /* Maps logical (xmodmap -pp) pointing device buttons (0-based) back
106       to physical (1-based) indices. */
107    static unsigned char g_pointer_log_to_phys_map[16];
108  static Cursor g_current_cursor;  static Cursor g_current_cursor;
109  static HCURSOR g_null_cursor = NULL;  static RD_HCURSOR g_null_cursor = NULL;
110  static Atom g_protocol_atom, g_kill_atom;  static Atom g_protocol_atom, g_kill_atom;
111  static BOOL g_focused;  extern Atom g_net_wm_state_atom;
112  static BOOL g_mouse_in_wnd;  extern Atom g_net_wm_desktop_atom;
113  static BOOL g_arch_match = False;       /* set to True if RGB XServer and little endian */  static RD_BOOL g_focused;
114    static RD_BOOL g_mouse_in_wnd;
115    /* Indicates that:
116       1) visual has 15, 16 or 24 depth and the same color channel masks
117          as its RDP equivalent (implies X server is LE),
118       2) host is LE
119       This will trigger an optimization whose real value is questionable.
120    */
121    static RD_BOOL g_compatible_arch;
122    /* Indicates whether RDP's bitmaps and our XImages have the same
123       binary format. If so, we can avoid an expensive translation.
124       Note that this can be true when g_compatible_arch is false,
125       e.g.:
126      
127         RDP(LE) <-> host(BE) <-> X-Server(LE)
128        
129       ('host' is the machine running rdesktop; the host simply memcpy's
130        so its endianess doesn't matter)
131     */
132    static RD_BOOL g_no_translate_image = False;
133    
134  /* endianness */  /* endianness */
135  static BOOL g_host_be;  static RD_BOOL g_host_be;
136  static BOOL g_xserver_be;  static RD_BOOL g_xserver_be;
137  static int g_red_shift_r, g_blue_shift_r, g_green_shift_r;  static int g_red_shift_r, g_blue_shift_r, g_green_shift_r;
138  static int g_red_shift_l, g_blue_shift_l, g_green_shift_l;  static int g_red_shift_l, g_blue_shift_l, g_green_shift_l;
139    
140  /* software backing store */  /* software backing store */
141  extern BOOL g_ownbackstore;  extern RD_BOOL g_ownbackstore;
142  static Pixmap g_backstore = 0;  static Pixmap g_backstore = 0;
143    
144  /* Moving in single app mode */  /* Moving in single app mode */
145  static BOOL g_moving_wnd;  static RD_BOOL g_moving_wnd;
146  static int g_move_x_offset = 0;  static int g_move_x_offset = 0;
147  static int g_move_y_offset = 0;  static int g_move_y_offset = 0;
148    static RD_BOOL g_using_full_workarea = False;
149    
150  #ifdef WITH_RDPSND  #ifdef WITH_RDPSND
151  extern int g_dsp_fd;  extern RD_BOOL g_rdpsnd;
 extern BOOL g_dsp_busy;  
 extern BOOL g_rdpsnd;  
152  #endif  #endif
153    
154  /* MWM decorations */  /* MWM decorations */
# Line 90  extern BOOL g_rdpsnd; Line 156  extern BOOL g_rdpsnd;
156  #define PROP_MOTIF_WM_HINTS_ELEMENTS    5  #define PROP_MOTIF_WM_HINTS_ELEMENTS    5
157  typedef struct  typedef struct
158  {  {
159          uint32 flags;          unsigned long flags;
160          uint32 functions;          unsigned long functions;
161          uint32 decorations;          unsigned long decorations;
162          sint32 inputMode;          long inputMode;
163          uint32 status;          unsigned long status;
164  }  }
165  PropMotifWmHints;  PropMotifWmHints;
166    
# Line 106  typedef struct Line 172  typedef struct
172  }  }
173  PixelColour;  PixelColour;
174    
175    #define ON_ALL_SEAMLESS_WINDOWS(func, args) \
176            do { \
177                    seamless_window *sw; \
178                    XRectangle rect; \
179                    if (!g_seamless_windows) break; \
180                    for (sw = g_seamless_windows; sw; sw = sw->next) { \
181                        rect.x = g_clip_rectangle.x - sw->xoffset; \
182                        rect.y = g_clip_rectangle.y - sw->yoffset; \
183                        rect.width = g_clip_rectangle.width; \
184                        rect.height = g_clip_rectangle.height; \
185                        XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded); \
186                        func args; \
187                    } \
188                    XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded); \
189            } while (0)
190    
191    static void
192    seamless_XFillPolygon(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
193    {
194            points[0].x -= xoffset;
195            points[0].y -= yoffset;
196            XFillPolygon(g_display, d, g_gc, points, npoints, Complex, CoordModePrevious);
197            points[0].x += xoffset;
198            points[0].y += yoffset;
199    }
200    
201    static void
202    seamless_XDrawLines(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
203    {
204            points[0].x -= xoffset;
205            points[0].y -= yoffset;
206            XDrawLines(g_display, d, g_gc, points, npoints, CoordModePrevious);
207            points[0].x += xoffset;
208            points[0].y += yoffset;
209    }
210    
211  #define FILL_RECTANGLE(x,y,cx,cy)\  #define FILL_RECTANGLE(x,y,cx,cy)\
212  { \  { \
213          XFillRectangle(g_display, g_wnd, g_gc, x, y, cx, cy); \          XFillRectangle(g_display, g_wnd, g_gc, x, y, cx, cy); \
214            ON_ALL_SEAMLESS_WINDOWS(XFillRectangle, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy)); \
215          if (g_ownbackstore) \          if (g_ownbackstore) \
216                  XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \                  XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \
217  }  }
# Line 119  PixelColour; Line 221  PixelColour;
221          XFillRectangle(g_display, g_ownbackstore ? g_backstore : g_wnd, g_gc, x, y, cx, cy); \          XFillRectangle(g_display, g_ownbackstore ? g_backstore : g_wnd, g_gc, x, y, cx, cy); \
222  }  }
223    
224    #define FILL_POLYGON(p,np)\
225    { \
226            XFillPolygon(g_display, g_wnd, g_gc, p, np, Complex, CoordModePrevious); \
227            if (g_ownbackstore) \
228                    XFillPolygon(g_display, g_backstore, g_gc, p, np, Complex, CoordModePrevious); \
229            ON_ALL_SEAMLESS_WINDOWS(seamless_XFillPolygon, (sw->wnd, p, np, sw->xoffset, sw->yoffset)); \
230    }
231    
232    #define DRAW_ELLIPSE(x,y,cx,cy,m)\
233    { \
234            switch (m) \
235            { \
236                    case 0: /* Outline */ \
237                            XDrawArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \
238                            ON_ALL_SEAMLESS_WINDOWS(XDrawArc, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \
239                            if (g_ownbackstore) \
240                                    XDrawArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
241                            break; \
242                    case 1: /* Filled */ \
243                            XFillArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \
244                            ON_ALL_SEAMLESS_WINDOWS(XFillArc, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \
245                            if (g_ownbackstore) \
246                                    XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
247                            break; \
248            } \
249    }
250    
251  /* colour maps */  /* colour maps */
252  extern BOOL g_owncolmap;  extern RD_BOOL g_owncolmap;
253  static Colormap g_xcolmap;  static Colormap g_xcolmap;
254  static uint32 *g_colmap = NULL;  static uint32 *g_colmap = NULL;
255    
256  #define TRANSLATE(col)          ( g_server_bpp != 8 ? translate_colour(col) : g_owncolmap ? col : g_colmap[col] )  #define TRANSLATE(col)          ( g_server_depth != 8 ? translate_colour(col) : g_owncolmap ? col : g_colmap[col] )
257  #define SET_FOREGROUND(col)     XSetForeground(g_display, g_gc, TRANSLATE(col));  #define SET_FOREGROUND(col)     XSetForeground(g_display, g_gc, TRANSLATE(col));
258  #define SET_BACKGROUND(col)     XSetBackground(g_display, g_gc, TRANSLATE(col));  #define SET_BACKGROUND(col)     XSetBackground(g_display, g_gc, TRANSLATE(col));
259    
# Line 150  static int rop2_map[] = { Line 279  static int rop2_map[] = {
279  #define SET_FUNCTION(rop2)      { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, rop2_map[rop2]); }  #define SET_FUNCTION(rop2)      { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, rop2_map[rop2]); }
280  #define RESET_FUNCTION(rop2)    { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); }  #define RESET_FUNCTION(rop2)    { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); }
281    
282    static seamless_window *
283    sw_get_window_by_id(unsigned long id)
284    {
285            seamless_window *sw;
286            for (sw = g_seamless_windows; sw; sw = sw->next)
287            {
288                    if (sw->id == id)
289                            return sw;
290            }
291            return NULL;
292    }
293    
294    
295    static seamless_window *
296    sw_get_window_by_wnd(Window wnd)
297    {
298            seamless_window *sw;
299            for (sw = g_seamless_windows; sw; sw = sw->next)
300            {
301                    if (sw->wnd == wnd)
302                            return sw;
303            }
304            return NULL;
305    }
306    
307    
308    static void
309    sw_remove_window(seamless_window * win)
310    {
311            seamless_window *sw, **prevnext = &g_seamless_windows;
312            for (sw = g_seamless_windows; sw; sw = sw->next)
313            {
314                    if (sw == win)
315                    {
316                            *prevnext = sw->next;
317                            sw->group->refcnt--;
318                            if (sw->group->refcnt == 0)
319                            {
320                                    XDestroyWindow(g_display, sw->group->wnd);
321                                    xfree(sw->group);
322                            }
323                            xfree(sw->position_timer);
324                            xfree(sw);
325                            return;
326                    }
327                    prevnext = &sw->next;
328            }
329            return;
330    }
331    
332    
333    /* Move all windows except wnd to new desktop */
334    static void
335    sw_all_to_desktop(Window wnd, unsigned int desktop)
336    {
337            seamless_window *sw;
338            for (sw = g_seamless_windows; sw; sw = sw->next)
339            {
340                    if (sw->wnd == wnd)
341                            continue;
342                    if (sw->desktop != desktop)
343                    {
344                            ewmh_move_to_desktop(sw->wnd, desktop);
345                            sw->desktop = desktop;
346                    }
347            }
348    }
349    
350    
351    /* Send our position */
352    static void
353    sw_update_position(seamless_window * sw)
354    {
355            XWindowAttributes wa;
356            int x, y;
357            Window child_return;
358            unsigned int serial;
359    
360            XGetWindowAttributes(g_display, sw->wnd, &wa);
361            XTranslateCoordinates(g_display, sw->wnd, wa.root,
362                                  -wa.border_width, -wa.border_width, &x, &y, &child_return);
363    
364            serial = seamless_send_position(sw->id, x, y, wa.width, wa.height, 0);
365    
366            sw->outstanding_position = True;
367            sw->outpos_serial = serial;
368    
369            sw->outpos_xoffset = x;
370            sw->outpos_yoffset = y;
371            sw->outpos_width = wa.width;
372            sw->outpos_height = wa.height;
373    }
374    
375    
376    /* Check if it's time to send our position */
377    static void
378    sw_check_timers()
379    {
380            seamless_window *sw;
381            struct timeval now;
382    
383            gettimeofday(&now, NULL);
384            for (sw = g_seamless_windows; sw; sw = sw->next)
385            {
386                    if (timerisset(sw->position_timer) && timercmp(sw->position_timer, &now, <))
387                    {
388                            timerclear(sw->position_timer);
389                            sw_update_position(sw);
390                    }
391            }
392    }
393    
394    
395    static void
396    sw_restack_window(seamless_window * sw, unsigned long behind)
397    {
398            seamless_window *sw_above;
399    
400            /* Remove window from stack */
401            for (sw_above = g_seamless_windows; sw_above; sw_above = sw_above->next)
402            {
403                    if (sw_above->behind == sw->id)
404                            break;
405            }
406    
407            if (sw_above)
408                    sw_above->behind = sw->behind;
409    
410            /* And then add it at the new position */
411            for (sw_above = g_seamless_windows; sw_above; sw_above = sw_above->next)
412            {
413                    if (sw_above->behind == behind)
414                            break;
415            }
416    
417            if (sw_above)
418                    sw_above->behind = sw->id;
419    
420            sw->behind = behind;
421    }
422    
423    
424    static void
425    sw_handle_restack(seamless_window * sw)
426    {
427            Status status;
428            Window root, parent, *children;
429            unsigned int nchildren, i;
430            seamless_window *sw_below;
431    
432            status = XQueryTree(g_display, RootWindowOfScreen(g_screen),
433                                &root, &parent, &children, &nchildren);
434            if (!status || !nchildren)
435                    return;
436    
437            sw_below = NULL;
438    
439            i = 0;
440            while (children[i] != sw->wnd)
441            {
442                    i++;
443                    if (i >= nchildren)
444                            goto end;
445            }
446    
447            for (i++; i < nchildren; i++)
448            {
449                    sw_below = sw_get_window_by_wnd(children[i]);
450                    if (sw_below)
451                            break;
452            }
453    
454            if (!sw_below && !sw->behind)
455                    goto end;
456            if (sw_below && (sw_below->id == sw->behind))
457                    goto end;
458    
459            if (sw_below)
460            {
461                    seamless_send_zchange(sw->id, sw_below->id, 0);
462                    sw_restack_window(sw, sw_below->id);
463            }
464            else
465            {
466                    seamless_send_zchange(sw->id, 0, 0);
467                    sw_restack_window(sw, 0);
468            }
469    
470          end:
471            XFree(children);
472    }
473    
474    
475    static seamless_group *
476    sw_find_group(unsigned long id, RD_BOOL dont_create)
477    {
478            seamless_window *sw;
479            seamless_group *sg;
480            XSetWindowAttributes attribs;
481    
482            for (sw = g_seamless_windows; sw; sw = sw->next)
483            {
484                    if (sw->group->id == id)
485                            return sw->group;
486            }
487    
488            if (dont_create)
489                    return NULL;
490    
491            sg = xmalloc(sizeof(seamless_group));
492    
493            sg->wnd =
494                    XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0,
495                                  CopyFromParent, CopyFromParent, CopyFromParent, 0, &attribs);
496    
497            sg->id = id;
498            sg->refcnt = 0;
499    
500            return sg;
501    }
502    
503    
504  static void  static void
505  mwm_hide_decorations(void)  mwm_hide_decorations(Window wnd)
506  {  {
507          PropMotifWmHints motif_hints;          PropMotifWmHints motif_hints;
508          Atom hintsatom;          Atom hintsatom;
# Line 168  mwm_hide_decorations(void) Line 519  mwm_hide_decorations(void)
519                  return;                  return;
520          }          }
521    
522          XChangeProperty(g_display, g_wnd, hintsatom, hintsatom, 32, PropModeReplace,          XChangeProperty(g_display, wnd, hintsatom, hintsatom, 32, PropModeReplace,
523                          (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);                          (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
524    
525  }  }
526    
527  #define SPLITCOLOUR15(colour, rv) \  #define SPLITCOLOUR15(colour, rv) \
# Line 203  mwm_hide_decorations(void) Line 555  mwm_hide_decorations(void)
555  #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \  #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \
556                          x = (x << 16) | (x >> 16); }                          x = (x << 16) | (x >> 16); }
557    
558    /* The following macros output the same octet sequences
559       on both BE and LE hosts: */
560    
561  #define BOUT16(o, x) { *(o++) = x >> 8; *(o++) = x; }  #define BOUT16(o, x) { *(o++) = x >> 8; *(o++) = x; }
562  #define BOUT24(o, x) { *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }  #define BOUT24(o, x) { *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
563  #define BOUT32(o, x) { *(o++) = x >> 24; *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }  #define BOUT32(o, x) { *(o++) = x >> 24; *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
# Line 214  static uint32 Line 569  static uint32
569  translate_colour(uint32 colour)  translate_colour(uint32 colour)
570  {  {
571          PixelColour pc;          PixelColour pc;
572          switch (g_server_bpp)          switch (g_server_depth)
573          {          {
574                  case 15:                  case 15:
575                          SPLITCOLOUR15(colour, pc);                          SPLITCOLOUR15(colour, pc);
# Line 225  translate_colour(uint32 colour) Line 580  translate_colour(uint32 colour)
580                  case 24:                  case 24:
581                          SPLITCOLOUR24(colour, pc);                          SPLITCOLOUR24(colour, pc);
582                          break;                          break;
583                    default:
584                            /* Avoid warning */
585                            pc.red = 0;
586                            pc.green = 0;
587                            pc.blue = 0;
588                            break;
589          }          }
590          return MAKECOLOUR(pc);          return MAKECOLOUR(pc);
591  }  }
# Line 276  translate8to16(const uint8 * data, uint8 Line 637  translate8to16(const uint8 * data, uint8
637  {  {
638          uint16 value;          uint16 value;
639    
640          if (g_arch_match)          if (g_compatible_arch)
641          {          {
642                  /* *INDENT-OFF* */                  /* *INDENT-OFF* */
643                  REPEAT2                  REPEAT2
# Line 310  translate8to24(const uint8 * data, uint8 Line 671  translate8to24(const uint8 * data, uint8
671  {  {
672          uint32 value;          uint32 value;
673    
674          if (g_xserver_be)          if (g_compatible_arch)
675          {          {
676                  while (out < end)                  while (out < end)
677                  {                  {
# Line 333  translate8to32(const uint8 * data, uint8 Line 694  translate8to32(const uint8 * data, uint8
694  {  {
695          uint32 value;          uint32 value;
696    
697          if (g_arch_match)          if (g_compatible_arch)
698          {          {
699                  /* *INDENT-OFF* */                  /* *INDENT-OFF* */
700                  REPEAT4                  REPEAT4
# Line 405  translate15to24(const uint16 * data, uin Line 766  translate15to24(const uint16 * data, uin
766          uint16 pixel;          uint16 pixel;
767          PixelColour pc;          PixelColour pc;
768    
769          if (g_arch_match)          if (g_compatible_arch)
770          {          {
771                  /* *INDENT-OFF* */                  /* *INDENT-OFF* */
772                  REPEAT3                  REPEAT3
# Line 455  translate15to32(const uint16 * data, uin Line 816  translate15to32(const uint16 * data, uin
816          uint32 value;          uint32 value;
817          PixelColour pc;          PixelColour pc;
818    
819          if (g_arch_match)          if (g_compatible_arch)
820          {          {
821                  /* *INDENT-OFF* */                  /* *INDENT-OFF* */
822                  REPEAT4                  REPEAT4
# Line 563  translate16to24(const uint16 * data, uin Line 924  translate16to24(const uint16 * data, uin
924          uint16 pixel;          uint16 pixel;
925          PixelColour pc;          PixelColour pc;
926    
927          if (g_arch_match)          if (g_compatible_arch)
928          {          {
929                  /* *INDENT-OFF* */                  /* *INDENT-OFF* */
930                  REPEAT3                  REPEAT3
# Line 633  translate16to32(const uint16 * data, uin Line 994  translate16to32(const uint16 * data, uin
994          uint32 value;          uint32 value;
995          PixelColour pc;          PixelColour pc;
996    
997          if (g_arch_match)          if (g_compatible_arch)
998          {          {
999                  /* *INDENT-OFF* */                  /* *INDENT-OFF* */
1000                  REPEAT4                  REPEAT4
# Line 662  translate16to32(const uint16 * data, uin Line 1023  translate16to32(const uint16 * data, uin
1023                  }                  }
1024                  else                  else
1025                  {                  {
1026                            while (out < end)
1027                          {                          {
1028                                  pixel = *(data++);                                  pixel = *(data++);
1029                                  SPLITCOLOUR16(pixel, pc);                                  SPLITCOLOUR16(pixel, pc);
# Line 761  translate24to32(const uint8 * data, uint Line 1123  translate24to32(const uint8 * data, uint
1123          uint32 value;          uint32 value;
1124          PixelColour pc;          PixelColour pc;
1125    
1126          if (g_arch_match)          if (g_compatible_arch)
1127          {          {
1128                  /* *INDENT-OFF* */                  /* *INDENT-OFF* */
1129    #ifdef NEED_ALIGN
1130                  REPEAT4                  REPEAT4
1131                  (                  (
 #ifdef NEED_ALIGN  
1132                          *(out++) = *(data++);                          *(out++) = *(data++);
1133                          *(out++) = *(data++);                          *(out++) = *(data++);
1134                          *(out++) = *(data++);                          *(out++) = *(data++);
1135                          *(out++) = 0;                          *(out++) = 0;
1136                    )
1137  #else  #else
1138                          *((uint32 *) out) = *((uint32 *) data);                  REPEAT4
1139                          out += 4;                  (
1140                          data += 3;                   /* Only read 3 bytes. Reading 4 bytes means reading beyond buffer. */
1141  #endif                   *((uint32 *) out) = *((uint16 *) data) + (*((uint8 *) data + 2) << 16);
1142                     out += 4;
1143                     data += 3;
1144                  )                  )
1145    #endif
1146                  /* *INDENT-ON* */                  /* *INDENT-ON* */
1147          }          }
1148          else if (g_xserver_be)          else if (g_xserver_be)
# Line 812  translate_image(int width, int height, u Line 1178  translate_image(int width, int height, u
1178          uint8 *out;          uint8 *out;
1179          uint8 *end;          uint8 *end;
1180    
1181          /* if server and xserver bpp match, */          /*
1182          /* and arch(endian) matches, no need to translate */             If RDP depth and X Visual depths match,
1183          /* just return data */             and arch(endian) matches, no need to translate:
1184          if (g_arch_match)             just return data.
1185               Note: select_visual should've already ensured g_no_translate
1186               is only set for compatible depths, but the RDP depth might've
1187               changed during connection negotiations.
1188             */
1189            if (g_no_translate_image)
1190          {          {
1191                  if (g_depth == 15 && g_server_bpp == 15)                  if ((g_depth == 15 && g_server_depth == 15) ||
1192                          return data;                      (g_depth == 16 && g_server_depth == 16) ||
1193                  if (g_depth == 16 && g_server_bpp == 16)                      (g_depth == 24 && g_server_depth == 24))
                         return data;  
                 if (g_depth == 24 && g_bpp == 24 && g_server_bpp == 24)  
1194                          return data;                          return data;
1195          }          }
1196    
# Line 829  translate_image(int width, int height, u Line 1198  translate_image(int width, int height, u
1198          out = (uint8 *) xmalloc(size);          out = (uint8 *) xmalloc(size);
1199          end = out + size;          end = out + size;
1200    
1201          switch (g_server_bpp)          switch (g_server_depth)
1202          {          {
1203                  case 24:                  case 24:
1204                          switch (g_bpp)                          switch (g_bpp)
# Line 894  translate_image(int width, int height, u Line 1263  translate_image(int width, int height, u
1263          return out;          return out;
1264  }  }
1265    
1266  BOOL  static void
1267    xwin_refresh_pointer_map(void)
1268    {
1269            unsigned char phys_to_log_map[sizeof(g_pointer_log_to_phys_map)];
1270            int i, pointer_buttons;
1271    
1272            pointer_buttons = XGetPointerMapping(g_display, phys_to_log_map, sizeof(phys_to_log_map));
1273            for (i = 0; i < pointer_buttons; ++i)
1274            {
1275                    /* This might produce multiple logical buttons mapping
1276                       to a single physical one, but hey, that's
1277                       life... */
1278                    g_pointer_log_to_phys_map[phys_to_log_map[i] - 1] = i + 1;
1279            }
1280    }
1281    
1282    RD_BOOL
1283  get_key_state(unsigned int state, uint32 keysym)  get_key_state(unsigned int state, uint32 keysym)
1284  {  {
1285          int modifierpos, key, keysymMask = 0;          int modifierpos, key, keysymMask = 0;
# Line 927  calculate_shifts(uint32 mask, int *shift Line 1312  calculate_shifts(uint32 mask, int *shift
1312          *shift_r = 8 - ffs(mask & ~(mask >> 1));          *shift_r = 8 - ffs(mask & ~(mask >> 1));
1313  }  }
1314    
1315  BOOL  /* Given a mask of a colour channel (e.g. XVisualInfo.red_mask),
1316  ui_init(void)     calculates the bits-per-pixel of this channel (a.k.a. colour weight).
1317     */
1318    static unsigned
1319    calculate_mask_weight(uint32 mask)
1320    {
1321            unsigned weight = 0;
1322            do
1323            {
1324                    weight += (mask & 1);
1325            }
1326            while (mask >>= 1);
1327            return weight;
1328    }
1329    
1330    static RD_BOOL
1331    select_visual(int screen_num)
1332  {  {
         XVisualInfo vi;  
1333          XPixmapFormatValues *pfm;          XPixmapFormatValues *pfm;
1334          uint16 test;          int pixmap_formats_count, visuals_count;
         int i, screen_num, nvisuals;  
1335          XVisualInfo *vmatches = NULL;          XVisualInfo *vmatches = NULL;
1336          XVisualInfo template;          XVisualInfo template;
1337          Bool TrueColorVisual = False;          int i;
1338            unsigned red_weight, blue_weight, green_weight;
1339    
1340          g_display = XOpenDisplay(NULL);          red_weight = blue_weight = green_weight = 0;
1341          if (g_display == NULL)  
1342            if (g_server_depth == -1)
1343          {          {
1344                  error("Failed to open display: %s\n", XDisplayName(NULL));                  g_server_depth = DisplayPlanes(g_display, DefaultScreen(g_display));
                 return False;  
1345          }          }
1346    
1347          screen_num = DefaultScreen(g_display);          pfm = XListPixmapFormats(g_display, &pixmap_formats_count);
1348          g_x_socket = ConnectionNumber(g_display);          if (pfm == NULL)
1349          g_screen = ScreenOfDisplay(g_display, screen_num);          {
1350          g_depth = DefaultDepthOfScreen(g_screen);                  error("Unable to get list of pixmap formats from display.\n");
1351                    XCloseDisplay(g_display);
1352                    return False;
1353            }
1354    
1355          /* Search for best TrueColor depth */          /* Search for best TrueColor visual */
1356          template.class = TrueColor;          template.class = TrueColor;
1357          vmatches = XGetVisualInfo(g_display, VisualClassMask, &template, &nvisuals);          template.screen = screen_num;
1358            vmatches =
1359                    XGetVisualInfo(g_display, VisualClassMask | VisualScreenMask, &template,
1360                                   &visuals_count);
1361            g_visual = NULL;
1362            g_no_translate_image = False;
1363            g_compatible_arch = False;
1364            if (vmatches != NULL)
1365            {
1366                    for (i = 0; i < visuals_count; ++i)
1367                    {
1368                            XVisualInfo *visual_info = &vmatches[i];
1369                            RD_BOOL can_translate_to_bpp = False;
1370                            int j;
1371    
1372                            /* Try to find a no-translation visual that'll
1373                               allow us to use RDP bitmaps directly as ZPixmaps. */
1374                            if (!g_xserver_be && (((visual_info->depth == 15) &&
1375                                                   /* R5G5B5 */
1376                                                   (visual_info->red_mask == 0x7c00) &&
1377                                                   (visual_info->green_mask == 0x3e0) &&
1378                                                   (visual_info->blue_mask == 0x1f)) ||
1379                                                  ((visual_info->depth == 16) &&
1380                                                   /* R5G6B5 */
1381                                                   (visual_info->red_mask == 0xf800) &&
1382                                                   (visual_info->green_mask == 0x7e0) &&
1383                                                   (visual_info->blue_mask == 0x1f)) ||
1384                                                  ((visual_info->depth == 24) &&
1385                                                   /* R8G8B8 */
1386                                                   (visual_info->red_mask == 0xff0000) &&
1387                                                   (visual_info->green_mask == 0xff00) &&
1388                                                   (visual_info->blue_mask == 0xff))))
1389                            {
1390                                    g_visual = visual_info->visual;
1391                                    g_depth = visual_info->depth;
1392                                    g_compatible_arch = !g_host_be;
1393                                    g_no_translate_image = (visual_info->depth == g_server_depth);
1394                                    if (g_no_translate_image)
1395                                            /* We found the best visual */
1396                                            break;
1397                            }
1398                            else
1399                            {
1400                                    g_compatible_arch = False;
1401                            }
1402    
1403                            if (visual_info->depth > 24)
1404                            {
1405                                    /* Avoid 32-bit visuals and likes like the plague.
1406                                       They're either untested or proven to work bad
1407                                       (e.g. nvidia's Composite 32-bit visual).
1408                                       Most implementation offer a 24-bit visual anyway. */
1409                                    continue;
1410                            }
1411    
1412          nvisuals--;                          /* Only care for visuals, for whose BPPs (not depths!)
1413          while (nvisuals >= 0)                             we have a translateXtoY function. */
1414          {                          for (j = 0; j < pixmap_formats_count; ++j)
1415                  if ((vmatches + nvisuals)->depth > g_depth)                          {
1416                  {                                  if (pfm[j].depth == visual_info->depth)
1417                          g_depth = (vmatches + nvisuals)->depth;                                  {
1418                                            if ((pfm[j].bits_per_pixel == 16) ||
1419                                                (pfm[j].bits_per_pixel == 24) ||
1420                                                (pfm[j].bits_per_pixel == 32))
1421                                            {
1422                                                    can_translate_to_bpp = True;
1423                                            }
1424                                            break;
1425                                    }
1426                            }
1427    
1428                            /* Prefer formats which have the most colour depth.
1429                               We're being truly aristocratic here, minding each
1430                               weight on its own. */
1431                            if (can_translate_to_bpp)
1432                            {
1433                                    unsigned vis_red_weight =
1434                                            calculate_mask_weight(visual_info->red_mask);
1435                                    unsigned vis_green_weight =
1436                                            calculate_mask_weight(visual_info->green_mask);
1437                                    unsigned vis_blue_weight =
1438                                            calculate_mask_weight(visual_info->blue_mask);
1439                                    if ((vis_red_weight >= red_weight)
1440                                        && (vis_green_weight >= green_weight)
1441                                        && (vis_blue_weight >= blue_weight))
1442                                    {
1443                                            red_weight = vis_red_weight;
1444                                            green_weight = vis_green_weight;
1445                                            blue_weight = vis_blue_weight;
1446                                            g_visual = visual_info->visual;
1447                                            g_depth = visual_info->depth;
1448                                    }
1449                            }
1450                  }                  }
1451                  nvisuals--;                  XFree(vmatches);
                 TrueColorVisual = True;  
1452          }          }
1453    
1454          test = 1;          if (g_visual != NULL)
         g_host_be = !(BOOL) (*(uint8 *) (&test));  
         g_xserver_be = (ImageByteOrder(g_display) == MSBFirst);  
   
         if ((g_server_bpp == 8) && ((!TrueColorVisual) || (g_depth <= 8)))  
1455          {          {
1456                  /* we use a colourmap, so the default visual should do */                  g_owncolmap = False;
1457                  g_visual = DefaultVisualOfScreen(g_screen);                  calculate_shifts(g_visual->red_mask, &g_red_shift_r, &g_red_shift_l);
1458                  g_depth = DefaultDepthOfScreen(g_screen);                  calculate_shifts(g_visual->green_mask, &g_green_shift_r, &g_green_shift_l);
1459                    calculate_shifts(g_visual->blue_mask, &g_blue_shift_r, &g_blue_shift_l);
                 /* Do not allocate colours on a TrueColor visual */  
                 if (g_visual->class == TrueColor)  
                 {  
                         g_owncolmap = False;  
                 }  
1460          }          }
1461          else          else
1462          {          {
1463                  /* need a truecolour visual */                  template.class = PseudoColor;
1464                  if (!XMatchVisualInfo(g_display, screen_num, g_depth, TrueColor, &vi))                  template.depth = 8;
1465                  {                  template.colormap_size = 256;
1466                          error("The display does not support true colour - high colour support unavailable.\n");                  vmatches =
1467                            XGetVisualInfo(g_display,
1468                                           VisualClassMask | VisualDepthMask | VisualColormapSizeMask,
1469                                           &template, &visuals_count);
1470                    if (vmatches == NULL)
1471                    {
1472                            error("No usable TrueColor or PseudoColor visuals on this display.\n");
1473                            XCloseDisplay(g_display);
1474                            XFree(pfm);
1475                          return False;                          return False;
1476                  }                  }
1477    
1478                  g_visual = vi.visual;                  /* we use a colourmap, so the default visual should do */
1479                  g_owncolmap = False;                  g_owncolmap = True;
1480                  calculate_shifts(vi.red_mask, &g_red_shift_r, &g_red_shift_l);                  g_visual = vmatches[0].visual;
1481                  calculate_shifts(vi.blue_mask, &g_blue_shift_r, &g_blue_shift_l);                  g_depth = vmatches[0].depth;
1482                  calculate_shifts(vi.green_mask, &g_green_shift_r, &g_green_shift_l);          }
1483    
1484                  /* if RGB video and everything is little endian */          g_bpp = 0;
1485                  if ((vi.red_mask > vi.green_mask && vi.green_mask > vi.blue_mask) &&          for (i = 0; i < pixmap_formats_count; ++i)
1486                      !g_xserver_be && !g_host_be)          {
1487                    XPixmapFormatValues *pf = &pfm[i];
1488                    if (pf->depth == g_depth)
1489                  {                  {
1490                          if (g_depth <= 16 || (g_red_shift_l == 16 && g_green_shift_l == 8 &&                          g_bpp = pf->bits_per_pixel;
1491                                                g_blue_shift_l == 0))  
1492                            if (g_no_translate_image)
1493                          {                          {
1494                                  g_arch_match = True;                                  switch (g_server_depth)
1495                                    {
1496                                            case 15:
1497                                            case 16:
1498                                                    if (g_bpp != 16)
1499                                                            g_no_translate_image = False;
1500                                                    break;
1501                                            case 24:
1502                                                    /* Yes, this will force image translation
1503                                                       on most modern servers which use 32 bits
1504                                                       for R8G8B8. */
1505                                                    if (g_bpp != 24)
1506                                                            g_no_translate_image = False;
1507                                                    break;
1508                                            default:
1509                                                    g_no_translate_image = False;
1510                                                    break;
1511                                    }
1512                          }                          }
                 }  
1513    
1514                  if (g_arch_match)                          /* Pixmap formats list is a depth-to-bpp mapping --
1515                  {                             there's just a single entry for every depth,
1516                          DEBUG(("Architectures match, enabling little endian optimisations.\n"));                             so we can safely break here */
1517                            break;
1518                  }                  }
1519          }          }
1520            XFree(pfm);
1521            pfm = NULL;
1522            return True;
1523    }
1524    
1525    static XErrorHandler g_old_error_handler;
1526    
1527          pfm = XListPixmapFormats(g_display, &i);  static int
1528          if (pfm != NULL)  error_handler(Display * dpy, XErrorEvent * eev)
1529    {
1530            if ((eev->error_code == BadMatch) && (eev->request_code == X_ConfigureWindow))
1531          {          {
1532                  /* Use maximum bpp for this depth - this is generally                  fprintf(stderr, "Got \"BadMatch\" when trying to restack windows.\n");
1533                     desirable, e.g. 24 bits->32 bits. */                  fprintf(stderr,
1534                  while (i--)                          "This is most likely caused by a broken window manager (commonly KWin).\n");
1535                  {                  return 0;
                         if ((pfm[i].depth == g_depth) && (pfm[i].bits_per_pixel > g_bpp))  
                         {  
                                 g_bpp = pfm[i].bits_per_pixel;  
                         }  
                 }  
                 XFree(pfm);  
1536          }          }
1537    
1538          if (g_bpp < 8)          return g_old_error_handler(dpy, eev);
1539    }
1540    
1541    RD_BOOL
1542    ui_init(void)
1543    {
1544            int screen_num;
1545    
1546            g_display = XOpenDisplay(NULL);
1547            if (g_display == NULL)
1548          {          {
1549                  error("Less than 8 bpp not currently supported.\n");                  error("Failed to open display: %s\n", XDisplayName(NULL));
                 XCloseDisplay(g_display);  
1550                  return False;                  return False;
1551          }          }
1552    
1553            {
1554                    uint16 endianess_test = 1;
1555                    g_host_be = !(RD_BOOL) (*(uint8 *) (&endianess_test));
1556            }
1557    
1558            g_old_error_handler = XSetErrorHandler(error_handler);
1559            g_xserver_be = (ImageByteOrder(g_display) == MSBFirst);
1560            screen_num = DefaultScreen(g_display);
1561            g_x_socket = ConnectionNumber(g_display);
1562            g_screen = ScreenOfDisplay(g_display, screen_num);
1563            g_depth = DefaultDepthOfScreen(g_screen);
1564    
1565            if (!select_visual(screen_num))
1566                    return False;
1567    
1568            if (g_no_translate_image)
1569            {
1570                    DEBUG(("Performance optimization possible: avoiding image translation (colour depth conversion).\n"));
1571            }
1572    
1573            if (g_server_depth > g_bpp)
1574            {
1575                    warning("Remote desktop colour depth %d higher than display colour depth %d.\n",
1576                            g_server_depth, g_bpp);
1577            }
1578    
1579            DEBUG(("RDP depth: %d, display depth: %d, display bpp: %d, X server BE: %d, host BE: %d\n",
1580                   g_server_depth, g_depth, g_bpp, g_xserver_be, g_host_be));
1581    
1582          if (!g_owncolmap)          if (!g_owncolmap)
1583          {          {
1584                  g_xcolmap =                  g_xcolmap =
1585                          XCreateColormap(g_display, RootWindowOfScreen(g_screen), g_visual,                          XCreateColormap(g_display, RootWindowOfScreen(g_screen), g_visual,
1586                                          AllocNone);                                          AllocNone);
1587                  if (g_depth <= 8)                  if (g_depth <= 8)
1588                          warning("Screen depth is 8 bits or lower: you may want to use -C for a private colourmap\n");                          warning("Display colour depth is %d bit: you may want to use -C for a private colourmap.\n", g_depth);
1589          }          }
1590    
1591          if ((!g_ownbackstore) && (DoesBackingStore(g_screen) != Always))          if ((!g_ownbackstore) && (DoesBackingStore(g_screen) != Always))
1592          {          {
1593                  warning("External BackingStore not available, using internal\n");                  warning("External BackingStore not available. Using internal.\n");
1594                  g_ownbackstore = True;                  g_ownbackstore = True;
1595          }          }
1596    
# Line 1057  ui_init(void) Line 1601  ui_init(void)
1601          {          {
1602                  g_width = WidthOfScreen(g_screen);                  g_width = WidthOfScreen(g_screen);
1603                  g_height = HeightOfScreen(g_screen);                  g_height = HeightOfScreen(g_screen);
1604                    g_using_full_workarea = True;
1605          }          }
1606          else if (g_width < 0)          else if (g_width < 0)
1607          {          {
1608                  /* Percent of screen */                  /* Percent of screen */
1609                    if (-g_width >= 100)
1610                            g_using_full_workarea = True;
1611                  g_height = HeightOfScreen(g_screen) * (-g_width) / 100;                  g_height = HeightOfScreen(g_screen) * (-g_width) / 100;
1612                  g_width = WidthOfScreen(g_screen) * (-g_width) / 100;                  g_width = WidthOfScreen(g_screen) * (-g_width) / 100;
1613          }          }
# Line 1068  ui_init(void) Line 1615  ui_init(void)
1615          {          {
1616                  /* Fetch geometry from _NET_WORKAREA */                  /* Fetch geometry from _NET_WORKAREA */
1617                  uint32 x, y, cx, cy;                  uint32 x, y, cx, cy;
   
1618                  if (get_current_workarea(&x, &y, &cx, &cy) == 0)                  if (get_current_workarea(&x, &y, &cx, &cy) == 0)
1619                  {                  {
1620                          g_width = cx;                          g_width = cx;
1621                          g_height = cy;                          g_height = cy;
1622                            g_using_full_workarea = True;
1623                  }                  }
1624                  else                  else
1625                  {                  {
1626                          warning("Failed to get workarea: probably your window manager does not support extended hints\n");                          warning("Failed to get workarea: probably your window manager does not support extended hints\n");
1627                          g_width = 800;                          g_width = WidthOfScreen(g_screen);
1628                          g_height = 600;                          g_height = HeightOfScreen(g_screen);
1629                  }                  }
1630          }          }
1631    
# Line 1086  ui_init(void) Line 1633  ui_init(void)
1633          g_width = (g_width + 3) & ~3;          g_width = (g_width + 3) & ~3;
1634    
1635          g_mod_map = XGetModifierMapping(g_display);          g_mod_map = XGetModifierMapping(g_display);
1636            xwin_refresh_pointer_map();
1637    
1638          xkeymap_init();          xkeymap_init();
1639    
# Line 1093  ui_init(void) Line 1641  ui_init(void)
1641                  g_IM = XOpenIM(g_display, NULL, NULL, NULL);                  g_IM = XOpenIM(g_display, NULL, NULL, NULL);
1642    
1643          xclip_init();          xclip_init();
1644            ewmh_init();
1645            if (g_seamless_rdp)
1646                    seamless_init();
1647    
1648          DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", g_server_bpp, g_bpp, g_depth));          DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", g_server_depth, g_bpp, g_depth));
1649    
1650          return True;          return True;
1651  }  }
# Line 1102  ui_init(void) Line 1653  ui_init(void)
1653  void  void
1654  ui_deinit(void)  ui_deinit(void)
1655  {  {
1656            while (g_seamless_windows)
1657            {
1658                    XDestroyWindow(g_display, g_seamless_windows->wnd);
1659                    sw_remove_window(g_seamless_windows);
1660            }
1661    
1662            xclip_deinit();
1663    
1664          if (g_IM != NULL)          if (g_IM != NULL)
1665                  XCloseIM(g_IM);                  XCloseIM(g_IM);
1666    
# Line 1118  ui_deinit(void) Line 1677  ui_deinit(void)
1677          g_display = NULL;          g_display = NULL;
1678  }  }
1679    
1680  BOOL  
1681    static void
1682    get_window_attribs(XSetWindowAttributes * attribs)
1683    {
1684            attribs->background_pixel = BlackPixelOfScreen(g_screen);
1685            attribs->background_pixel = WhitePixelOfScreen(g_screen);
1686            attribs->border_pixel = WhitePixelOfScreen(g_screen);
1687            attribs->backing_store = g_ownbackstore ? NotUseful : Always;
1688            attribs->override_redirect = g_fullscreen;
1689            attribs->colormap = g_xcolmap;
1690    }
1691    
1692    static void
1693    get_input_mask(long *input_mask)
1694    {
1695            *input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
1696                    VisibilityChangeMask | FocusChangeMask | StructureNotifyMask;
1697    
1698            if (g_sendmotion)
1699                    *input_mask |= PointerMotionMask;
1700            if (g_ownbackstore)
1701                    *input_mask |= ExposureMask;
1702            if (g_fullscreen || g_grab_keyboard)
1703                    *input_mask |= EnterWindowMask;
1704            if (g_grab_keyboard)
1705                    *input_mask |= LeaveWindowMask;
1706    }
1707    
1708    RD_BOOL
1709  ui_create_window(void)  ui_create_window(void)
1710  {  {
1711          uint8 null_pointer_mask[1] = { 0x80 };          uint8 null_pointer_mask[1] = { 0x80 };
# Line 1134  ui_create_window(void) Line 1721  ui_create_window(void)
1721          wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;          wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;
1722          wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;          wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;
1723    
1724          attribs.background_pixel = BlackPixelOfScreen(g_screen);          /* Handle -x-y portion of geometry string */
1725          attribs.border_pixel = WhitePixelOfScreen(g_screen);          if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
1726          attribs.backing_store = g_ownbackstore ? NotUseful : Always;                  g_xpos = WidthOfScreen(g_screen) + g_xpos - g_width;
1727          attribs.override_redirect = g_fullscreen;          if (g_ypos < 0 || (g_ypos == 0 && (g_pos & 4)))
1728          attribs.colormap = g_xcolmap;                  g_ypos = HeightOfScreen(g_screen) + g_ypos - g_height;
1729    
1730            get_window_attribs(&attribs);
1731    
1732          g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,          g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
1733                                wndheight, 0, g_depth, InputOutput, g_visual,                                wndheight, 0, g_depth, InputOutput, g_visual,
# Line 1146  ui_create_window(void) Line 1735  ui_create_window(void)
1735                                CWBorderPixel, &attribs);                                CWBorderPixel, &attribs);
1736    
1737          if (g_gc == NULL)          if (g_gc == NULL)
1738            {
1739                  g_gc = XCreateGC(g_display, g_wnd, 0, NULL);                  g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
1740                    ui_reset_clip();
1741            }
1742    
1743          if (g_create_bitmap_gc == NULL)          if (g_create_bitmap_gc == NULL)
1744                  g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL);                  g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL);
# Line 1161  ui_create_window(void) Line 1753  ui_create_window(void)
1753          }          }
1754    
1755          XStoreName(g_display, g_wnd, g_title);          XStoreName(g_display, g_wnd, g_title);
1756            ewmh_set_wm_name(g_wnd, g_title);
1757    
1758          if (g_hide_decorations)          if (g_hide_decorations)
1759                  mwm_hide_decorations();                  mwm_hide_decorations(g_wnd);
1760    
1761          classhints = XAllocClassHint();          classhints = XAllocClassHint();
1762          if (classhints != NULL)          if (classhints != NULL)
# Line 1177  ui_create_window(void) Line 1770  ui_create_window(void)
1770          if (sizehints)          if (sizehints)
1771          {          {
1772                  sizehints->flags = PMinSize | PMaxSize;                  sizehints->flags = PMinSize | PMaxSize;
1773                    if (g_pos)
1774                            sizehints->flags |= PPosition;
1775                  sizehints->min_width = sizehints->max_width = g_width;                  sizehints->min_width = sizehints->max_width = g_width;
1776                  sizehints->min_height = sizehints->max_height = g_height;                  sizehints->min_height = sizehints->max_height = g_height;
1777                  XSetWMNormalHints(g_display, g_wnd, sizehints);                  XSetWMNormalHints(g_display, g_wnd, sizehints);
# Line 1188  ui_create_window(void) Line 1783  ui_create_window(void)
1783                  XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);                  XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);
1784          }          }
1785    
1786          input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |          get_input_mask(&input_mask);
                 VisibilityChangeMask | FocusChangeMask;  
   
         if (g_sendmotion)  
                 input_mask |= PointerMotionMask;  
         if (g_ownbackstore)  
                 input_mask |= ExposureMask;  
         if (g_fullscreen || g_grab_keyboard)  
                 input_mask |= EnterWindowMask;  
         if (g_grab_keyboard)  
                 input_mask |= LeaveWindowMask;  
1787    
1788          if (g_IM != NULL)          if (g_IM != NULL)
1789          {          {
# Line 1283  xwin_toggle_fullscreen(void) Line 1868  xwin_toggle_fullscreen(void)
1868  {  {
1869          Pixmap contents = 0;          Pixmap contents = 0;
1870    
1871            if (g_seamless_active)
1872                    /* Turn off SeamlessRDP mode */
1873                    ui_seamless_toggle();
1874    
1875          if (!g_ownbackstore)          if (!g_ownbackstore)
1876          {          {
1877                  /* need to save contents of window */                  /* need to save contents of window */
# Line 1303  xwin_toggle_fullscreen(void) Line 1892  xwin_toggle_fullscreen(void)
1892          }          }
1893  }  }
1894    
1895  /* Process all events in Xlib queue  static void
1896    handle_button_event(XEvent xevent, RD_BOOL down)
1897    {
1898            uint16 button, flags = 0;
1899            g_last_gesturetime = xevent.xbutton.time;
1900            /* Reverse the pointer button mapping, e.g. in the case of
1901               "left-handed mouse mode"; the RDP session expects to
1902               receive physical buttons (true in mstsc as well) and
1903               logical button behavior depends on the remote desktop's own
1904               mouse settings */
1905            xevent.xbutton.button = g_pointer_log_to_phys_map[xevent.xbutton.button - 1];
1906            button = xkeymap_translate_button(xevent.xbutton.button);
1907            if (button == 0)
1908                    return;
1909    
1910            if (down)
1911                    flags = MOUSE_FLAG_DOWN;
1912    
1913            /* Stop moving window when button is released, regardless of cursor position */
1914            if (g_moving_wnd && (xevent.type == ButtonRelease))
1915                    g_moving_wnd = False;
1916    
1917            /* If win_button_size is nonzero, enable single app mode */
1918            if (xevent.xbutton.y < g_win_button_size)
1919            {
1920                    /*  Check from right to left: */
1921                    if (xevent.xbutton.x >= g_width - g_win_button_size)
1922                    {
1923                            /* The close button, continue */
1924                            ;
1925                    }
1926                    else if (xevent.xbutton.x >= g_width - g_win_button_size * 2)
1927                    {
1928                            /* The maximize/restore button. Do not send to
1929                               server.  It might be a good idea to change the
1930                               cursor or give some other visible indication
1931                               that rdesktop inhibited this click */
1932                            if (xevent.type == ButtonPress)
1933                                    return;
1934                    }
1935                    else if (xevent.xbutton.x >= g_width - g_win_button_size * 3)
1936                    {
1937                            /* The minimize button. Iconify window. */
1938                            if (xevent.type == ButtonRelease)
1939                            {
1940                                    /* Release the mouse button outside the minimize button, to prevent the
1941                                       actual minimazation to happen */
1942                                    rdp_send_input(time(NULL), RDP_INPUT_MOUSE, button, 1, 1);
1943                                    XIconifyWindow(g_display, g_wnd, DefaultScreen(g_display));
1944                                    return;
1945                            }
1946                    }
1947                    else if (xevent.xbutton.x <= g_win_button_size)
1948                    {
1949                            /* The system menu. Ignore. */
1950                            if (xevent.type == ButtonPress)
1951                                    return;
1952                    }
1953                    else
1954                    {
1955                            /* The title bar. */
1956                            if (xevent.type == ButtonPress)
1957                            {
1958                                    if (!g_fullscreen && g_hide_decorations && !g_using_full_workarea)
1959                                    {
1960                                            g_moving_wnd = True;
1961                                            g_move_x_offset = xevent.xbutton.x;
1962                                            g_move_y_offset = xevent.xbutton.y;
1963                                    }
1964                                    return;
1965                            }
1966                    }
1967            }
1968    
1969            if (xevent.xmotion.window == g_wnd)
1970            {
1971                    rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1972                                   flags | button, xevent.xbutton.x, xevent.xbutton.y);
1973            }
1974            else
1975            {
1976                    /* SeamlessRDP */
1977                    rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1978                                   flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root);
1979            }
1980    }
1981    
1982    
1983    /* Process events in Xlib queue
1984     Returns 0 after user quit, 1 otherwise */     Returns 0 after user quit, 1 otherwise */
1985  static int  static int
1986  xwin_process_events(void)  xwin_process_events(void)
1987  {  {
1988          XEvent xevent;          XEvent xevent;
1989          KeySym keysym;          KeySym keysym;
         uint16 button, flags;  
1990          uint32 ev_time;          uint32 ev_time;
         key_translation tr;  
1991          char str[256];          char str[256];
1992          Status status;          Status status;
1993            int events = 0;
1994            seamless_window *sw;
1995    
1996          while (XPending(g_display) > 0)          while ((XPending(g_display) > 0) && events++ < 20)
1997          {          {
1998                  XNextEvent(g_display, &xevent);                  XNextEvent(g_display, &xevent);
1999    
# Line 1326  xwin_process_events(void) Line 2003  xwin_process_events(void)
2003                          continue;                          continue;
2004                  }                  }
2005    
                 flags = 0;  
   
2006                  switch (xevent.type)                  switch (xevent.type)
2007                  {                  {
2008                          case VisibilityNotify:                          case VisibilityNotify:
2009                                  g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;                                  if (xevent.xvisibility.window == g_wnd)
2010                                            g_Unobscured =
2011                                                    xevent.xvisibility.state == VisibilityUnobscured;
2012    
2013                                  break;                                  break;
2014                          case ClientMessage:                          case ClientMessage:
2015                                  /* the window manager told us to quit */                                  /* the window manager told us to quit */
# Line 1364  xwin_process_events(void) Line 2042  xwin_process_events(void)
2042                                                        str, sizeof(str), &keysym, NULL);                                                        str, sizeof(str), &keysym, NULL);
2043                                  }                                  }
2044    
2045                                  DEBUG_KBD(("KeyPress for (keysym 0x%lx, %s)\n", keysym,                                  DEBUG_KBD(("KeyPress for keysym (0x%lx, %s)\n", keysym,
2046                                             get_ksname(keysym)));                                             get_ksname(keysym)));
2047    
2048                                  ev_time = time(NULL);                                  ev_time = time(NULL);
2049                                  if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))                                  if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
2050                                          break;                                          break;
2051    
2052                                  tr = xkeymap_translate_key(keysym,                                  xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2053                                                             xevent.xkey.keycode, xevent.xkey.state);                                                    ev_time, True, 0);
   
                                 if (tr.scancode == 0)  
                                         break;  
   
                                 save_remote_modifiers(tr.scancode);  
                                 ensure_remote_modifiers(ev_time, tr);  
                                 rdp_send_scancode(ev_time, RDP_KEYPRESS, tr.scancode);  
                                 restore_remote_modifiers(ev_time, tr.scancode);  
   
2054                                  break;                                  break;
2055    
2056                          case KeyRelease:                          case KeyRelease:
# Line 1389  xwin_process_events(void) Line 2058  xwin_process_events(void)
2058                                  XLookupString((XKeyEvent *) & xevent, str,                                  XLookupString((XKeyEvent *) & xevent, str,
2059                                                sizeof(str), &keysym, NULL);                                                sizeof(str), &keysym, NULL);
2060    
2061                                  DEBUG_KBD(("\nKeyRelease for (keysym 0x%lx, %s)\n", keysym,                                  DEBUG_KBD(("\nKeyRelease for keysym (0x%lx, %s)\n", keysym,
2062                                             get_ksname(keysym)));                                             get_ksname(keysym)));
2063    
2064                                  ev_time = time(NULL);                                  ev_time = time(NULL);
2065                                  if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))                                  if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
2066                                          break;                                          break;
2067    
2068                                  tr = xkeymap_translate_key(keysym,                                  xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
2069                                                             xevent.xkey.keycode, xevent.xkey.state);                                                    ev_time, False, 0);
   
                                 if (tr.scancode == 0)  
                                         break;  
   
                                 rdp_send_scancode(ev_time, RDP_KEYRELEASE, tr.scancode);  
2070                                  break;                                  break;
2071    
2072                          case ButtonPress:                          case ButtonPress:
2073                                  flags = MOUSE_FLAG_DOWN;                                  handle_button_event(xevent, True);
2074                                  /* fall through */                                  break;
2075    
2076                          case ButtonRelease:                          case ButtonRelease:
2077                                  g_last_gesturetime = xevent.xbutton.time;                                  handle_button_event(xevent, False);
                                 button = xkeymap_translate_button(xevent.xbutton.button);  
                                 if (button == 0)  
                                         break;  
   
                                 /* If win_button_size is nonzero, enable single app mode */  
                                 if (xevent.xbutton.y < g_win_button_size)  
                                 {  
                                         /* Stop moving window when button is released, regardless of cursor position */  
                                         if (g_moving_wnd && (xevent.type == ButtonRelease))  
                                                 g_moving_wnd = False;  
   
                                         /*  Check from right to left: */  
   
                                         if (xevent.xbutton.x >= g_width - g_win_button_size)  
                                         {  
                                                 /* The close button, continue */  
                                                 ;  
                                         }  
                                         else if (xevent.xbutton.x >=  
                                                  g_width - g_win_button_size * 2)  
                                         {  
                                                 /* The maximize/restore button. Do not send to  
                                                    server.  It might be a good idea to change the  
                                                    cursor or give some other visible indication  
                                                    that rdesktop inhibited this click */  
                                                 break;  
                                         }  
                                         else if (xevent.xbutton.x >=  
                                                  g_width - g_win_button_size * 3)  
                                         {  
                                                 /* The minimize button. Iconify window. */  
                                                 XIconifyWindow(g_display, g_wnd,  
                                                                DefaultScreen(g_display));  
                                                 break;  
                                         }  
                                         else if (xevent.xbutton.x <= g_win_button_size)  
                                         {  
                                                 /* The system menu. Ignore. */  
                                                 break;  
                                         }  
                                         else  
                                         {  
                                                 /* The title bar. */  
                                                 if ((xevent.type == ButtonPress) && !g_fullscreen  
                                                     && g_hide_decorations)  
                                                 {  
                                                         g_moving_wnd = True;  
                                                         g_move_x_offset = xevent.xbutton.x;  
                                                         g_move_y_offset = xevent.xbutton.y;  
                                                 }  
                                                 break;  
   
                                         }  
                                 }  
   
                                 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,  
                                                flags | button, xevent.xbutton.x, xevent.xbutton.y);  
2078                                  break;                                  break;
2079    
2080                          case MotionNotify:                          case MotionNotify:
# Line 1482  xwin_process_events(void) Line 2089  xwin_process_events(void)
2089                                  if (g_fullscreen && !g_focused)                                  if (g_fullscreen && !g_focused)
2090                                          XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,                                          XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
2091                                                         CurrentTime);                                                         CurrentTime);
2092                                  rdp_send_input(time(NULL), RDP_INPUT_MOUSE,  
2093                                                 MOUSE_FLAG_MOVE, xevent.xmotion.x, xevent.xmotion.y);                                  if (xevent.xmotion.window == g_wnd)
2094                                    {
2095                                            rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2096                                                           xevent.xmotion.x, xevent.xmotion.y);
2097                                    }
2098                                    else
2099                                    {
2100                                            /* SeamlessRDP */
2101                                            rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
2102                                                           xevent.xmotion.x_root,
2103                                                           xevent.xmotion.y_root);
2104                                    }
2105                                  break;                                  break;
2106    
2107                          case FocusIn:                          case FocusIn:
# Line 1494  xwin_process_events(void) Line 2112  xwin_process_events(void)
2112                                  if (g_grab_keyboard && g_mouse_in_wnd)                                  if (g_grab_keyboard && g_mouse_in_wnd)
2113                                          XGrabKeyboard(g_display, g_wnd, True,                                          XGrabKeyboard(g_display, g_wnd, True,
2114                                                        GrabModeAsync, GrabModeAsync, CurrentTime);                                                        GrabModeAsync, GrabModeAsync, CurrentTime);
2115    
2116                                    sw = sw_get_window_by_wnd(xevent.xfocus.window);
2117                                    if (!sw)
2118                                            break;
2119    
2120                                    if (sw->id != g_seamless_focused)
2121                                    {
2122                                            seamless_send_focus(sw->id, 0);
2123                                            g_seamless_focused = sw->id;
2124                                    }
2125                                  break;                                  break;
2126    
2127                          case FocusOut:                          case FocusOut:
# Line 1526  xwin_process_events(void) Line 2154  xwin_process_events(void)
2154                                  break;                                  break;
2155    
2156                          case Expose:                          case Expose:
2157                                  XCopyArea(g_display, g_backstore, g_wnd, g_gc,                                  if (xevent.xexpose.window == g_wnd)
2158                                            xevent.xexpose.x, xevent.xexpose.y,                                  {
2159                                            xevent.xexpose.width,                                          XCopyArea(g_display, g_backstore, xevent.xexpose.window,
2160                                            xevent.xexpose.height,                                                    g_gc,
2161                                            xevent.xexpose.x, xevent.xexpose.y);                                                    xevent.xexpose.x, xevent.xexpose.y,
2162                                                      xevent.xexpose.width, xevent.xexpose.height,
2163                                                      xevent.xexpose.x, xevent.xexpose.y);
2164                                    }
2165                                    else
2166                                    {
2167                                            sw = sw_get_window_by_wnd(xevent.xexpose.window);
2168                                            if (!sw)
2169                                                    break;
2170                                            XCopyArea(g_display, g_backstore,
2171                                                      xevent.xexpose.window, g_gc,
2172                                                      xevent.xexpose.x + sw->xoffset,
2173                                                      xevent.xexpose.y + sw->yoffset,
2174                                                      xevent.xexpose.width,
2175                                                      xevent.xexpose.height, xevent.xexpose.x,
2176                                                      xevent.xexpose.y);
2177                                    }
2178    
2179                                  break;                                  break;
2180    
2181                          case MappingNotify:                          case MappingNotify:
# Line 1545  xwin_process_events(void) Line 2190  xwin_process_events(void)
2190                                          XFreeModifiermap(g_mod_map);                                          XFreeModifiermap(g_mod_map);
2191                                          g_mod_map = XGetModifierMapping(g_display);                                          g_mod_map = XGetModifierMapping(g_display);
2192                                  }                                  }
2193    
2194                                    if (xevent.xmapping.request == MappingPointer)
2195                                    {
2196                                            xwin_refresh_pointer_map();
2197                                    }
2198    
2199                                  break;                                  break;
2200    
2201                                  /* clipboard stuff */                                  /* clipboard stuff */
# Line 1559  xwin_process_events(void) Line 2210  xwin_process_events(void)
2210                                  break;                                  break;
2211                          case PropertyNotify:                          case PropertyNotify:
2212                                  xclip_handle_PropertyNotify(&xevent.xproperty);                                  xclip_handle_PropertyNotify(&xevent.xproperty);
2213                                    if (xevent.xproperty.window == g_wnd)
2214                                            break;
2215                                    if (xevent.xproperty.window == DefaultRootWindow(g_display))
2216                                            break;
2217    
2218                                    /* seamless */
2219                                    sw = sw_get_window_by_wnd(xevent.xproperty.window);
2220                                    if (!sw)
2221                                            break;
2222    
2223                                    if ((xevent.xproperty.atom == g_net_wm_state_atom)
2224                                        && (xevent.xproperty.state == PropertyNewValue))
2225                                    {
2226                                            sw->state = ewmh_get_window_state(sw->wnd);
2227                                            seamless_send_state(sw->id, sw->state, 0);
2228                                    }
2229    
2230                                    if ((xevent.xproperty.atom == g_net_wm_desktop_atom)
2231                                        && (xevent.xproperty.state == PropertyNewValue))
2232                                    {
2233                                            sw->desktop = ewmh_get_window_desktop(sw->wnd);
2234                                            sw_all_to_desktop(sw->wnd, sw->desktop);
2235                                    }
2236    
2237                                    break;
2238                            case MapNotify:
2239                                    if (!g_seamless_active)
2240                                            rdp_send_client_window_status(1);
2241                                    break;
2242                            case UnmapNotify:
2243                                    if (!g_seamless_active)
2244                                            rdp_send_client_window_status(0);
2245                                    break;
2246                            case ConfigureNotify:
2247                                    if (!g_seamless_active)
2248                                            break;
2249    
2250                                    sw = sw_get_window_by_wnd(xevent.xconfigure.window);
2251                                    if (!sw)
2252                                            break;
2253    
2254                                    gettimeofday(sw->position_timer, NULL);
2255                                    if (sw->position_timer->tv_usec + SEAMLESSRDP_POSITION_TIMER >=
2256                                        1000000)
2257                                    {
2258                                            sw->position_timer->tv_usec +=
2259                                                    SEAMLESSRDP_POSITION_TIMER - 1000000;
2260                                            sw->position_timer->tv_sec += 1;
2261                                    }
2262                                    else
2263                                    {
2264                                            sw->position_timer->tv_usec += SEAMLESSRDP_POSITION_TIMER;
2265                                    }
2266    
2267                                    sw_handle_restack(sw);
2268                                  break;                                  break;
2269                  }                  }
2270          }          }
# Line 1573  ui_select(int rdp_socket) Line 2279  ui_select(int rdp_socket)
2279          int n;          int n;
2280          fd_set rfds, wfds;          fd_set rfds, wfds;
2281          struct timeval tv;          struct timeval tv;
2282          BOOL s_timeout = False;          RD_BOOL s_timeout = False;
2283    
2284          while (True)          while (True)
2285          {          {
# Line 1583  ui_select(int rdp_socket) Line 2289  ui_select(int rdp_socket)
2289                          /* User quit */                          /* User quit */
2290                          return 0;                          return 0;
2291    
2292                    if (g_seamless_active)
2293                            sw_check_timers();
2294    
2295                  FD_ZERO(&rfds);                  FD_ZERO(&rfds);
2296                  FD_ZERO(&wfds);                  FD_ZERO(&wfds);
2297                  FD_SET(rdp_socket, &rfds);                  FD_SET(rdp_socket, &rfds);
2298                  FD_SET(g_x_socket, &rfds);                  FD_SET(g_x_socket, &rfds);
2299    
 #ifdef WITH_RDPSND  
                 /* FIXME: there should be an API for registering fds */  
                 if (g_dsp_busy)  
                 {  
                         FD_SET(g_dsp_fd, &wfds);  
                         n = (g_dsp_fd > n) ? g_dsp_fd : n;  
                 }  
 #endif  
2300                  /* default timeout */                  /* default timeout */
2301                  tv.tv_sec = 60;                  tv.tv_sec = 60;
2302                  tv.tv_usec = 0;                  tv.tv_usec = 0;
2303    
2304    #ifdef WITH_RDPSND
2305                    rdpsnd_add_fds(&n, &rfds, &wfds, &tv);
2306    #endif
2307    
2308                  /* add redirection handles */                  /* add redirection handles */
2309                  rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);                  rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
2310                    seamless_select_timeout(&tv);
2311    
2312                  n++;                  n++;
2313    
# Line 1611  ui_select(int rdp_socket) Line 2317  ui_select(int rdp_socket)
2317                                  error("select: %s\n", strerror(errno));                                  error("select: %s\n", strerror(errno));
2318    
2319                          case 0:                          case 0:
2320    #ifdef WITH_RDPSND
2321                                    rdpsnd_check_fds(&rfds, &wfds);
2322    #endif
2323    
2324                                  /* Abort serial read calls */                                  /* Abort serial read calls */
2325                                  if (s_timeout)                                  if (s_timeout)
2326                                          rdpdr_check_fds(&rfds, &wfds, (BOOL) True);                                          rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) True);
2327                                  continue;                                  continue;
2328                  }                  }
2329    
2330                  rdpdr_check_fds(&rfds, &wfds, (BOOL) False);  #ifdef WITH_RDPSND
2331                    rdpsnd_check_fds(&rfds, &wfds);
2332    #endif
2333    
2334                    rdpdr_check_fds(&rfds, &wfds, (RD_BOOL) False);
2335    
2336                  if (FD_ISSET(rdp_socket, &rfds))                  if (FD_ISSET(rdp_socket, &rfds))
2337                          return 1;                          return 1;
2338    
 #ifdef WITH_RDPSND  
                 if (g_dsp_busy && FD_ISSET(g_dsp_fd, &wfds))  
                         wave_out_play();  
 #endif  
2339          }          }
2340  }  }
2341    
# Line 1635  ui_move_pointer(int x, int y) Line 2345  ui_move_pointer(int x, int y)
2345          XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);          XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
2346  }  }
2347    
2348  HBITMAP  RD_HBITMAP
2349  ui_create_bitmap(int width, int height, uint8 * data)  ui_create_bitmap(int width, int height, uint8 * data)
2350  {  {
2351          XImage *image;          XImage *image;
# Line 1643  ui_create_bitmap(int width, int height, Line 2353  ui_create_bitmap(int width, int height,
2353          uint8 *tdata;          uint8 *tdata;
2354          int bitmap_pad;          int bitmap_pad;
2355    
2356          if (g_server_bpp == 8)          if (g_server_depth == 8)
2357          {          {
2358                  bitmap_pad = 8;                  bitmap_pad = 8;
2359          }          }
# Line 1665  ui_create_bitmap(int width, int height, Line 2375  ui_create_bitmap(int width, int height,
2375          XFree(image);          XFree(image);
2376          if (tdata != data)          if (tdata != data)
2377                  xfree(tdata);                  xfree(tdata);
2378          return (HBITMAP) bitmap;          return (RD_HBITMAP) bitmap;
2379  }  }
2380    
2381  void  void
# Line 1675  ui_paint_bitmap(int x, int y, int cx, in Line 2385  ui_paint_bitmap(int x, int y, int cx, in
2385          uint8 *tdata;          uint8 *tdata;
2386          int bitmap_pad;          int bitmap_pad;
2387    
2388          if (g_server_bpp == 8)          if (g_server_depth == 8)
2389          {          {
2390                  bitmap_pad = 8;                  bitmap_pad = 8;
2391          }          }
# Line 1695  ui_paint_bitmap(int x, int y, int cx, in Line 2405  ui_paint_bitmap(int x, int y, int cx, in
2405          {          {
2406                  XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);                  XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2407                  XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);                  XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2408                    ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2409                                            (g_display, g_backstore, sw->wnd, g_gc, x, y, cx, cy,
2410                                             x - sw->xoffset, y - sw->yoffset));
2411          }          }
2412          else          else
2413          {          {
2414                  XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);                  XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2415                    ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2416                                            (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
2417                                             x - sw->xoffset, y - sw->yoffset));
2418          }          }
2419    
2420          XFree(image);          XFree(image);
# Line 1707  ui_paint_bitmap(int x, int y, int cx, in Line 2423  ui_paint_bitmap(int x, int y, int cx, in
2423  }  }
2424    
2425  void  void
2426  ui_destroy_bitmap(HBITMAP bmp)  ui_destroy_bitmap(RD_HBITMAP bmp)
2427  {  {
2428          XFreePixmap(g_display, (Pixmap) bmp);          XFreePixmap(g_display, (Pixmap) bmp);
2429  }  }
2430    
2431  HGLYPH  RD_HGLYPH
2432  ui_create_glyph(int width, int height, uint8 * data)  ui_create_glyph(int width, int height, uint8 * data)
2433  {  {
2434          XImage *image;          XImage *image;
# Line 1734  ui_create_glyph(int width, int height, u Line 2450  ui_create_glyph(int width, int height, u
2450          XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);          XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
2451    
2452          XFree(image);          XFree(image);
2453          return (HGLYPH) bitmap;          return (RD_HGLYPH) bitmap;
2454  }  }
2455    
2456  void  void
2457  ui_destroy_glyph(HGLYPH glyph)  ui_destroy_glyph(RD_HGLYPH glyph)
2458  {  {
2459          XFreePixmap(g_display, (Pixmap) glyph);          XFreePixmap(g_display, (Pixmap) glyph);
2460  }  }
2461    
2462  HCURSOR  RD_HCURSOR
2463  ui_create_cursor(unsigned int x, unsigned int y, int width, int height,  ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
2464                   uint8 * andmask, uint8 * xormask)                   uint8 * andmask, uint8 * xormask)
2465  {  {
2466          HGLYPH maskglyph, cursorglyph;          RD_HGLYPH maskglyph, cursorglyph;
2467          XColor bg, fg;          XColor bg, fg;
2468          Cursor xcursor;          Cursor xcursor;
2469          uint8 *cursor, *pcursor;          uint8 *cursor, *pcursor;
# Line 1811  ui_create_cursor(unsigned int x, unsigne Line 2527  ui_create_cursor(unsigned int x, unsigne
2527          ui_destroy_glyph(cursorglyph);          ui_destroy_glyph(cursorglyph);
2528          xfree(mask);          xfree(mask);
2529          xfree(cursor);          xfree(cursor);
2530          return (HCURSOR) xcursor;          return (RD_HCURSOR) xcursor;
2531  }  }
2532    
2533  void  void
2534  ui_set_cursor(HCURSOR cursor)  ui_set_cursor(RD_HCURSOR cursor)
2535  {  {
2536          g_current_cursor = (Cursor) cursor;          g_current_cursor = (Cursor) cursor;
2537          XDefineCursor(g_display, g_wnd, g_current_cursor);          XDefineCursor(g_display, g_wnd, g_current_cursor);
2538            ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor));
2539  }  }
2540    
2541  void  void
2542  ui_destroy_cursor(HCURSOR cursor)  ui_destroy_cursor(RD_HCURSOR cursor)
2543  {  {
2544          XFreeCursor(g_display, (Cursor) cursor);          XFreeCursor(g_display, (Cursor) cursor);
2545  }  }
# Line 1840  ui_set_null_cursor(void) Line 2557  ui_set_null_cursor(void)
2557                  (xc)->flags = DoRed | DoGreen | DoBlue;                  (xc)->flags = DoRed | DoGreen | DoBlue;
2558    
2559    
2560  HCOLOURMAP  RD_HCOLOURMAP
2561  ui_create_colourmap(COLOURMAP * colours)  ui_create_colourmap(COLOURMAP * colours)
2562  {  {
2563          COLOURENTRY *entry;          COLOURENTRY *entry;
# Line 1936  ui_create_colourmap(COLOURMAP * colours) Line 2653  ui_create_colourmap(COLOURMAP * colours)
2653                  XStoreColors(g_display, map, xcolours, ncolours);                  XStoreColors(g_display, map, xcolours, ncolours);
2654    
2655                  xfree(xcolours);                  xfree(xcolours);
2656                  return (HCOLOURMAP) map;                  return (RD_HCOLOURMAP) map;
2657          }          }
2658  }  }
2659    
2660  void  void
2661  ui_destroy_colourmap(HCOLOURMAP map)  ui_destroy_colourmap(RD_HCOLOURMAP map)
2662  {  {
2663          if (!g_owncolmap)          if (!g_owncolmap)
2664                  xfree(map);                  xfree(map);
# Line 1950  ui_destroy_colourmap(HCOLOURMAP map) Line 2667  ui_destroy_colourmap(HCOLOURMAP map)
2667  }  }
2668    
2669  void  void
2670  ui_set_colourmap(HCOLOURMAP map)  ui_set_colourmap(RD_HCOLOURMAP map)
2671  {  {
2672          if (!g_owncolmap)          if (!g_owncolmap)
2673          {          {
# Line 1960  ui_set_colourmap(HCOLOURMAP map) Line 2677  ui_set_colourmap(HCOLOURMAP map)
2677                  g_colmap = (uint32 *) map;                  g_colmap = (uint32 *) map;
2678          }          }
2679          else          else
2680            {
2681                  XSetWindowColormap(g_display, g_wnd, (Colormap) map);                  XSetWindowColormap(g_display, g_wnd, (Colormap) map);
2682                    ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
2683            }
2684  }  }
2685    
2686  void  void
2687  ui_set_clip(int x, int y, int cx, int cy)  ui_set_clip(int x, int y, int cx, int cy)
2688  {  {
2689          XRectangle rect;          g_clip_rectangle.x = x;
2690            g_clip_rectangle.y = y;
2691          rect.x = x;          g_clip_rectangle.width = cx;
2692          rect.y = y;          g_clip_rectangle.height = cy;
2693          rect.width = cx;          XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
         rect.height = cy;  
         XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);  
2694  }  }
2695    
2696  void  void
2697  ui_reset_clip(void)  ui_reset_clip(void)
2698  {  {
2699          XRectangle rect;          g_clip_rectangle.x = 0;
2700            g_clip_rectangle.y = 0;
2701          rect.x = 0;          g_clip_rectangle.width = g_width;
2702          rect.y = 0;          g_clip_rectangle.height = g_height;
2703          rect.width = g_width;          XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
         rect.height = g_height;  
         XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);  
2704  }  }
2705    
2706  void  void
# Line 2039  ui_patblt(uint8 opcode, Line 2755  ui_patblt(uint8 opcode,
2755                          FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);                          FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2756                          XSetFillStyle(g_display, g_gc, FillSolid);                          XSetFillStyle(g_display, g_gc, FillSolid);
2757                          XSetTSOrigin(g_display, g_gc, 0, 0);                          XSetTSOrigin(g_display, g_gc, 0, 0);
2758                          ui_destroy_glyph((HGLYPH) fill);                          ui_destroy_glyph((RD_HGLYPH) fill);
2759                          break;                          break;
2760    
2761                  case 3: /* Pattern */                  case 3: /* Pattern */
# Line 2054  ui_patblt(uint8 opcode, Line 2770  ui_patblt(uint8 opcode,
2770                          FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);                          FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2771                          XSetFillStyle(g_display, g_gc, FillSolid);                          XSetFillStyle(g_display, g_gc, FillSolid);
2772                          XSetTSOrigin(g_display, g_gc, 0, 0);                          XSetTSOrigin(g_display, g_gc, 0, 0);
2773                          ui_destroy_glyph((HGLYPH) fill);                          ui_destroy_glyph((RD_HGLYPH) fill);
2774                          break;                          break;
2775    
2776                  default:                  default:
# Line 2065  ui_patblt(uint8 opcode, Line 2781  ui_patblt(uint8 opcode,
2781    
2782          if (g_ownbackstore)          if (g_ownbackstore)
2783                  XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);                  XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2784            ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2785                                    (g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc,
2786                                     x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2787  }  }
2788    
2789  void  void
# Line 2075  ui_screenblt(uint8 opcode, Line 2794  ui_screenblt(uint8 opcode,
2794          SET_FUNCTION(opcode);          SET_FUNCTION(opcode);
2795          if (g_ownbackstore)          if (g_ownbackstore)
2796          {          {
2797                  if (g_Unobscured)                  XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
2798                  {                            g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2799                          XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);                  XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
                         XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x,  
                                   y);  
                 }  
                 else  
                 {  
                         XCopyArea(g_display, g_backstore, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);  
                         XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x,  
                                   y);  
                 }  
2800          }          }
2801          else          else
2802          {          {
2803                  XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);                  XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2804          }          }
2805    
2806            ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2807                                    (g_display, g_ownbackstore ? g_backstore : g_wnd,
2808                                     sw->wnd, g_gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2809    
2810          RESET_FUNCTION(opcode);          RESET_FUNCTION(opcode);
2811  }  }
2812    
2813  void  void
2814  ui_memblt(uint8 opcode,  ui_memblt(uint8 opcode,
2815            /* dest */ int x, int y, int cx, int cy,            /* dest */ int x, int y, int cx, int cy,
2816            /* src */ HBITMAP src, int srcx, int srcy)            /* src */ RD_HBITMAP src, int srcx, int srcy)
2817  {  {
2818          SET_FUNCTION(opcode);          SET_FUNCTION(opcode);
2819          XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);          XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2820            ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2821                                    (g_display, (Pixmap) src, sw->wnd, g_gc,
2822                                     srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
2823          if (g_ownbackstore)          if (g_ownbackstore)
2824                  XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);                  XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2825          RESET_FUNCTION(opcode);          RESET_FUNCTION(opcode);
# Line 2110  ui_memblt(uint8 opcode, Line 2828  ui_memblt(uint8 opcode,
2828  void  void
2829  ui_triblt(uint8 opcode,  ui_triblt(uint8 opcode,
2830            /* dest */ int x, int y, int cx, int cy,            /* dest */ int x, int y, int cx, int cy,
2831            /* src */ HBITMAP src, int srcx, int srcy,            /* src */ RD_HBITMAP src, int srcx, int srcy,
2832            /* brush */ BRUSH * brush, int bgcolour, int fgcolour)            /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2833  {  {
2834          /* This is potentially difficult to do in general. Until someone          /* This is potentially difficult to do in general. Until someone
# Line 2148  ui_line(uint8 opcode, Line 2866  ui_line(uint8 opcode,
2866          SET_FUNCTION(opcode);          SET_FUNCTION(opcode);
2867          SET_FOREGROUND(pen->colour);          SET_FOREGROUND(pen->colour);
2868          XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);          XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
2869            ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc,
2870                                                startx - sw->xoffset, starty - sw->yoffset,
2871                                                endx - sw->xoffset, endy - sw->yoffset));
2872          if (g_ownbackstore)          if (g_ownbackstore)
2873                  XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);                  XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
2874          RESET_FUNCTION(opcode);          RESET_FUNCTION(opcode);
# Line 2162  ui_rect( Line 2883  ui_rect(
2883          FILL_RECTANGLE(x, y, cx, cy);          FILL_RECTANGLE(x, y, cx, cy);
2884  }  }
2885    
2886    void
2887    ui_polygon(uint8 opcode,
2888               /* mode */ uint8 fillmode,
2889               /* dest */ RD_POINT * point, int npoints,
2890               /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2891    {
2892            uint8 style, i, ipattern[8];
2893            Pixmap fill;
2894    
2895            SET_FUNCTION(opcode);
2896    
2897            switch (fillmode)
2898            {
2899                    case ALTERNATE:
2900                            XSetFillRule(g_display, g_gc, EvenOddRule);
2901                            break;
2902                    case WINDING:
2903                            XSetFillRule(g_display, g_gc, WindingRule);
2904                            break;
2905                    default:
2906                            unimpl("fill mode %d\n", fillmode);
2907            }
2908    
2909            if (brush)
2910                    style = brush->style;
2911            else
2912                    style = 0;
2913    
2914            switch (style)
2915            {
2916                    case 0: /* Solid */
2917                            SET_FOREGROUND(fgcolour);
2918                            FILL_POLYGON((XPoint *) point, npoints);
2919                            break;
2920    
2921                    case 2: /* Hatch */
2922                            fill = (Pixmap) ui_create_glyph(8, 8,
2923                                                            hatch_patterns + brush->pattern[0] * 8);
2924                            SET_FOREGROUND(fgcolour);
2925                            SET_BACKGROUND(bgcolour);
2926                            XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2927                            XSetStipple(g_display, g_gc, fill);
2928                            XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2929                            FILL_POLYGON((XPoint *) point, npoints);
2930                            XSetFillStyle(g_display, g_gc, FillSolid);
2931                            XSetTSOrigin(g_display, g_gc, 0, 0);
2932                            ui_destroy_glyph((RD_HGLYPH) fill);
2933                            break;
2934    
2935                    case 3: /* Pattern */
2936                            for (i = 0; i != 8; i++)
2937                                    ipattern[7 - i] = brush->pattern[i];
2938                            fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2939                            SET_FOREGROUND(bgcolour);
2940                            SET_BACKGROUND(fgcolour);
2941                            XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2942                            XSetStipple(g_display, g_gc, fill);
2943                            XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2944                            FILL_POLYGON((XPoint *) point, npoints);
2945                            XSetFillStyle(g_display, g_gc, FillSolid);
2946                            XSetTSOrigin(g_display, g_gc, 0, 0);
2947                            ui_destroy_glyph((RD_HGLYPH) fill);
2948                            break;
2949    
2950                    default:
2951                            unimpl("brush %d\n", brush->style);
2952            }
2953    
2954            RESET_FUNCTION(opcode);
2955    }
2956    
2957    void
2958    ui_polyline(uint8 opcode,
2959                /* dest */ RD_POINT * points, int npoints,
2960                /* pen */ PEN * pen)
2961    {
2962            /* TODO: set join style */
2963            SET_FUNCTION(opcode);
2964            SET_FOREGROUND(pen->colour);
2965            XDrawLines(g_display, g_wnd, g_gc, (XPoint *) points, npoints, CoordModePrevious);
2966            if (g_ownbackstore)
2967                    XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
2968                               CoordModePrevious);
2969    
2970            ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
2971                                    (sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
2972    
2973            RESET_FUNCTION(opcode);
2974    }
2975    
2976    void
2977    ui_ellipse(uint8 opcode,
2978               /* mode */ uint8 fillmode,
2979               /* dest */ int x, int y, int cx, int cy,
2980               /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2981    {
2982            uint8 style, i, ipattern[8];
2983            Pixmap fill;
2984    
2985            SET_FUNCTION(opcode);
2986    
2987            if (brush)
2988                    style = brush->style;
2989            else
2990                    style = 0;
2991    
2992            switch (style)
2993            {
2994                    case 0: /* Solid */
2995                            SET_FOREGROUND(fgcolour);
2996                            DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2997                            break;
2998    
2999                    case 2: /* Hatch */
3000                            fill = (Pixmap) ui_create_glyph(8, 8,
3001                                                            hatch_patterns + brush->pattern[0] * 8);
3002                            SET_FOREGROUND(fgcolour);
3003                            SET_BACKGROUND(bgcolour);
3004                            XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3005                            XSetStipple(g_display, g_gc, fill);
3006                            XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3007                            DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3008                            XSetFillStyle(g_display, g_gc, FillSolid);
3009                            XSetTSOrigin(g_display, g_gc, 0, 0);
3010                            ui_destroy_glyph((RD_HGLYPH) fill);
3011                            break;
3012    
3013                    case 3: /* Pattern */
3014                            for (i = 0; i != 8; i++)
3015                                    ipattern[7 - i] = brush->pattern[i];
3016                            fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
3017                            SET_FOREGROUND(bgcolour);
3018                            SET_BACKGROUND(fgcolour);
3019                            XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
3020                            XSetStipple(g_display, g_gc, fill);
3021                            XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
3022                            DRAW_ELLIPSE(x, y, cx, cy, fillmode);
3023                            XSetFillStyle(g_display, g_gc, FillSolid);
3024                            XSetTSOrigin(g_display, g_gc, 0, 0);
3025                            ui_destroy_glyph((RD_HGLYPH) fill);
3026                            break;
3027    
3028                    default:
3029                            unimpl("brush %d\n", brush->style);
3030            }
3031    
3032            RESET_FUNCTION(opcode);
3033    }
3034    
3035  /* warning, this function only draws on wnd or backstore, not both */  /* warning, this function only draws on wnd or backstore, not both */
3036  void  void
3037  ui_draw_glyph(int mixmode,  ui_draw_glyph(int mixmode,
3038                /* dest */ int x, int y, int cx, int cy,                /* dest */ int x, int y, int cx, int cy,
3039                /* src */ HGLYPH glyph, int srcx, int srcy,                /* src */ RD_HGLYPH glyph, int srcx, int srcy,
3040                int bgcolour, int fgcolour)                int bgcolour, int fgcolour)
3041  {  {
3042          SET_FOREGROUND(fgcolour);          SET_FOREGROUND(fgcolour);
# Line 2217  ui_draw_glyph(int mixmode, Line 3087  ui_draw_glyph(int mixmode,
3087  }  }
3088    
3089  void  void
3090  ui_draw_text(uint8 font, uint8 flags, int mixmode, int x, int y,  ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
3091               int clipx, int clipy, int clipcx, int clipcy,               int clipx, int clipy, int clipcx, int clipcy,
3092               int boxx, int boxy, int boxcx, int boxcy, int bgcolour,               int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
3093               int fgcolour, uint8 * text, uint8 length)               int bgcolour, int fgcolour, uint8 * text, uint8 length)
3094  {  {
3095            /* TODO: use brush appropriately */
3096    
3097          FONTGLYPH *glyph;          FONTGLYPH *glyph;
3098          int i, j, xyoffset, x1, y1;          int i, j, xyoffset, x1, y1;
3099          DATABLOB *entry;          DATABLOB *entry;
# Line 2253  ui_draw_text(uint8 font, uint8 flags, in Line 3125  ui_draw_text(uint8 font, uint8 flags, in
3125                  switch (text[i])                  switch (text[i])
3126                  {                  {
3127                          case 0xff:                          case 0xff:
3128                                  if (i + 2 < length)                                  /* At least two bytes needs to follow */
3129                                          cache_put_text(text[i + 1], text, text[i + 2]);                                  if (i + 3 > length)
                                 else  
3130                                  {                                  {
3131                                          error("this shouldn't be happening\n");                                          warning("Skipping short 0xff command:");
3132                                          exit(1);                                          for (j = 0; j < length; j++)
3133                                                    fprintf(stderr, "%02x ", text[j]);
3134                                            fprintf(stderr, "\n");
3135                                            i = length = 0;
3136                                            break;
3137                                  }                                  }
3138                                    cache_put_text(text[i + 1], text, text[i + 2]);
3139                                    i += 3;
3140                                    length -= i;
3141                                  /* this will move pointer from start to first character after FF command */                                  /* this will move pointer from start to first character after FF command */
3142                                  length -= i + 3;                                  text = &(text[i]);
                                 text = &(text[i + 3]);  
3143                                  i = 0;                                  i = 0;
3144                                  break;                                  break;
3145    
3146                          case 0xfe:                          case 0xfe:
3147                                    /* At least one byte needs to follow */
3148                                    if (i + 2 > length)
3149                                    {
3150                                            warning("Skipping short 0xfe command:");
3151                                            for (j = 0; j < length; j++)
3152                                                    fprintf(stderr, "%02x ", text[j]);
3153                                            fprintf(stderr, "\n");
3154                                            i = length = 0;
3155                                            break;
3156                                    }
3157                                  entry = cache_get_text(text[i + 1]);                                  entry = cache_get_text(text[i + 1]);
3158                                  if (entry != NULL)                                  if (entry->data != NULL)
3159                                  {                                  {
3160                                          if ((((uint8 *) (entry->data))[1] ==                                          if ((((uint8 *) (entry->data))[1] == 0)
3161                                               0) && (!(flags & TEXT2_IMPLICIT_X)))                                              && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
3162                                          {                                          {
3163                                                  if (flags & TEXT2_VERTICAL)                                                  if (flags & TEXT2_VERTICAL)
3164                                                          y += text[i + 2];                                                          y += text[i + 2];
# Line 2303  ui_draw_text(uint8 font, uint8 flags, in Line 3190  ui_draw_text(uint8 font, uint8 flags, in
3190          if (g_ownbackstore)          if (g_ownbackstore)
3191          {          {
3192                  if (boxcx > 1)                  if (boxcx > 1)
3193                    {
3194                          XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,                          XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
3195                                    boxy, boxcx, boxcy, boxx, boxy);                                    boxy, boxcx, boxcy, boxx, boxy);
3196                            ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3197                                                    (g_display, g_backstore, sw->wnd, g_gc,
3198                                                     boxx, boxy,
3199                                                     boxcx, boxcy,
3200                                                     boxx - sw->xoffset, boxy - sw->yoffset));
3201                    }
3202                  else                  else
3203                    {
3204                          XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,                          XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
3205                                    clipy, clipcx, clipcy, clipx, clipy);                                    clipy, clipcx, clipcy, clipx, clipy);
3206                            ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3207                                                    (g_display, g_backstore, sw->wnd, g_gc,
3208                                                     clipx, clipy,
3209                                                     clipcx, clipcy, clipx - sw->xoffset,
3210                                                     clipy - sw->yoffset));
3211                    }
3212          }          }
3213  }  }
3214    
# Line 2320  ui_desktop_save(uint32 offset, int x, in Line 3221  ui_desktop_save(uint32 offset, int x, in
3221          if (g_ownbackstore)          if (g_ownbackstore)
3222          {          {
3223                  image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);                  image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
3224                    exit_if_null(image);
3225          }          }
3226          else          else
3227          {          {
3228                  pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);                  pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
3229                  XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);                  XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
3230                  image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);                  image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
3231                    exit_if_null(image);
3232                  XFreePixmap(g_display, pix);                  XFreePixmap(g_display, pix);
3233          }          }
3234    
# Line 2347  ui_desktop_restore(uint32 offset, int x, Line 3250  ui_desktop_restore(uint32 offset, int x,
3250                  return;                  return;
3251    
3252          image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,          image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
3253                               (char *) data, cx, cy, BitmapPad(g_display), cx * g_bpp / 8);                               (char *) data, cx, cy, g_bpp, 0);
3254    
3255          if (g_ownbackstore)          if (g_ownbackstore)
3256          {          {
3257                  XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);                  XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
3258                  XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);                  XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
3259                    ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3260                                            (g_display, g_backstore, sw->wnd, g_gc,
3261                                             x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3262          }          }
3263          else          else
3264          {          {
3265                  XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);                  XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
3266                    ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3267                                            (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
3268                                             x - sw->xoffset, y - sw->yoffset));
3269          }          }
3270    
3271          XFree(image);          XFree(image);
# Line 2372  void Line 3281  void
3281  ui_end_update(void)  ui_end_update(void)
3282  {  {
3283  }  }
3284    
3285    
3286    void
3287    ui_seamless_begin(RD_BOOL hidden)
3288    {
3289            if (!g_seamless_rdp)
3290                    return;
3291    
3292            if (g_seamless_started)
3293                    return;
3294    
3295            g_seamless_started = True;
3296            g_seamless_hidden = hidden;
3297    
3298            if (!hidden)
3299                    ui_seamless_toggle();
3300    }
3301    
3302    
3303    void
3304    ui_seamless_hide_desktop()
3305    {
3306            if (!g_seamless_rdp)
3307                    return;
3308    
3309            if (!g_seamless_started)
3310                    return;
3311    
3312            if (g_seamless_active)
3313                    ui_seamless_toggle();
3314    
3315            g_seamless_hidden = True;
3316    }
3317    
3318    
3319    void
3320    ui_seamless_unhide_desktop()
3321    {
3322            if (!g_seamless_rdp)
3323                    return;
3324    
3325            if (!g_seamless_started)
3326                    return;
3327    
3328            g_seamless_hidden = False;
3329    
3330            ui_seamless_toggle();
3331    }
3332    
3333    
3334    void
3335    ui_seamless_toggle()
3336    {
3337            if (!g_seamless_rdp)
3338                    return;
3339    
3340            if (!g_seamless_started)
3341                    return;
3342    
3343            if (g_seamless_hidden)
3344                    return;
3345    
3346            if (g_seamless_active)
3347            {
3348                    /* Deactivate */
3349                    while (g_seamless_windows)
3350                    {
3351                            XDestroyWindow(g_display, g_seamless_windows->wnd);
3352                            sw_remove_window(g_seamless_windows);
3353                    }
3354                    XMapWindow(g_display, g_wnd);
3355            }
3356            else
3357            {
3358                    /* Activate */
3359                    XUnmapWindow(g_display, g_wnd);
3360                    seamless_send_sync();
3361            }
3362    
3363            g_seamless_active = !g_seamless_active;
3364    }
3365    
3366    
3367    void
3368    ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent,
3369                              unsigned long flags)
3370    {
3371            Window wnd;
3372            XSetWindowAttributes attribs;
3373            XClassHint *classhints;
3374            XSizeHints *sizehints;
3375            XWMHints *wmhints;
3376            long input_mask;
3377            seamless_window *sw, *sw_parent;
3378    
3379            if (!g_seamless_active)
3380                    return;
3381    
3382            /* Ignore CREATEs for existing windows */
3383            sw = sw_get_window_by_id(id);
3384            if (sw)
3385                    return;
3386    
3387            get_window_attribs(&attribs);
3388            wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
3389                                InputOutput, g_visual,
3390                                CWBackPixel | CWBackingStore | CWColormap | CWBorderPixel, &attribs);
3391    
3392            XStoreName(g_display, wnd, "SeamlessRDP");
3393            ewmh_set_wm_name(wnd, "SeamlessRDP");
3394    
3395            mwm_hide_decorations(wnd);
3396    
3397            classhints = XAllocClassHint();
3398            if (classhints != NULL)
3399            {
3400                    classhints->res_name = "rdesktop";
3401                    classhints->res_class = "SeamlessRDP";
3402                    XSetClassHint(g_display, wnd, classhints);
3403                    XFree(classhints);
3404            }
3405    
3406            /* WM_NORMAL_HINTS */
3407            sizehints = XAllocSizeHints();
3408            if (sizehints != NULL)
3409            {
3410                    sizehints->flags = USPosition;
3411                    XSetWMNormalHints(g_display, wnd, sizehints);
3412                    XFree(sizehints);
3413            }
3414    
3415            /* Parent-less transient windows */
3416            if (parent == 0xFFFFFFFF)
3417            {
3418                    XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3419                    /* Some buggy wm:s (kwin) do not handle the above, so fake it
3420                       using some other hints. */
3421                    ewmh_set_window_popup(wnd);
3422            }
3423            /* Normal transient windows */
3424            else if (parent != 0x00000000)
3425            {
3426                    sw_parent = sw_get_window_by_id(parent);
3427                    if (sw_parent)
3428                            XSetTransientForHint(g_display, wnd, sw_parent->wnd);
3429                    else
3430                            warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3431            }
3432    
3433            if (flags & SEAMLESSRDP_CREATE_MODAL)
3434            {
3435                    /* We do this to support buggy wm:s (*cough* metacity *cough*)
3436                       somewhat at least */
3437                    if (parent == 0x00000000)
3438                            XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen));
3439                    ewmh_set_window_modal(wnd);
3440            }
3441    
3442            /* FIXME: Support for Input Context:s */
3443    
3444            get_input_mask(&input_mask);
3445            input_mask |= PropertyChangeMask;
3446    
3447            XSelectInput(g_display, wnd, input_mask);
3448    
3449            /* handle the WM_DELETE_WINDOW protocol. FIXME: When killing a
3450               seamless window, we could try to close the window on the
3451               serverside, instead of terminating rdesktop */
3452            XSetWMProtocols(g_display, wnd, &g_kill_atom, 1);
3453    
3454            sw = xmalloc(sizeof(seamless_window));
3455            sw->wnd = wnd;
3456            sw->id = id;
3457            sw->behind = 0;
3458            sw->group = sw_find_group(group, False);
3459            sw->group->refcnt++;
3460            sw->xoffset = 0;
3461            sw->yoffset = 0;
3462            sw->width = 0;
3463            sw->height = 0;
3464            sw->state = SEAMLESSRDP_NOTYETMAPPED;
3465            sw->desktop = 0;
3466            sw->position_timer = xmalloc(sizeof(struct timeval));
3467            timerclear(sw->position_timer);
3468    
3469            sw->outstanding_position = False;
3470            sw->outpos_serial = 0;
3471            sw->outpos_xoffset = sw->outpos_yoffset = 0;
3472            sw->outpos_width = sw->outpos_height = 0;
3473    
3474            sw->next = g_seamless_windows;
3475            g_seamless_windows = sw;
3476    
3477            /* WM_HINTS */
3478            wmhints = XAllocWMHints();
3479            if (wmhints)
3480            {
3481                    wmhints->flags = WindowGroupHint;
3482                    wmhints->window_group = sw->group->wnd;
3483                    XSetWMHints(g_display, sw->wnd, wmhints);
3484                    XFree(wmhints);
3485            }
3486    }
3487    
3488    
3489    void
3490    ui_seamless_destroy_window(unsigned long id, unsigned long flags)
3491    {
3492            seamless_window *sw;
3493    
3494            if (!g_seamless_active)
3495                    return;
3496    
3497            sw = sw_get_window_by_id(id);
3498            if (!sw)
3499            {
3500                    warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3501                    return;
3502            }
3503    
3504            XDestroyWindow(g_display, sw->wnd);
3505            sw_remove_window(sw);
3506    }
3507    
3508    
3509    void
3510    ui_seamless_destroy_group(unsigned long id, unsigned long flags)
3511    {
3512            seamless_window *sw, *sw_next;
3513    
3514            if (!g_seamless_active)
3515                    return;
3516    
3517            for (sw = g_seamless_windows; sw; sw = sw_next)
3518            {
3519                    sw_next = sw->next;
3520    
3521                    if (sw->group->id == id)
3522                    {
3523                            XDestroyWindow(g_display, sw->wnd);
3524                            sw_remove_window(sw);
3525                    }
3526            }
3527    }
3528    
3529    
3530    void
3531    ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags)
3532    {
3533            seamless_window *sw;
3534    
3535            if (!g_seamless_active)
3536                    return;
3537    
3538            sw = sw_get_window_by_id(id);
3539            if (!sw)
3540            {
3541                    warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3542                    return;
3543            }
3544    
3545            /* We ignore server updates until it has handled our request. */
3546            if (sw->outstanding_position)
3547                    return;
3548    
3549            if (!width || !height)
3550                    /* X11 windows must be at least 1x1 */
3551                    return;
3552    
3553            sw->xoffset = x;
3554            sw->yoffset = y;
3555            sw->width = width;
3556            sw->height = height;
3557    
3558            /* If we move the window in a maximized state, then KDE won't
3559               accept restoration */
3560            switch (sw->state)
3561            {
3562                    case SEAMLESSRDP_MINIMIZED:
3563                    case SEAMLESSRDP_MAXIMIZED:
3564                            return;
3565            }
3566    
3567            /* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3568            XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3569    }
3570    
3571    
3572    void
3573    ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags)
3574    {
3575            seamless_window *sw;
3576    
3577            if (!g_seamless_active)
3578                    return;
3579    
3580            sw = sw_get_window_by_id(id);
3581            if (!sw)
3582            {
3583                    warning("ui_seamless_restack_window: No information for window 0x%lx\n", id);
3584                    return;
3585            }
3586    
3587            if (behind)
3588            {
3589                    seamless_window *sw_behind;
3590                    Window wnds[2];
3591    
3592                    sw_behind = sw_get_window_by_id(behind);
3593                    if (!sw_behind)
3594                    {
3595                            warning("ui_seamless_restack_window: No information for window 0x%lx\n",
3596                                    behind);
3597                            return;
3598                    }
3599    
3600                    wnds[1] = sw_behind->wnd;
3601                    wnds[0] = sw->wnd;
3602    
3603                    XRestackWindows(g_display, wnds, 2);
3604            }
3605            else
3606            {
3607                    XRaiseWindow(g_display, sw->wnd);
3608            }
3609    
3610            sw_restack_window(sw, behind);
3611    }
3612    
3613    
3614    void
3615    ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags)
3616    {
3617            seamless_window *sw;
3618    
3619            if (!g_seamless_active)
3620                    return;
3621    
3622            sw = sw_get_window_by_id(id);
3623            if (!sw)
3624            {
3625                    warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
3626                    return;
3627            }
3628    
3629            /* FIXME: Might want to convert the name for non-EWMH WMs */
3630            XStoreName(g_display, sw->wnd, title);
3631            ewmh_set_wm_name(sw->wnd, title);
3632    }
3633    
3634    
3635    void
3636    ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags)
3637    {
3638            seamless_window *sw;
3639    
3640            if (!g_seamless_active)
3641                    return;
3642    
3643            sw = sw_get_window_by_id(id);
3644            if (!sw)
3645            {
3646                    warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
3647                    return;
3648            }
3649    
3650            switch (state)
3651            {
3652                    case SEAMLESSRDP_NORMAL:
3653                    case SEAMLESSRDP_MAXIMIZED:
3654                            ewmh_change_state(sw->wnd, state);
3655                            XMapWindow(g_display, sw->wnd);
3656                            break;
3657                    case SEAMLESSRDP_MINIMIZED:
3658                            /* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
3659                               the Window Manager should probably just ignore the request, since
3660                               _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
3661                               such as minimization, rather than an independent state." Besides,
3662                               XIconifyWindow is easier. */
3663                            if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
3664                            {
3665                                    XWMHints *hints;
3666                                    hints = XGetWMHints(g_display, sw->wnd);
3667                                    if (hints)
3668                                    {
3669                                            hints->flags |= StateHint;
3670                                            hints->initial_state = IconicState;
3671                                            XSetWMHints(g_display, sw->wnd, hints);
3672                                            XFree(hints);
3673                                    }
3674                                    XMapWindow(g_display, sw->wnd);
3675                            }
3676                            else
3677                                    XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display));
3678                            break;
3679                    default:
3680                            warning("SeamlessRDP: Invalid state %d\n", state);
3681                            break;
3682            }
3683    
3684            sw->state = state;
3685    }
3686    
3687    
3688    void
3689    ui_seamless_syncbegin(unsigned long flags)
3690    {
3691            if (!g_seamless_active)
3692                    return;
3693    
3694            /* Destroy all seamless windows */
3695            while (g_seamless_windows)
3696            {
3697                    XDestroyWindow(g_display, g_seamless_windows->wnd);
3698                    sw_remove_window(g_seamless_windows);
3699            }
3700    }
3701    
3702    
3703    void
3704    ui_seamless_ack(unsigned int serial)
3705    {
3706            seamless_window *sw;
3707            for (sw = g_seamless_windows; sw; sw = sw->next)
3708            {
3709                    if (sw->outstanding_position && (sw->outpos_serial == serial))
3710                    {
3711                            sw->xoffset = sw->outpos_xoffset;
3712                            sw->yoffset = sw->outpos_yoffset;
3713                            sw->width = sw->outpos_width;
3714                            sw->height = sw->outpos_height;
3715                            sw->outstanding_position = False;
3716    
3717                            /* Do a complete redraw of the window as part of the
3718                               completion of the move. This is to remove any
3719                               artifacts caused by our lack of synchronization. */
3720                            XCopyArea(g_display, g_backstore,
3721                                      sw->wnd, g_gc,
3722                                      sw->xoffset, sw->yoffset, sw->width, sw->height, 0, 0);
3723    
3724                            break;
3725                    }
3726            }
3727    }

Legend:
Removed from v.821  
changed lines
  Added in v.1407

  ViewVC Help
Powered by ViewVC 1.1.26