/[rdesktop]/sourceforge.net/branches/seamlessrdp-branch/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/branches/seamlessrdp-branch/rdesktop/xwin.c

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

revision 10 by matty, Tue Aug 15 10:23:24 2000 UTC revision 822 by stargo, Mon Feb 28 22:38:24 2005 UTC
# Line 1  Line 1 
1  /*  /* -*- c-basic-offset: 8 -*-
2     rdesktop: A Remote Desktop Protocol client.     rdesktop: A Remote Desktop Protocol client.
3     User interface services - X-Windows     User interface services - X Window System
4     Copyright (C) Matthew Chapman 1999-2000     Copyright (C) Matthew Chapman 1999-2002
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
8     the Free Software Foundation; either version 2 of the License, or     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.     (at your option) any later version.
10      
11     This program is distributed in the hope that it will be useful,     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.     GNU General Public License for more details.
15      
16     You should have received a copy of the GNU General Public License     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */  */
20    
21  #include <X11/Xlib.h>  #include <X11/Xlib.h>
22    #include <X11/Xutil.h>
23    #include <unistd.h>
24    #include <sys/time.h>
25  #include <time.h>  #include <time.h>
26    #include <errno.h>
27    #include <strings.h>
28  #include "rdesktop.h"  #include "rdesktop.h"
29    #include "xproto.h"
30    
31  extern int width;  extern int g_width;
32  extern int height;  extern int g_height;
33  extern BOOL motion;  extern int g_xpos;
34    extern int g_ypos;
35  static Display *display;  extern BOOL g_sendmotion;
36  static Window wnd;  extern BOOL g_fullscreen;
37  static GC gc;  extern BOOL g_grab_keyboard;
38  static Visual *visual;  extern BOOL g_hide_decorations;
39  static XIM IM;  extern char g_title[];
40    extern int g_server_bpp;
41    extern int g_win_button_size;
42    
43    Display *g_display;
44    Time g_last_gesturetime;
45    static int g_x_socket;
46    static Screen *g_screen;
47    Window g_wnd;
48    extern uint32 g_embed_wnd;
49    BOOL g_enable_compose = False;
50    BOOL g_Unobscured;              /* used for screenblt */
51    static GC g_gc = NULL;
52    static GC g_create_bitmap_gc = NULL;
53    static GC g_create_glyph_gc = NULL;
54    static Visual *g_visual;
55    static int g_depth;
56    static int g_bpp;
57    static XIM g_IM;
58    static XIC g_IC;
59    static XModifierKeymap *g_mod_map;
60    static Cursor g_current_cursor;
61    static HCURSOR g_null_cursor = NULL;
62    static Atom g_protocol_atom, g_kill_atom;
63    static BOOL g_focused;
64    static BOOL g_mouse_in_wnd;
65    static BOOL g_arch_match = False;       /* set to True if RGB XServer and little endian */
66    
67    /* endianness */
68    static BOOL g_host_be;
69    static BOOL g_xserver_be;
70    static int g_red_shift_r, g_blue_shift_r, g_green_shift_r;
71    static int g_red_shift_l, g_blue_shift_l, g_green_shift_l;
72    
73    /* software backing store */
74    extern BOOL g_ownbackstore;
75    static Pixmap g_backstore = 0;
76    
77    /* Moving in single app mode */
78    static BOOL g_moving_wnd;
79    static int g_move_x_offset = 0;
80    static int g_move_y_offset = 0;
81    
82    #ifdef WITH_RDPSND
83    extern int g_dsp_fd;
84    extern BOOL g_dsp_busy;
85    extern BOOL g_rdpsnd;
86    #endif
87    
88    /* MWM decorations */
89    #define MWM_HINTS_DECORATIONS   (1L << 1)
90    #define PROP_MOTIF_WM_HINTS_ELEMENTS    5
91    typedef struct
92    {
93            uint32 flags;
94            uint32 functions;
95            uint32 decorations;
96            sint32 inputMode;
97            uint32 status;
98    }
99    PropMotifWmHints;
100    
101    typedef struct
102    {
103            uint32 red;
104            uint32 green;
105            uint32 blue;
106    }
107    PixelColour;
108    
109    
110    #define FILL_RECTANGLE(x,y,cx,cy)\
111    { \
112            XFillRectangle(g_display, g_wnd, g_gc, x, y, cx, cy); \
113            if (g_ownbackstore) \
114                    XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \
115    }
116    
117    #define FILL_RECTANGLE_BACKSTORE(x,y,cx,cy)\
118    { \
119            XFillRectangle(g_display, g_ownbackstore ? g_backstore : g_wnd, g_gc, x, y, cx, cy); \
120    }
121    
122    /* colour maps */
123    extern BOOL g_owncolmap;
124    static Colormap g_xcolmap;
125    static uint32 *g_colmap = NULL;
126    
127    #define TRANSLATE(col)          ( g_server_bpp != 8 ? translate_colour(col) : g_owncolmap ? col : g_colmap[col] )
128    #define SET_FOREGROUND(col)     XSetForeground(g_display, g_gc, TRANSLATE(col));
129    #define SET_BACKGROUND(col)     XSetBackground(g_display, g_gc, TRANSLATE(col));
130    
131  BOOL ui_create_window(char *title)  static int rop2_map[] = {
132            GXclear,                /* 0 */
133            GXnor,                  /* DPon */
134            GXandInverted,          /* DPna */
135            GXcopyInverted,         /* Pn */
136            GXandReverse,           /* PDna */
137            GXinvert,               /* Dn */
138            GXxor,                  /* DPx */
139            GXnand,                 /* DPan */
140            GXand,                  /* DPa */
141            GXequiv,                /* DPxn */
142            GXnoop,                 /* D */
143            GXorInverted,           /* DPno */
144            GXcopy,                 /* P */
145            GXorReverse,            /* PDno */
146            GXor,                   /* DPo */
147            GXset                   /* 1 */
148    };
149    
150    #define SET_FUNCTION(rop2)      { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, rop2_map[rop2]); }
151    #define RESET_FUNCTION(rop2)    { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); }
152    
153    static void
154    mwm_hide_decorations(void)
155  {  {
156          Screen *screen;          PropMotifWmHints motif_hints;
157          XSetWindowAttributes attribs;          Atom hintsatom;
         unsigned long input_mask;  
         int i;  
158    
159          display = XOpenDisplay(NULL);          /* setup the property */
160          if (display == NULL)          motif_hints.flags = MWM_HINTS_DECORATIONS;
161                  return False;          motif_hints.decorations = 0;
162    
163            /* get the atom for the property */
164            hintsatom = XInternAtom(g_display, "_MOTIF_WM_HINTS", False);
165            if (!hintsatom)
166            {
167                    warning("Failed to get atom _MOTIF_WM_HINTS: probably your window manager does not support MWM hints\n");
168                    return;
169            }
170    
171            XChangeProperty(g_display, g_wnd, hintsatom, hintsatom, 32, PropModeReplace,
172                            (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
173    }
174    
175    #define SPLITCOLOUR15(colour, rv) \
176    { \
177            rv.red = ((colour >> 7) & 0xf8) | ((colour >> 12) & 0x7); \
178            rv.green = ((colour >> 2) & 0xf8) | ((colour >> 8) & 0x7); \
179            rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
180    }
181    
182    #define SPLITCOLOUR16(colour, rv) \
183    { \
184            rv.red = ((colour >> 8) & 0xf8) | ((colour >> 13) & 0x7); \
185            rv.green = ((colour >> 3) & 0xfc) | ((colour >> 9) & 0x3); \
186            rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
187    } \
188    
189    #define SPLITCOLOUR24(colour, rv) \
190    { \
191            rv.blue = (colour & 0xff0000) >> 16; \
192            rv.green = (colour & 0x00ff00) >> 8; \
193            rv.red = (colour & 0x0000ff); \
194    }
195    
196    #define MAKECOLOUR(pc) \
197            ((pc.red >> g_red_shift_r) << g_red_shift_l) \
198                    | ((pc.green >> g_green_shift_r) << g_green_shift_l) \
199                    | ((pc.blue >> g_blue_shift_r) << g_blue_shift_l) \
200    
201    #define BSWAP16(x) { x = (((x & 0xff) << 8) | (x >> 8)); }
202    #define BSWAP24(x) { x = (((x & 0xff) << 16) | (x >> 16) | (x & 0xff00)); }
203    #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \
204                            x = (x << 16) | (x >> 16); }
205    
206    #define BOUT16(o, x) { *(o++) = x >> 8; *(o++) = x; }
207    #define BOUT24(o, x) { *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
208    #define BOUT32(o, x) { *(o++) = x >> 24; *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
209    #define LOUT16(o, x) { *(o++) = x; *(o++) = x >> 8; }
210    #define LOUT24(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; }
211    #define LOUT32(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; *(o++) = x >> 24; }
212    
213    static uint32
214    translate_colour(uint32 colour)
215    {
216            PixelColour pc;
217            switch (g_server_bpp)
218            {
219                    case 15:
220                            SPLITCOLOUR15(colour, pc);
221                            break;
222                    case 16:
223                            SPLITCOLOUR16(colour, pc);
224                            break;
225                    case 24:
226                            SPLITCOLOUR24(colour, pc);
227                            break;
228            }
229            return MAKECOLOUR(pc);
230    }
231    
232    /* indent is confused by UNROLL8 */
233    /* *INDENT-OFF* */
234    
235    /* repeat and unroll, similar to bitmap.c */
236    /* potentialy any of the following translate */
237    /* functions can use repeat but just doing */
238    /* the most common ones */
239    
240    #define UNROLL8(stm) { stm stm stm stm stm stm stm stm }
241    /* 2 byte output repeat */
242    #define REPEAT2(stm) \
243    { \
244            while (out <= end - 8 * 2) \
245                    UNROLL8(stm) \
246            while (out < end) \
247                    { stm } \
248    }
249    /* 3 byte output repeat */
250    #define REPEAT3(stm) \
251    { \
252            while (out <= end - 8 * 3) \
253                    UNROLL8(stm) \
254            while (out < end) \
255                    { stm } \
256    }
257    /* 4 byte output repeat */
258    #define REPEAT4(stm) \
259    { \
260            while (out <= end - 8 * 4) \
261                    UNROLL8(stm) \
262            while (out < end) \
263                    { stm } \
264    }
265    /* *INDENT-ON* */
266    
267    static void
268    translate8to8(const uint8 * data, uint8 * out, uint8 * end)
269    {
270            while (out < end)
271                    *(out++) = (uint8) g_colmap[*(data++)];
272    }
273    
274    static void
275    translate8to16(const uint8 * data, uint8 * out, uint8 * end)
276    {
277            uint16 value;
278    
279            if (g_arch_match)
280            {
281                    /* *INDENT-OFF* */
282                    REPEAT2
283                    (
284                            *((uint16 *) out) = g_colmap[*(data++)];
285                            out += 2;
286                    )
287                    /* *INDENT-ON* */
288            }
289            else if (g_xserver_be)
290            {
291                    while (out < end)
292                    {
293                            value = (uint16) g_colmap[*(data++)];
294                            BOUT16(out, value);
295                    }
296            }
297            else
298            {
299                    while (out < end)
300                    {
301                            value = (uint16) g_colmap[*(data++)];
302                            LOUT16(out, value);
303                    }
304            }
305    }
306    
307    /* little endian - conversion happens when colourmap is built */
308    static void
309    translate8to24(const uint8 * data, uint8 * out, uint8 * end)
310    {
311            uint32 value;
312    
313            if (g_xserver_be)
314            {
315                    while (out < end)
316                    {
317                            value = g_colmap[*(data++)];
318                            BOUT24(out, value);
319                    }
320            }
321            else
322            {
323                    while (out < end)
324                    {
325                            value = g_colmap[*(data++)];
326                            LOUT24(out, value);
327                    }
328            }
329    }
330    
331    static void
332    translate8to32(const uint8 * data, uint8 * out, uint8 * end)
333    {
334            uint32 value;
335    
336            if (g_arch_match)
337            {
338                    /* *INDENT-OFF* */
339                    REPEAT4
340                    (
341                            *((uint32 *) out) = g_colmap[*(data++)];
342                            out += 4;
343                    )
344                    /* *INDENT-ON* */
345            }
346            else if (g_xserver_be)
347            {
348                    while (out < end)
349                    {
350                            value = g_colmap[*(data++)];
351                            BOUT32(out, value);
352                    }
353            }
354            else
355            {
356                    while (out < end)
357                    {
358                            value = g_colmap[*(data++)];
359                            LOUT32(out, value);
360                    }
361            }
362    }
363    
364    static void
365    translate15to16(const uint16 * data, uint8 * out, uint8 * end)
366    {
367            uint16 pixel;
368            uint16 value;
369            PixelColour pc;
370    
371            if (g_xserver_be)
372            {
373                    while (out < end)
374                    {
375                            pixel = *(data++);
376                            if (g_host_be)
377                            {
378                                    BSWAP16(pixel);
379                            }
380                            SPLITCOLOUR15(pixel, pc);
381                            value = MAKECOLOUR(pc);
382                            BOUT16(out, value);
383                    }
384            }
385            else
386            {
387                    while (out < end)
388                    {
389                            pixel = *(data++);
390                            if (g_host_be)
391                            {
392                                    BSWAP16(pixel);
393                            }
394                            SPLITCOLOUR15(pixel, pc);
395                            value = MAKECOLOUR(pc);
396                            LOUT16(out, value);
397                    }
398            }
399    }
400    
401    static void
402    translate15to24(const uint16 * data, uint8 * out, uint8 * end)
403    {
404            uint32 value;
405            uint16 pixel;
406            PixelColour pc;
407    
408            if (g_arch_match)
409            {
410                    /* *INDENT-OFF* */
411                    REPEAT3
412                    (
413                            pixel = *(data++);
414                            SPLITCOLOUR15(pixel, pc);
415                            *(out++) = pc.blue;
416                            *(out++) = pc.green;
417                            *(out++) = pc.red;
418                    )
419                    /* *INDENT-ON* */
420            }
421            else if (g_xserver_be)
422            {
423                    while (out < end)
424                    {
425                            pixel = *(data++);
426                            if (g_host_be)
427                            {
428                                    BSWAP16(pixel);
429                            }
430                            SPLITCOLOUR15(pixel, pc);
431                            value = MAKECOLOUR(pc);
432                            BOUT24(out, value);
433                    }
434            }
435            else
436            {
437                    while (out < end)
438                    {
439                            pixel = *(data++);
440                            if (g_host_be)
441                            {
442                                    BSWAP16(pixel);
443                            }
444                            SPLITCOLOUR15(pixel, pc);
445                            value = MAKECOLOUR(pc);
446                            LOUT24(out, value);
447                    }
448            }
449    }
450    
451    static void
452    translate15to32(const uint16 * data, uint8 * out, uint8 * end)
453    {
454            uint16 pixel;
455            uint32 value;
456            PixelColour pc;
457    
458            if (g_arch_match)
459            {
460                    /* *INDENT-OFF* */
461                    REPEAT4
462                    (
463                            pixel = *(data++);
464                            SPLITCOLOUR15(pixel, pc);
465                            *(out++) = pc.blue;
466                            *(out++) = pc.green;
467                            *(out++) = pc.red;
468                            *(out++) = 0;
469                    )
470                    /* *INDENT-ON* */
471            }
472            else if (g_xserver_be)
473            {
474                    while (out < end)
475                    {
476                            pixel = *(data++);
477                            if (g_host_be)
478                            {
479                                    BSWAP16(pixel);
480                            }
481                            SPLITCOLOUR15(pixel, pc);
482                            value = MAKECOLOUR(pc);
483                            BOUT32(out, value);
484                    }
485            }
486            else
487            {
488                    while (out < end)
489                    {
490                            pixel = *(data++);
491                            if (g_host_be)
492                            {
493                                    BSWAP16(pixel);
494                            }
495                            SPLITCOLOUR15(pixel, pc);
496                            value = MAKECOLOUR(pc);
497                            LOUT32(out, value);
498                    }
499            }
500    }
501    
502    static void
503    translate16to16(const uint16 * data, uint8 * out, uint8 * end)
504    {
505            uint16 pixel;
506            uint16 value;
507            PixelColour pc;
508    
509            if (g_xserver_be)
510            {
511                    if (g_host_be)
512                    {
513                            while (out < end)
514                            {
515                                    pixel = *(data++);
516                                    BSWAP16(pixel);
517                                    SPLITCOLOUR16(pixel, pc);
518                                    value = MAKECOLOUR(pc);
519                                    BOUT16(out, value);
520                            }
521                    }
522                    else
523                    {
524                            while (out < end)
525                            {
526                                    pixel = *(data++);
527                                    SPLITCOLOUR16(pixel, pc);
528                                    value = MAKECOLOUR(pc);
529                                    BOUT16(out, value);
530                            }
531                    }
532            }
533            else
534            {
535                    if (g_host_be)
536                    {
537                            while (out < end)
538                            {
539                                    pixel = *(data++);
540                                    BSWAP16(pixel);
541                                    SPLITCOLOUR16(pixel, pc);
542                                    value = MAKECOLOUR(pc);
543                                    LOUT16(out, value);
544                            }
545                    }
546                    else
547                    {
548                            while (out < end)
549                            {
550                                    pixel = *(data++);
551                                    SPLITCOLOUR16(pixel, pc);
552                                    value = MAKECOLOUR(pc);
553                                    LOUT16(out, value);
554                            }
555                    }
556            }
557    }
558    
559    static void
560    translate16to24(const uint16 * data, uint8 * out, uint8 * end)
561    {
562            uint32 value;
563            uint16 pixel;
564            PixelColour pc;
565    
566            if (g_arch_match)
567            {
568                    /* *INDENT-OFF* */
569                    REPEAT3
570                    (
571                            pixel = *(data++);
572                            SPLITCOLOUR16(pixel, pc);
573                            *(out++) = pc.blue;
574                            *(out++) = pc.green;
575                            *(out++) = pc.red;
576                    )
577                    /* *INDENT-ON* */
578            }
579            else if (g_xserver_be)
580            {
581                    if (g_host_be)
582                    {
583                            while (out < end)
584                            {
585                                    pixel = *(data++);
586                                    BSWAP16(pixel);
587                                    SPLITCOLOUR16(pixel, pc);
588                                    value = MAKECOLOUR(pc);
589                                    BOUT24(out, value);
590                            }
591                    }
592                    else
593                    {
594                            while (out < end)
595                            {
596                                    pixel = *(data++);
597                                    SPLITCOLOUR16(pixel, pc);
598                                    value = MAKECOLOUR(pc);
599                                    BOUT24(out, value);
600                            }
601                    }
602            }
603            else
604            {
605                    if (g_host_be)
606                    {
607                            while (out < end)
608                            {
609                                    pixel = *(data++);
610                                    BSWAP16(pixel);
611                                    SPLITCOLOUR16(pixel, pc);
612                                    value = MAKECOLOUR(pc);
613                                    LOUT24(out, value);
614                            }
615                    }
616                    else
617                    {
618                            while (out < end)
619                            {
620                                    pixel = *(data++);
621                                    SPLITCOLOUR16(pixel, pc);
622                                    value = MAKECOLOUR(pc);
623                                    LOUT24(out, value);
624                            }
625                    }
626            }
627    }
628    
629          /* Check the screen supports 8-bit depth. */  static void
630          screen = DefaultScreenOfDisplay(display);  translate16to32(const uint16 * data, uint8 * out, uint8 * end)
631          for (i = 0; i < screen->ndepths; i++)  {
632                  if (screen->depths[i].depth == 8)          uint16 pixel;
633            uint32 value;
634            PixelColour pc;
635    
636            if (g_arch_match)
637            {
638                    /* *INDENT-OFF* */
639                    REPEAT4
640                    (
641                            pixel = *(data++);
642                            SPLITCOLOUR16(pixel, pc);
643                            *(out++) = pc.blue;
644                            *(out++) = pc.green;
645                            *(out++) = pc.red;
646                            *(out++) = 0;
647                    )
648                    /* *INDENT-ON* */
649            }
650            else if (g_xserver_be)
651            {
652                    if (g_host_be)
653                    {
654                            while (out < end)
655                            {
656                                    pixel = *(data++);
657                                    BSWAP16(pixel);
658                                    SPLITCOLOUR16(pixel, pc);
659                                    value = MAKECOLOUR(pc);
660                                    BOUT32(out, value);
661                            }
662                    }
663                    else
664                    {
665                            {
666                                    pixel = *(data++);
667                                    SPLITCOLOUR16(pixel, pc);
668                                    value = MAKECOLOUR(pc);
669                                    BOUT32(out, value);
670                            }
671                    }
672            }
673            else
674            {
675                    if (g_host_be)
676                    {
677                            while (out < end)
678                            {
679                                    pixel = *(data++);
680                                    BSWAP16(pixel);
681                                    SPLITCOLOUR16(pixel, pc);
682                                    value = MAKECOLOUR(pc);
683                                    LOUT32(out, value);
684                            }
685                    }
686                    else
687                    {
688                            while (out < end)
689                            {
690                                    pixel = *(data++);
691                                    SPLITCOLOUR16(pixel, pc);
692                                    value = MAKECOLOUR(pc);
693                                    LOUT32(out, value);
694                            }
695                    }
696            }
697    }
698    
699    static void
700    translate24to16(const uint8 * data, uint8 * out, uint8 * end)
701    {
702            uint32 pixel = 0;
703            uint16 value;
704            PixelColour pc;
705    
706            while (out < end)
707            {
708                    pixel = *(data++) << 16;
709                    pixel |= *(data++) << 8;
710                    pixel |= *(data++);
711                    SPLITCOLOUR24(pixel, pc);
712                    value = MAKECOLOUR(pc);
713                    if (g_xserver_be)
714                    {
715                            BOUT16(out, value);
716                    }
717                    else
718                    {
719                            LOUT16(out, value);
720                    }
721            }
722    }
723    
724    static void
725    translate24to24(const uint8 * data, uint8 * out, uint8 * end)
726    {
727            uint32 pixel;
728            uint32 value;
729            PixelColour pc;
730    
731            if (g_xserver_be)
732            {
733                    while (out < end)
734                    {
735                            pixel = *(data++) << 16;
736                            pixel |= *(data++) << 8;
737                            pixel |= *(data++);
738                            SPLITCOLOUR24(pixel, pc);
739                            value = MAKECOLOUR(pc);
740                            BOUT24(out, value);
741                    }
742            }
743            else
744            {
745                    while (out < end)
746                    {
747                            pixel = *(data++) << 16;
748                            pixel |= *(data++) << 8;
749                            pixel |= *(data++);
750                            SPLITCOLOUR24(pixel, pc);
751                            value = MAKECOLOUR(pc);
752                            LOUT24(out, value);
753                    }
754            }
755    }
756    
757    static void
758    translate24to32(const uint8 * data, uint8 * out, uint8 * end)
759    {
760            uint32 pixel;
761            uint32 value;
762            PixelColour pc;
763    
764            if (g_arch_match)
765            {
766                    /* *INDENT-OFF* */
767    #ifdef NEED_ALIGN
768                    REPEAT4
769                    (
770                            *(out++) = *(data++);
771                            *(out++) = *(data++);
772                            *(out++) = *(data++);
773                            *(out++) = 0;
774                    )
775    #else
776                    REPEAT4
777                    (
778                            *((uint32 *) out) = *((uint32 *) data);
779                            out += 4;
780                            data += 3;
781                    )
782    #endif
783                    /* *INDENT-ON* */
784            }
785            else if (g_xserver_be)
786            {
787                    while (out < end)
788                    {
789                            pixel = *(data++) << 16;
790                            pixel |= *(data++) << 8;
791                            pixel |= *(data++);
792                            SPLITCOLOUR24(pixel, pc);
793                            value = MAKECOLOUR(pc);
794                            BOUT32(out, value);
795                    }
796            }
797            else
798            {
799                    while (out < end)
800                    {
801                            pixel = *(data++) << 16;
802                            pixel |= *(data++) << 8;
803                            pixel |= *(data++);
804                            SPLITCOLOUR24(pixel, pc);
805                            value = MAKECOLOUR(pc);
806                            LOUT32(out, value);
807                    }
808            }
809    }
810    
811    static uint8 *
812    translate_image(int width, int height, uint8 * data)
813    {
814            int size;
815            uint8 *out;
816            uint8 *end;
817    
818            /* if server and xserver bpp match, */
819            /* and arch(endian) matches, no need to translate */
820            /* just return data */
821            if (g_arch_match)
822            {
823                    if (g_depth == 15 && g_server_bpp == 15)
824                            return data;
825                    if (g_depth == 16 && g_server_bpp == 16)
826                            return data;
827                    if (g_depth == 24 && g_bpp == 24 && g_server_bpp == 24)
828                            return data;
829            }
830    
831            size = width * height * (g_bpp / 8);
832            out = (uint8 *) xmalloc(size);
833            end = out + size;
834    
835            switch (g_server_bpp)
836            {
837                    case 24:
838                            switch (g_bpp)
839                            {
840                                    case 32:
841                                            translate24to32(data, out, end);
842                                            break;
843                                    case 24:
844                                            translate24to24(data, out, end);
845                                            break;
846                                    case 16:
847                                            translate24to16(data, out, end);
848                                            break;
849                            }
850                          break;                          break;
851                    case 16:
852                            switch (g_bpp)
853                            {
854                                    case 32:
855                                            translate16to32((uint16 *) data, out, end);
856                                            break;
857                                    case 24:
858                                            translate16to24((uint16 *) data, out, end);
859                                            break;
860                                    case 16:
861                                            translate16to16((uint16 *) data, out, end);
862                                            break;
863                            }
864                            break;
865                    case 15:
866                            switch (g_bpp)
867                            {
868                                    case 32:
869                                            translate15to32((uint16 *) data, out, end);
870                                            break;
871                                    case 24:
872                                            translate15to24((uint16 *) data, out, end);
873                                            break;
874                                    case 16:
875                                            translate15to16((uint16 *) data, out, end);
876                                            break;
877                            }
878                            break;
879                    case 8:
880                            switch (g_bpp)
881                            {
882                                    case 8:
883                                            translate8to8(data, out, end);
884                                            break;
885                                    case 16:
886                                            translate8to16(data, out, end);
887                                            break;
888                                    case 24:
889                                            translate8to24(data, out, end);
890                                            break;
891                                    case 32:
892                                            translate8to32(data, out, end);
893                                            break;
894                            }
895                            break;
896            }
897            return out;
898    }
899    
900    BOOL
901    get_key_state(unsigned int state, uint32 keysym)
902    {
903            int modifierpos, key, keysymMask = 0;
904            int offset;
905    
906            KeyCode keycode = XKeysymToKeycode(g_display, keysym);
907    
908            if (keycode == NoSymbol)
909                    return False;
910    
911            for (modifierpos = 0; modifierpos < 8; modifierpos++)
912            {
913                    offset = g_mod_map->max_keypermod * modifierpos;
914    
915                    for (key = 0; key < g_mod_map->max_keypermod; key++)
916                    {
917                            if (g_mod_map->modifiermap[offset + key] == keycode)
918                                    keysymMask |= 1 << modifierpos;
919                    }
920            }
921    
922            return (state & keysymMask) ? True : False;
923    }
924    
925    static void
926    calculate_shifts(uint32 mask, int *shift_r, int *shift_l)
927    {
928            *shift_l = ffs(mask) - 1;
929            mask >>= *shift_l;
930            *shift_r = 8 - ffs(mask & ~(mask >> 1));
931    }
932    
933          if (i >= screen->ndepths)  BOOL
934    ui_init(void)
935    {
936            XVisualInfo vi;
937            XPixmapFormatValues *pfm;
938            uint16 test;
939            int i, screen_num, nvisuals;
940            XVisualInfo *vmatches = NULL;
941            XVisualInfo template;
942            Bool TrueColorVisual = False;
943    
944            g_display = XOpenDisplay(NULL);
945            if (g_display == NULL)
946          {          {
947                  ERROR("8-bit depth required (in this version).\n");                  error("Failed to open display: %s\n", XDisplayName(NULL));
                 XCloseDisplay(display);  
948                  return False;                  return False;
949          }          }
950    
951          visual = DefaultVisual(display, DefaultScreen(display));          screen_num = DefaultScreen(g_display);
952            g_x_socket = ConnectionNumber(g_display);
953            g_screen = ScreenOfDisplay(g_display, screen_num);
954            g_depth = DefaultDepthOfScreen(g_screen);
955    
956            /* Search for best TrueColor depth */
957            template.class = TrueColor;
958            vmatches = XGetVisualInfo(g_display, VisualClassMask, &template, &nvisuals);
959    
960          attribs.background_pixel = BlackPixel(display, DefaultScreen(display));          nvisuals--;
961          attribs.backing_store = Always;          while (nvisuals >= 0)
962          wnd = XCreateWindow(display, DefaultRootWindow(display),          {
963                          0, 0, width, height, 0, 8, InputOutput, visual,                  if ((vmatches + nvisuals)->depth > g_depth)
964                          CWBackingStore | CWBackPixel, &attribs);                  {
965                            g_depth = (vmatches + nvisuals)->depth;
966          XStoreName(display, wnd, title);                  }
967          XMapWindow(display, wnd);                  nvisuals--;
968                    TrueColorVisual = True;
969          input_mask  = KeyPressMask | KeyReleaseMask;          }
970          input_mask |= ButtonPressMask | ButtonReleaseMask;  
971          if (motion)          test = 1;
972                  input_mask |= PointerMotionMask;          g_host_be = !(BOOL) (*(uint8 *) (&test));
973            g_xserver_be = (ImageByteOrder(g_display) == MSBFirst);
974    
975            if ((g_server_bpp == 8) && ((!TrueColorVisual) || (g_depth <= 8)))
976            {
977                    /* we use a colourmap, so the default visual should do */
978                    g_visual = DefaultVisualOfScreen(g_screen);
979                    g_depth = DefaultDepthOfScreen(g_screen);
980    
981                    /* Do not allocate colours on a TrueColor visual */
982                    if (g_visual->class == TrueColor)
983                    {
984                            g_owncolmap = False;
985                    }
986            }
987            else
988            {
989                    /* need a truecolour visual */
990                    if (!XMatchVisualInfo(g_display, screen_num, g_depth, TrueColor, &vi))
991                    {
992                            error("The display does not support true colour - high colour support unavailable.\n");
993                            return False;
994                    }
995    
996          XSelectInput(display, wnd, input_mask);                  g_visual = vi.visual;
997          gc = XCreateGC(display, wnd, 0, NULL);                  g_owncolmap = False;
998                    calculate_shifts(vi.red_mask, &g_red_shift_r, &g_red_shift_l);
999                    calculate_shifts(vi.blue_mask, &g_blue_shift_r, &g_blue_shift_l);
1000                    calculate_shifts(vi.green_mask, &g_green_shift_r, &g_green_shift_l);
1001    
1002                    /* if RGB video and everything is little endian */
1003                    if ((vi.red_mask > vi.green_mask && vi.green_mask > vi.blue_mask) &&
1004                        !g_xserver_be && !g_host_be)
1005                    {
1006                            if (g_depth <= 16 || (g_red_shift_l == 16 && g_green_shift_l == 8 &&
1007                                                  g_blue_shift_l == 0))
1008                            {
1009                                    g_arch_match = True;
1010                            }
1011                    }
1012    
1013                    if (g_arch_match)
1014                    {
1015                            DEBUG(("Architectures match, enabling little endian optimisations.\n"));
1016                    }
1017            }
1018    
1019            pfm = XListPixmapFormats(g_display, &i);
1020            if (pfm != NULL)
1021            {
1022                    /* Use maximum bpp for this depth - this is generally
1023                       desirable, e.g. 24 bits->32 bits. */
1024                    while (i--)
1025                    {
1026                            if ((pfm[i].depth == g_depth) && (pfm[i].bits_per_pixel > g_bpp))
1027                            {
1028                                    g_bpp = pfm[i].bits_per_pixel;
1029                            }
1030                    }
1031                    XFree(pfm);
1032            }
1033    
1034            if (g_bpp < 8)
1035            {
1036                    error("Less than 8 bpp not currently supported.\n");
1037                    XCloseDisplay(g_display);
1038                    return False;
1039            }
1040    
1041            if (!g_owncolmap)
1042            {
1043                    g_xcolmap =
1044                            XCreateColormap(g_display, RootWindowOfScreen(g_screen), g_visual,
1045                                            AllocNone);
1046                    if (g_depth <= 8)
1047                            warning("Screen depth is 8 bits or lower: you may want to use -C for a private colourmap\n");
1048            }
1049    
1050            if ((!g_ownbackstore) && (DoesBackingStore(g_screen) != Always))
1051            {
1052                    warning("External BackingStore not available, using internal\n");
1053                    g_ownbackstore = True;
1054            }
1055    
1056            /*
1057             * Determine desktop size
1058             */
1059            if (g_fullscreen)
1060            {
1061                    g_width = WidthOfScreen(g_screen);
1062                    g_height = HeightOfScreen(g_screen);
1063            }
1064            else if (g_width < 0)
1065            {
1066                    /* Percent of screen */
1067                    g_height = HeightOfScreen(g_screen) * (-g_width) / 100;
1068                    g_width = WidthOfScreen(g_screen) * (-g_width) / 100;
1069            }
1070            else if (g_width == 0)
1071            {
1072                    /* Fetch geometry from _NET_WORKAREA */
1073                    uint32 x, y, cx, cy;
1074    
1075                    if (get_current_workarea(&x, &y, &cx, &cy) == 0)
1076                    {
1077                            g_width = cx;
1078                            g_height = cy;
1079                    }
1080                    else
1081                    {
1082                            warning("Failed to get workarea: probably your window manager does not support extended hints\n");
1083                            g_width = 800;
1084                            g_height = 600;
1085                    }
1086            }
1087    
1088            /* make sure width is a multiple of 4 */
1089            g_width = (g_width + 3) & ~3;
1090    
1091            g_mod_map = XGetModifierMapping(g_display);
1092    
1093            xkeymap_init();
1094    
1095            if (g_enable_compose)
1096                    g_IM = XOpenIM(g_display, NULL, NULL, NULL);
1097    
1098            xclip_init();
1099    
1100            DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", g_server_bpp, g_bpp, g_depth));
1101    
         IM = XOpenIM(display, NULL, NULL, NULL);  
1102          return True;          return True;
1103  }  }
1104    
1105  void ui_destroy_window()  void
1106    ui_deinit(void)
1107  {  {
1108          XCloseIM(IM);          if (g_IM != NULL)
1109          XFreeGC(display, gc);                  XCloseIM(g_IM);
1110          XDestroyWindow(display, wnd);  
1111          XCloseDisplay(display);          if (g_null_cursor != NULL)
1112                    ui_destroy_cursor(g_null_cursor);
1113    
1114            XFreeModifiermap(g_mod_map);
1115    
1116            if (g_ownbackstore)
1117                    XFreePixmap(g_display, g_backstore);
1118    
1119            XFreeGC(g_display, g_gc);
1120            XCloseDisplay(g_display);
1121            g_display = NULL;
1122  }  }
1123    
1124  static uint8 xwin_translate_key(unsigned long key)  BOOL
1125    ui_create_window(void)
1126  {  {
1127          DEBUG("KEY(code=0x%lx)\n", key);          uint8 null_pointer_mask[1] = { 0x80 };
1128            uint8 null_pointer_data[24] = { 0x00 };
1129    
1130            XSetWindowAttributes attribs;
1131            XClassHint *classhints;
1132            XSizeHints *sizehints;
1133            int wndwidth, wndheight;
1134            long input_mask, ic_input_mask;
1135            XEvent xevent;
1136    
1137            wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;
1138            wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;
1139    
1140            attribs.background_pixel = BlackPixelOfScreen(g_screen);
1141            attribs.border_pixel = WhitePixelOfScreen(g_screen);
1142            attribs.backing_store = g_ownbackstore ? NotUseful : Always;
1143            attribs.override_redirect = g_fullscreen;
1144            attribs.colormap = g_xcolmap;
1145    
1146            g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
1147                                  wndheight, 0, g_depth, InputOutput, g_visual,
1148                                  CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
1149                                  CWBorderPixel, &attribs);
1150    
1151            if (g_gc == NULL)
1152                    g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
1153    
1154            if (g_create_bitmap_gc == NULL)
1155                    g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL);
1156    
1157            if ((g_ownbackstore) && (g_backstore == 0))
1158            {
1159                    g_backstore = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1160    
1161                    /* clear to prevent rubbish being exposed at startup */
1162                    XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
1163                    XFillRectangle(g_display, g_backstore, g_gc, 0, 0, g_width, g_height);
1164            }
1165    
1166            XStoreName(g_display, g_wnd, g_title);
1167    
1168          if ((key > 8) && (key <= 0x60))          if (g_hide_decorations)
1169                  return (key - 8);                  mwm_hide_decorations();
1170    
1171          switch (key)          classhints = XAllocClassHint();
1172            if (classhints != NULL)
1173          {          {
1174                  case 0x62: /* left arrow */                  classhints->res_name = classhints->res_class = "rdesktop";
1175                          return 0x48;                  XSetClassHint(g_display, g_wnd, classhints);
1176                  case 0x64: /* up arrow */                  XFree(classhints);
                         return 0x4b;  
                 case 0x66: /* down arrow */  
                         return 0x4d;  
                 case 0x68: /* right arrow */  
                         return 0x50;  
                 case 0x73: /* Windows key */  
                         DEBUG("CHECKPOINT\n");  
1177          }          }
1178    
1179          return 0;          sizehints = XAllocSizeHints();
1180            if (sizehints)
1181            {
1182                    sizehints->flags = PMinSize | PMaxSize;
1183                    sizehints->min_width = sizehints->max_width = g_width;
1184                    sizehints->min_height = sizehints->max_height = g_height;
1185                    XSetWMNormalHints(g_display, g_wnd, sizehints);
1186                    XFree(sizehints);
1187            }
1188    
1189            if (g_embed_wnd)
1190            {
1191                    XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);
1192            }
1193    
1194            input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
1195                    VisibilityChangeMask | FocusChangeMask;
1196    
1197            if (g_sendmotion)
1198                    input_mask |= PointerMotionMask;
1199            if (g_ownbackstore)
1200                    input_mask |= ExposureMask;
1201            if (g_fullscreen || g_grab_keyboard)
1202                    input_mask |= EnterWindowMask;
1203            if (g_grab_keyboard)
1204                    input_mask |= LeaveWindowMask;
1205    
1206            if (g_IM != NULL)
1207            {
1208                    g_IC = XCreateIC(g_IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
1209                                     XNClientWindow, g_wnd, XNFocusWindow, g_wnd, NULL);
1210    
1211                    if ((g_IC != NULL)
1212                        && (XGetICValues(g_IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
1213                            input_mask |= ic_input_mask;
1214            }
1215    
1216            XSelectInput(g_display, g_wnd, input_mask);
1217            XMapWindow(g_display, g_wnd);
1218    
1219            /* wait for VisibilityNotify */
1220            do
1221            {
1222                    XMaskEvent(g_display, VisibilityChangeMask, &xevent);
1223            }
1224            while (xevent.type != VisibilityNotify);
1225            g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1226    
1227            g_focused = False;
1228            g_mouse_in_wnd = False;
1229    
1230            /* handle the WM_DELETE_WINDOW protocol */
1231            g_protocol_atom = XInternAtom(g_display, "WM_PROTOCOLS", True);
1232            g_kill_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", True);
1233            XSetWMProtocols(g_display, g_wnd, &g_kill_atom, 1);
1234    
1235            /* create invisible 1x1 cursor to be used as null cursor */
1236            if (g_null_cursor == NULL)
1237                    g_null_cursor = ui_create_cursor(0, 0, 1, 1, null_pointer_mask, null_pointer_data);
1238    
1239            return True;
1240  }  }
1241    
1242  static uint16 xwin_translate_mouse(unsigned long button)  void
1243    ui_resize_window()
1244  {  {
1245          switch (button)          XSizeHints *sizehints;
1246            Pixmap bs;
1247    
1248            sizehints = XAllocSizeHints();
1249            if (sizehints)
1250          {          {
1251                  case Button1: /* left */                  sizehints->flags = PMinSize | PMaxSize;
1252                          return MOUSE_FLAG_BUTTON1;                  sizehints->min_width = sizehints->max_width = g_width;
1253                  case Button2: /* middle */                  sizehints->min_height = sizehints->max_height = g_height;
1254                          return MOUSE_FLAG_BUTTON3;                  XSetWMNormalHints(g_display, g_wnd, sizehints);
1255                  case Button3: /* right */                  XFree(sizehints);
                         return MOUSE_FLAG_BUTTON2;  
1256          }          }
1257    
1258          return 0;          if (!(g_fullscreen || g_embed_wnd))
1259            {
1260                    XResizeWindow(g_display, g_wnd, g_width, g_height);
1261            }
1262    
1263            /* create new backstore pixmap */
1264            if (g_backstore != 0)
1265            {
1266                    bs = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1267                    XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
1268                    XFillRectangle(g_display, bs, g_gc, 0, 0, g_width, g_height);
1269                    XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, g_width, g_height, 0, 0);
1270                    XFreePixmap(g_display, g_backstore);
1271                    g_backstore = bs;
1272            }
1273  }  }
1274    
1275  void ui_process_events()  void
1276    ui_destroy_window(void)
1277  {  {
1278          XEvent event;          if (g_IC != NULL)
1279          uint8 scancode;                  XDestroyIC(g_IC);
         uint16 button;  
         uint32 ev_time;  
1280    
1281          if (display == NULL)          XDestroyWindow(g_display, g_wnd);
1282                  return;  }
1283    
1284    void
1285    xwin_toggle_fullscreen(void)
1286    {
1287            Pixmap contents = 0;
1288    
1289          while (XCheckWindowEvent(display, wnd, 0xffffffff, &event))          if (!g_ownbackstore)
1290          {          {
1291                  ev_time = time(NULL);                  /* need to save contents of window */
1292                    contents = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1293                    XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, g_width, g_height, 0, 0);
1294            }
1295    
1296            ui_destroy_window();
1297            g_fullscreen = !g_fullscreen;
1298            ui_create_window();
1299    
1300            XDefineCursor(g_display, g_wnd, g_current_cursor);
1301    
1302                  switch (event.type)          if (!g_ownbackstore)
1303            {
1304                    XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, g_width, g_height, 0, 0);
1305                    XFreePixmap(g_display, contents);
1306            }
1307    }
1308    
1309    /* Process all events in Xlib queue
1310       Returns 0 after user quit, 1 otherwise */
1311    static int
1312    xwin_process_events(void)
1313    {
1314            XEvent xevent;
1315            KeySym keysym;
1316            uint16 button, flags;
1317            uint32 ev_time;
1318            key_translation tr;
1319            char str[256];
1320            Status status;
1321    
1322            while (XPending(g_display) > 0)
1323            {
1324                    XNextEvent(g_display, &xevent);
1325    
1326                    if ((g_IC != NULL) && (XFilterEvent(&xevent, None) == True))
1327                  {                  {
1328                            DEBUG_KBD(("Filtering event\n"));
1329                            continue;
1330                    }
1331    
1332                    flags = 0;
1333    
1334                    switch (xevent.type)
1335                    {
1336                            case VisibilityNotify:
1337                                    g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1338                                    break;
1339                            case ClientMessage:
1340                                    /* the window manager told us to quit */
1341                                    if ((xevent.xclient.message_type == g_protocol_atom)
1342                                        && ((Atom) xevent.xclient.data.l[0] == g_kill_atom))
1343                                            /* Quit */
1344                                            return 0;
1345                                    break;
1346    
1347                          case KeyPress:                          case KeyPress:
1348                                  scancode = xwin_translate_key(event.xkey.keycode);                                  g_last_gesturetime = xevent.xkey.time;
1349                                  if (scancode == 0)                                  if (g_IC != NULL)
1350                                            /* Multi_key compatible version */
1351                                    {
1352                                            XmbLookupString(g_IC,
1353                                                            &xevent.xkey, str, sizeof(str), &keysym,
1354                                                            &status);
1355                                            if (!((status == XLookupKeySym) || (status == XLookupBoth)))
1356                                            {
1357                                                    error("XmbLookupString failed with status 0x%x\n",
1358                                                          status);
1359                                                    break;
1360                                            }
1361                                    }
1362                                    else
1363                                    {
1364                                            /* Plain old XLookupString */
1365                                            DEBUG_KBD(("\nNo input context, using XLookupString\n"));
1366                                            XLookupString((XKeyEvent *) & xevent,
1367                                                          str, sizeof(str), &keysym, NULL);
1368                                    }
1369    
1370                                    DEBUG_KBD(("KeyPress for (keysym 0x%lx, %s)\n", keysym,
1371                                               get_ksname(keysym)));
1372    
1373                                    ev_time = time(NULL);
1374                                    if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
1375                                          break;                                          break;
1376    
1377                                  rdp_send_input(ev_time, RDP_INPUT_SCANCODE, 0,                                  tr = xkeymap_translate_key(keysym,
1378                                                  scancode, 0);                                                             xevent.xkey.keycode, xevent.xkey.state);
1379    
1380                                    if (tr.scancode == 0)
1381                                            break;
1382    
1383                                    save_remote_modifiers(tr.scancode);
1384                                    ensure_remote_modifiers(ev_time, tr);
1385                                    rdp_send_scancode(ev_time, RDP_KEYPRESS, tr.scancode);
1386                                    restore_remote_modifiers(ev_time, tr.scancode);
1387    
1388                                  break;                                  break;
1389    
1390                          case KeyRelease:                          case KeyRelease:
1391                                  scancode = xwin_translate_key(event.xkey.keycode);                                  g_last_gesturetime = xevent.xkey.time;
1392                                  if (scancode == 0)                                  XLookupString((XKeyEvent *) & xevent, str,
1393                                                  sizeof(str), &keysym, NULL);
1394    
1395                                    DEBUG_KBD(("\nKeyRelease for (keysym 0x%lx, %s)\n", keysym,
1396                                               get_ksname(keysym)));
1397    
1398                                    ev_time = time(NULL);
1399                                    if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
1400                                            break;
1401    
1402                                    tr = xkeymap_translate_key(keysym,
1403                                                               xevent.xkey.keycode, xevent.xkey.state);
1404    
1405                                    if (tr.scancode == 0)
1406                                          break;                                          break;
1407    
1408                                  rdp_send_input(ev_time, RDP_INPUT_SCANCODE,                                  rdp_send_scancode(ev_time, RDP_KEYRELEASE, tr.scancode);
                                                 KBD_FLAG_DOWN | KBD_FLAG_UP,  
                                                 scancode, 0);  
1409                                  break;                                  break;
1410    
1411                          case ButtonPress:                          case ButtonPress:
1412                                  button = xwin_translate_mouse(event.xbutton.button);                                  flags = MOUSE_FLAG_DOWN;
1413                                    /* fall through */
1414    
1415                            case ButtonRelease:
1416                                    g_last_gesturetime = xevent.xbutton.time;
1417                                    button = xkeymap_translate_button(xevent.xbutton.button);
1418                                  if (button == 0)                                  if (button == 0)
1419                                          break;                                          break;
1420    
1421                                  rdp_send_input(ev_time, RDP_INPUT_MOUSE,                                  /* If win_button_size is nonzero, enable single app mode */
1422                                                  button | MOUSE_FLAG_DOWN,                                  if (xevent.xbutton.y < g_win_button_size)
1423                                                  event.xbutton.x,                                  {
1424                                                  event.xbutton.y);                                          /* Stop moving window when button is released, regardless of cursor position */
1425                                            if (g_moving_wnd && (xevent.type == ButtonRelease))
1426                                                    g_moving_wnd = False;
1427    
1428                                            /*  Check from right to left: */
1429    
1430                                            if (xevent.xbutton.x >= g_width - g_win_button_size)
1431                                            {
1432                                                    /* The close button, continue */
1433                                                    ;
1434                                            }
1435                                            else if (xevent.xbutton.x >=
1436                                                     g_width - g_win_button_size * 2)
1437                                            {
1438                                                    /* The maximize/restore button. Do not send to
1439                                                       server.  It might be a good idea to change the
1440                                                       cursor or give some other visible indication
1441                                                       that rdesktop inhibited this click */
1442                                                    break;
1443                                            }
1444                                            else if (xevent.xbutton.x >=
1445                                                     g_width - g_win_button_size * 3)
1446                                            {
1447                                                    /* The minimize button. Iconify window. */
1448                                                    XIconifyWindow(g_display, g_wnd,
1449                                                                   DefaultScreen(g_display));
1450                                                    break;
1451                                            }
1452                                            else if (xevent.xbutton.x <= g_win_button_size)
1453                                            {
1454                                                    /* The system menu. Ignore. */
1455                                                    break;
1456                                            }
1457                                            else
1458                                            {
1459                                                    /* The title bar. */
1460                                                    if ((xevent.type == ButtonPress) && !g_fullscreen
1461                                                        && g_hide_decorations)
1462                                                    {
1463                                                            g_moving_wnd = True;
1464                                                            g_move_x_offset = xevent.xbutton.x;
1465                                                            g_move_y_offset = xevent.xbutton.y;
1466                                                    }
1467                                                    break;
1468    
1469                                            }
1470                                    }
1471    
1472                                    rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1473                                                   flags | button, xevent.xbutton.x, xevent.xbutton.y);
1474                                  break;                                  break;
1475    
1476                          case ButtonRelease:                          case MotionNotify:
1477                                  button = xwin_translate_mouse(event.xbutton.button);                                  if (g_moving_wnd)
1478                                  if (button == 0)                                  {
1479                                            XMoveWindow(g_display, g_wnd,
1480                                                        xevent.xmotion.x_root - g_move_x_offset,
1481                                                        xevent.xmotion.y_root - g_move_y_offset);
1482                                          break;                                          break;
1483                                    }
1484    
1485                                  rdp_send_input(ev_time, RDP_INPUT_MOUSE,                                  if (g_fullscreen && !g_focused)
1486                                                  button,                                          XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1487                                                  event.xbutton.x,                                                         CurrentTime);
1488                                                  event.xbutton.y);                                  rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1489                                                   MOUSE_FLAG_MOVE, xevent.xmotion.x, xevent.xmotion.y);
1490                                  break;                                  break;
1491    
1492                          case MotionNotify:                          case FocusIn:
1493                                  rdp_send_input(ev_time, RDP_INPUT_MOUSE,                                  if (xevent.xfocus.mode == NotifyGrab)
1494                                                  MOUSE_FLAG_MOVE,                                          break;
1495                                                  event.xmotion.x,                                  g_focused = True;
1496                                                  event.xmotion.y);                                  reset_modifier_keys();
1497                                    if (g_grab_keyboard && g_mouse_in_wnd)
1498                                            XGrabKeyboard(g_display, g_wnd, True,
1499                                                          GrabModeAsync, GrabModeAsync, CurrentTime);
1500                                    break;
1501    
1502                            case FocusOut:
1503                                    if (xevent.xfocus.mode == NotifyUngrab)
1504                                            break;
1505                                    g_focused = False;
1506                                    if (xevent.xfocus.mode == NotifyWhileGrabbed)
1507                                            XUngrabKeyboard(g_display, CurrentTime);
1508                                    break;
1509    
1510                            case EnterNotify:
1511                                    /* we only register for this event when in fullscreen mode */
1512                                    /* or grab_keyboard */
1513                                    g_mouse_in_wnd = True;
1514                                    if (g_fullscreen)
1515                                    {
1516                                            XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1517                                                           CurrentTime);
1518                                            break;
1519                                    }
1520                                    if (g_focused)
1521                                            XGrabKeyboard(g_display, g_wnd, True,
1522                                                          GrabModeAsync, GrabModeAsync, CurrentTime);
1523                                    break;
1524    
1525                            case LeaveNotify:
1526                                    /* we only register for this event when grab_keyboard */
1527                                    g_mouse_in_wnd = False;
1528                                    XUngrabKeyboard(g_display, CurrentTime);
1529                                    break;
1530    
1531                            case Expose:
1532                                    XCopyArea(g_display, g_backstore, g_wnd, g_gc,
1533                                              xevent.xexpose.x, xevent.xexpose.y,
1534                                              xevent.xexpose.width,
1535                                              xevent.xexpose.height,
1536                                              xevent.xexpose.x, xevent.xexpose.y);
1537                                    break;
1538    
1539                            case MappingNotify:
1540                                    /* Refresh keyboard mapping if it has changed. This is important for
1541                                       Xvnc, since it allocates keycodes dynamically */
1542                                    if (xevent.xmapping.request == MappingKeyboard
1543                                        || xevent.xmapping.request == MappingModifier)
1544                                            XRefreshKeyboardMapping(&xevent.xmapping);
1545    
1546                                    if (xevent.xmapping.request == MappingModifier)
1547                                    {
1548                                            XFreeModifiermap(g_mod_map);
1549                                            g_mod_map = XGetModifierMapping(g_display);
1550                                    }
1551                                    break;
1552    
1553                                    /* clipboard stuff */
1554                            case SelectionNotify:
1555                                    xclip_handle_SelectionNotify(&xevent.xselection);
1556                                    break;
1557                            case SelectionRequest:
1558                                    xclip_handle_SelectionRequest(&xevent.xselectionrequest);
1559                                    break;
1560                            case SelectionClear:
1561                                    xclip_handle_SelectionClear();
1562                                    break;
1563                            case PropertyNotify:
1564                                    xclip_handle_PropertyNotify(&xevent.xproperty);
1565                                    break;
1566                  }                  }
1567          }          }
1568            /* Keep going */
1569            return 1;
1570  }  }
1571    
1572  void ui_move_pointer(int x, int y)  /* Returns 0 after user quit, 1 otherwise */
1573    int
1574    ui_select(int rdp_socket)
1575  {  {
1576          XWarpPointer(display, wnd, wnd, 0, 0, 0, 0, x, y);          int n;
1577            fd_set rfds, wfds;
1578            struct timeval tv;
1579            BOOL s_timeout = False;
1580    
1581            while (True)
1582            {
1583                    n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
1584                    /* Process any events already waiting */
1585                    if (!xwin_process_events())
1586                            /* User quit */
1587                            return 0;
1588    
1589                    FD_ZERO(&rfds);
1590                    FD_ZERO(&wfds);
1591                    FD_SET(rdp_socket, &rfds);
1592                    FD_SET(g_x_socket, &rfds);
1593    
1594    #ifdef WITH_RDPSND
1595                    /* FIXME: there should be an API for registering fds */
1596                    if (g_dsp_busy)
1597                    {
1598                            FD_SET(g_dsp_fd, &wfds);
1599                            n = (g_dsp_fd > n) ? g_dsp_fd : n;
1600                    }
1601    #endif
1602                    /* default timeout */
1603                    tv.tv_sec = 60;
1604                    tv.tv_usec = 0;
1605    
1606                    /* add redirection handles */
1607                    rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
1608    
1609                    n++;
1610    
1611                    switch (select(n, &rfds, &wfds, NULL, &tv))
1612                    {
1613                            case -1:
1614                                    error("select: %s\n", strerror(errno));
1615    
1616                            case 0:
1617                                    /* Abort serial read calls */
1618                                    if (s_timeout)
1619                                            rdpdr_check_fds(&rfds, &wfds, (BOOL) True);
1620                                    continue;
1621                    }
1622    
1623                    rdpdr_check_fds(&rfds, &wfds, (BOOL) False);
1624    
1625                    if (FD_ISSET(rdp_socket, &rfds))
1626                            return 1;
1627    
1628    #ifdef WITH_RDPSND
1629                    if (g_dsp_busy && FD_ISSET(g_dsp_fd, &wfds))
1630                            wave_out_play();
1631    #endif
1632            }
1633  }  }
1634    
1635  HBITMAP ui_create_bitmap(int width, int height, uint8 *data)  void
1636    ui_move_pointer(int x, int y)
1637    {
1638            XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
1639    }
1640    
1641    HBITMAP
1642    ui_create_bitmap(int width, int height, uint8 * data)
1643  {  {
1644          XImage *image;          XImage *image;
1645          Pixmap bitmap;          Pixmap bitmap;
1646            uint8 *tdata;
1647            int bitmap_pad;
1648    
1649          bitmap = XCreatePixmap(display, wnd, width, height, 8);          if (g_server_bpp == 8)
1650            {
1651                    bitmap_pad = 8;
1652            }
1653            else
1654            {
1655                    bitmap_pad = g_bpp;
1656    
1657                    if (g_bpp == 24)
1658                            bitmap_pad = 32;
1659            }
1660    
1661            tdata = (g_owncolmap ? data : translate_image(width, height, data));
1662            bitmap = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
1663            image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
1664                                 (char *) tdata, width, height, bitmap_pad, 0);
1665    
1666            XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height);
1667    
         image = XCreateImage(display, visual, 8, ZPixmap, 0,  
                                 data, width, height, 8, width);  
         XSetFunction(display, gc, GXcopy);  
         XPutImage(display, bitmap, gc, image, 0, 0, 0, 0, width, height);  
