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