Fix infinite loop problem in wineclipsrv startup.
[wine] / windows / x11drv / wineclipsrv.c
1 /*
2  *  Wine Clipboard Server
3  *
4  *      Copyright 1999  Noel Borthwick
5  *
6  * USAGE:
7  *       wineclipsrv [selection_mask] [debugClass_mask] [clearAllSelections]
8  *
9  * The optional selection-mask argument is a bit mask of the selection
10  * types to be acquired. Currently two selections are supported:
11  *   1. PRIMARY (mask value 1)
12  *   2. CLIPBOARD (mask value 2).
13  *
14  * debugClass_mask is a bit mask of all debugging classes for which messages
15  * are to be output. The standard Wine debug class set FIXME(1), ERR(2),
16  * WARN(4) and TRACE(8) are supported.
17  *
18  * If clearAllSelections == 1 *all* selections are lost whenever a SelectionClear
19  * event is received.
20  *
21  * If no arguments are supplied the server aquires all selections. (mask value 3)
22  * and defaults to output of only FIXME(1) and ERR(2) messages. The default for
23  * clearAllSelections is 0.
24  *
25  * NOTES:
26  *
27  *    The Wine Clipboard Server is a standalone XLib application whose 
28  * purpose is to manage the X selection when Wine exits.
29  * The server itself is started automatically with the appropriate
30  * selection masks, whenever Wine exits after acquiring the PRIMARY and/or
31  * CLIPBOARD selection. (See X11DRV_CLIPBOARD_ResetOwner)
32  * When the server starts, it first proceeds to capture the selection data from
33  * Wine and then takes over the selection ownership. It does this by querying
34  * the current selection owner(of the specified selections) for the TARGETS
35  * selection target. It then proceeds to cache all the formats exposed by
36  * TARGETS. If the selection does not support the TARGETS target, or if no
37  * target formats are exposed, the server simply exits.
38  * Once the cache has been filled, the server then actually acquires ownership
39  * of the respective selection and begins fielding selection requests.
40  * Selection requests are serviced from the cache. If a selection is lost the
41  * server flushes its internal cache, destroying all data previously saved.
42  * Once ALL selections have been lost the server terminates.
43  *
44  * TODO:
45  */
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <X11/Xlib.h>
50 #include <X11/Xutil.h>
51 #include <X11/Xos.h>
52 #include <X11/Xatom.h>
53
54 #include "config.h"
55
56 /*
57  *  Lightweight debug definitions for Wine Clipboard Server.
58  *  The standard FIXME, ERR, WARN & TRACE classes are supported
59  *  without debug channels.
60  *  The standard defines NO_TRACE_MSGS and NO_DEBUG_MSGS will compile out
61  *  TRACE, WARN and ERR and FIXME message displays.
62  */
63
64 /* Internal definitions (do not use these directly) */
65
66 enum __DEBUG_CLASS { __DBCL_FIXME, __DBCL_ERR, __DBCL_WARN, __DBCL_TRACE, __DBCL_COUNT };
67
68 extern char __debug_msg_enabled[__DBCL_COUNT];
69
70 extern const char * const debug_cl_name[__DBCL_COUNT];
71
72 #define DEBUG_CLASS_COUNT __DBCL_COUNT
73
74 #define __GET_DEBUGGING(dbcl)    (__debug_msg_enabled[(dbcl)])
75 #define __SET_DEBUGGING(dbcl,on) (__debug_msg_enabled[(dbcl)] = (on))
76
77
78 #define __DPRINTF(dbcl) \
79     (!__GET_DEBUGGING(dbcl) || \
80     (printf("%s:%s:%s ", debug_cl_name[(dbcl)], progname, __FUNCTION__),0)) \
81     ? 0 : printf
82
83 #define __DUMMY_DPRINTF 1 ? (void)0 : (void)((int (*)(char *, ...)) NULL)
84
85 /* use configure to allow user to compile out debugging messages */
86 #ifndef NO_TRACE_MSGS
87   #define TRACE        __DPRINTF(__DBCL_TRACE)
88 #else
89   #define TRACE        __DUMMY_DPRINTF
90 #endif /* NO_TRACE_MSGS */
91
92 #ifndef NO_DEBUG_MSGS
93   #define WARN         __DPRINTF(__DBCL_WARN)
94   #define FIXME        __DPRINTF(__DBCL_FIXME)
95 #else
96   #define WARN         __DUMMY_DPRINTF
97   #define FIXME        __DUMMY_DPRINTF
98 #endif /* NO_DEBUG_MSGS */
99
100 /* define error macro regardless of what is configured */
101 #define ERR        __DPRINTF(__DBCL_ERR)
102
103
104 #define TRUE 1
105 #define FALSE 0
106 typedef int BOOL;
107
108 /* Internal definitions for debugging messages(do not use these directly) */
109 const char * const debug_cl_name[] = { "fixme", "err", "warn", "trace" };
110 char __debug_msg_enabled[DEBUG_CLASS_COUNT] = {1, 1, 0, 0};
111
112
113 /* Selection masks */
114
115 #define S_NOSELECTION    0
116 #define S_PRIMARY        1
117 #define S_CLIPBOARD      2
118
119 /* Debugging class masks */
120
121 #define C_FIXME          1
122 #define C_ERR            2
123 #define C_WARN           4
124 #define C_TRACE          8
125
126 /*
127  * Global variables 
128  */
129
130 static Display *g_display = NULL;
131 static int screen_num;
132 static char *progname;                 /* name this program was invoked by */
133 static Window g_win = 0;               /* the hidden clipboard server window */
134 static GC g_gc = 0;
135
136 static char *g_szOutOfMemory = "Insufficient memory!\n";
137
138 /* X selection context info */
139 static char _CLIPBOARD[] = "CLIPBOARD";        /* CLIPBOARD atom name */
140 static int  g_selectionToAcquire = 0;          /* Masks for the selection to be acquired */
141 static int  g_selectionAcquired = 0;           /* Contains the current selection masks */
142 static int  g_clearAllSelections = 0;          /* If TRUE *all* selections are lost on SelectionClear */
143     
144 /* Selection cache */
145 typedef struct tag_CACHEENTRY
146 {
147     Atom target;
148     Atom type;
149     int nFormat;
150     int nElements;
151     void *pData;
152 } CACHEENTRY, *PCACHEENTRY;
153
154 static PCACHEENTRY g_pPrimaryCache = NULL;     /* Primary selection cache */
155 static PCACHEENTRY g_pClipboardCache = NULL;   /* Clipboard selection cache */
156 static unsigned long g_cPrimaryTargets = 0;    /* Number of TARGETS reported by PRIMARY selection */
157 static unsigned long g_cClipboardTargets = 0;  /* Number of TARGETS reported by CLIPBOARD selection */
158
159 /* Event names */
160 static const char * const event_names[] =
161 {
162   "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
163   "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
164   "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
165   "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
166   "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
167   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
168   "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
169   "ClientMessage", "MappingNotify"
170 };
171
172
173 /*
174  * Prototypes 
175  */
176
177 BOOL Init(int argc, char **argv);
178 void TerminateServer( int ret );
179 int AcquireSelection();
180 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache );
181 void EmptyCache(PCACHEENTRY pCache, int nItems);
182 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry );
183 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry );
184 void EVENT_ProcessEvent( XEvent *event );
185 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent );
186 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple );
187 void EVENT_SelectionClear( XSelectionClearEvent *event );
188 void EVENT_PropertyNotify( XPropertyEvent *event );
189 Pixmap DuplicatePixmap(Pixmap pixmap);
190 void TextOut(Window win, GC gc, char *pStr);
191 void getGC(Window win, GC *gc);
192
193
194 int main(int argc, char **argv)
195 {
196     XEvent event;
197
198     if ( !Init(argc, argv) )
199         exit(0);
200     
201     /* Acquire the selection after retrieving all clipboard data
202      * owned by the current selection owner. If we were unable to
203      * Acquire any selection, terminate right away.
204      */
205     if ( AcquireSelection() == S_NOSELECTION )
206         TerminateServer(0);
207
208     TRACE("Clipboard server running...\n");
209     
210     /* Start an X event loop */
211     while (1)
212     {
213         XNextEvent(g_display, &event);
214         
215         EVENT_ProcessEvent( &event );
216     }
217 }
218
219
220 /**************************************************************************
221  *              Init()
222  *  Initialize the clipboard server
223  */
224 BOOL Init(int argc, char **argv)
225 {
226     unsigned int width, height; /* window size */
227     unsigned int border_width = 4;      /* four pixels */
228     unsigned int display_width, display_height;
229     char *window_name = "Wine Clipboard Server";
230     XSizeHints *size_hints = NULL;
231     XWMHints *wm_hints = NULL;
232     XClassHint *class_hints = NULL;
233     XTextProperty windowName;
234     char *display_name = NULL;
235     
236     progname = argv[0];
237     
238     if (!(size_hints = XAllocSizeHints()))
239     {
240         ERR(g_szOutOfMemory);
241         return 0;
242     }
243     if (!(wm_hints = XAllocWMHints()))
244     {
245         ERR(g_szOutOfMemory);
246         return 0;
247     }
248     if (!(class_hints = XAllocClassHint()))
249     {
250         ERR(g_szOutOfMemory);
251         return 0;
252     }
253     
254     /* connect to X server */
255     if ( (g_display=XOpenDisplay(display_name)) == NULL )
256     {
257         ERR( "cannot connect to X server %s\n", XDisplayName(display_name));
258         return 0;
259     }
260     
261     /* get screen size from display structure macro */
262     screen_num = DefaultScreen(g_display);
263     display_width = DisplayWidth(g_display, screen_num);
264     display_height = DisplayHeight(g_display, screen_num);
265     
266     /* size window with enough room for text */
267     width = display_width/3, height = display_height/4;
268     
269     /* create opaque window */
270     g_win = XCreateSimpleWindow(g_display, RootWindow(g_display,screen_num),
271                     0, 0, width, height, border_width, BlackPixel(g_display,
272                     screen_num), WhitePixel(g_display,screen_num));
273     
274     
275     /* Set size hints for window manager.  The window manager may
276      * override these settings. */
277     
278     /* x, y, width, and height hints are now taken from
279      * the actual settings of the window when mapped. Note
280      * that PPosition and PSize must be specified anyway. */
281     
282     size_hints->flags = PPosition | PSize | PMinSize;
283     size_hints->min_width = 300;
284     size_hints->min_height = 200;
285     
286     /* These calls store window_name into XTextProperty structures
287      * and sets the other fields properly. */
288     if (XStringListToTextProperty(&window_name, 1, &windowName) == 0)
289     {
290        ERR( "structure allocation for windowName failed.\n");
291        TerminateServer(-1);
292     }
293             
294     wm_hints->initial_state = NormalState;
295     wm_hints->input = True;
296     wm_hints->flags = StateHint | InputHint;
297     
298     class_hints->res_name = progname;
299     class_hints->res_class = "WineClipSrv";
300
301     XSetWMProperties(g_display, g_win, &windowName, NULL, 
302                     argv, argc, size_hints, wm_hints, 
303                     class_hints);
304
305     /* Select event types wanted */
306     XSelectInput(g_display, g_win, ExposureMask | KeyPressMask | 
307                     ButtonPressMask | StructureNotifyMask | PropertyChangeMask );
308     
309     /* create GC for text and drawing */
310     getGC(g_win, &g_gc);
311     
312     /* Display window */
313     /* XMapWindow(g_display, g_win); */
314
315     /* Set the selections to be acquired from the command line argument.
316      * If none specified, default to all selections we understand.
317      */
318     if (argc > 1)
319         g_selectionToAcquire = atoi(argv[1]);
320     else
321         g_selectionToAcquire = S_PRIMARY | S_CLIPBOARD;
322
323     /* Set the debugging class state from the command line argument */
324     if (argc > 2)
325     {
326         int dbgClasses = atoi(argv[2]);
327         
328         __SET_DEBUGGING(__DBCL_FIXME, dbgClasses & C_FIXME);
329         __SET_DEBUGGING(__DBCL_ERR, dbgClasses & C_ERR);
330         __SET_DEBUGGING(__DBCL_WARN, dbgClasses & C_WARN);
331         __SET_DEBUGGING(__DBCL_TRACE, dbgClasses & C_TRACE);
332     }
333         
334     /* Set the "ClearSelections" state from the command line argument */
335     if (argc > 3)
336         g_clearAllSelections = atoi(argv[3]);
337     
338     return TRUE;
339 }
340
341
342 /**************************************************************************
343  *              TerminateServer()
344  */
345 void TerminateServer( int ret )
346 {
347     TRACE("Terminating Wine clipboard server...\n");
348     
349     /* Free Primary and Clipboard selection caches */
350     EmptyCache(g_pPrimaryCache, g_cPrimaryTargets);
351     EmptyCache(g_pClipboardCache, g_cClipboardTargets);
352
353     if (g_gc)
354         XFreeGC(g_display, g_gc);
355
356     if (g_display)
357         XCloseDisplay(g_display);
358
359     exit(ret);
360 }
361
362
363 /**************************************************************************
364  *              AcquireSelection()
365  *
366  * Acquire the selection after retrieving all clipboard data owned by 
367  * the current selection owner.
368  */
369 int AcquireSelection()
370 {
371     Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
372
373     /*
374      *  For all selections we need to acquire, get a list of all targets
375      *  supplied by the current selection owner.
376      */
377     if (g_selectionToAcquire & S_PRIMARY)
378     {
379         TRACE("Acquiring PRIMARY selection...\n");
380         g_cPrimaryTargets = CacheDataFormats( XA_PRIMARY, &g_pPrimaryCache );
381         TRACE("Cached %ld formats...\n", g_cPrimaryTargets);
382     }
383     if (g_selectionToAcquire & S_CLIPBOARD)
384     {
385         TRACE("Acquiring CLIPBOARD selection...\n");
386         g_cClipboardTargets = CacheDataFormats( xaClipboard, &g_pClipboardCache );
387         TRACE("Cached %ld formats...\n", g_cClipboardTargets);
388     }
389
390     /*
391      * Now that we have cached the data, we proceed to acquire the selections
392      */
393     if (g_cPrimaryTargets)
394     {
395         /* Acquire the PRIMARY selection */
396         while (XGetSelectionOwner(g_display,XA_PRIMARY) != g_win)
397             XSetSelectionOwner(g_display, XA_PRIMARY, g_win, CurrentTime);
398         
399         g_selectionAcquired |= S_PRIMARY;
400     }
401     else
402         TRACE("No PRIMARY targets - ownership not acquired.\n");
403     
404     if (g_cClipboardTargets)
405     {
406         /* Acquire the CLIPBOARD selection */
407         while (XGetSelectionOwner(g_display,xaClipboard) != g_win)
408             XSetSelectionOwner(g_display, xaClipboard, g_win, CurrentTime);
409
410         g_selectionAcquired |= S_CLIPBOARD;
411     }
412     else
413         TRACE("No CLIPBOARD targets - ownership not acquired.\n");
414
415     return g_selectionAcquired;
416 }
417
418 BOOL GetSelectionEvent(Atom SelectionSrc, XEvent *xe)
419 {
420     time_t end_time;
421
422     /* Set up a 10 second time out */
423     end_time=time(NULL)+10;
424
425     do
426     {
427         struct timeval nap;
428
429         if (XCheckTypedWindowEvent(g_display, g_win, SelectionNotify, xe))
430         {
431             if( xe->xselection.selection == SelectionSrc )
432                 return TRUE;
433         }
434
435         if (time(NULL)>end_time)
436             break;
437
438         /* Sleep a bit to make this busy wait less brutal */
439         nap.tv_sec = 0;
440         nap.tv_usec = 10;
441         select(0, NULL, NULL, NULL, &nap);
442     }
443     while (TRUE);
444
445     return FALSE;
446 }
447
448 /**************************************************************************
449  *              CacheDataFormats
450  *
451  * Allocates and caches the list of data formats available from the current selection.
452  * This queries the selection owner for the TARGETS property and saves all
453  * reported property types.
454  */
455 int CacheDataFormats( Atom SelectionSrc, PCACHEENTRY *ppCache )
456 {
457     XEvent         xe;
458     Atom           aTargets;
459     Atom           atype=AnyPropertyType;
460     int            aformat;
461     unsigned long  remain;
462     unsigned long  cSelectionTargets = 0;
463     Atom*          targetList=NULL;
464     Window         ownerSelection = 0;
465
466     if (!ppCache)
467         return 0;
468     *ppCache = NULL;
469
470     /* Get the selection owner */
471     ownerSelection = XGetSelectionOwner(g_display, SelectionSrc);
472     if ( ownerSelection == None )
473         return cSelectionTargets;
474
475     /*
476      * Query the selection owner for the TARGETS property
477      */
478     aTargets = XInternAtom(g_display, "TARGETS", False);
479
480     TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
481           XGetAtomName(g_display, SelectionSrc), (unsigned)ownerSelection );
482           
483     XConvertSelection(g_display, SelectionSrc, aTargets,
484                     XInternAtom(g_display, "SELECTION_DATA", False),
485                     g_win, CurrentTime);
486
487     /*
488      * Wait until SelectionNotify is received
489      */
490     if (!GetSelectionEvent(SelectionSrc, &xe))
491         return 0;
492
493     /* Verify that the selection returned a valid TARGETS property */
494     if ( (xe.xselection.target != aTargets)
495           || (xe.xselection.property == None) )
496     {
497         TRACE("\tCould not retrieve TARGETS\n");
498         return cSelectionTargets;
499     }
500
501     /* Read the TARGETS property contents */
502     if(XGetWindowProperty(g_display, xe.xselection.requestor, xe.xselection.property,
503                             0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
504                             &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
505         TRACE("\tCouldn't read TARGETS property\n");
506     else
507     {
508        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
509              XGetAtomName(g_display,atype),aformat,cSelectionTargets, remain);
510        /*
511         * The TARGETS property should have returned us a list of atoms
512         * corresponding to each selection target format supported.
513         */
514        if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
515        {
516           int i;
517
518           /* Allocate the selection cache */
519           *ppCache = (PCACHEENTRY)calloc(cSelectionTargets, sizeof(CACHEENTRY));
520           
521           /* Cache these formats in the selection cache */
522           for (i = 0; i < cSelectionTargets; i++)
523           {
524               char *itemFmtName = XGetAtomName(g_display, targetList[i]);
525           
526               TRACE("\tAtom# %d: '%s'\n", i, itemFmtName);
527               
528               /* Populate the cache entry */
529               if (!FillCacheEntry( SelectionSrc, targetList[i], &((*ppCache)[i])))
530                   ERR("Failed to fill cache entry!\n");
531
532               XFree(itemFmtName);
533           }
534        }
535
536        /* Free the list of targets */
537        XFree(targetList);
538     }
539     
540     return cSelectionTargets;
541 }
542
543 /***********************************************************************
544  *           FillCacheEntry
545  *
546  *   Populates the specified cache entry
547  */
548 BOOL FillCacheEntry( Atom SelectionSrc, Atom target, PCACHEENTRY pCacheEntry )
549 {
550     XEvent            xe;
551     Window            w;
552     Atom              prop, reqType;
553     Atom              atype=AnyPropertyType;
554     int               aformat;
555     unsigned long     nitems,remain,itemSize;
556     long              lRequestLength;
557     unsigned char*    val=NULL;
558     BOOL              bRet = FALSE;
559
560     TRACE("Requesting %s selection from %s...\n",
561           XGetAtomName(g_display, target),
562           XGetAtomName(g_display, SelectionSrc) );
563
564     /* Ask the selection owner to convert the selection to the target format */
565     XConvertSelection(g_display, SelectionSrc, target,
566                     XInternAtom(g_display, "SELECTION_DATA", False),
567                     g_win, CurrentTime);
568
569     /* wait until SelectionNotify is received */
570     if (!GetSelectionEvent(SelectionSrc,&xe))
571         return bRet;
572
573     /* Now proceed to retrieve the actual converted property from
574      * the SELECTION_DATA atom */
575
576     w = xe.xselection.requestor;
577     prop = xe.xselection.property;
578     reqType = xe.xselection.target;
579     
580     if(prop == None)
581     {
582         TRACE("\tOwner failed to convert selection!\n");
583         return bRet;
584     }
585        
586     TRACE("\tretrieving property %s from window %ld into %s\n",
587           XGetAtomName(g_display,reqType), (long)w, XGetAtomName(g_display,prop) );
588
589     /*
590      * First request a zero length in order to figure out the request size.
591      */
592     if(XGetWindowProperty(g_display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
593                             &atype, &aformat, &nitems, &itemSize, &val) != Success)
594     {
595         WARN("\tcouldn't get property size\n");
596         return bRet;
597     }
598
599     /* Free zero length return data if any */
600     if ( val )
601     {
602        XFree(val);
603        val = NULL;
604     }
605     
606     TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
607     lRequestLength = (itemSize * aformat/8)/4  + 1;
608     
609     /*
610      * Retrieve the actual property in the required X format.
611      */
612     if(XGetWindowProperty(g_display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
613                           &atype, &aformat, &nitems, &remain, &val) != Success)
614     {
615         WARN("\tcouldn't read property\n");
616         return bRet;
617     }
618
619     TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
620           atype ? XGetAtomName(g_display,atype) : NULL, aformat,nitems,remain,val);
621     
622     if (remain)
623     {
624         WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
625         goto END;
626     }
627
628     /*
629      * Populate the cache entry
630      */
631     pCacheEntry->target = target;
632     pCacheEntry->type = atype;
633     pCacheEntry->nFormat = aformat;
634     pCacheEntry->nElements = nitems;
635
636     if (atype == XA_PIXMAP)
637     {
638         Pixmap *pPixmap = (Pixmap *)val;
639         Pixmap newPixmap = DuplicatePixmap( *pPixmap );
640         pPixmap = (Pixmap*)calloc(1, sizeof(Pixmap));
641         *pPixmap = newPixmap;
642         pCacheEntry->pData = pPixmap;
643     }
644     else
645         pCacheEntry->pData = val;
646
647 END:
648     /* Delete the property on the window now that we are done
649      * This will send a PropertyNotify event to the selection owner. */
650     XDeleteProperty(g_display,w,prop);
651     
652     return TRUE;
653 }
654
655
656 /***********************************************************************
657  *           LookupCacheItem
658  *
659  *   Lookup a target atom in the cache and get the matching cache entry
660  */
661 BOOL LookupCacheItem( Atom selection, Atom target, PCACHEENTRY *ppCacheEntry )
662 {
663     int i;
664     int             nCachetargets = 0;
665     PCACHEENTRY     pCache = NULL;
666     Atom            xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
667
668     /* Locate the cache to be used based on the selection type */
669     if ( selection == XA_PRIMARY )
670     {
671         pCache = g_pPrimaryCache;
672         nCachetargets = g_cPrimaryTargets;
673     }
674     else if ( selection == xaClipboard )
675     {
676         pCache = g_pClipboardCache;
677         nCachetargets = g_cClipboardTargets;
678     }
679
680     if (!pCache || !ppCacheEntry)
681         return FALSE;
682
683     *ppCacheEntry = NULL;
684     
685     /* Look for the target item in the cache */
686     for (i = 0; i < nCachetargets; i++)
687     {
688         if (pCache[i].target == target)
689         {
690             *ppCacheEntry = &pCache[i];
691             return TRUE;
692         }
693     }
694
695     return FALSE;
696 }
697
698
699 /***********************************************************************
700  *           EmptyCache
701  *
702  *   Empties the specified cache
703  */
704 void EmptyCache(PCACHEENTRY pCache, int nItems)
705 {
706     int i;
707     
708     if (!pCache)
709         return;
710
711     /* Release all items in the cache */
712     for (i = 0; i < nItems; i++)
713     {
714         if (pCache[i].target && pCache[i].pData)
715         {
716             /* If we have a Pixmap, free it first */
717             if (pCache[i].target == XA_PIXMAP || pCache[i].target == XA_BITMAP)
718             {
719                 Pixmap *pPixmap = (Pixmap *)pCache[i].pData;
720                 
721                 TRACE("Freeing %s (handle=%ld)...\n",
722                       XGetAtomName(g_display, pCache[i].target), *pPixmap);
723                 
724                 XFreePixmap(g_display, *pPixmap);
725
726                 /* Free the cached data item (allocated by us) */
727                 free(pCache[i].pData);
728             }
729             else
730             {
731                 TRACE("Freeing %s (%p)...\n",
732                       XGetAtomName(g_display, pCache[i].target), pCache[i].pData);
733             
734                 /* Free the cached data item (allocated by X) */
735                 XFree(pCache[i].pData);
736             }
737         }
738     }
739
740     /* Destroy the cache */
741     free(pCache);
742 }
743
744
745 /***********************************************************************
746  *           EVENT_ProcessEvent
747  *
748  * Process an X event.
749  */
750 void EVENT_ProcessEvent( XEvent *event )
751 {
752   /*
753   TRACE(" event %s for Window %08lx\n", event_names[event->type], event->xany.window );
754   */
755     
756   switch (event->type)
757   {
758       case Expose:
759           /* don't draw the window */
760           if (event->xexpose.count != 0)
761                   break;
762
763           /* Output something */
764           TextOut(g_win, g_gc, "Click here to terminate");
765           break;
766           
767       case ConfigureNotify:
768           break;
769           
770       case ButtonPress:
771               /* fall into KeyPress (no break) */
772       case KeyPress:
773           TerminateServer(1);
774           break;
775
776       case SelectionRequest:
777           EVENT_SelectionRequest( (XSelectionRequestEvent *)event, FALSE );
778           break;
779   
780       case SelectionClear:
781           EVENT_SelectionClear( (XSelectionClearEvent*)event );
782           break;
783         
784       case PropertyNotify:
785           // EVENT_PropertyNotify( (XPropertyEvent *)event );
786           break;
787
788       default: /* ignore all other events */
789           break;
790           
791   } /* end switch */
792
793 }
794
795
796 /***********************************************************************
797  *           EVENT_SelectionRequest_MULTIPLE
798  *  Service a MULTIPLE selection request event
799  *  rprop contains a list of (target,property) atom pairs.
800  *  The first atom names a target and the second names a property.
801  *  The effect is as if we have received a sequence of SelectionRequest events
802  *  (one for each atom pair) except that:
803  *  1. We reply with a SelectionNotify only when all the requested conversions
804  *  have been performed.
805  *  2. If we fail to convert the target named by an atom in the MULTIPLE property,
806  *  we replace the atom in the property by None.
807  */
808 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
809 {
810     Atom           rprop;
811     Atom           atype=AnyPropertyType;
812     int            aformat;
813     unsigned long  remain;
814     Atom*          targetPropList=NULL;
815     unsigned long  cTargetPropList = 0;
816 /*  Atom           xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
817     
818    /* If the specified property is None the requestor is an obsolete client.
819     * We support these by using the specified target atom as the reply property.
820     */
821     rprop = pevent->property;
822     if( rprop == None ) 
823         rprop = pevent->target;
824     if (!rprop)
825         goto END;
826
827     /* Read the MULTIPLE property contents. This should contain a list of
828      * (target,property) atom pairs.
829      */
830     if(XGetWindowProperty(g_display, pevent->requestor, rprop,
831                             0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
832                             &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
833         TRACE("\tCouldn't read MULTIPLE property\n");
834     else
835     {
836        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
837                      XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
838
839        /*
840         * Make sure we got what we expect.
841         * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
842         * in a MULTIPLE selection request should be of type ATOM_PAIR.
843         * However some X apps(such as XPaint) are not compliant with this and return
844         * a user defined atom in atype when XGetWindowProperty is called.
845         * The data *is* an atom pair but is not denoted as such.
846         */
847        if(aformat == 32 /* atype == xAtomPair */ )
848        {
849           int i;
850           
851           /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
852            * for each (target,property) pair */
853
854           for (i = 0; i < cTargetPropList; i+=2)
855           {
856               char *targetName = XGetAtomName(g_display, targetPropList[i]);
857               char *propName = XGetAtomName(g_display, targetPropList[i+1]);
858               XSelectionRequestEvent event;
859
860               TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
861               XFree(targetName);
862               XFree(propName);
863               
864               /* We must have a non "None" property to service a MULTIPLE target atom */
865               if ( !targetPropList[i+1] )
866               {
867                   TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
868                   continue;
869               }
870               
871               /* Set up an XSelectionRequestEvent for this (target,property) pair */
872               memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
873               event.target = targetPropList[i];
874               event.property = targetPropList[i+1];
875                   
876               /* Fire a SelectionRequest, informing the handler that we are processing
877                * a MULTIPLE selection request event.
878                */
879               EVENT_SelectionRequest( &event, TRUE );
880           }
881        }
882
883        /* Free the list of targets/properties */
884        XFree(targetPropList);
885     }
886
887 END:
888     return rprop;
889 }
890
891
892 /***********************************************************************
893  *           EVENT_SelectionRequest
894  *  Process an event selection request event.
895  *  The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
896  *  recursively while servicing a "MULTIPLE" selection target.
897  *
898  */
899 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
900 {
901   XSelectionEvent result;
902   Atom            rprop = None;
903   Window          request = event->requestor;
904   Atom            xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
905   PCACHEENTRY     pCacheEntry = NULL;
906   void            *pData = NULL;
907   Pixmap          pixmap;
908
909   /* If the specified property is None the requestor is an obsolete client.
910    * We support these by using the specified target atom as the reply property.
911    */
912   rprop = event->property;
913   if( rprop == None ) 
914       rprop = event->target;
915
916   TRACE("Request for %s in selection %s\n",
917         XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
918
919   /* Handle MULTIPLE requests -  rprop contains a list of (target, property) atom pairs */
920   if(event->target == xaMultiple)
921   {
922       /* MULTIPLE selection request - will call us back recursively */
923       rprop = EVENT_SelectionRequest_MULTIPLE( event );
924       goto END;
925   }
926
927   /* Lookup the requested target property in the cache */
928   if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
929   {
930       TRACE("Item not available in cache!\n");
931       goto END;
932   }
933
934   /* Update the X property */
935   TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
936
937   /* If we have a request for a pixmap, return a duplicate */
938   
939   if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
940   {
941     Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
942     pixmap = DuplicatePixmap( *pPixmap );
943     pData = &pixmap;
944   }
945   else
946     pData = pCacheEntry->pData;
947   
948   XChangeProperty(g_display, request, rprop,
949                     pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
950                     (unsigned char *)pData, pCacheEntry->nElements);
951
952 END:
953   if( rprop == None) 
954       TRACE("\tRequest ignored\n");
955
956   /* reply to sender 
957    * SelectionNotify should be sent only at the end of a MULTIPLE request
958    */
959   if ( !bIsMultiple )
960   {
961     result.type = SelectionNotify;
962     result.display = g_display;
963     result.requestor = request;
964     result.selection = event->selection;
965     result.property = rprop;
966     result.target = event->target;
967     result.time = event->time;
968     TRACE("Sending SelectionNotify event...\n");
969     XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
970   }
971 }
972
973
974 /***********************************************************************
975  *           EVENT_SelectionClear
976  *   We receive this event when another client grabs the X selection.
977  *   If we lost both PRIMARY and CLIPBOARD we must terminate.
978  */
979 void EVENT_SelectionClear( XSelectionClearEvent *event )
980 {
981   Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
982     
983   TRACE("()\n");
984
985   /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
986    * dictate that *all* selections should be cleared on loss of a selection,
987    * we must give up all the selections we own.
988    */
989   if ( g_clearAllSelections || (event->selection == xaClipboard) )
990   {
991       TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
992       
993       /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
994       if ( (event->selection == xaClipboard)
995            && (g_selectionAcquired & S_PRIMARY) )
996       {
997           XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
998       }
999       
1000       /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD  */
1001       if ( (event->selection == XA_PRIMARY)
1002            && (g_selectionAcquired & S_CLIPBOARD) )
1003       {
1004           XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
1005       }
1006       
1007       g_selectionAcquired = S_NOSELECTION;   /* Clear the selection masks */
1008   }
1009   else if (event->selection == XA_PRIMARY)
1010   {
1011       TRACE("Lost PRIMARY selection...\n");
1012       g_selectionAcquired &= ~S_PRIMARY;     /* Clear the PRIMARY flag */
1013   }
1014
1015   /* Once we lose all our selections we have nothing more to do */
1016   if (g_selectionAcquired == S_NOSELECTION)
1017       TerminateServer(1);
1018 }
1019
1020 /***********************************************************************
1021  *           EVENT_PropertyNotify
1022  *   We use this to release resources like Pixmaps when a selection
1023  *   client no longer needs them.
1024  */
1025 void EVENT_PropertyNotify( XPropertyEvent *event )
1026 {
1027   TRACE("()\n");
1028
1029   /* Check if we have any resources to free */
1030
1031   switch(event->state)
1032   {
1033     case PropertyDelete:
1034     {
1035       TRACE("\tPropertyDelete for atom %s on window %ld\n",
1036                     XGetAtomName(event->display, event->atom), (long)event->window);
1037       
1038       /* FreeResources( event->atom ); */
1039       break;
1040     }
1041
1042     case PropertyNewValue:
1043     {
1044       TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1045                     XGetAtomName(event->display, event->atom), (long)event->window);
1046       break;
1047     }
1048     
1049     default:
1050       break;
1051   }
1052 }
1053
1054 /***********************************************************************
1055  *           DuplicatePixmap
1056  */
1057 Pixmap DuplicatePixmap(Pixmap pixmap)
1058 {
1059     Pixmap newPixmap;
1060     XImage *xi;
1061     Window root;
1062     int x,y;               /* Unused */
1063     unsigned border_width; /* Unused */
1064     unsigned int depth, width, height;
1065
1066     TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1067           
1068     /* Get the Pixmap dimensions and bit depth */
1069     if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1070                              &border_width, &depth) )
1071         return 0;
1072
1073     TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1074           width, height, depth);
1075     
1076     newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1077         
1078     xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1079
1080     XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1081
1082     XDestroyImage(xi);
1083     
1084     TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1085     return newPixmap;
1086 }
1087
1088 /***********************************************************************
1089  *           getGC
1090  * Get a GC to use for drawing
1091  */
1092 void getGC(Window win, GC *gc)
1093 {
1094         unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1095         XGCValues values;
1096         unsigned int line_width = 6;
1097         int line_style = LineOnOffDash;
1098         int cap_style = CapRound;
1099         int join_style = JoinRound;
1100         int dash_offset = 0;
1101         static char dash_list[] = {12, 24};
1102         int list_length = 2;
1103
1104         /* Create default Graphics Context */
1105         *gc = XCreateGC(g_display, win, valuemask, &values);
1106
1107         /* specify black foreground since default window background is 
1108          * white and default foreground is undefined. */
1109         XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1110
1111         /* set line attributes */
1112         XSetLineAttributes(g_display, *gc, line_width, line_style, 
1113                         cap_style, join_style);
1114
1115         /* set dashes */
1116         XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1117 }
1118
1119
1120 /***********************************************************************
1121  *           TextOut
1122  */
1123 void TextOut(Window win, GC gc, char *pStr)
1124 {
1125         int y_offset, x_offset;
1126
1127         y_offset = 10;
1128         x_offset = 2;
1129
1130         /* output text, centered on each line */
1131         XDrawString(g_display, win, gc, x_offset, y_offset, pStr, 
1132                         strlen(pStr));
1133 }