1668          XFree(image);          XFree(image);
1669                    if (tdata != data)
1670          return (HBITMAP)bitmap;                  xfree(tdata);
1671            return (HBITMAP) bitmap;
1672  }  }
1673    
1674  void ui_paint_bitmap(int x, int y, int cx, int cy,  void
1675                          int width, int height, uint8 *data)  ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
1676  {  {
1677          XImage *image;          XImage *image;
1678            uint8 *tdata;
1679            int bitmap_pad;
1680    
1681            if (g_server_bpp == 8)
1682            {
1683                    bitmap_pad = 8;
1684            }
1685            else
1686            {
1687                    bitmap_pad = g_bpp;
1688    
1689                    if (g_bpp == 24)
1690                            bitmap_pad = 32;
1691            }
1692    
1693            tdata = (g_owncolmap ? data : translate_image(width, height, data));
1694            image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
1695                                 (char *) tdata, width, height, bitmap_pad, 0);
1696    
1697            if (g_ownbackstore)
1698            {
1699                    XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
1700                    XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
1701            }
1702            else
1703            {
1704                    XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
1705            }
1706    
1707          image = XCreateImage(display, visual, 8, ZPixmap, 0,          XFree(image);
1708                                  data, width, height, 8, width);          if (tdata != data)
1709          XSetFunction(display, gc, GXcopy);                  xfree(tdata);
         XPutImage(display, wnd, gc, image, 0, 0, x, y, cx, cy);  
         XFree(image);  
1710  }  }
1711    
1712  void ui_destroy_bitmap(HBITMAP bmp)  void
1713    ui_destroy_bitmap(HBITMAP bmp)
1714  {  {
1715          XFreePixmap(display, (Pixmap)bmp);          XFreePixmap(g_display, (Pixmap) bmp);
1716  }  }
1717    
1718  HGLYPH ui_create_glyph(int width, int height, uint8 *data)  HGLYPH
1719    ui_create_glyph(int width, int height, uint8 * data)
1720  {  {
1721          XImage *image;          XImage *image;
1722          Pixmap bitmap;          Pixmap bitmap;
1723          int scanline;          int scanline;
         GC gc;  
1724    
1725          scanline = (width + 7) / 8;          scanline = (width + 7) / 8;
1726    
1727          bitmap = XCreatePixmap(display, wnd, width, height, 1);          bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1);
1728          gc = XCreateGC(display, bitmap, 0, NULL);          if (g_create_glyph_gc == 0)
1729                    g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL);
1730    
1731            image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data,
1732                                 width, height, 8, scanline);
1733            image->byte_order = MSBFirst;
1734            image->bitmap_bit_order = MSBFirst;
1735            XInitImage(image);
1736    
1737            XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
1738    
         image = XCreateImage(display, visual, 1, ZPixmap, 0,  
                                 data, width, height, 8, scanline);  
         XSetFunction(display, gc, GXcopy);  
         XPutImage(display, bitmap, gc, image, 0, 0, 0, 0, width, height);  
