- Add clipboard support for copying/pasting bitmaps or Pixmaps between Wine
[wine] / windows / x11drv / clipboard.c
1 /*
2  * X11 clipboard windows driver
3  *
4  * Copyright 1994 Martin Ayotte
5  *           1996 Alex Korobka
6  *           1999 Noel Borthwick
7  *
8  * NOTES:
9  *    This file contains the X specific implementation for the windows
10  *    Clipboard API.
11  *
12  *    Wine's internal clipboard is exposed to external apps via the X
13  *    selection mechanism.
14  *    Currently the driver asserts ownership via two selection atoms:
15  *    1. PRIMARY(XA_PRIMARY)
16  *    2. CLIPBOARD
17  *
18  *    In our implementation, the CLIPBOARD selection takes precedence over PRIMARY.
19  *    i.e. if a CLIPBOARD selection is available, it is used instead of PRIMARY.
20  *    When Wine taks ownership of the clipboard, it takes ownership of BOTH selections.
21  *    While giving up selection ownership, if the CLIPBOARD selection is lost,
22  *    it will lose both PRIMARY and CLIPBOARD and empty the clipboard.
23  *    However if only PRIMARY is lost, it will continue to hold the CLIPBOARD selection
24  *    (leaving the clipboard cache content unaffected).
25  *
26  *      Every format exposed via a windows clipboard format is also exposed through
27  *    a corresponding X selection target. A selection target atom is synthesized
28  *    whenever a new Windows clipboard format is registered via RegisterClipboardFormat,
29  *    or when a built in format is used for the first time.
30  *    Windows native format are exposed by prefixing the format name with "<WCF>"
31  *    This allows us to uniquely identify windows native formats exposed by other
32  *    running WINE apps.
33  *
34  *      In order to allow external applications to query WINE for supported formats,
35  *    we respond to the "TARGETS" selection target. (See EVENT_SelectionRequest
36  *    for implementation) We use the same mechanism to query external clients for
37  *    availability of a particular format, by cacheing the list of available targets
38  *    by using the clipboard cache's "delayed render" mechanism. If a selection client
39  *    does not support the "TARGETS" selection target, we actually attempt to retrieve
40  *    the format requested as a fallback mechanism.
41  *
42  *      Certain Windows native formats are automatically converted to X native formats
43  *    and vice versa. If a native format is available in the selection, it takes
44  *    precedence, in order to avoid unnecessary conversions.
45  *
46  */
47
48 #include "config.h"
49
50 #ifndef X_DISPLAY_MISSING
51
52 #include <X11/Xatom.h>
53 #include <string.h>
54 #include "ts_xlib.h"
55
56 #include "wine/winuser16.h"
57 #include "clipboard.h"
58 #include "debugtools.h"
59 #include "message.h"
60 #include "win.h"
61 #include "windef.h"
62 #include "x11drv.h"
63 #include "bitmap.h"
64 #include "commctrl.h"
65 #include "heap.h"
66
67 DEFAULT_DEBUG_CHANNEL(clipboard)
68
69 /* Selection masks */
70
71 #define S_NOSELECTION    0
72 #define S_PRIMARY        1
73 #define S_CLIPBOARD      2
74
75 /* X selection context info */
76
77 static char _CLIPBOARD[] = "CLIPBOARD";        /* CLIPBOARD atom name */
78 static char FMT_PREFIX[] = "<WCF>";            /* Prefix for windows specific formats */
79 static int    selectionAcquired = 0;           /* Contains the current selection masks */
80 static Window selectionWindow = None;          /* The top level X window which owns the selection */
81 static Window selectionPrevWindow = None;      /* The last X window that owned the selection */
82 static Window PrimarySelectionOwner = None;    /* The window which owns the primary selection */
83 static Window ClipboardSelectionOwner = None;  /* The window which owns the clipboard selection */
84 static unsigned long cSelectionTargets = 0;    /* Number of target formats reported by TARGETS selection */
85 static Atom selectionCacheSrc = XA_PRIMARY;    /* The selection source from which the clipboard cache was filled */
86
87 /*
88  * Dynamic pointer arrays to manage destruction of Pixmap resources
89  */
90 static HDPA PropDPA = NULL;
91 static HDPA PixmapDPA = NULL;
92
93
94
95 /**************************************************************************
96  *              X11DRV_CLIPBOARD_MapPropertyToFormat
97  *
98  *  Map an X selection property type atom name to a windows clipboard format ID
99  */
100 UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName)
101 {
102     /*
103      * If the property name starts with FMT_PREFIX strip this off and
104      * get the ID for a custom Windows registered format with this name.
105      * We can also understand STRING, PIXMAP and BITMAP.
106      */
107     if ( NULL == itemFmtName )
108         return 0;
109     else if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) )
110         return RegisterClipboardFormatA(itemFmtName + strlen(FMT_PREFIX));
111     else if ( 0 == strcmp(itemFmtName, "STRING") )
112         return CF_OEMTEXT;
113     else if ( 0 == strcmp(itemFmtName, "PIXMAP")
114                 ||  0 == strcmp(itemFmtName, "BITMAP") )
115     {
116         /*
117          * Return CF_DIB as first preference, if WINE is the selection owner
118          * and if CF_DIB exists in the cache.
119          * If wine dowsn't own the selection we always return CF_DIB
120          */
121         if ( !X11DRV_CLIPBOARD_IsSelectionowner() )
122             return CF_DIB;
123         else if ( CLIPBOARD_IsPresent(CF_DIB) )
124             return CF_DIB;
125         else
126             return CF_BITMAP;
127     }
128
129     WARN("\tNo mapping to Windows clipboard format for property %s\n", itemFmtName);
130     return 0;
131 }
132
133 /**************************************************************************
134  *              X11DRV_CLIPBOARD_MapFormatToProperty
135  *
136  *  Map a windows clipboard format ID to an X selection property atom
137  */
138 Atom X11DRV_CLIPBOARD_MapFormatToProperty(UINT wFormat)
139 {
140     Atom prop = None;
141     
142     switch (wFormat)
143     {
144         case CF_OEMTEXT:
145         case CF_TEXT:
146             prop = XA_STRING;
147             break;
148
149         case CF_DIB:
150         case CF_BITMAP:
151         {
152             /*
153              * Request a PIXMAP, only if WINE is NOT the selection owner,
154              * AND the requested format is not in the cache.
155              */
156             if ( !X11DRV_CLIPBOARD_IsSelectionowner() && !CLIPBOARD_IsPresent(wFormat) )
157             {
158               prop = XA_PIXMAP;
159               break;
160             }
161             /* Fall thru to the default case in order to use the native format */
162         }
163         
164         default:
165         {
166             /*
167              * If an X atom is registered for this format, return that
168              * Otherwise register a new atom.
169              */
170             char str[256];
171             char *fmtName = CLIPBOARD_GetFormatName(wFormat);
172             strcpy(str, FMT_PREFIX);
173
174             if (fmtName)
175             {
176                 strncat(str, fmtName, sizeof(str) - strlen(FMT_PREFIX));
177                 prop = TSXInternAtom(display, str, False);
178             }
179             break;
180         }
181     }
182
183     if (prop == None)
184         TRACE("\tNo mapping to X property for Windows clipboard format %d(%s)\n",
185               wFormat, CLIPBOARD_GetFormatName(wFormat));
186     
187     return prop;
188 }
189
190 /**************************************************************************
191  *              X11DRV_CLIPBOARD_IsNativeProperty
192  *
193  *  Checks if a property is a native property type
194  */
195 BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop)
196 {
197     char *itemFmtName = TSXGetAtomName(display, prop);
198     BOOL bRet = FALSE;
199     
200     if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) )
201         bRet = TRUE;
202     
203     TSXFree(itemFmtName);
204     return bRet;
205 }
206
207
208 /**************************************************************************
209  *              X11DRV_CLIPBOARD_CacheDataFormats
210  *
211  * Caches the list of data formats available from the current selection.
212  * This queries the selection owner for the TARGETS property and saves all
213  * reported property types.
214  */
215 int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName )
216 {
217     HWND           hWnd = NULL;
218     HWND           hWndClipWindow = GetOpenClipboardWindow();
219     WND*           wnd = NULL;
220     XEvent         xe;
221     Atom           aTargets;
222     Atom           atype=AnyPropertyType;
223     int            aformat;
224     unsigned long  remain;
225     Atom*          targetList=NULL;
226     Window         w;
227     Window         ownerSelection = NULL;
228         
229     /*
230      * Empty the clipboard cache 
231      */
232     CLIPBOARD_EmptyCache(TRUE);
233
234     cSelectionTargets = 0;
235     selectionCacheSrc = SelectionName;
236     
237     hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
238
239     ownerSelection = TSXGetSelectionOwner(display, SelectionName);
240     if ( !hWnd || (ownerSelection == None) )
241         return cSelectionTargets;
242
243     /*
244      * Query the selection owner for the TARGETS property
245      */
246     wnd = WIN_FindWndPtr(hWnd);
247     w = X11DRV_WND_FindXWindow(wnd);
248     WIN_ReleaseWndPtr(wnd);
249     wnd = NULL;
250
251     aTargets = TSXInternAtom(display, "TARGETS", False);
252
253     TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
254           TSXGetAtomName(display, selectionCacheSrc), (unsigned)ownerSelection );
255           
256     EnterCriticalSection( &X11DRV_CritSection );
257     XConvertSelection(display, selectionCacheSrc, aTargets,
258                     TSXInternAtom(display, "SELECTION_DATA", False),
259                     w, CurrentTime);
260
261     /*
262      * Wait until SelectionNotify is received
263      */
264     while( TRUE )
265     {
266        if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
267            if( xe.xselection.selection == selectionCacheSrc )
268                break;
269     }
270     LeaveCriticalSection( &X11DRV_CritSection );
271
272     /* Verify that the selection returned a valid TARGETS property */
273     if ( (xe.xselection.target != aTargets)
274           || (xe.xselection.property == None) )
275     {
276         TRACE("\tCould not retrieve TARGETS\n");
277         return cSelectionTargets;
278     }
279
280     /* Read the TARGETS property contents */
281     if(TSXGetWindowProperty(display, xe.xselection.requestor, xe.xselection.property,
282                             0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
283                             &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
284         TRACE("\tCouldn't read TARGETS property\n");
285     else
286     {
287        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
288              TSXGetAtomName(display,atype),aformat,cSelectionTargets, remain);
289        /*
290         * The TARGETS property should have returned us a list of atoms
291         * corresponding to each selection target format supported.
292         */
293        if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
294        {
295           int i;
296           LPWINE_CLIPFORMAT lpFormat;
297           
298           /* Cache these formats in the clipboard cache */
299
300           for (i = 0; i < cSelectionTargets; i++)
301           {
302               char *itemFmtName = TSXGetAtomName(display, targetList[i]);
303               UINT wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
304
305               /*
306                * If the clipboard format maps to a Windows format, simply store
307                * the atom identifier and record its availablity status
308                * in the clipboard cache.
309                */
310               if (wFormat)
311               {
312                   lpFormat = CLIPBOARD_LookupFormat( wFormat );
313                   
314                   /* Don't replace if the property already cached is a native format,
315                    * or if a PIXMAP is being replaced by a BITMAP.
316                    */
317                   if (lpFormat->wDataPresent &&
318                         ( X11DRV_CLIPBOARD_IsNativeProperty(lpFormat->drvData)
319                           || (lpFormat->drvData == XA_PIXMAP && targetList[i] == XA_BITMAP) )
320                      )
321                   {
322                       TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s (Skipped)\n",
323                             i, itemFmtName, wFormat, lpFormat->Name);
324                   }
325                   else
326                   {
327                       lpFormat->wDataPresent = 1;
328                       lpFormat->drvData = targetList[i];
329                       TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s\n",
330                             i, itemFmtName, wFormat, lpFormat->Name);
331                   }
332               }
333               
334               TSXFree(itemFmtName);
335           }
336        }
337
338        /* Free the list of targets */
339        TSXFree(targetList);
340     }
341     
342     return cSelectionTargets;
343 }
344
345 /**************************************************************************
346  *              X11DRV_CLIPBOARD_ReadSelection
347  *  Reads the contents of the X selection property into the WINE clipboard cache
348  *  converting the selection into a format compatible with the windows clipboard
349  *  if possible.
350  *  This method is invoked only to read the contents of a the selection owned
351  *  by an external application. i.e. when we do not own the X selection.
352  */
353 static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, Atom reqType)
354 {
355     Atom              atype=AnyPropertyType;
356     int               aformat;
357     unsigned long     nitems,remain,itemSize;
358     long              lRequestLength;
359     unsigned char*    val=NULL;
360     LPWINE_CLIPFORMAT lpFormat;
361     BOOL              bRet = FALSE;
362     HWND              hWndClipWindow = GetOpenClipboardWindow();
363
364     
365     if(prop == None)
366         return bRet;
367
368     TRACE("Reading X selection...\n");
369
370     TRACE("\tretrieving property %s from window %ld into %s\n",
371           TSXGetAtomName(display,reqType), (long)w, TSXGetAtomName(display,prop) );
372
373     /*
374      * First request a zero length in order to figure out the request size.
375      */
376     if(TSXGetWindowProperty(display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
377                             &atype, &aformat, &nitems, &itemSize, &val) != Success)
378     {
379         WARN("\tcouldn't get property size\n");
380         return bRet;
381     }
382
383     /* Free zero length return data if any */
384     if ( val )
385     {
386        TSXFree(val);
387        val = NULL;
388     }
389     
390     TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
391     lRequestLength = (itemSize * aformat/8)/4  + 1;
392     
393     /*
394      * Retrieve the actual property in the required X format.
395      */
396     if(TSXGetWindowProperty(display,w,prop,0,lRequestLength,False,AnyPropertyType/*reqType*/,
397                             &atype, &aformat, &nitems, &remain, &val) != Success)
398     {
399         WARN("\tcouldn't read property\n");
400         return bRet;
401     }
402
403     TRACE("\tType %s,Format %d,nitems %ld,remain %ld,value %s\n",
404           atype ? TSXGetAtomName(display,atype) : NULL, aformat,nitems,remain,val);
405     
406     if (remain)
407     {
408         WARN("\tCouldn't read entire property- selection may be too large! Remain=%ld\n", remain);
409         goto END;
410     }
411     
412     /*
413      * Translate the X property into the appropriate Windows clipboard
414      * format, if possible.
415      */
416     if ( (reqType == XA_STRING)
417          && (atype == XA_STRING) && (aformat == 8) ) /* treat Unix text as CF_OEMTEXT */
418     {
419       HANDLE16   hText = 0;
420       int          i,inlcount = 0;
421       char*      lpstr;
422  
423       TRACE("\tselection is '%s'\n",val);
424  
425       for(i=0; i <= nitems; i++)
426           if( val[i] == '\n' ) inlcount++;
427  
428       if( nitems )
429       {
430         hText=GlobalAlloc16(GMEM_MOVEABLE, nitems + inlcount + 1);
431         if( (lpstr = (char*)GlobalLock16(hText)) )
432         {
433           for(i=0,inlcount=0; i <= nitems; i++)
434           {
435              if( val[i] == '\n' ) lpstr[inlcount++]='\r';
436              lpstr[inlcount++]=val[i];
437           }
438           GlobalUnlock16(hText);
439         }
440         else
441             hText = 0;
442       }
443       
444       if( hText )
445       {
446           /* delete previous CF_TEXT and CF_OEMTEXT data */
447           lpFormat = CLIPBOARD_LookupFormat(CF_TEXT);
448           if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) 
449               CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
450           
451           lpFormat = CLIPBOARD_LookupFormat(CF_OEMTEXT);
452           if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32)  
453               CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
454  
455           /* Update the CF_OEMTEXT record */
456           lpFormat->wDataPresent = 1;
457           lpFormat->hData32 = 0;
458           lpFormat->hData16 = hText;
459  
460           bRet = TRUE;
461       }
462     }
463     else if ( reqType == XA_PIXMAP || reqType == XA_BITMAP ) /* treat PIXMAP as CF_DIB or CF_BITMAP */
464     {
465       /* Get the first pixmap handle passed to us */
466       Pixmap *pPixmap = (Pixmap *)val;
467       HANDLE hTargetImage = NULL;  /* Handle to store the converted bitmap or DIB */
468       
469       if (aformat != 32 || nitems < 1 || atype != XA_PIXMAP
470           || (wFormat != CF_BITMAP && wFormat != CF_DIB))
471       {
472           WARN("\tUnimplemented format conversion request\n");
473           goto END;
474       }
475           
476       if ( wFormat == CF_BITMAP )
477       {
478         /* For CF_BITMAP requests we must return an HBITMAP */
479         hTargetImage = X11DRV_BITMAP_CreateBitmapFromPixmap(*pPixmap, TRUE);
480       }
481       else if (wFormat == CF_DIB)
482       {
483         HWND hwnd = GetOpenClipboardWindow();
484         HDC hdc = GetDC(hwnd);
485         
486         /* For CF_DIB requests we must return an HGLOBAL storing a packed DIB */
487         hTargetImage = X11DRV_DIB_CreateDIBFromPixmap(*pPixmap, hdc, TRUE);
488         
489         ReleaseDC(hdc, hwnd);
490       }
491
492       if (!hTargetImage)
493       {
494           WARN("PIXMAP conversion failed!\n" );
495           goto END;
496       }
497       
498       /* Delete previous clipboard data */
499       lpFormat = CLIPBOARD_LookupFormat(wFormat);
500       if (lpFormat->wDataPresent && (lpFormat->hData16 || lpFormat->hData32))
501           CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
502       
503       /* Update the clipboard record */
504       lpFormat->wDataPresent = 1;
505       lpFormat->hData32 = hTargetImage;
506       lpFormat->hData16 = 0;
507
508       bRet = TRUE;
509     }
510  
511     /* For native properties simply copy the X data without conversion */
512     else if (X11DRV_CLIPBOARD_IsNativeProperty(reqType)) /* <WCF>* */
513     {
514       HANDLE hClipData = 0;
515       void*  lpClipData;
516       int cBytes = nitems * aformat/8;
517
518       if( cBytes )
519       {
520         /* Turn on the DDESHARE flag to enable shared 32 bit memory */
521         hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cBytes );
522         if( (lpClipData = GlobalLock(hClipData)) )
523         {
524             memcpy(lpClipData, val, cBytes);
525             GlobalUnlock(hClipData);
526         }
527         else
528             hClipData = 0;
529       }
530       
531       if( hClipData )
532       {
533           /* delete previous clipboard record if any */
534           lpFormat = CLIPBOARD_LookupFormat(wFormat);
535           if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) 
536               CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
537           
538           /* Update the clipboard record */
539           lpFormat->wDataPresent = 1;
540           lpFormat->hData32 = hClipData;
541           lpFormat->hData16 = 0;
542
543           bRet = TRUE;
544       }
545     }
546     else
547     {
548         WARN("\tUnimplemented format conversion request\n");
549         goto END;
550     }
551
552 END:
553     /* Delete the property on the window now that we are done
554      * This will send a PropertyNotify event to the selection owner. */
555     TSXDeleteProperty(display,w,prop);
556     
557     /* Free the retrieved property data */
558     if (val)
559        TSXFree(val);
560     
561     return bRet;
562 }
563
564 /**************************************************************************
565  *              X11DRV_CLIPBOARD_ReleaseSelection
566  *
567  * Release an XA_PRIMARY or XA_CLIPBOARD selection that we own, in response
568  * to a SelectionClear event.
569  * This can occur in response to another client grabbing the X selection.
570  * If the XA_CLIPBOARD selection is lost we relinquish XA_PRIMARY as well.
571  */
572 void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
573 {
574     Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
575     
576     /* w is the window that lost selection,
577      * 
578      * selectionPrevWindow is nonzero if CheckSelection() was called. 
579      */
580
581     TRACE("\tevent->window = %08x (sw = %08x, spw=%08x)\n", 
582           (unsigned)w, (unsigned)selectionWindow, (unsigned)selectionPrevWindow );
583
584     if( selectionAcquired )
585     {
586         if( w == selectionWindow || selectionPrevWindow == None)
587         {
588             /* alright, we really lost it */
589
590             if ( selType == xaClipboard )  /* completely give up the selection */
591             {
592               TRACE("Lost CLIPBOARD selection\n");
593                 
594               /* We are completely giving up the selection.
595                * Make sure we can open the windows clipboard first. */
596               
597               if ( !OpenClipboard(hwnd) )
598               {
599                   /*
600                    * We can't empty the clipboard if we cant open it so abandon.
601                    * Wine will think that it still owns the selection but this is
602                    * safer than losing the selection without properly emptying
603                    * the clipboard. Perhaps we should forcibly re-assert ownership
604                    * of the CLIPBOARD selection in this case...
605                    */
606                   ERR("\tClipboard is busy. Could not give up selection!\n");
607                   return;
608               }
609
610               selectionPrevWindow = selectionWindow;
611               selectionWindow = None;
612               PrimarySelectionOwner = ClipboardSelectionOwner = 0;
613               
614               /* Voluntarily give up the PRIMARY selection if we still own it */
615               if ( selectionAcquired & S_PRIMARY )
616               {
617                   XEvent xe;
618                   TRACE("Releasing XA_PRIMARY selection\n");
619                   
620                   TSXSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
621
622                   /* Wait until SelectionClear is processed */
623                   if( selectionPrevWindow )
624                       while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
625                                                       SelectionClear, &xe ) );
626               }
627
628               /* Empty the windows clipboard.
629                * We should pretend that we still own the selection BEFORE calling
630                * EmptyClipboard() since otherwise this has the side effect of
631                * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection
632                * to be re-acquired by us!
633                */
634               selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
635               EmptyClipboard();
636               selectionAcquired = S_NOSELECTION;
637               
638               CloseClipboard();
639
640               /* Give up ownership of the windows clipboard */
641               CLIPBOARD_ReleaseOwner();
642             }
643             else if ( selType == XA_PRIMARY ) /* Give up only PRIMARY selection */
644             {
645                 TRACE("Lost PRIMARY selection\n");
646                 PrimarySelectionOwner = 0;
647                 selectionAcquired &= ~S_PRIMARY;  /* clear S_PRIMARY mask */
648             }
649
650             cSelectionTargets = 0;
651         }
652         /* but we'll keep existing data for internal use */
653         else if( w == selectionPrevWindow )
654         {
655             Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
656             
657             w = TSXGetSelectionOwner(display, XA_PRIMARY);
658             if( w == None )
659                 TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
660
661             w = TSXGetSelectionOwner(display, xaClipboard);
662             if( w == None )
663                 TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime);
664         }
665     }
666
667     selectionPrevWindow = None;
668 }
669
670 /**************************************************************************
671  *              X11DRV_CLIPBOARD_Empty
672  *  Voluntarily release all currently owned X selections
673  */
674 void X11DRV_CLIPBOARD_Release()
675 {
676     if( selectionAcquired )
677     {
678         XEvent xe;
679         Window savePrevWindow = selectionWindow;
680         Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
681         BOOL bHasPrimarySelection = selectionAcquired & S_PRIMARY;
682
683         selectionAcquired   = S_NOSELECTION;
684         selectionPrevWindow = selectionWindow;
685         selectionWindow     = None;
686       
687         TRACE("\tgiving up selection (spw = %08x)\n", 
688              (unsigned)selectionPrevWindow);
689       
690         EnterCriticalSection(&X11DRV_CritSection);
691
692         TRACE("Releasing CLIPBOARD selection\n");
693         XSetSelectionOwner(display, xaClipboard, None, CurrentTime);
694         if( selectionPrevWindow )
695             while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
696                                             SelectionClear, &xe ) );
697
698         if ( bHasPrimarySelection )
699         {
700             TRACE("Releasing XA_PRIMARY selection\n");
701             selectionPrevWindow = savePrevWindow; /* May be cleared in X11DRV_CLIPBOARD_ReleaseSelection */
702             XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
703     
704             if( selectionPrevWindow )
705                 while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
706                                                 SelectionClear, &xe ) );
707         }
708         
709         LeaveCriticalSection(&X11DRV_CritSection);
710     }
711
712     /* Get rid of any Pixmap resources we may still have */
713     if (PropDPA)
714         DPA_Destroy( PropDPA );
715     if (PixmapDPA)
716     {
717       int i;
718       Pixmap pixmap;
719       for( i = 0; ; i++ )
720       {
721         if ( (pixmap = ((Pixmap)DPA_GetPtr(PixmapDPA, i))) )
722           XFreePixmap(display, pixmap);
723         else
724           break;
725       }
726       DPA_Destroy( PixmapDPA );
727     }
728     PixmapDPA = PropDPA = NULL;
729 }
730
731 /**************************************************************************
732  *              X11DRV_CLIPBOARD_Acquire()
733  */
734 void X11DRV_CLIPBOARD_Acquire()
735 {
736     Window       owner;
737     HWND         hWndClipWindow = GetOpenClipboardWindow();
738
739     /*
740      * Acquire X selection if we don't already own it.
741      * Note that we only acquire the selection if it hasn't been already
742      * acquired by us, and ignore the fact that another X window may be
743      * asserting ownership. The reason for this is we need *any* top level
744      * X window to hold selection ownership. The actual clipboard data requests
745      * are made via GetClipboardData from EVENT_SelectionRequest and this
746      * ensures that the real HWND owner services the request.
747      * If the owning X window gets destroyed the selection ownership is
748      * re-cycled to another top level X window in X11DRV_CLIPBOARD_ResetOwner.
749      *
750      */
751
752     if ( !(selectionAcquired == (S_PRIMARY | S_CLIPBOARD)) )
753     {
754         Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
755         WND *tmpWnd = WIN_FindWndPtr( hWndClipWindow ? hWndClipWindow : AnyPopup() );
756         owner = X11DRV_WND_FindXWindow(tmpWnd );
757         WIN_ReleaseWndPtr(tmpWnd);
758
759         /* Grab PRIMARY selection if not owned */
760         if ( !(selectionAcquired & S_PRIMARY) )
761             TSXSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
762         
763         /* Grab CLIPBOARD selection if not owned */
764         if ( !(selectionAcquired & S_CLIPBOARD) )
765             TSXSetSelectionOwner(display, xaClipboard, owner, CurrentTime);
766
767         if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner )
768             selectionAcquired |= S_PRIMARY;
769
770         if( TSXGetSelectionOwner(display,xaClipboard) == owner)
771             selectionAcquired |= S_CLIPBOARD;
772
773         if (selectionAcquired)
774         {
775             /* Create dynamic pointer arrays to manage Pixmap resources we may expose */
776             if (!PropDPA)
777                 PropDPA = DPA_CreateEx( 2, SystemHeap );
778             if (!PixmapDPA)
779                 PixmapDPA = DPA_CreateEx( 2, SystemHeap );
780             
781             selectionWindow = owner;
782             TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
783         }
784     }
785 }
786
787 /**************************************************************************
788  *              X11DRV_CLIPBOARD_IsFormatAvailable
789  *
790  * Checks if the specified format is available in the current selection
791  * Only invoked when WINE is not the selection owner
792  */
793 BOOL X11DRV_CLIPBOARD_IsFormatAvailable(UINT wFormat)
794 {
795     Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
796     Window ownerPrimary = TSXGetSelectionOwner(display,XA_PRIMARY);
797     Window ownerClipboard = TSXGetSelectionOwner(display,xaClipboard);
798
799     /*
800      * If the selection has not been previously cached, or the selection has changed,
801      * try and cache the list of available selection targets from the current selection.
802      */
803     if ( !cSelectionTargets || (PrimarySelectionOwner != ownerPrimary)
804                          || (ClipboardSelectionOwner != ownerClipboard) )
805     {
806         /*
807          * First try cacheing the CLIPBOARD selection.
808          * If unavailable try PRIMARY.
809          */
810         if ( X11DRV_CLIPBOARD_CacheDataFormats(xaClipboard) == 0 )
811         {
812             X11DRV_CLIPBOARD_CacheDataFormats(XA_PRIMARY);
813         }
814
815         ClipboardSelectionOwner = ownerClipboard;
816         PrimarySelectionOwner = ownerPrimary;
817     }
818
819     /* Exit if there is no selection */
820     if ( !ownerClipboard && !ownerPrimary )
821         return FALSE;
822
823     if ( wFormat == CF_TEXT )
824         wFormat = CF_OEMTEXT;
825     
826     /* Check if the format is available in the clipboard cache */
827     if ( CLIPBOARD_IsPresent(wFormat) )
828         return TRUE;
829
830     /*
831      * Many X client apps (such as XTerminal) don't support being queried
832      * for the "TARGETS" target atom. To handle such clients we must actually
833      * try to convert the selection to the requested type.
834      */
835     if ( !cSelectionTargets )
836         return X11DRV_CLIPBOARD_GetData( wFormat );
837         
838     return FALSE;
839 }
840
841 /**************************************************************************
842  *              X11DRV_CLIPBOARD_RegisterFormat
843  *
844  * Registers a custom X clipboard format
845  * Returns: TRUE - success,  FALSE - failure
846  */
847 BOOL X11DRV_CLIPBOARD_RegisterFormat( LPCSTR FormatName )
848 {
849     Atom prop = None;
850     char str[256];
851     
852     /*
853      * If an X atom is registered for this format, return that
854      * Otherwise register a new atom.
855      */
856     if (FormatName)
857     {
858         /* Add a WINE specific prefix to the format */
859         strcpy(str, FMT_PREFIX);
860         strncat(str, FormatName, sizeof(str) - strlen(FMT_PREFIX));
861         prop = TSXInternAtom(display, str, False);
862     }
863     
864     return (prop) ? TRUE : FALSE;
865 }
866
867 /**************************************************************************
868  *              X11DRV_CLIPBOARD_IsSelectionowner
869  *
870  * Returns: TRUE - We(WINE) own the selection, FALSE - Selection not owned by us
871  */
872 BOOL X11DRV_CLIPBOARD_IsSelectionowner()
873 {
874     return selectionAcquired;
875 }
876
877 /**************************************************************************
878  *              X11DRV_CLIPBOARD_SetData
879  *
880  * We don't need to do anything special here since the clipboard code
881  * maintains the cache. 
882  *
883  */
884 void X11DRV_CLIPBOARD_SetData(UINT wFormat)
885 {
886     /* Make sure we have acquired the X selection */
887     X11DRV_CLIPBOARD_Acquire();
888 }
889
890 /**************************************************************************
891  *              X11DRV_CLIPBOARD_GetData
892  *
893  * This method is invoked only when we DO NOT own the X selection
894  *
895  * NOTE: Clipboard driver doesn't get requests for CF_TEXT data, only
896  * for CF_OEMTEXT.
897  * We always get the data from the selection client each time,
898  * since we have no way of determining if the data in our cache is stale.
899  */
900 BOOL X11DRV_CLIPBOARD_GetData(UINT wFormat)
901 {
902     BOOL bRet = selectionAcquired;
903     HWND hWndClipWindow = GetOpenClipboardWindow();
904     HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
905     WND* wnd = NULL;
906     LPWINE_CLIPFORMAT lpFormat;
907
908     if( !selectionAcquired && (wnd = WIN_FindWndPtr(hWnd)) )
909     {
910         XEvent xe;
911         Atom propRequest;
912         Window w = X11DRV_WND_FindXWindow(wnd);
913         WIN_ReleaseWndPtr(wnd);
914         wnd = NULL;
915
916         /* Map the format ID requested to an X selection property.
917          * If the format is in the cache, use the atom associated
918          * with it.
919          */
920         
921         lpFormat = CLIPBOARD_LookupFormat( wFormat );
922         if (lpFormat && lpFormat->wDataPresent && lpFormat->drvData)
923             propRequest = (Atom)lpFormat->drvData;
924         else
925             propRequest = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat);
926
927         if (propRequest)
928         {
929             TRACE("Requesting %s selection from %s...\n",
930                   TSXGetAtomName(display, propRequest),
931                   TSXGetAtomName(display, selectionCacheSrc) );
932     
933             EnterCriticalSection( &X11DRV_CritSection );
934             XConvertSelection(display, selectionCacheSrc, propRequest,
935                             TSXInternAtom(display, "SELECTION_DATA", False),
936                             w, CurrentTime);
937         
938             /* wait until SelectionNotify is received */
939     
940             while( TRUE )
941             {
942                if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
943                    if( xe.xselection.selection == selectionCacheSrc )
944                        break;
945             }
946             LeaveCriticalSection( &X11DRV_CritSection );
947         
948             /*
949              *  Read the contents of the X selection property into WINE's
950              *  clipboard cache converting the selection to be compatible if possible.
951              */
952             bRet = X11DRV_CLIPBOARD_ReadSelection( wFormat,
953                                                    xe.xselection.requestor,
954                                                    xe.xselection.property,
955                                                    xe.xselection.target);
956         }
957         else
958             bRet = FALSE;
959
960         TRACE("\tpresent %s = %i\n", CLIPBOARD_GetFormatName(wFormat), bRet );
961     }
962     
963     return bRet;
964 }
965
966 /**************************************************************************
967  *              X11DRV_CLIPBOARD_ResetOwner
968  *
969  * Called from DestroyWindow() to prevent X selection from being lost when
970  * a top level window is destroyed, by switching ownership to another top
971  * level window.
972  * Any top level window can own the selection. See X11DRV_CLIPBOARD_Acquire
973  * for a more detailed description of this.
974  */
975 void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
976 {
977     HWND hWndClipOwner = 0;
978     Window XWnd = X11DRV_WND_GetXWindow(pWnd);
979     Atom xaClipboard;
980     BOOL bLostSelection = FALSE;
981
982     /* There is nothing to do if we don't own the selection,
983      * or if the X window which currently owns the selecion is different
984      * from the one passed in.
985      */
986     if ( !selectionAcquired || XWnd != selectionWindow
987          || selectionWindow == None )
988        return;
989
990     if ( (bFooBar && XWnd) || (!bFooBar && !XWnd) )
991        return;
992
993     hWndClipOwner = GetClipboardOwner();
994     xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
995     
996     TRACE("clipboard owner = %04x, selection window = %08x\n",
997           hWndClipOwner, (unsigned)selectionWindow);
998
999 #if(0)
1000     /* Check if all formats are already in the clipboard cache */
1001     if( !CLIPBOARD_IsCacheRendered() )
1002     {
1003         SendMessage16(hWndClipOwner,WM_RENDERALLFORMATS,0,0L);
1004
1005         /* check if all formats were rendered */
1006         if ( !CLIPBOARD_IsCacheRendered() )
1007         {
1008             ERR("\tCould not render all formats\n");
1009             CLIPBOARD_ReleaseOwner();
1010         }
1011     }
1012 #endif
1013     
1014     /* now try to salvage current selection from being destroyed by X */
1015
1016     TRACE("\tchecking %08x\n", (unsigned) XWnd);
1017
1018     selectionPrevWindow = selectionWindow;
1019     selectionWindow = None;
1020
1021     if( pWnd->next ) 
1022         selectionWindow = X11DRV_WND_GetXWindow(pWnd->next);
1023     else if( pWnd->parent )
1024          if( pWnd->parent->child != pWnd ) 
1025              selectionWindow = X11DRV_WND_GetXWindow(pWnd->parent->child);
1026
1027     if( selectionWindow != None )
1028     {
1029         /* We must pretend that we don't own the selection while making the switch
1030          * since a SelectionClear event will be sent to the last owner.
1031          * If there is no owner X11DRV_CLIPBOARD_ReleaseSelection will do nothing.
1032          */
1033         int saveSelectionState = selectionAcquired;
1034         selectionAcquired = False;
1035
1036         TRACE("\tswitching selection from %08x to %08x\n", 
1037                     (unsigned)selectionPrevWindow, (unsigned)selectionWindow);
1038     
1039         /* Assume ownership for the PRIMARY and CLIPBOARD selection */
1040         if ( saveSelectionState & S_PRIMARY )
1041             TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
1042         
1043         TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime);
1044         
1045         /* Lose the selection if something went wrong */
1046         if ( ( (saveSelectionState & S_PRIMARY) &&
1047                (TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow) )
1048              || (TSXGetSelectionOwner(display, xaClipboard) != selectionWindow) )
1049         {
1050             bLostSelection = TRUE;
1051             goto END;
1052         }
1053         else
1054         {
1055             /* Update selection state */
1056             selectionAcquired = saveSelectionState;
1057             if (saveSelectionState & S_PRIMARY)
1058                PrimarySelectionOwner = selectionWindow;
1059             
1060             ClipboardSelectionOwner = selectionWindow;
1061         }
1062     }
1063     else
1064     {
1065         bLostSelection = TRUE;
1066         goto END;
1067     }
1068
1069 END:
1070     if (bLostSelection)
1071     {
1072        /* Empty the windows clipboard.
1073         * We should pretend that we still own the selection BEFORE calling
1074         * EmptyClipboard() since otherwise this has the side effect of
1075         * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection
1076         * to be re-acquired by us!
1077         */
1078
1079        TRACE("\tLost the selection! Emptying the clipboard...\n");
1080     
1081        OpenClipboard(NULL);
1082        selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
1083        EmptyClipboard();
1084        selectionAcquired = S_NOSELECTION;
1085        
1086        CloseClipboard();
1087  
1088        /* Give up ownership of the windows clipboard */
1089        CLIPBOARD_ReleaseOwner();
1090        ClipboardSelectionOwner = PrimarySelectionOwner = 0;
1091        selectionWindow = 0;
1092     }
1093 }
1094
1095 /**************************************************************************
1096  *              X11DRV_CLIPBOARD_RegisterPixmapResource
1097  * Registers a Pixmap resource which is to be associated with a property Atom.
1098  * When the property is destroyed we also destroy the Pixmap through the
1099  * PropertyNotify event.
1100  */
1101 BOOL X11DRV_CLIPBOARD_RegisterPixmapResource( Atom property, Pixmap pixmap )
1102 {
1103   if ( -1 == DPA_InsertPtr( PropDPA, 0, (void*)property ) )
1104     return FALSE;
1105     
1106   if ( -1 == DPA_InsertPtr( PixmapDPA, 0, (void*)pixmap ) )
1107     return FALSE;
1108
1109   return TRUE;
1110 }
1111
1112 /**************************************************************************
1113  *              X11DRV_CLIPBOARD_FreeResources
1114  *
1115  * Called from EVENT_PropertyNotify() to give us a chance to destroy
1116  * any resources associated with this property.
1117  */
1118 void X11DRV_CLIPBOARD_FreeResources( Atom property )
1119 {
1120     /* Do a simple linear search to see if we have a Pixmap resource
1121      * associated with this property and release it.
1122      */
1123     int i;
1124     Pixmap pixmap;
1125     Atom cacheProp = NULL;
1126     for( i = 0; ; i++ )
1127     {
1128         if ( !(cacheProp = ((Atom)DPA_GetPtr(PropDPA, i))) )
1129             break;
1130         
1131         if ( cacheProp == property )
1132         {
1133             /* Lookup the associated Pixmap and free it */
1134             pixmap = (Pixmap)DPA_GetPtr(PixmapDPA, i);
1135   
1136             TRACE("Releasing pixmap %ld for Property %s\n",
1137                   (long)pixmap, TSXGetAtomName(display, cacheProp));
1138             
1139             XFreePixmap(display, pixmap);
1140
1141             /* Free the entries from the table */
1142             DPA_DeletePtr(PropDPA, i);
1143             DPA_DeletePtr(PixmapDPA, i);
1144         }
1145     }
1146 }
1147
1148 #endif /* !defined(X_DISPLAY_MISSING) */