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