1739          XFree(image);          XFree(image);
1740          XFreeGC(display, gc);          return (HGLYPH) bitmap;
1741            }
1742          return (HGLYPH)bitmap;  
1743    void
1744    ui_destroy_glyph(HGLYPH glyph)
1745    {
1746            XFreePixmap(g_display, (Pixmap) glyph);
1747    }
1748    
1749    HCURSOR
1750    ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
1751                     uint8 * andmask, uint8 * xormask)
1752    {
1753            HGLYPH maskglyph, cursorglyph;
1754            XColor bg, fg;
1755            Cursor xcursor;
1756            uint8 *cursor, *pcursor;
1757            uint8 *mask, *pmask;
1758            uint8 nextbit;
1759            int scanline, offset;
1760            int i, j;
1761    
1762            scanline = (width + 7) / 8;
1763            offset = scanline * height;
1764    
1765            cursor = (uint8 *) xmalloc(offset);
1766            memset(cursor, 0, offset);
1767    
1768            mask = (uint8 *) xmalloc(offset);
1769            memset(mask, 0, offset);
1770    
1771            /* approximate AND and XOR masks with a monochrome X pointer */
1772            for (i = 0; i < height; i++)
1773            {
1774                    offset -= scanline;
1775                    pcursor = &cursor[offset];
1776                    pmask = &mask[offset];
1777    
1778                    for (j = 0; j < scanline; j++)
1779                    {
1780                            for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
1781                            {
1782                                    if (xormask[0] || xormask[1] || xormask[2])
1783                                    {
1784                                            *pcursor |= (~(*andmask) & nextbit);
1785                                            *pmask |= nextbit;
1786                                    }
1787                                    else
1788                                    {
1789                                            *pcursor |= ((*andmask) & nextbit);
1790                                            *pmask |= (~(*andmask) & nextbit);
1791                                    }
1792    
1793                                    xormask += 3;
1794                            }
1795    
1796                            andmask++;
1797                            pcursor++;
1798                            pmask++;
1799                    }
1800            }
1801    
1802            fg.red = fg.blue = fg.green = 0xffff;
1803            bg.red = bg.blue = bg.green = 0x0000;
1804            fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
1805    
1806            cursorglyph = ui_create_glyph(width, height, cursor);
1807            maskglyph = ui_create_glyph(width, height, mask);
1808    
1809            xcursor =
1810                    XCreatePixmapCursor(g_display, (Pixmap) cursorglyph,
1811                                        (Pixmap) maskglyph, &fg, &bg, x, y);
1812    
1813            ui_destroy_glyph(maskglyph);
1814            ui_destroy_glyph(cursorglyph);
1815            xfree(mask);
1816            xfree(cursor);
1817            return (HCURSOR) xcursor;
1818  }  }
1819    
1820  void ui_destroy_glyph(HGLYPH glyph)  void
1821    ui_set_cursor(HCURSOR cursor)
1822  {  {
1823          XFreePixmap(display, (Pixmap)glyph);          g_current_cursor = (Cursor) cursor;
1824            XDefineCursor(g_display, g_wnd, g_current_cursor);
1825  }  }
1826    
1827  HCOLOURMAP ui_create_colourmap(COLOURMAP *colours)  void
1828    ui_destroy_cursor(HCURSOR cursor)
1829    {
1830            XFreeCursor(g_display, (Cursor) cursor);
1831    }
1832    
1833    void
1834    ui_set_null_cursor(void)
1835    {
1836            ui_set_cursor(g_null_cursor);
1837    }
1838    
1839    #define MAKE_XCOLOR(xc,c) \
1840                    (xc)->red   = ((c)->red   << 8) | (c)->red; \
1841                    (xc)->green = ((c)->green << 8) | (c)->green; \
1842                    (xc)->blue  = ((c)->blue  << 8) | (c)->blue; \
1843                    (xc)->flags = DoRed | DoGreen | DoBlue;
1844    
1845    
1846    HCOLOURMAP
1847    ui_create_colourmap(COLOURMAP * colours)
1848  {  {
1849          COLOURENTRY *entry;          COLOURENTRY *entry;
         XColor *xcolours, *xentry;  
         Colormap map;  
1850          int i, ncolours = colours->ncolours;          int i, ncolours = colours->ncolours;
1851            if (!g_owncolmap)
         xcolours = xmalloc(sizeof(XColor) * ncolours);  
         for (i = 0; i < ncolours; i++)  
1852          {          {
1853                  entry = &colours->colours[i];                  uint32 *map = (uint32 *) xmalloc(sizeof(*g_colmap) * ncolours);
1854                  xentry = &xcolours[i];                  XColor xentry;
1855                    XColor xc_cache[256];
1856                    uint32 colour;
1857                    int colLookup = 256;
1858                    for (i = 0; i < ncolours; i++)
1859                    {
1860                            entry = &colours->colours[i];
1861                            MAKE_XCOLOR(&xentry, entry);
1862    
1863                            if (XAllocColor(g_display, g_xcolmap, &xentry) == 0)
1864                            {
1865                                    /* Allocation failed, find closest match. */
1866                                    int j = 256;
1867                                    int nMinDist = 3 * 256 * 256;
1868                                    long nDist = nMinDist;
1869    
1870                                    /* only get the colors once */
1871                                    while (colLookup--)
1872                                    {
1873                                            xc_cache[colLookup].pixel = colLookup;
1874                                            xc_cache[colLookup].red = xc_cache[colLookup].green =
1875                                                    xc_cache[colLookup].blue = 0;
1876                                            xc_cache[colLookup].flags = 0;
1877                                            XQueryColor(g_display,
1878                                                        DefaultColormap(g_display,
1879                                                                        DefaultScreen(g_display)),
1880                                                        &xc_cache[colLookup]);
1881                                    }
1882                                    colLookup = 0;
1883    
1884                                    /* approximate the pixel */
1885                                    while (j--)
1886                                    {
1887                                            if (xc_cache[j].flags)
1888                                            {
1889                                                    nDist = ((long) (xc_cache[j].red >> 8) -
1890                                                             (long) (xentry.red >> 8)) *
1891                                                            ((long) (xc_cache[j].red >> 8) -
1892                                                             (long) (xentry.red >> 8)) +
1893                                                            ((long) (xc_cache[j].green >> 8) -
1894                                                             (long) (xentry.green >> 8)) *
1895                                                            ((long) (xc_cache[j].green >> 8) -
1896                                                             (long) (xentry.green >> 8)) +
1897                                                            ((long) (xc_cache[j].blue >> 8) -
1898                                                             (long) (xentry.blue >> 8)) *
1899                                                            ((long) (xc_cache[j].blue >> 8) -
1900                                                             (long) (xentry.blue >> 8));
1901                                            }
1902                                            if (nDist < nMinDist)
1903                                            {
1904                                                    nMinDist = nDist;
1905                                                    xentry.pixel = j;
1906                                            }
1907                                    }
1908                            }
1909                            colour = xentry.pixel;
1910    
1911                            /* update our cache */
1912                            if (xentry.pixel < 256)
1913                            {
1914                                    xc_cache[xentry.pixel].red = xentry.red;
1915                                    xc_cache[xentry.pixel].green = xentry.green;
1916                                    xc_cache[xentry.pixel].blue = xentry.blue;
1917    
1918                            }
1919    
1920                  xentry->pixel = i;                          map[i] = colour;
1921                  xentry->red = entry->red << 8;                  }
1922                  xentry->blue = entry->blue << 8;                  return map;
                 xentry->green = entry->green << 8;  
                 xentry->flags = DoRed | DoBlue | DoGreen;  
1923          }          }
1924            else
1925            {
1926                    XColor *xcolours, *xentry;
1927                    Colormap map;
1928    
1929          map = XCreateColormap(display, wnd, visual, AllocAll);                  xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
1930          XStoreColors(display, map, xcolours, ncolours);                  for (i = 0; i < ncolours; i++)
1931                    {
1932                            entry = &colours->colours[i];
1933                            xentry = &xcolours[i];
1934                            xentry->pixel = i;
1935                            MAKE_XCOLOR(xentry, entry);
1936                    }
1937    
1938          xfree(xcolours);                  map = XCreateColormap(g_display, g_wnd, g_visual, AllocAll);
1939          return (HCOLOURMAP)map;                  XStoreColors(g_display, map, xcolours, ncolours);
1940    
1941                    xfree(xcolours);
1942                    return (HCOLOURMAP) map;
1943            }
1944  }  }
1945    
1946  void ui_destroy_colourmap(HCOLOURMAP map)  void
1947    ui_destroy_colourmap(HCOLOURMAP map)
1948  {  {
1949          XFreeColormap(display, (Colormap)map);          if (!g_owncolmap)
1950                    xfree(map);
1951            else
1952                    XFreeColormap(g_display, (Colormap) map);
1953  }  }
1954    
1955  void ui_set_colourmap(HCOLOURMAP map)  void
1956    ui_set_colourmap(HCOLOURMAP map)
1957  {  {
1958          XSetWindowColormap(display, wnd, (Colormap)map);          if (!g_owncolmap)
1959            {
1960                    if (g_colmap)
1961                            xfree(g_colmap);
1962    
1963                    g_colmap = (uint32 *) map;
1964            }
1965            else
1966                    XSetWindowColormap(g_display, g_wnd, (Colormap) map);
1967  }  }
1968    
1969  void ui_set_clip(int x, int y, int cx, int cy)  void
1970    ui_set_clip(int x, int y, int cx, int cy)
1971  {  {
1972          XRectangle rect;          XRectangle rect;
1973    
# Line 303  void ui_set_clip(int x, int y, int cx, i Line 1975  void ui_set_clip(int x, int y, int cx, i
1975          rect.y = y;          rect.y = y;
1976          rect.width = cx;          rect.width = cx;
1977          rect.height = cy;          rect.height = cy;
1978          XSetClipRectangles(display, gc, 0, 0, &rect, 1, YXBanded);          XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);
1979  }  }
1980    
1981  void ui_reset_clip()  void
1982    ui_reset_clip(void)
1983  {  {
1984          XRectangle rect;          XRectangle rect;
1985    
1986          rect.x = 0;          rect.x = 0;
1987          rect.y = 0;          rect.y = 0;
1988          rect.width = width;          rect.width = g_width;
1989          rect.height = height;          rect.height = g_height;
1990          XSetClipRectangles(display, gc, 0, 0, &rect, 1, YXBanded);          XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);
1991  }  }
1992    
1993  void ui_bell()  void
1994    ui_bell(void)
1995  {  {
1996          XBell(display, 0);          XBell(g_display, 0);
1997  }  }
1998    
1999  static int rop2_map[] = {  void
2000          GXclear,        /* 0 */  ui_destblt(uint8 opcode,
2001          GXnor,          /* DPon */             /* dest */ int x, int y, int cx, int cy)
         GXandInverted,  /* DPna */  
         GXcopyInverted, /* Pn */  
         GXandReverse,   /* PDna */  
         GXinvert,       /* Dn */  
         GXxor,          /* DPx */  
         GXnand,         /* DPan */  
         GXand,          /* DPa */  
         GXequiv,        /* DPxn */  
         GXnoop,         /* D */  
         GXorInverted,   /* DPno */  
         GXcopy,         /* P */  
         GXorReverse,    /* PDno */  
         GXor,           /* DPo */  
         GXset           /* 1 */  
 };  
   
 static void xwin_set_function(uint8 rop2)  
