/[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 6 by matty, Wed Jul 5 07:44:21 2000 UTC revision 309 by jsorg71, Tue Feb 4 05:32:13 2003 UTC
# Line 1  Line 1 
1  /*  /*
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 <time.h>
24    #include <errno.h>
25    #include "rdesktop.h"
26    
27    extern int width;
28    extern int height;
29    extern BOOL sendmotion;
30    extern BOOL fullscreen;
31    extern BOOL grab_keyboard;
32    extern BOOL hide_decorations;
33    extern char title[];
34    extern int server_bpp;
35    BOOL enable_compose = False;
36    BOOL focused;
37    BOOL mouse_in_wnd;
38    
39    Display *display;
40    static int x_socket;
41    static Screen *screen;
42    static Window wnd;
43    static GC gc;
44    static Visual *visual;
45    static int depth;
46    static int bpp;
47    static XIM IM;
48    static XIC IC;
49    static XModifierKeymap *mod_map;
50    static Cursor current_cursor;
51    static Atom protocol_atom, kill_atom;
52    
53  HWINDOW ui_create_window(int width, int height)  /* endianness */
54    static BOOL host_be;
55    static BOOL xserver_be;
56    
57    /* software backing store */
58    static BOOL ownbackstore;
59    static Pixmap backstore;
60    
61    /* MWM decorations */
62    #define MWM_HINTS_DECORATIONS   (1L << 1)
63    #define PROP_MOTIF_WM_HINTS_ELEMENTS    5
64    typedef struct
65  {  {
66          struct window *wnd;          uint32 flags;
67          Display *display;          uint32 functions;
68          Window window;          uint32 decorations;
69          int black;          sint32 inputMode;
70          GC gc;          uint32 status;
71    }
72    PropMotifWmHints;
73    
74    
75    #define FILL_RECTANGLE(x,y,cx,cy)\
76    { \
77            XFillRectangle(display, wnd, gc, x, y, cx, cy); \
78            if (ownbackstore) \
79                    XFillRectangle(display, backstore, gc, x, y, cx, cy); \
80    }
81    
82    #define FILL_RECTANGLE_BACKSTORE(x,y,cx,cy)\
83    { \
84            XFillRectangle(display, ownbackstore ? backstore : wnd, gc, x, y, cx, cy); \
85    }
86    
87    /* colour maps */
88    BOOL owncolmap = False;
89    static Colormap xcolmap;
90    static uint32 *colmap;
91    
92    #define TRANSLATE(col)          ( server_bpp != 8 ? col : owncolmap ? col : translate_colour(colmap[col]) )
93    #define SET_FOREGROUND(col)     XSetForeground(display, gc, TRANSLATE(col));
94    #define SET_BACKGROUND(col)     XSetBackground(display, gc, TRANSLATE(col));
95    
96    static int rop2_map[] = {
97            GXclear,                /* 0 */
98            GXnor,                  /* DPon */
99            GXandInverted,          /* DPna */
100            GXcopyInverted,         /* Pn */
101            GXandReverse,           /* PDna */
102            GXinvert,               /* Dn */
103            GXxor,                  /* DPx */
104            GXnand,                 /* DPan */
105            GXand,                  /* DPa */
106            GXequiv,                /* DPxn */
107            GXnoop,                 /* D */
108            GXorInverted,           /* DPno */
109            GXcopy,                 /* P */
110            GXorReverse,            /* PDno */
111            GXor,                   /* DPo */
112            GXset                   /* 1 */
113    };
114    
115    #define SET_FUNCTION(rop2)      { if (rop2 != ROP2_COPY) XSetFunction(display, gc, rop2_map[rop2]); }
116    #define RESET_FUNCTION(rop2)    { if (rop2 != ROP2_COPY) XSetFunction(display, gc, GXcopy); }
117    
118    void
119    mwm_hide_decorations(void)
120    {
121            PropMotifWmHints motif_hints;
122            Atom hintsatom;
123    
124            /* setup the property */
125            motif_hints.flags = MWM_HINTS_DECORATIONS;
126            motif_hints.decorations = 0;
127    
128            /* get the atom for the property */
129            hintsatom = XInternAtom(display, "_MOTIF_WM_HINTS", False);
130            if (!hintsatom)
131            {
132                    warning("Failed to get atom _MOTIF_WM_HINTS: probably your window manager does not support MWM hints\n");
133                    return;
134            }
135    
136            XChangeProperty(display, wnd, hintsatom, hintsatom, 32, PropModeReplace,
137                            (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
138    }
139    
140    static void
141    translate8(uint8 * data, uint8 * out, uint8 * end)
142    {
143            while (out < end)
144                    *(out++) = (uint8) colmap[*(data++)];
145    }
146    
147    static void
148    translate16(uint8 * data, uint16 * out, uint16 * end)
149    {
150            while (out < end)
151                    *(out++) = (uint16) colmap[*(data++)];
152    }
153    
154    /* little endian - conversion happens when colourmap is built */
155    static void
156    translate24(uint8 * data, uint8 * out, uint8 * end)
157    {
158            uint32 value;
159    
160            while (out < end)
161            {
162                    value = colmap[*(data++)];
163                    *(out++) = value;
164                    *(out++) = value >> 8;
165                    *(out++) = value >> 16;
166            }
167    }
168    
169    static void
170    translate32(uint8 * data, uint32 * out, uint32 * end)
171    {
172            while (out < end)
173                    *(out++) = colmap[*(data++)];
174    }
175    
176    static uint8 *
177    translate_image(int width, int height, uint8 * data)
178    {
179            int size = width * height * bpp / 8;
180            uint8 *out = xmalloc(size);
181            uint8 *end = out + size;
182    
183            switch (bpp)
184            {
185                    case 8:
186                            translate8(data, out, end);
187                            break;
188    
189                    case 16:
190                            translate16(data, (uint16 *) out, (uint16 *) end);
191                            break;
192    
193                    case 24:
194                            translate24(data, out, end);
195                            break;
196    
197                    case 32:
198                            translate32(data, (uint32 *) out, (uint32 *) end);
199                            break;
200            }
201    
202            return out;
203    }
204    
205    #define BSWAP16(x) { x = (((x & 0xff) << 8) | (x >> 8)); }
206    #define BSWAP24(x) { x = (((x & 0xff) << 16) | (x >> 16) | ((x >> 8) & 0xff00)); }
207    #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \
208                            x = (x << 16) | (x >> 16); }
209    
210    static uint32
211    translate_colour(uint32 colour)
212    {
213            switch (bpp)
214            {
215                    case 16:
216                            if (host_be != xserver_be)
217                                    BSWAP16(colour);
218                            break;
219    
220                    case 24:
221                            if (xserver_be)
222                                    BSWAP24(colour);
223                            break;
224    
225                    case 32:
226                            if (host_be != xserver_be)
227                                    BSWAP32(colour);
228                            break;
229            }
230    
231            return colour;
232    }
233    
234    BOOL
235    get_key_state(unsigned int state, uint32 keysym)
236    {
237            int modifierpos, key, keysymMask = 0;
238            int offset;
239    
240            KeyCode keycode = XKeysymToKeycode(display, keysym);
241    
242            if (keycode == NoSymbol)
243                    return False;
244    
245            for (modifierpos = 0; modifierpos < 8; modifierpos++)
246            {
247                    offset = mod_map->max_keypermod * modifierpos;
248    
249                    for (key = 0; key < mod_map->max_keypermod; key++)
250                    {
251                            if (mod_map->modifiermap[offset + key] == keycode)
252                                    keysymMask |= 1 << modifierpos;
253                    }
254            }
255    
256            return (state & keysymMask) ? True : False;
257    }
258    
259    BOOL
260    ui_init(void)
261    {
262            XPixmapFormatValues *pfm;
263            uint16 test;
264            int i;
265    
266          display = XOpenDisplay(NULL);          display = XOpenDisplay(NULL);
267          if (display == NULL)          if (display == NULL)
268                  return NULL;          {
269                    error("Failed to open display: %s\n", XDisplayName(NULL));
270                    return False;
271            }
272    
273            x_socket = ConnectionNumber(display);
274            screen = DefaultScreenOfDisplay(display);
275            visual = DefaultVisualOfScreen(screen);
276            depth = DefaultDepthOfScreen(screen);
277    
278            pfm = XListPixmapFormats(display, &i);
279            if (pfm != NULL)
280            {
281                    /* Use maximum bpp for this depth - this is generally
282                       desirable, e.g. 24 bits->32 bits. */
283                    while (i--)
284                    {
285                            if ((pfm[i].depth == depth) && (pfm[i].bits_per_pixel > bpp))
286                            {
287                                    bpp = pfm[i].bits_per_pixel;
288                            }
289                    }
290                    XFree(pfm);
291            }
292    
293            if (bpp < 8)
294            {
295                    error("Less than 8 bpp not currently supported.\n");
296                    XCloseDisplay(display);
297                    return False;
298            }
299    
300            if (owncolmap != True)
301            {
302                    xcolmap = DefaultColormapOfScreen(screen);
303                    if (depth <= 8)
304                            warning("Screen depth is 8 bits or lower: you may want to use -C for a private colourmap\n");
305            }
306    
307            gc = XCreateGC(display, RootWindowOfScreen(screen), 0, NULL);
308    
309            if (DoesBackingStore(screen) != Always)
310                    ownbackstore = True;
311    
312            test = 1;
313            host_be = !(BOOL) (*(uint8 *) (&test));
314            xserver_be = (ImageByteOrder(display) == MSBFirst);
315    
316            if ((width == 0) || (height == 0))
317            {
318                    /* Fetch geometry from _NET_WORKAREA */
319                    uint32 x, y, cx, cy;
320    
321                    if (get_current_workarea(&x, &y, &cx, &cy) == 0)
322                    {
323                            width = cx;
324                            height = cy;
325                    }
326                    else
327                    {
328                            warning("Failed to get workarea: probably your window manager does not support extended hints\n");
329                            width = 800;
330                            height = 600;
331                    }
332            }
333    
334            if (fullscreen)
335            {
336                    width = WidthOfScreen(screen);
337                    height = HeightOfScreen(screen);
338            }
339    
340          black = BlackPixel(display, DefaultScreen(display));          /* make sure width is a multiple of 4 */
341          window = XCreateSimpleWindow(display, DefaultRootWindow(display),          width = (width + 3) & ~3;
                                 0, 0, width, height, 0, black, black);  
342    
343          XMapWindow(display, window);          if (ownbackstore)
344          XSync(display, True);          {
345                    backstore =
346                            XCreatePixmap(display, RootWindowOfScreen(screen), width, height, depth);
347    
348          gc = XCreateGC(display, window, 0, NULL);                  /* clear to prevent rubbish being exposed at startup */
349                    XSetForeground(display, gc, BlackPixelOfScreen(screen));
350                    XFillRectangle(display, backstore, gc, 0, 0, width, height);
351            }
352    
353          wnd = xmalloc(sizeof(struct window));          mod_map = XGetModifierMapping(display);
354          wnd->display = display;  
355          wnd->wnd = window;          if (enable_compose)
356          wnd->gc = gc;                  IM = XOpenIM(display, NULL, NULL, NULL);
357          return wnd;  
358            xkeymap_init();
359            return True;
360    }
361    
362    void
363    ui_deinit(void)
364    {
365            if (IM != NULL)
366                    XCloseIM(IM);
367    
368            XFreeModifiermap(mod_map);
369    
370            if (ownbackstore)
371                    XFreePixmap(display, backstore);
372    
373            XFreeGC(display, gc);
374            XCloseDisplay(display);
375            display = NULL;
376    }
377    
378    BOOL
379    ui_create_window(void)
380    {
381            XSetWindowAttributes attribs;
382            XClassHint *classhints;
383            XSizeHints *sizehints;
384            int wndwidth, wndheight;
385            long input_mask, ic_input_mask;
386            XEvent xevent;
387    
388            wndwidth = fullscreen ? WidthOfScreen(screen) : width;
389            wndheight = fullscreen ? HeightOfScreen(screen) : height;
390    
391            attribs.background_pixel = BlackPixelOfScreen(screen);
392            attribs.backing_store = ownbackstore ? NotUseful : Always;
393            attribs.override_redirect = fullscreen;
394    
395            wnd = XCreateWindow(display, RootWindowOfScreen(screen), 0, 0, wndwidth, wndheight,
396                                0, CopyFromParent, InputOutput, CopyFromParent,
397                                CWBackPixel | CWBackingStore | CWOverrideRedirect, &attribs);
398    
399            XStoreName(display, wnd, title);
400    
401            if (hide_decorations)
402                    mwm_hide_decorations();
403    
404            classhints = XAllocClassHint();
405            if (classhints != NULL)
406            {
407                    classhints->res_name = classhints->res_class = "rdesktop";
408                    XSetClassHint(display, wnd, classhints);
409                    XFree(classhints);
410            }
411    
412            sizehints = XAllocSizeHints();
413            if (sizehints)
414            {
415                    sizehints->flags = PMinSize | PMaxSize;
416                    sizehints->min_width = sizehints->max_width = width;
417                    sizehints->min_height = sizehints->max_height = height;
418                    XSetWMNormalHints(display, wnd, sizehints);
419                    XFree(sizehints);
420            }
421    
422            input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
423                    VisibilityChangeMask | FocusChangeMask;
424    
425            if (sendmotion)
426                    input_mask |= PointerMotionMask;
427            if (ownbackstore)
428                    input_mask |= ExposureMask;
429            if (fullscreen || grab_keyboard)
430                    input_mask |= EnterWindowMask;
431            if (grab_keyboard)
432                    input_mask |= LeaveWindowMask;
433    
434            if (IM != NULL)
435            {
436                    IC = XCreateIC(IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
437                                   XNClientWindow, wnd, XNFocusWindow, wnd, NULL);
438    
439                    if ((IC != NULL)
440                        && (XGetICValues(IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
441                            input_mask |= ic_input_mask;
442            }
443    
444            XSelectInput(display, wnd, input_mask);
445            XMapWindow(display, wnd);
446    
447            /* wait for VisibilityNotify */
448            do
449            {
450                    XMaskEvent(display, VisibilityChangeMask, &xevent);
451            }
452            while (xevent.type != VisibilityNotify);
453    
454            focused = False;
455            mouse_in_wnd = False;
456    
457            /* handle the WM_DELETE_WINDOW protocol */
458            protocol_atom = XInternAtom(display, "WM_PROTOCOLS", True);
459            kill_atom = XInternAtom(display, "WM_DELETE_WINDOW", True);
460            XSetWMProtocols(display, wnd, &kill_atom, 1);
461    
462            return True;
463  }  }
464    
465  void ui_destroy_window(HWINDOW wnd)  void
466    ui_destroy_window(void)
467  {  {
468          XFreeGC(wnd->display, wnd->gc);          if (IC != NULL)
469          XDestroyWindow(wnd->display, wnd->wnd);                  XDestroyIC(IC);
470          XCloseDisplay(wnd->display);  
471            XDestroyWindow(display, wnd);
472  }  }
473    
474  HBITMAP ui_create_bitmap(HWINDOW wnd, int width, int height, uint8 *data)  void
475    xwin_toggle_fullscreen(void)
476    {
477            Pixmap contents = 0;
478    
479            if (!ownbackstore)
480            {
481                    /* need to save contents of window */
482                    contents = XCreatePixmap(display, wnd, width, height, depth);
483                    XCopyArea(display, wnd, contents, gc, 0, 0, width, height, 0, 0);
484            }
485    
486            ui_destroy_window();
487            fullscreen = !fullscreen;
488            ui_create_window();
489    
490            XDefineCursor(display, wnd, current_cursor);
491    
492            if (!ownbackstore)
493            {
494                    XCopyArea(display, contents, wnd, gc, 0, 0, width, height, 0, 0);
495                    XFreePixmap(display, contents);
496            }
497    }
498    
499    /* Process all events in Xlib queue
500       Returns 0 after user quit, 1 otherwise */
501    static int
502    xwin_process_events(void)
503    {
504            XEvent xevent;
505            KeySym keysym;
506            uint16 button, flags;
507            uint32 ev_time;
508            key_translation tr;
509            char str[256];
510            Status status;
511            unsigned int state;
512            Window wdummy;
513            int dummy;
514    
515            while (XPending(display) > 0)
516            {
517                    XNextEvent(display, &xevent);
518    
519                    if ((IC != NULL) && (XFilterEvent(&xevent, None) == True))
520                    {
521                            DEBUG_KBD(("Filtering event\n"));
522                            continue;
523                    }
524    
525                    flags = 0;
526    
527                    switch (xevent.type)
528                    {
529                            case ClientMessage:
530                                    /* the window manager told us to quit */
531                                    if ((xevent.xclient.message_type == protocol_atom)
532                                        && (xevent.xclient.data.l[0] == kill_atom))
533                                            /* Quit */
534                                            return 0;
535                                    break;
536    
537                            case KeyPress:
538                                    if (IC != NULL)
539                                            /* Multi_key compatible version */
540                                    {
541                                            XmbLookupString(IC,
542                                                            (XKeyPressedEvent *) &
543                                                            xevent, str, sizeof(str), &keysym, &status);
544                                            if (!((status == XLookupKeySym) || (status == XLookupBoth)))
545                                            {
546                                                    error("XmbLookupString failed with status 0x%x\n",
547                                                          status);
548                                                    break;
549                                            }
550                                    }
551                                    else
552                                    {
553                                            /* Plain old XLookupString */
554                                            DEBUG_KBD(("\nNo input context, using XLookupString\n"));
555                                            XLookupString((XKeyEvent *) & xevent,
556                                                          str, sizeof(str), &keysym, NULL);
557                                    }
558    
559                                    DEBUG_KBD(("KeyPress for (keysym 0x%lx, %s)\n", keysym,
560                                               get_ksname(keysym)));
561    
562                                    ev_time = time(NULL);
563                                    if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
564                                            break;
565    
566                                    tr = xkeymap_translate_key(keysym,
567                                                               xevent.xkey.keycode, xevent.xkey.state);
568    
569                                    if (tr.scancode == 0)
570                                            break;
571    
572                                    ensure_remote_modifiers(ev_time, tr);
573    
574                                    rdp_send_scancode(ev_time, RDP_KEYPRESS, tr.scancode);
575                                    break;
576    
577                            case KeyRelease:
578                                    XLookupString((XKeyEvent *) & xevent, str,
579                                                  sizeof(str), &keysym, NULL);
580    
581                                    DEBUG_KBD(("\nKeyRelease for (keysym 0x%lx, %s)\n", keysym,
582                                               get_ksname(keysym)));
583    
584                                    ev_time = time(NULL);
585                                    if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
586                                            break;
587    
588                                    tr = xkeymap_translate_key(keysym,
589                                                               xevent.xkey.keycode, xevent.xkey.state);
590    
591                                    if (tr.scancode == 0)
592                                            break;
593    
594                                    rdp_send_scancode(ev_time, RDP_KEYRELEASE, tr.scancode);
595                                    break;
596    
597                            case ButtonPress:
598                                    flags = MOUSE_FLAG_DOWN;
599                                    /* fall through */
600    
601                            case ButtonRelease:
602                                    button = xkeymap_translate_button(xevent.xbutton.button);
603                                    if (button == 0)
604                                            break;
605    
606                                    rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
607                                                   flags | button, xevent.xbutton.x, xevent.xbutton.y);
608                                    break;
609    
610                            case MotionNotify:
611                                    if (fullscreen && !focused)
612                                            XSetInputFocus(display, wnd, RevertToPointerRoot,
613                                                           CurrentTime);
614                                    rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
615                                                   MOUSE_FLAG_MOVE, xevent.xmotion.x, xevent.xmotion.y);
616                                    break;
617    
618                            case FocusIn:
619                                    if (xevent.xfocus.mode == NotifyGrab)
620                                            break;
621                                    focused = True;
622                                    XQueryPointer(display, wnd, &wdummy, &wdummy, &dummy, &dummy,
623                                                  &dummy, &dummy, &state);
624                                    reset_modifier_keys(state);
625                                    if (grab_keyboard && mouse_in_wnd)
626                                            XGrabKeyboard(display, wnd, True,
627                                                          GrabModeAsync, GrabModeAsync, CurrentTime);
628                                    break;
629    
630                            case FocusOut:
631                                    if (xevent.xfocus.mode == NotifyUngrab)
632                                            break;
633                                    focused = False;
634                                    if (xevent.xfocus.mode == NotifyWhileGrabbed)
635                                            XUngrabKeyboard(display, CurrentTime);
636                                    break;
637    
638                            case EnterNotify:
639                                    /* we only register for this event when in fullscreen mode */
640                                    /* or grab_keyboard */
641                                    mouse_in_wnd = True;
642                                    if (fullscreen)
643                                    {
644                                            XSetInputFocus(display, wnd, RevertToPointerRoot,
645                                                           CurrentTime);
646                                            break;
647                                    }
648                                    if (focused)
649                                            XGrabKeyboard(display, wnd, True,
650                                                          GrabModeAsync, GrabModeAsync, CurrentTime);
651                                    break;
652    
653                            case LeaveNotify:
654                                    /* we only register for this event when grab_keyboard */
655                                    mouse_in_wnd = False;
656                                    XUngrabKeyboard(display, CurrentTime);
657                                    break;
658    
659                            case Expose:
660                                    XCopyArea(display, backstore, wnd, gc,
661                                              xevent.xexpose.x, xevent.xexpose.y,
662                                              xevent.xexpose.width,
663                                              xevent.xexpose.height,
664                                              xevent.xexpose.x, xevent.xexpose.y);
665                                    break;
666    
667                            case MappingNotify:
668                                    /* Refresh keyboard mapping if it has changed. This is important for
669                                       Xvnc, since it allocates keycodes dynamically */
670                                    if (xevent.xmapping.request == MappingKeyboard
671                                        || xevent.xmapping.request == MappingModifier)
672                                            XRefreshKeyboardMapping(&xevent.xmapping);
673    
674                                    if (xevent.xmapping.request == MappingModifier)
675                                    {
676                                            XFreeModifiermap(mod_map);
677                                            mod_map = XGetModifierMapping(display);
678                                    }
679                                    break;
680    
681                    }
682            }
683            /* Keep going */
684            return 1;
685    }
686    
687    /* Returns 0 after user quit, 1 otherwise */
688    int
689    ui_select(int rdp_socket)
690    {
691            int n = (rdp_socket > x_socket) ? rdp_socket + 1 : x_socket + 1;
692            fd_set rfds;
693    
694            FD_ZERO(&rfds);
695    
696            while (True)
697            {
698                    /* Process any events already waiting */
699                    if (!xwin_process_events())
700                            /* User quit */
701                            return 0;
702    
703                    FD_ZERO(&rfds);
704                    FD_SET(rdp_socket, &rfds);
705                    FD_SET(x_socket, &rfds);
706    
707                    switch (select(n, &rfds, NULL, NULL, NULL))
708                    {
709                            case -1:
710                                    error("select: %s\n", strerror(errno));
711    
712                            case 0:
713                                    continue;
714                    }
715    
716                    if (FD_ISSET(rdp_socket, &rfds))
717                            return 1;
718            }
719    }
720    
721    void
722    ui_move_pointer(int x, int y)
723    {
724            XWarpPointer(display, wnd, wnd, 0, 0, 0, 0, x, y);
725    }
726    
727    HBITMAP
728    ui_create_bitmap(int width, int height, uint8 * data)
729    {
730            XImage *image;
731            Pixmap bitmap;
732            uint8 *tdata;
733    
734            tdata = (owncolmap ? data : translate_image(width, height, data));
735            bitmap = XCreatePixmap(display, wnd, width, height, depth);
736            image = XCreateImage(display, visual, depth, ZPixmap, 0,
737                                 (char *) tdata, width, height, 8, 0);
738    
739            XPutImage(display, bitmap, gc, image, 0, 0, 0, 0, width, height);
740    
741            XFree(image);
742            if (!owncolmap)
743                    xfree(tdata);
744            return (HBITMAP) bitmap;
745    }
746    
747    void
748    ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
749    {
750            XImage *image;
751            uint8 *tdata;
752    
753            if (server_bpp == 16)
754            {
755                    image = XCreateImage(display, visual, depth, ZPixmap, 0,
756                                         (char *) data, width, height, 16, 0);
757    
758                    if (ownbackstore)
759                    {
760                            XPutImage(display, backstore, gc, image, 0, 0, x, y, cx, cy);
761                            XCopyArea(display, backstore, wnd, gc, x, y, cx, cy, x, y);
762                    }
763                    else
764                    {
765                            XPutImage(display, wnd, gc, image, 0, 0, x, y, cx, cy);
766                    }
767    
768                    XFree(image);
769                    return;
770            }
771            tdata = (owncolmap ? data : translate_image(width, height, data));
772            image = XCreateImage(display, visual, depth, ZPixmap, 0,
773                                 (char *) tdata, width, height, 8, 0);
774    
775            if (ownbackstore)
776            {
777                    XPutImage(display, backstore, gc, image, 0, 0, x, y, cx, cy);
778                    XCopyArea(display, backstore, wnd, gc, x, y, cx, cy, x, y);
779            }
780            else
781            {
782                    XPutImage(display, wnd, gc, image, 0, 0, x, y, cx, cy);
783            }
784    
785            XFree(image);
786            if (!owncolmap)
787                    xfree(tdata);
788    }
789    
790    void
791    ui_destroy_bitmap(HBITMAP bmp)
792    {
793            XFreePixmap(display, (Pixmap) bmp);
794    }
795    
796    HGLYPH
797    ui_create_glyph(int width, int height, uint8 * data)
798  {  {
799          XImage *image;          XImage *image;
800          Visual *visual;          Pixmap bitmap;
801            int scanline;
802            GC gc;
803    
804            scanline = (width + 7) / 8;
805    
806            bitmap = XCreatePixmap(display, wnd, width, height, 1);
807            gc = XCreateGC(display, bitmap, 0, NULL);
808    
809            image = XCreateImage(display, visual, 1, ZPixmap, 0, (char *) data,
810                                 width, height, 8, scanline);
811            image->byte_order = MSBFirst;
812            image->bitmap_bit_order = MSBFirst;
813            XInitImage(image);
814    
815            XPutImage(display, bitmap, gc, image, 0, 0, 0, 0, width, height);
816    
817            XFree(image);
818            XFreeGC(display, gc);
819            return (HGLYPH) bitmap;
820    }
821    
822    void
823    ui_destroy_glyph(HGLYPH glyph)
824    {
825            XFreePixmap(display, (Pixmap) glyph);
826    }
827    
828    HCURSOR
829    ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
830                     uint8 * andmask, uint8 * xormask)
831    {
832            HGLYPH maskglyph, cursorglyph;
833            XColor bg, fg;
834            Cursor xcursor;
835            uint8 *cursor, *pcursor;
836            uint8 *mask, *pmask;
837            uint8 nextbit;
838            int scanline, offset;
839            int i, j;
840    
841            scanline = (width + 7) / 8;
842            offset = scanline * height;
843    
844            cursor = xmalloc(offset);
845            memset(cursor, 0, offset);
846    
847            mask = xmalloc(offset);
848            memset(mask, 0, offset);
849    
850            /* approximate AND and XOR masks with a monochrome X pointer */
851            for (i = 0; i < height; i++)
852            {
853                    offset -= scanline;
854                    pcursor = &cursor[offset];
855                    pmask = &mask[offset];
856    
857                    for (j = 0; j < scanline; j++)
858                    {
859                            for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
860                            {
861                                    if (xormask[0] || xormask[1] || xormask[2])
862                                    {
863                                            *pcursor |= (~(*andmask) & nextbit);
864                                            *pmask |= nextbit;
865                                    }
866                                    else
867                                    {
868                                            *pcursor |= ((*andmask) & nextbit);
869                                            *pmask |= (~(*andmask) & nextbit);
870                                    }
871    
872                                    xormask += 3;
873                            }
874    
875                            andmask++;
876                            pcursor++;
877                            pmask++;
878                    }
879            }
880    
881            fg.red = fg.blue = fg.green = 0xffff;
882            bg.red = bg.blue = bg.green = 0x0000;
883            fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
884    
885            cursorglyph = ui_create_glyph(width, height, cursor);
886            maskglyph = ui_create_glyph(width, height, mask);
887    
888            xcursor =
889                    XCreatePixmapCursor(display, (Pixmap) cursorglyph,
890                                        (Pixmap) maskglyph, &fg, &bg, x, y);
891    
892            ui_destroy_glyph(maskglyph);
893            ui_destroy_glyph(cursorglyph);
894            xfree(mask);
895            xfree(cursor);
896            return (HCURSOR) xcursor;
897    }
898    
899    void
900    ui_set_cursor(HCURSOR cursor)
901    {
902            current_cursor = (Cursor) cursor;
903            XDefineCursor(display, wnd, current_cursor);
904    }
905    
906    void
907    ui_destroy_cursor(HCURSOR cursor)
908    {
909            XFreeCursor(display, (Cursor) cursor);
910    }
911    
912    #define MAKE_XCOLOR(xc,c) \
913                    (xc)->red   = ((c)->red   << 8) | (c)->red; \
914                    (xc)->green = ((c)->green << 8) | (c)->green; \
915                    (xc)->blue  = ((c)->blue  << 8) | (c)->blue; \
916                    (xc)->flags = DoRed | DoGreen | DoBlue;
917    
918    
919    HCOLOURMAP
920    ui_create_colourmap(COLOURMAP * colours)
921    {
922            COLOURENTRY *entry;
923            int i, ncolours = colours->ncolours;
924            if (!owncolmap)
925            {
926                    uint32 *map = xmalloc(sizeof(*colmap) * ncolours);
927                    XColor xentry;
928                    XColor xc_cache[256];
929                    uint32 colour;
930                    int colLookup = 256;
931                    for (i = 0; i < ncolours; i++)
932                    {
933                            entry = &colours->colours[i];
934                            MAKE_XCOLOR(&xentry, entry);
935    
936                            if (XAllocColor(display, xcolmap, &xentry) == 0)
937                            {
938                                    /* Allocation failed, find closest match. */
939                                    int j = 256;
940                                    int nMinDist = 3 * 256 * 256;
941                                    long nDist = nMinDist;
942    
943                                    /* only get the colors once */
944                                    while (colLookup--)
945                                    {
946                                            xc_cache[colLookup].pixel = colLookup;
947                                            xc_cache[colLookup].red = xc_cache[colLookup].green =
948                                                    xc_cache[colLookup].blue = 0;
949                                            xc_cache[colLookup].flags = 0;
950                                            XQueryColor(display,
951                                                        DefaultColormap(display,
952                                                                        DefaultScreen(display)),
953                                                        &xc_cache[colLookup]);
954                                    }
955                                    colLookup = 0;
956    
957                                    /* approximate the pixel */
958                                    while (j--)
959                                    {
960                                            if (xc_cache[j].flags)
961                                            {
962                                                    nDist = ((long) (xc_cache[j].red >> 8) -
963                                                             (long) (xentry.red >> 8)) *
964                                                            ((long) (xc_cache[j].red >> 8) -
965                                                             (long) (xentry.red >> 8)) +
966                                                            ((long) (xc_cache[j].green >> 8) -
967                                                             (long) (xentry.green >> 8)) *
968                                                            ((long) (xc_cache[j].green >> 8) -
969                                                             (long) (xentry.green >> 8)) +
970                                                            ((long) (xc_cache[j].blue >> 8) -
971                                                             (long) (xentry.blue >> 8)) *
972                                                            ((long) (xc_cache[j].blue >> 8) -
973                                                             (long) (xentry.blue >> 8));
974                                            }
975                                            if (nDist < nMinDist)
976                                            {
977                                                    nMinDist = nDist;
978                                                    xentry.pixel = j;
979                                            }
980                                    }
981                            }
982                            colour = xentry.pixel;
983    
984                            /* update our cache */
985                            if (xentry.pixel < 256)
986                            {
987                                    xc_cache[xentry.pixel].red = xentry.red;
988                                    xc_cache[xentry.pixel].green = xentry.green;
989                                    xc_cache[xentry.pixel].blue = xentry.blue;
990    
991                            }
992    
993    
994                            /* byte swap here to make translate_image faster */
995                            map[i] = translate_colour(colour);
996                    }
997                    return map;
998            }
999            else
1000            {
1001                    XColor *xcolours, *xentry;
1002                    Colormap map;
1003    
1004                    xcolours = xmalloc(sizeof(XColor) * ncolours);
1005                    for (i = 0; i < ncolours; i++)
1006                    {
1007                            entry = &colours->colours[i];
1008                            xentry = &xcolours[i];
1009                            xentry->pixel = i;
1010                            MAKE_XCOLOR(xentry, entry);
1011                    }
1012    
1013                    map = XCreateColormap(display, wnd, visual, AllocAll);
1014                    XStoreColors(display, map, xcolours, ncolours);
1015    
1016                    xfree(xcolours);
1017                    return (HCOLOURMAP) map;
1018            }
1019    }
1020    
1021    void
1022    ui_destroy_colourmap(HCOLOURMAP map)
1023    {
1024            if (!owncolmap)
1025                    xfree(map);
1026            else
1027                    XFreeColormap(display, (Colormap) map);
1028    }
1029    
1030    void
1031    ui_set_colourmap(HCOLOURMAP map)
1032    {
1033            if (!owncolmap)
1034                    colmap = map;
1035            else
1036                    XSetWindowColormap(display, wnd, (Colormap) map);
1037    }
1038    
1039    void
1040    ui_set_clip(int x, int y, int cx, int cy)
1041    {
1042            XRectangle rect;
1043    
1044            rect.x = x;
1045            rect.y = y;
1046            rect.width = cx;
1047            rect.height = cy;
1048            XSetClipRectangles(display, gc, 0, 0, &rect, 1, YXBanded);
1049    }
1050    
1051    void
1052    ui_reset_clip(void)
1053    {
1054            XRectangle rect;
1055    
1056            rect.x = 0;
1057            rect.y = 0;
1058            rect.width = width;
1059            rect.height = height;
1060            XSetClipRectangles(display, gc, 0, 0, &rect, 1, YXBanded);
1061    }
1062    
1063    void
1064    ui_bell(void)
1065    {
1066            XBell(display, 0);
1067    }
1068    
1069          visual = DefaultVisual(wnd->display, DefaultScreen(wnd->display));  void
1070          image = XCreateImage(wnd->display, visual, 8, ZPixmap, 0,  ui_destblt(uint8 opcode,
1071                                  data, width, height, 32, width);             /* dest */ int x, int y, int cx, int cy)
1072    {
1073            SET_FUNCTION(opcode);
1074            FILL_RECTANGLE(x, y, cx, cy);
1075            RESET_FUNCTION(opcode);
1076    }
1077    
1078    void
1079    ui_patblt(uint8 opcode,
1080              /* dest */ int x, int y, int cx, int cy,
1081              /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
1082    {
1083            Pixmap fill;
1084            uint8 i, ipattern[8];
1085    
1086            SET_FUNCTION(opcode);
1087    
1088            switch (brush->style)
1089            {
1090                    case 0: /* Solid */
1091                            SET_FOREGROUND(fgcolour);
1092                            FILL_RECTANGLE(x, y, cx, cy);
1093                            break;
1094    
1095                    case 3: /* Pattern */
1096                            for (i = 0; i != 8; i++)
1097                                    ipattern[7 - i] = brush->pattern[i];
1098                            fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
1099    
1100                            SET_FOREGROUND(bgcolour);
1101                            SET_BACKGROUND(fgcolour);
1102                            XSetFillStyle(display, gc, FillOpaqueStippled);
1103                            XSetStipple(display, gc, fill);
1104                            XSetTSOrigin(display, gc, brush->xorigin, brush->yorigin);
1105    
1106                            FILL_RECTANGLE(x, y, cx, cy);
1107    
1108          return (HBITMAP)image;                          XSetFillStyle(display, gc, FillSolid);
1109                            XSetTSOrigin(display, gc, 0, 0);
1110                            ui_destroy_glyph((HGLYPH) fill);
1111                            break;
1112    
1113                    default:
1114                            unimpl("brush %d\n", brush->style);
1115            }
1116    
1117            RESET_FUNCTION(opcode);
1118    }
1119    
1120    void
1121    ui_screenblt(uint8 opcode,
1122                 /* dest */ int x, int y, int cx, int cy,
1123                 /* src */ int srcx, int srcy)
1124    {
1125            SET_FUNCTION(opcode);
1126            XCopyArea(display, wnd, wnd, gc, srcx, srcy, cx, cy, x, y);
1127            if (ownbackstore)
1128                    XCopyArea(display, backstore, backstore, gc, srcx, srcy, cx, cy, x, y);
1129            RESET_FUNCTION(opcode);
1130  }  }
1131    
1132  void ui_destroy_bitmap(HBITMAP bmp)  void
1133    ui_memblt(uint8 opcode,
1134              /* dest */ int x, int y, int cx, int cy,
1135              /* src */ HBITMAP src, int srcx, int srcy)
1136  {  {
1137          XDestroyImage((XImage *)bmp);          SET_FUNCTION(opcode);
1138            XCopyArea(display, (Pixmap) src, wnd, gc, srcx, srcy, cx, cy, x, y);
1139            if (ownbackstore)
1140                    XCopyArea(display, (Pixmap) src, backstore, gc, srcx, srcy, cx, cy, x, y);
1141            RESET_FUNCTION(opcode);
1142  }  }
1143    
1144  void ui_paint_bitmap(HWINDOW wnd, HBITMAP bmp, int x, int y)  void
1145    ui_triblt(uint8 opcode,
1146              /* dest */ int x, int y, int cx, int cy,
1147              /* src */ HBITMAP src, int srcx, int srcy,
1148              /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
1149  {  {
1150          XImage *image = (XImage *)bmp;          /* This is potentially difficult to do in general. Until someone
1151               comes up with a more efficient way of doing it I am using cases. */
1152    
1153            switch (opcode)
1154            {
1155                    case 0x69:      /* PDSxxn */
1156                            ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
1157                            ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
1158                            break;
1159    
1160                    case 0xb8:      /* PSDPxax */
1161                            ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
1162                            ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
1163                            ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
1164                            break;
1165    
1166                    case 0xc0:      /* PSa */
1167                            ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
1168                            ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
1169                            break;
1170    
1171                    default:
1172                            unimpl("triblt 0x%x\n", opcode);
1173                            ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
1174            }
1175    }
1176    
1177    void
1178    ui_line(uint8 opcode,
1179            /* dest */ int startx, int starty, int endx, int endy,
1180            /* pen */ PEN * pen)
1181    {
1182            SET_FUNCTION(opcode);
1183            SET_FOREGROUND(pen->colour);
1184            XDrawLine(display, wnd, gc, startx, starty, endx, endy);
1185            if (ownbackstore)
1186                    XDrawLine(display, backstore, gc, startx, starty, endx, endy);
1187            RESET_FUNCTION(opcode);
1188    }
1189    
1190    void
1191    ui_rect(
1192                   /* dest */ int x, int y, int cx, int cy,
1193                   /* brush */ int colour)
1194    {
1195            SET_FOREGROUND(colour);
1196            FILL_RECTANGLE(x, y, cx, cy);
1197    }
1198    
1199    /* warning, this function only draws on wnd or backstore, not both */
1200    void
1201    ui_draw_glyph(int mixmode,
1202                  /* dest */ int x, int y, int cx, int cy,
1203                  /* src */ HGLYPH glyph, int srcx, int srcy,
1204                  int bgcolour, int fgcolour)
1205    {
1206            SET_FOREGROUND(fgcolour);
1207            SET_BACKGROUND(bgcolour);
1208    
1209            XSetFillStyle(display, gc,
1210                          (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
1211            XSetStipple(display, gc, (Pixmap) glyph);
1212            XSetTSOrigin(display, gc, x, y);
1213    
1214            FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
1215    
1216            XSetFillStyle(display, gc, FillSolid);
1217    }
1218    
1219    #define DO_GLYPH(ttext,idx) \
1220    {\
1221      glyph = cache_get_font (font, ttext[idx]);\
1222      if (!(flags & TEXT2_IMPLICIT_X))\
1223        {\
1224          xyoffset = ttext[++idx];\
1225          if ((xyoffset & 0x80))\
1226            {\
1227              if (flags & TEXT2_VERTICAL) \
1228                y += ttext[idx+1] | (ttext[idx+2] << 8);\
1229              else\
1230                x += ttext[idx+1] | (ttext[idx+2] << 8);\
1231              idx += 2;\
1232            }\
1233          else\
1234            {\
1235              if (flags & TEXT2_VERTICAL) \
1236                y += xyoffset;\
1237              else\
1238                x += xyoffset;\
1239            }\
1240        }\
1241      if (glyph != NULL)\
1242        {\
1243          ui_draw_glyph (mixmode, x + glyph->offset,\
1244                         y + glyph->baseline,\
1245                         glyph->width, glyph->height,\
1246                         glyph->pixmap, 0, 0, bgcolour, fgcolour);\
1247          if (flags & TEXT2_IMPLICIT_X)\
1248            x += glyph->width;\
1249        }\
1250    }
1251    
1252    void
1253    ui_draw_text(uint8 font, uint8 flags, int mixmode, int x, int y,
1254                 int clipx, int clipy, int clipcx, int clipcy,
1255                 int boxx, int boxy, int boxcx, int boxcy, int bgcolour,
1256                 int fgcolour, uint8 * text, uint8 length)
1257    {
1258            FONTGLYPH *glyph;
1259            int i, j, xyoffset;
1260            DATABLOB *entry;
1261    
1262            SET_FOREGROUND(bgcolour);
1263    
1264            if (boxcx > 1)
1265            {
1266                    FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
1267            }
1268            else if (mixmode == MIX_OPAQUE)
1269            {
1270                    FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
1271            }
1272    
1273            /* Paint text, character by character */
1274            for (i = 0; i < length;)
1275            {
1276                    switch (text[i])
1277                    {
1278                            case 0xff:
1279                                    if (i + 2 < length)
1280                                            cache_put_text(text[i + 1], text, text[i + 2]);
1281                                    else
1282                                    {
1283                                            error("this shouldn't be happening\n");
1284                                            exit(1);
1285                                    }
1286                                    /* this will move pointer from start to first character after FF command */
1287                                    length -= i + 3;
1288                                    text = &(text[i + 3]);
1289                                    i = 0;
1290                                    break;
1291    
1292                            case 0xfe:
1293                                    entry = cache_get_text(text[i + 1]);
1294                                    if (entry != NULL)
1295                                    {
1296                                            if ((((uint8 *) (entry->data))[1] ==
1297                                                 0) && (!(flags & TEXT2_IMPLICIT_X)))
1298                                            {
1299                                                    if (flags & TEXT2_VERTICAL)
1300                                                            y += text[i + 2];
1301                                                    else
1302                                                            x += text[i + 2];
1303                                            }
1304                                            for (j = 0; j < entry->size; j++)
1305                                                    DO_GLYPH(((uint8 *) (entry->data)), j);
1306                                    }
1307                                    if (i + 2 < length)
1308                                            i += 3;
1309                                    else
1310                                            i += 2;
1311                                    length -= i;
1312                                    /* this will move pointer from start to first character after FE command */
1313                                    text = &(text[i]);
1314                                    i = 0;
1315                                    break;
1316    
1317                            default:
1318                                    DO_GLYPH(text, i);
1319                                    i++;
1320                                    break;
1321                    }
1322            }
1323            if (ownbackstore)
1324            {
1325                    if (boxcx > 1)
1326                            XCopyArea(display, backstore, wnd, gc, boxx,
1327                                      boxy, boxcx, boxcy, boxx, boxy);
1328                    else
1329                            XCopyArea(display, backstore, wnd, gc, clipx,
1330                                      clipy, clipcx, clipcy, clipx, clipy);
1331            }
1332    }
1333    
1334    void
1335    ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
1336    {
1337            Pixmap pix;
1338            XImage *image;
1339    
1340            if (ownbackstore)
1341            {
1342                    image = XGetImage(display, backstore, x, y, cx, cy, AllPlanes, ZPixmap);
1343            }
1344            else
1345            {
1346                    pix = XCreatePixmap(display, wnd, cx, cy, depth);
1347                    XCopyArea(display, wnd, pix, gc, x, y, cx, cy, 0, 0);
1348                    image = XGetImage(display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
1349                    XFreePixmap(display, pix);
1350            }
1351    
1352            offset *= bpp / 8;
1353            cache_put_desktop(offset, cx, cy, image->bytes_per_line, bpp / 8, (uint8 *) image->data);
1354    
1355            XDestroyImage(image);
1356    }
1357    
1358    void
1359    ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
1360    {
1361            XImage *image;
1362            uint8 *data;
1363    
1364            offset *= bpp / 8;
1365            data = cache_get_desktop(offset, cx, cy, bpp / 8);
1366            if (data == NULL)
1367                    return;
1368    
1369            image = XCreateImage(display, visual, depth, ZPixmap, 0,
1370                                 (char *) data, cx, cy, BitmapPad(display), cx * bpp / 8);
1371    
1372          XPutImage(wnd->display, wnd->wnd, wnd->gc, image,          if (ownbackstore)
1373                          0, 0, x, y, image->width, image->height);          {
1374                    XPutImage(display, backstore, gc, image, 0, 0, x, y, cx, cy);
1375                    XCopyArea(display, backstore, wnd, gc, x, y, cx, cy, x, y);
1376            }
1377            else
1378            {
1379                    XPutImage(display, wnd, gc, image, 0, 0, x, y, cx, cy);
1380            }
1381    
1382          XSync(wnd->display, True);          XFree(image);
1383  }  }

Legend:
Removed from v.6  
changed lines
  Added in v.309

  ViewVC Help
Powered by ViewVC 1.1.26