Added regedit unit test, a couple minor changes to regedit.
[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 #if 0
837           EVENT_PropertyNotify( (XPropertyEvent *)event );
838 #endif
839           break;
840
841       default: /* ignore all other events */
842           break;
843
844   } /* end switch */
845
846 }
847
848
849 /***********************************************************************
850  *           EVENT_SelectionRequest_MULTIPLE
851  *  Service a MULTIPLE selection request event
852  *  rprop contains a list of (target,property) atom pairs.
853  *  The first atom names a target and the second names a property.
854  *  The effect is as if we have received a sequence of SelectionRequest events
855  *  (one for each atom pair) except that:
856  *  1. We reply with a SelectionNotify only when all the requested conversions
857  *  have been performed.
858  *  2. If we fail to convert the target named by an atom in the MULTIPLE property,
859  *  we replace the atom in the property by None.
860  */
861 Atom EVENT_SelectionRequest_MULTIPLE( XSelectionRequestEvent *pevent )
862 {
863     Atom           rprop;
864     Atom           atype=AnyPropertyType;
865     int            aformat;
866     unsigned long  remain;
867     Atom*          targetPropList=NULL;
868     unsigned long  cTargetPropList = 0;
869 /*  Atom           xAtomPair = XInternAtom(g_display, "ATOM_PAIR", False); */
870
871    /* If the specified property is None the requestor is an obsolete client.
872     * We support these by using the specified target atom as the reply property.
873     */
874     rprop = pevent->property;
875     if( rprop == None )
876         rprop = pevent->target;
877     if (!rprop)
878         goto END;
879
880     /* Read the MULTIPLE property contents. This should contain a list of
881      * (target,property) atom pairs.
882      */
883     if(XGetWindowProperty(g_display, pevent->requestor, rprop,
884                             0, 0x3FFF, False, AnyPropertyType, &atype, &aformat,
885                             &cTargetPropList, &remain, (unsigned char**)&targetPropList) != Success)
886         TRACE("\tCouldn't read MULTIPLE property\n");
887     else
888     {
889        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
890                      XGetAtomName(g_display,atype),aformat,cTargetPropList,remain);
891
892        /*
893         * Make sure we got what we expect.
894         * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
895         * in a MULTIPLE selection request should be of type ATOM_PAIR.
896         * However some X apps(such as XPaint) are not compliant with this and return
897         * a user defined atom in atype when XGetWindowProperty is called.
898         * The data *is* an atom pair but is not denoted as such.
899         */
900        if(aformat == 32 /* atype == xAtomPair */ )
901        {
902           int i;
903
904           /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
905            * for each (target,property) pair */
906
907           for (i = 0; i < cTargetPropList; i+=2)
908           {
909               char *targetName = XGetAtomName(g_display, targetPropList[i]);
910               char *propName = XGetAtomName(g_display, targetPropList[i+1]);
911               XSelectionRequestEvent event;
912
913               TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n", i/2, targetName, propName);
914               XFree(targetName);
915               XFree(propName);
916
917               /* We must have a non "None" property to service a MULTIPLE target atom */
918               if ( !targetPropList[i+1] )
919               {
920                   TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
921                   continue;
922               }
923
924               /* Set up an XSelectionRequestEvent for this (target,property) pair */
925               memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
926               event.target = targetPropList[i];
927               event.property = targetPropList[i+1];
928
929               /* Fire a SelectionRequest, informing the handler that we are processing
930                * a MULTIPLE selection request event.
931                */
932               EVENT_SelectionRequest( &event, TRUE );
933           }
934        }
935
936        /* Free the list of targets/properties */
937        XFree(targetPropList);
938     }
939
940 END:
941     return rprop;
942 }
943
944
945 /***********************************************************************
946  *           EVENT_SelectionRequest
947  *  Process an event selection request event.
948  *  The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
949  *  recursively while servicing a "MULTIPLE" selection target.
950  *
951  */
952 void EVENT_SelectionRequest( XSelectionRequestEvent *event, BOOL bIsMultiple )
953 {
954   XSelectionEvent result;
955   Atom            rprop = None;
956   Window          request = event->requestor;
957   Atom            xaMultiple = XInternAtom(g_display, "MULTIPLE", False);
958   PCACHEENTRY     pCacheEntry = NULL;
959   void            *pData = NULL;
960   Pixmap          pixmap;
961
962   /* If the specified property is None the requestor is an obsolete client.
963    * We support these by using the specified target atom as the reply property.
964    */
965   rprop = event->property;
966   if( rprop == None )
967       rprop = event->target;
968
969   TRACE("Request for %s in selection %s\n",
970         XGetAtomName(g_display, event->target), XGetAtomName(g_display, event->selection));
971
972   /* Handle MULTIPLE requests -  rprop contains a list of (target, property) atom pairs */
973   if(event->target == xaMultiple)
974   {
975       /* MULTIPLE selection request - will call us back recursively */
976       rprop = EVENT_SelectionRequest_MULTIPLE( event );
977       goto END;
978   }
979
980   /* Lookup the requested target property in the cache */
981   if ( !LookupCacheItem(event->selection, event->target, &pCacheEntry) )
982   {
983       TRACE("Item not available in cache!\n");
984       goto END;
985   }
986
987   /* Update the X property */
988   TRACE("\tUpdating property %s...\n", XGetAtomName(g_display, rprop));
989
990   /* If we have a request for a pixmap, return a duplicate */
991
992   if(event->target == XA_PIXMAP || event->target == XA_BITMAP)
993   {
994     Pixmap *pPixmap = (Pixmap *)pCacheEntry->pData;
995     pixmap = DuplicatePixmap( *pPixmap );
996     pData = &pixmap;
997   }
998   else
999     pData = pCacheEntry->pData;
1000
1001   XChangeProperty(g_display, request, rprop,
1002                     pCacheEntry->type, pCacheEntry->nFormat, PropModeReplace,
1003                     (unsigned char *)pData, pCacheEntry->nElements);
1004
1005 END:
1006   if( rprop == None)
1007       TRACE("\tRequest ignored\n");
1008
1009   /* reply to sender
1010    * SelectionNotify should be sent only at the end of a MULTIPLE request
1011    */
1012   if ( !bIsMultiple )
1013   {
1014     result.type = SelectionNotify;
1015     result.display = g_display;
1016     result.requestor = request;
1017     result.selection = event->selection;
1018     result.property = rprop;
1019     result.target = event->target;
1020     result.time = event->time;
1021     TRACE("Sending SelectionNotify event...\n");
1022     XSendEvent(g_display,event->requestor,False,NoEventMask,(XEvent*)&result);
1023   }
1024 }
1025
1026
1027 /***********************************************************************
1028  *           EVENT_SelectionClear
1029  *   We receive this event when another client grabs the X selection.
1030  *   If we lost both PRIMARY and CLIPBOARD we must terminate.
1031  */
1032 void EVENT_SelectionClear( XSelectionClearEvent *event )
1033 {
1034   Atom xaClipboard = XInternAtom(g_display, _CLIPBOARD, False);
1035
1036   TRACE("()\n");
1037
1038   /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
1039    * dictate that *all* selections should be cleared on loss of a selection,
1040    * we must give up all the selections we own.
1041    */
1042   if ( g_clearAllSelections || (event->selection == xaClipboard) )
1043   {
1044       TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
1045
1046       /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
1047       if ( (event->selection == xaClipboard)
1048            && (g_selectionAcquired & S_PRIMARY) )
1049       {
1050           XSetSelectionOwner(g_display, XA_PRIMARY, None, CurrentTime);
1051       }
1052
1053       /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD  */
1054       if ( (event->selection == XA_PRIMARY)
1055            && (g_selectionAcquired & S_CLIPBOARD) )
1056       {
1057           XSetSelectionOwner(g_display, xaClipboard, None, CurrentTime);
1058       }
1059
1060       g_selectionAcquired = S_NOSELECTION;   /* Clear the selection masks */
1061   }
1062   else if (event->selection == XA_PRIMARY)
1063   {
1064       TRACE("Lost PRIMARY selection...\n");
1065       g_selectionAcquired &= ~S_PRIMARY;     /* Clear the PRIMARY flag */
1066   }
1067
1068   /* Once we lose all our selections we have nothing more to do */
1069   if (g_selectionAcquired == S_NOSELECTION)
1070       TerminateServer(1);
1071 }
1072
1073 /***********************************************************************
1074  *           EVENT_PropertyNotify
1075  *   We use this to release resources like Pixmaps when a selection
1076  *   client no longer needs them.
1077  */
1078 void EVENT_PropertyNotify( XPropertyEvent *event )
1079 {
1080   TRACE("()\n");
1081
1082   /* Check if we have any resources to free */
1083
1084   switch(event->state)
1085   {
1086     case PropertyDelete:
1087     {
1088       TRACE("\tPropertyDelete for atom %s on window %ld\n",
1089                     XGetAtomName(event->display, event->atom), (long)event->window);
1090
1091       /* FreeResources( event->atom ); */
1092       break;
1093     }
1094
1095     case PropertyNewValue:
1096     {
1097       TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1098                     XGetAtomName(event->display, event->atom), (long)event->window);
1099       break;
1100     }
1101
1102     default:
1103       break;
1104   }
1105 }
1106
1107 /***********************************************************************
1108  *           DuplicatePixmap
1109  */
1110 Pixmap DuplicatePixmap(Pixmap pixmap)
1111 {
1112     Pixmap newPixmap;
1113     XImage *xi;
1114     Window root;
1115     int x,y;               /* Unused */
1116     unsigned border_width; /* Unused */
1117     unsigned int depth, width, height;
1118
1119     TRACE("\t() Pixmap=%ld\n", (long)pixmap);
1120
1121     /* Get the Pixmap dimensions and bit depth */
1122     if ( 0 == XGetGeometry(g_display, pixmap, &root, &x, &y, &width, &height,
1123                              &border_width, &depth) )
1124         return 0;
1125
1126     TRACE("\tPixmap properties: width=%d, height=%d, depth=%d\n",
1127           width, height, depth);
1128
1129     newPixmap = XCreatePixmap(g_display, g_win, width, height, depth);
1130
1131     xi = XGetImage(g_display, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1132
1133     XPutImage(g_display, newPixmap, g_gc, xi, 0, 0, 0, 0, width, height);
1134
1135     XDestroyImage(xi);
1136
1137     TRACE("\t() New Pixmap=%ld\n", (long)newPixmap);
1138     return newPixmap;
1139 }
1140
1141 /***********************************************************************
1142  *           getGC
1143  * Get a GC to use for drawing
1144  */
1145 void getGC(Window win, GC *gc)
1146 {
1147         unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
1148         XGCValues values;
1149         unsigned int line_width = 6;
1150         int line_style = LineOnOffDash;
1151         int cap_style = CapRound;
1152         int join_style = JoinRound;
1153         int dash_offset = 0;
1154         static char dash_list[] = {12, 24};
1155         int list_length = 2;
1156
1157         /* Create default Graphics Context */
1158         *gc = XCreateGC(g_display, win, valuemask, &values);
1159
1160         /* specify black foreground since default window background is
1161          * white and default foreground is undefined. */
1162         XSetForeground(g_display, *gc, BlackPixel(g_display,screen_num));
1163
1164         /* set line attributes */
1165         XSetLineAttributes(g_display, *gc, line_width, line_style,
1166                         cap_style, join_style);
1167
1168         /* set dashes */
1169         XSetDashes(g_display, *gc, dash_offset, dash_list, list_length);
1170 }
1171
1172
1173 /***********************************************************************
1174  *           TextOut
1175  */
1176 void TextOut(Window win, GC gc, char *pStr)
1177 {
1178         int y_offset, x_offset;
1179
1180         y_offset = 10;
1181         x_offset = 2;
1182
1183         /* output text, centered on each line */
1184         XDrawString(g_display, win, gc, x_offset, y_offset, pStr,
1185                         strlen(pStr));
1186 }