2002  {  {
2003          XSetFunction(display, gc, rop2_map[rop2]);          SET_FUNCTION(opcode);
2004            FILL_RECTANGLE(x, y, cx, cy);
2005            RESET_FUNCTION(opcode);
2006  }  }
2007    
2008  void ui_destblt(uint8 opcode,  static uint8 hatch_patterns[] = {
2009          /* dest */  int x, int y, int cx, int cy)          0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0 - bsHorizontal */
2010  {          0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /* 1 - bsVertical */
2011          xwin_set_function(opcode);          0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, /* 2 - bsFDiagonal */
2012            0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /* 3 - bsBDiagonal */
2013          XFillRectangle(display, wnd, gc, x, y, cx, cy);          0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, /* 4 - bsCross */
2014  }          0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81  /* 5 - bsDiagCross */
2015    };
2016    
2017  void ui_patblt(uint8 opcode,  void
2018          /* dest */  int x, int y, int cx, int cy,  ui_patblt(uint8 opcode,
2019          /* brush */ BRUSH *brush, int bgcolour, int fgcolour)            /* dest */ int x, int y, int cx, int cy,
2020              /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2021  {  {
         Display *dpy = display;  
2022          Pixmap fill;          Pixmap fill;
2023            uint8 i, ipattern[8];
2024    
2025          xwin_set_function(opcode);          SET_FUNCTION(opcode);
2026    
2027          switch (brush->style)          switch (brush->style)
2028          {          {
2029                  case 0: /* Solid */                  case 0: /* Solid */
2030                          XSetForeground(dpy, gc, fgcolour);                          SET_FOREGROUND(fgcolour);
2031                          XFillRectangle(dpy, wnd, gc, x, y, cx, cy);                          FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2032                          break;                          break;
2033    
2034                  case 3: /* Pattern */                  case 2: /* Hatch */
2035                          fill = (Pixmap)ui_create_glyph(8, 8, brush->pattern);                          fill = (Pixmap) ui_create_glyph(8, 8,
2036                                                            hatch_patterns + brush->pattern[0] * 8);
2037                          XSetForeground(dpy, gc, fgcolour);                          SET_FOREGROUND(fgcolour);
2038                          XSetBackground(dpy, gc, bgcolour);                          SET_BACKGROUND(bgcolour);
2039                          XSetFillStyle(dpy, gc, FillOpaqueStippled);                          XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2040                          XSetStipple(dpy, gc, fill);                          XSetStipple(g_display, g_gc, fill);
2041                            XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2042                          XFillRectangle(dpy, wnd, gc, x, y, cx, cy);                          FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2043                            XSetFillStyle(g_display, g_gc, FillSolid);
2044                            XSetTSOrigin(g_display, g_gc, 0, 0);
2045                            ui_destroy_glyph((HGLYPH) fill);
2046                            break;
2047    
2048                          XSetFillStyle(dpy, gc, FillSolid);                  case 3: /* Pattern */
2049                          ui_destroy_glyph((HGLYPH)fill);                          for (i = 0; i != 8; i++)
2050                                    ipattern[7 - i] = brush->pattern[i];
2051                            fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2052                            SET_FOREGROUND(bgcolour);
2053                            SET_BACKGROUND(fgcolour);
2054                            XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2055                            XSetStipple(g_display, g_gc, fill);
2056                            XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2057                            FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2058                            XSetFillStyle(g_display, g_gc, FillSolid);
2059                            XSetTSOrigin(g_display, g_gc, 0, 0);
2060                            ui_destroy_glyph((HGLYPH) fill);
2061                          break;                          break;
2062    
2063                  default:                  default:
2064                          NOTIMP("brush %d\n", brush->style);                          unimpl("brush %d\n", brush->style);
2065          }          }
 }  
2066    
2067  void ui_screenblt(uint8 opcode,          RESET_FUNCTION(opcode);
                 /* dest */ int x, int y, int cx, int cy,  
                 /* src */  int srcx, int srcy)  
 {  
         xwin_set_function(opcode);  
2068    
2069          XCopyArea(display, wnd, wnd, gc, srcx, srcy,          if (g_ownbackstore)
2070                          cx, cy, x, y);                  XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2071  }  }
2072    
2073  void ui_memblt(uint8 opcode,  void
2074          /* dest */  int x, int y, int cx, int cy,  ui_screenblt(uint8 opcode,
2075          /* src */   HBITMAP src, int srcx, int srcy)               /* dest */ int x, int y, int cx, int cy,
2076                 /* src */ int srcx, int srcy)
2077  {  {
2078          xwin_set_function(opcode);          SET_FUNCTION(opcode);
2079            if (g_ownbackstore)
2080          XCopyArea(display, (Pixmap)src, wnd, gc, srcx, srcy,          {
2081                          cx, cy, x, y);                  if (g_Unobscured)
2082                    {
2083                            XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2084                            XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x,
2085                                      y);
2086                    }
2087                    else
2088                    {
2089                            XCopyArea(g_display, g_backstore, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2090                            XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x,
2091                                      y);
2092                    }
2093            }
2094            else
2095            {
2096                    XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2097            }
2098            RESET_FUNCTION(opcode);
2099  }  }
2100    
2101  void ui_triblt(uint8 opcode,  void
2102          /* dest */  int x, int y, int cx, int cy,  ui_memblt(uint8 opcode,
2103          /* src */   HBITMAP src, int srcx, int srcy,            /* dest */ int x, int y, int cx, int cy,
2104          /* brush */ BRUSH *brush, int bgcolour, int fgcolour)            /* src */ HBITMAP src, int srcx, int srcy)
2105    {
2106            SET_FUNCTION(opcode);
2107            XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2108            if (g_ownbackstore)
2109                    XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2110            RESET_FUNCTION(opcode);
2111    }
2112    
2113    void
2114    ui_triblt(uint8 opcode,
2115              /* dest */ int x, int y, int cx, int cy,
2116              /* src */ HBITMAP src, int srcx, int srcy,
2117              /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2118  {  {
2119          /* This is potentially difficult to do in general. Until someone          /* This is potentially difficult to do in general. Until someone
2120             comes up with a more efficient way of doing it I am using cases. */             comes up with a more efficient way of doing it I am using cases. */
2121    
2122          switch (opcode)          switch (opcode)
2123          {          {
2124                  case 0xb8: /* PSDPxax */                  case 0x69:      /* PDSxxn */
2125                          ui_patblt(ROP2_XOR, x, y, cx, cy,                          ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
2126                                          brush, bgcolour, fgcolour);                          ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
                         ui_memblt(ROP2_AND, x, y, cx, cy,  
                                         src, srcx, srcy);  
                         ui_patblt(ROP2_XOR, x, y, cx, cy,  
                                         brush, bgcolour, fgcolour);  
2127                          break;                          break;
2128    
2129                  default:                  case 0xb8:      /* PSDPxax */
2130                          NOTIMP("triblt 0x%x\n", opcode);                          ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2131                          ui_memblt(ROP2_COPY, x, y, cx, cy,                          ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
2132                                          brush, bgcolour, fgcolour);                          ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
         }  
 }  
   
 void ui_line(uint8 opcode,  
         /* dest */  int startx, int starty, int endx, int endy,  
         /* pen */   PEN *pen)  
 {  
         xwin_set_function(opcode);  
   
         XSetForeground(display, gc, pen->colour);  
         XDrawLine(display, wnd, gc, startx, starty, endx, endy);  
 }  
   
 void ui_rect(  
         /* dest */  int x, int y, int cx, int cy,  
         /* brush */ int colour)  
 {  
         xwin_set_function(ROP2_COPY);  
   
         XSetForeground(display, gc, colour);  
         XFillRectangle(display, wnd, gc, x, y, cx, cy);  
 }  
   
 void ui_draw_glyph(int mixmode,  
         /* dest */ int x, int y, int cx, int cy,  
         /* src */  HGLYPH glyph, int srcx, int srcy, int bgcolour, int fgcolour)  
 {  
         Pixmap pixmap = (Pixmap)glyph;  
   
         xwin_set_function(ROP2_COPY);  
   
         XSetForeground(display, gc, fgcolour);  
   
         switch (mixmode)  
         {  
                 case MIX_TRANSPARENT:  
                         XSetStipple(display, gc, pixmap);  
                         XSetFillStyle(display, gc, FillStippled);  
                         XSetTSOrigin(display, gc, x, y);  
                         XFillRectangle(display, wnd, gc,  
                                         x, y, cx, cy);  
                         XSetFillStyle(display, gc, FillSolid);  
2133                          break;                          break;
2134    
2135                  case MIX_OPAQUE:                  case 0xc0:      /* PSa */
2136                          XSetBackground(display, gc, bgcolour);                          ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2137                          XCopyPlane(display, pixmap, wnd, gc,                          ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
                                         srcx, srcy, cx, cy, x, y, 1);  
2138                          break;                          break;
2139    
2140                  default:                  default:
2141                          NOTIMP("mix %d\n", mixmode);                          unimpl("triblt 0x%x\n", opcode);
2142                            ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2143          }          }
2144  }  }
2145    
2146  void ui_draw_text(uint8 font, uint8 flags, int mixmode, int x,  void
2147                          int y, int boxx, int boxy, int boxcx, int boxcy,  ui_line(uint8 opcode,
2148                          int bgcolour, int fgcolour, uint8 *text, uint8 length)          /* dest */ int startx, int starty, int endx, int endy,
2149            /* pen */ PEN * pen)
2150    {
2151            SET_FUNCTION(opcode);
2152            SET_FOREGROUND(pen->colour);
2153            XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
2154            if (g_ownbackstore)
2155                    XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
2156            RESET_FUNCTION(opcode);
2157    }
2158    
2159    void
2160    ui_rect(
2161                   /* dest */ int x, int y, int cx, int cy,
2162                   /* brush */ int colour)
2163    {
2164            SET_FOREGROUND(colour);
2165            FILL_RECTANGLE(x, y, cx, cy);
2166    }
2167    
2168    /* warning, this function only draws on wnd or backstore, not both */
2169    void
2170    ui_draw_glyph(int mixmode,
2171                  /* dest */ int x, int y, int cx, int cy,
2172                  /* src */ HGLYPH glyph, int srcx, int srcy,
2173                  int bgcolour, int fgcolour)
2174    {
2175            SET_FOREGROUND(fgcolour);
2176            SET_BACKGROUND(bgcolour);
2177    
2178            XSetFillStyle(g_display, g_gc,
2179                          (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
2180            XSetStipple(g_display, g_gc, (Pixmap) glyph);
2181            XSetTSOrigin(g_display, g_gc, x, y);
2182    
2183            FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2184    
2185            XSetFillStyle(g_display, g_gc, FillSolid);
2186    }
2187    
2188    #define DO_GLYPH(ttext,idx) \
2189    {\
2190      glyph = cache_get_font (font, ttext[idx]);\
2191      if (!(flags & TEXT2_IMPLICIT_X))\
2192      {\
2193        xyoffset = ttext[++idx];\
2194        if ((xyoffset & 0x80))\
2195        {\
2196          if (flags & TEXT2_VERTICAL)\
2197            y += ttext[idx+1] | (ttext[idx+2] << 8);\
2198          else\
2199            x += ttext[idx+1] | (ttext[idx+2] << 8);\
2200          idx += 2;\
2201        }\
2202        else\
2203        {\
2204          if (flags & TEXT2_VERTICAL)\
2205            y += xyoffset;\
2206          else\
2207            x += xyoffset;\
2208        }\
2209      }\
2210      if (glyph != NULL)\
2211      {\
2212        x1 = x + glyph->offset;\
2213        y1 = y + glyph->baseline;\
2214        XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
2215        XSetTSOrigin(g_display, g_gc, x1, y1);\
2216        FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
2217        if (flags & TEXT2_IMPLICIT_X)\
2218          x += glyph->width;\
2219      }\
2220    }
2221    
2222    void
2223    ui_draw_text(uint8 font, uint8 flags, int mixmode, int x, int y,
2224                 int clipx, int clipy, int clipcx, int clipcy,
2225                 int boxx, int boxy, int boxcx, int boxcy, int bgcolour,
2226                 int fgcolour, uint8 * text, uint8 length)
2227  {  {
2228          FONTGLYPH *glyph;          FONTGLYPH *glyph;
2229          int i;          int i, j, xyoffset, x1, y1;
2230            DATABLOB *entry;
2231    
2232            SET_FOREGROUND(bgcolour);
2233    
2234            /* Sometimes, the boxcx value is something really large, like
2235               32691. This makes XCopyArea fail with Xvnc. The code below
2236               is a quick fix. */
2237            if (boxx + boxcx > g_width)
2238                    boxcx = g_width - boxx;
2239    
2240          if (boxcx > 1)          if (boxcx > 1)
2241          {          {
2242                  ui_rect(boxx, boxy, boxcx, boxcy, bgcolour);                  FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
2243            }
2244            else if (mixmode == MIX_OPAQUE)
2245            {
2246                    FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
2247          }          }
2248    
2249            SET_FOREGROUND(fgcolour);
2250            SET_BACKGROUND(bgcolour);
2251            XSetFillStyle(g_display, g_gc, FillStippled);
2252    
2253          /* Paint text, character by character */          /* Paint text, character by character */
2254          for (i = 0; i < length; i++)          for (i = 0; i < length;)
2255          {          {
2256                  glyph = cache_get_font(font, text[i]);                  switch (text[i])
   
                 if (glyph != NULL)  
2257                  {                  {
2258                          ui_draw_glyph(mixmode, x,                          case 0xff:
2259                                          y + (short)glyph->baseline,                                  if (i + 2 < length)
2260                                          glyph->width, glyph->height,                                          cache_put_text(text[i + 1], text, text[i + 2]);
2261                                          glyph->pixmap, 0, 0,                                  else
2262                                          bgcolour, fgcolour);                                  {
2263                                            error("this shouldn't be happening\n");
2264                                            exit(1);
2265                                    }
2266                                    /* this will move pointer from start to first character after FF command */
2267                                    length -= i + 3;
2268                                    text = &(text[i + 3]);
2269                                    i = 0;
2270                                    break;
2271    
2272                            case 0xfe:
2273                                    entry = cache_get_text(text[i + 1]);
2274                                    if (entry != NULL)
2275                                    {
2276                                            if ((((uint8 *) (entry->data))[1] ==
2277                                                 0) && (!(flags & TEXT2_IMPLICIT_X)))
2278                                            {
2279                                                    if (flags & TEXT2_VERTICAL)
2280                                                            y += text[i + 2];
2281                                                    else
2282                                                            x += text[i + 2];
2283                                            }
2284                                            for (j = 0; j < entry->size; j++)
2285                                                    DO_GLYPH(((uint8 *) (entry->data)), j);
2286                                    }
2287                                    if (i + 2 < length)
2288                                            i += 3;
2289                                    else
2290                                            i += 2;
2291                                    length -= i;
2292                                    /* this will move pointer from start to first character after FE command */
2293                                    text = &(text[i]);
2294                                    i = 0;
2295                                    break;
2296    
2297                          if (flags & TEXT2_IMPLICIT_X)                          default:
2298                                  x += glyph->width;                                  DO_GLYPH(text, i);
2299                          else                                  i++;
2300                                  x += text[++i];                                  break;
2301                  }                  }
2302          }          }
2303    
2304            XSetFillStyle(g_display, g_gc, FillSolid);
2305    
2306            if (g_ownbackstore)
2307            {
2308                    if (boxcx > 1)
2309                            XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
2310                                      boxy, boxcx, boxcy, boxx, boxy);
2311                    else
2312                            XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
2313                                      clipy, clipcx, clipcy, clipx, clipy);
2314            }
2315  }  }
2316    
2317  void ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)  void
2318    ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
2319  {  {
2320            Pixmap pix;
2321          XImage *image;          XImage *image;
         int scanline;  
2322    
2323          scanline = (cx + 3) & ~3;          if (g_ownbackstore)
2324          STATUS("XGetImage(%p,%x,%d,%d,%d,%d,%x,%d)\n", display, wnd, x, y,          {
2325                  cx, cy, 0xffffffff, ZPixmap);                  image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
2326          image = XGetImage(display, wnd, x, y, cx, cy, 0xffffffff, ZPixmap);          }
2327          cache_put_desktop(offset, scanline*cy, image->data);          else
2328          XFree(image->data);          {
2329          XFree(image);                  pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
2330                    XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
2331                    image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
2332                    XFreePixmap(g_display, pix);
2333            }
2334    
2335            offset *= g_bpp / 8;
2336            cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
2337    
2338            XDestroyImage(image);
2339  }  }
2340    
2341  void ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)  void
2342    ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
2343  {  {
2344          XImage *image;          XImage *image;
         int scanline;  
2345          uint8 *data;          uint8 *data;
2346    
2347          scanline = (cx + 3) & ~3;          offset *= g_bpp / 8;
2348          data = cache_get_desktop(offset, scanline*cy);          data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
2349          if (data == NULL)          if (data == NULL)
2350                  return;                  return;
2351    
2352          image = XCreateImage(display, visual, 8, ZPixmap, 0,          image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2353                                  data, cx, cy, 32, scanline);                               (char *) data, cx, cy, BitmapPad(g_display), cx * g_bpp / 8);
2354          XSetFunction(display, gc, GXcopy);  
2355          XPutImage(display, wnd, gc, image, 0, 0, x, y, cx, cy);          if (g_ownbackstore)
2356            {
2357                    XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2358                    XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2359            }
2360            else
2361            {
2362                    XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2363            }
2364    
2365          XFree(image);          XFree(image);
2366  }  }
2367    
2368    /* these do nothing here but are used in uiports */
2369    void
2370    ui_begin_update(void)
2371    {
2372    }
2373    
2374    void
2375    ui_end_update(void)
2376    {
2377    }

Legend:
Removed from v.10  
changed lines
  Added in v.822

  ViewVC Help
Powered by ViewVC 1.1.26