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

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

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

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

Legend:
Removed from v.7  
changed lines
  Added in v.566

  ViewVC Help
Powered by ViewVC 1.1.26