Don't hang around for a minute if we can't exec the clipboard server.
[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  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * NOTES:
23  *    This file contains the X specific implementation for the windows
24  *    Clipboard API.
25  *
26  *    Wine's internal clipboard is exposed to external apps via the X
27  *    selection mechanism.
28  *    Currently the driver asserts ownership via two selection atoms:
29  *    1. PRIMARY(XA_PRIMARY)
30  *    2. CLIPBOARD
31  *
32  *    In our implementation, the CLIPBOARD selection takes precedence over PRIMARY,
33  *    i.e. if a CLIPBOARD selection is available, it is used instead of PRIMARY.
34  *    When Wine takes ownership of the clipboard, it takes ownership of BOTH selections.
35  *    While giving up selection ownership, if the CLIPBOARD selection is lost,
36  *    it will lose both PRIMARY and CLIPBOARD and empty the clipboard.
37  *    However if only PRIMARY is lost, it will continue to hold the CLIPBOARD selection
38  *    (leaving the clipboard cache content unaffected).
39  *
40  *      Every format exposed via a windows clipboard format is also exposed through
41  *    a corresponding X selection target. A selection target atom is synthesized
42  *    whenever a new Windows clipboard format is registered via RegisterClipboardFormat,
43  *    or when a built-in format is used for the first time.
44  *    Windows native format are exposed by prefixing the format name with "<WCF>"
45  *    This allows us to uniquely identify windows native formats exposed by other
46  *    running WINE apps.
47  *
48  *      In order to allow external applications to query WINE for supported formats,
49  *    we respond to the "TARGETS" selection target. (See EVENT_SelectionRequest
50  *    for implementation) We use the same mechanism to query external clients for
51  *    availability of a particular format, by caching the list of available targets
52  *    by using the clipboard cache's "delayed render" mechanism. If a selection client
53  *    does not support the "TARGETS" selection target, we actually attempt to retrieve
54  *    the format requested as a fallback mechanism.
55  *
56  *      Certain Windows native formats are automatically converted to X native formats
57  *    and vice versa. If a native format is available in the selection, it takes
58  *    precedence, in order to avoid unnecessary conversions.
59  *
60  */
61
62 #include "config.h"
63
64 #include <string.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <unistd.h>
68 #include <fcntl.h>
69
70 #include "ts_xlib.h"
71 #include "winreg.h"
72 #include "clipboard.h"
73 #include "win.h"
74 #include "x11drv.h"
75 #include "wine/debug.h"
76
77 WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
78
79 /* Selection masks */
80
81 #define S_NOSELECTION    0
82 #define S_PRIMARY        1
83 #define S_CLIPBOARD      2
84
85 /* X selection context info */
86
87 static char _CLIPBOARD[] = "CLIPBOARD";        /* CLIPBOARD atom name */
88 static char FMT_PREFIX[] = "<WCF>";            /* Prefix for windows specific formats */
89 static int selectionAcquired = 0;              /* Contains the current selection masks */
90 static Window selectionWindow = None;          /* The top level X window which owns the selection */
91 static Window selectionPrevWindow = None;      /* The last X window that owned the selection */
92 static Window PrimarySelectionOwner = None;    /* The window which owns the primary selection */
93 static Window ClipboardSelectionOwner = None;  /* The window which owns the clipboard selection */
94 static unsigned long cSelectionTargets = 0;    /* Number of target formats reported by TARGETS selection */
95 static Atom selectionCacheSrc = XA_PRIMARY;    /* The selection source from which the clipboard cache was filled */
96 static HANDLE selectionClearEvent = 0;/* Synchronization object used to block until server is started */
97
98 typedef struct tagPROPERTY
99 {
100     struct tagPROPERTY *next;
101     Atom                atom;
102     Pixmap              pixmap;
103 } PROPERTY;
104
105 static PROPERTY *prop_head;
106
107
108 /**************************************************************************
109  *              X11DRV_CLIPBOARD_MapPropertyToFormat
110  *
111  *  Map an X selection property type atom name to a windows clipboard format ID
112  */
113 UINT X11DRV_CLIPBOARD_MapPropertyToFormat(char *itemFmtName)
114 {
115     /*
116      * If the property name starts with FMT_PREFIX strip this off and
117      * get the ID for a custom Windows registered format with this name.
118      * We can also understand STRING, PIXMAP and BITMAP.
119      */
120     if ( NULL == itemFmtName )
121         return 0;
122     else if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) )
123         return RegisterClipboardFormatA(itemFmtName + strlen(FMT_PREFIX));
124     else if ( 0 == strcmp(itemFmtName, "STRING") )
125         return CF_UNICODETEXT;
126     else if ( 0 == strcmp(itemFmtName, "PIXMAP")
127                 ||  0 == strcmp(itemFmtName, "BITMAP") )
128     {
129         /*
130          * Return CF_DIB as first preference, if WINE is the selection owner
131          * and if CF_DIB exists in the cache.
132          * If wine dowsn't own the selection we always return CF_DIB
133          */
134         if ( !X11DRV_IsSelectionOwner() )
135             return CF_DIB;
136         else if ( CLIPBOARD_IsPresent(CF_DIB) )
137             return CF_DIB;
138         else
139             return CF_BITMAP;
140     }
141
142     WARN("\tNo mapping to Windows clipboard format for property %s\n", itemFmtName);
143     return 0;
144 }
145
146 /**************************************************************************
147  *              X11DRV_CLIPBOARD_MapFormatToProperty
148  *
149  *  Map a windows clipboard format ID to an X selection property atom
150  */
151 Atom X11DRV_CLIPBOARD_MapFormatToProperty(UINT wFormat)
152 {
153     Atom prop = None;
154     
155     switch (wFormat)
156     {
157         /* We support only CF_UNICODETEXT, other formats are synthesized */
158         case CF_OEMTEXT:
159         case CF_TEXT:
160             return None;
161
162         case CF_UNICODETEXT:
163             prop = XA_STRING;
164             break;
165
166         case CF_DIB:
167         case CF_BITMAP:
168         {
169             /*
170              * Request a PIXMAP, only if WINE is NOT the selection owner,
171              * AND the requested format is not in the cache.
172              */
173             if ( !X11DRV_IsSelectionOwner() && !CLIPBOARD_IsPresent(wFormat) )
174             {
175               prop = XA_PIXMAP;
176               break;
177             }
178             /* Fall through to the default case in order to use the native format */
179         }
180         
181         default:
182         {
183             /*
184              * If an X atom is registered for this format, return that
185              * Otherwise register a new atom.
186              */
187             char str[256];
188             char *fmtName = CLIPBOARD_GetFormatName(wFormat);
189             strcpy(str, FMT_PREFIX);
190
191             if (fmtName)
192             {
193                 strncat(str, fmtName, sizeof(str) - strlen(FMT_PREFIX));
194                 prop = TSXInternAtom(thread_display(), str, False);
195             }
196             break;
197         }
198     }
199
200     if (prop == None)
201         TRACE("\tNo mapping to X property for Windows clipboard format %d(%s)\n",
202               wFormat, CLIPBOARD_GetFormatName(wFormat));
203     
204     return prop;
205 }
206
207 /**************************************************************************
208  *              X11DRV_CLIPBOARD_IsNativeProperty
209  *
210  *  Checks if a property is a native Wine property type
211  */
212 BOOL X11DRV_CLIPBOARD_IsNativeProperty(Atom prop)
213 {
214     char *itemFmtName = TSXGetAtomName(thread_display(), prop);
215     BOOL bRet = FALSE;
216     
217     if ( 0 == strncmp(itemFmtName, FMT_PREFIX, strlen(FMT_PREFIX)) )
218         bRet = TRUE;
219     
220     TSXFree(itemFmtName);
221     return bRet;
222 }
223
224
225 /**************************************************************************
226  *              X11DRV_CLIPBOARD_LaunchServer
227  * Launches the clipboard server. This is called from X11DRV_CLIPBOARD_ResetOwner
228  * when the selection can no longer be recyled to another top level window.
229  * In order to make the selection persist after Wine shuts down a server
230  * process is launched which services subsequent selection requests.
231  */
232 BOOL X11DRV_CLIPBOARD_LaunchServer()
233 {
234     int iWndsLocks;
235     char clearSelection[8] = "0";
236     int persistent_selection = 1;
237     HKEY hkey;
238     int fd[2], err;
239
240     /* If persistant selection has been disabled in the .winerc Clipboard section,
241      * don't launch the server
242      */
243     if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Clipboard", &hkey))
244     {
245         char buffer[20];
246         DWORD type, count = sizeof(buffer);
247         if(!RegQueryValueExA(hkey, "PersistentSelection", 0, &type, buffer, &count))
248             persistent_selection = atoi(buffer);
249
250         /* Get the clear selection preference */
251         count = sizeof(clearSelection);
252         RegQueryValueExA(hkey, "ClearAllSelections", 0, &type, clearSelection, &count);
253         RegCloseKey(hkey);
254     }
255     if ( !persistent_selection )
256         return FALSE;
257
258     /*  Start up persistant WINE X clipboard server process which will
259      *  take ownership of the X selection and continue to service selection
260      *  requests from other apps.
261      */
262
263     if(pipe(fd) == -1) return FALSE;
264     fcntl(fd[1], F_SETFD, 1); /* set close on exec */
265
266     selectionWindow = selectionPrevWindow;
267     if ( !fork() )
268     {
269         /* NOTE: This code only executes in the context of the child process
270          * Do note make any Wine specific calls here.
271          */
272         int dbgClasses = 0;
273         char selMask[8], dbgClassMask[8];
274
275         close(fd[0]);
276         sprintf(selMask, "%d", selectionAcquired);
277
278         /* Build the debug class mask to pass to the server, by inheriting
279          * the settings for the clipboard debug channel.
280          */
281         dbgClasses |= FIXME_ON(clipboard) ? 1 : 0;
282         dbgClasses |= ERR_ON(clipboard) ? 2 : 0;
283         dbgClasses |= WARN_ON(clipboard) ? 4 : 0;
284         dbgClasses |= TRACE_ON(clipboard) ? 8 : 0;
285         sprintf(dbgClassMask, "%d", dbgClasses);
286
287         /* Exec the clipboard server passing it the selection and debug class masks */
288         execl( BINDIR "/wineclipsrv", "wineclipsrv",
289                selMask, dbgClassMask, clearSelection, NULL );
290         execlp( "wineclipsrv", "wineclipsrv", selMask, dbgClassMask, clearSelection, NULL );
291         execl( "./windows/x11drv/wineclipsrv", "wineclipsrv",
292                selMask, dbgClassMask, clearSelection, NULL );
293
294         /* Exec Failed! */
295         perror("Could not start Wine clipboard server");
296         write(fd[1], &err, sizeof(err));
297         _exit( 1 ); /* Exit the child process */
298     }
299     close(fd[1]);
300
301     if(read(fd[0], &err, sizeof(err)) > 0) { /* exec failed */
302         close(fd[0]);
303         return FALSE;
304     }
305     close(fd[0]);
306
307     /* Wait until the clipboard server acquires the selection.
308      * We must release the windows lock to enable Wine to process
309      * selection messages in response to the servers requests.
310      */
311     
312     iWndsLocks = WIN_SuspendWndsLock();
313
314     /* We must wait until the server finishes acquiring the selection,
315      * before proceeding, otherwise the window which owns the selection
316      * will be destroyed prematurely!
317      * Create a non-signalled, auto-reset event which will be set by
318      * X11DRV_CLIPBOARD_ReleaseSelection, and wait until this gets
319      * signalled before proceeding.
320      */
321
322     if ( !(selectionClearEvent = CreateEventA(NULL, FALSE, FALSE, NULL)) )
323         ERR("Could not create wait object. Clipboard server won't start!\n");
324     else
325     {
326         /* Wait until we lose the selection, timing out after a minute */
327         DWORD start_time, timeout, elapsed, ret;
328
329         TRACE("Waiting for clipboard server to acquire selection\n");
330
331         timeout = 60000;
332         start_time = GetTickCount();
333         elapsed=0;
334         do
335         {
336             ret = MsgWaitForMultipleObjects( 1, &selectionClearEvent, FALSE, timeout - elapsed, QS_ALLINPUT );
337             if (ret != WAIT_OBJECT_0+1)
338                 break;
339             elapsed = GetTickCount() - start_time;
340             if (elapsed > timeout)
341                 break;
342         }
343         while (1);
344         if ( ret != WAIT_OBJECT_0 )
345             TRACE("Server could not acquire selection, or a timeout occurred!\n");
346         else
347             TRACE("Server successfully acquired selection\n");
348
349         /* Release the event */
350         CloseHandle(selectionClearEvent);
351         selectionClearEvent = 0;
352     }
353
354     WIN_RestoreWndsLock(iWndsLocks);
355     
356     return TRUE;
357 }
358
359
360 /**************************************************************************
361  *              X11DRV_CLIPBOARD_CacheDataFormats
362  *
363  * Caches the list of data formats available from the current selection.
364  * This queries the selection owner for the TARGETS property and saves all
365  * reported property types.
366  */
367 int X11DRV_CLIPBOARD_CacheDataFormats( Atom SelectionName )
368 {
369     Display *display = thread_display();
370     HWND           hWnd = 0;
371     HWND           hWndClipWindow = GetOpenClipboardWindow();
372     XEvent         xe;
373     Atom           aTargets;
374     Atom           atype=AnyPropertyType;
375     int            aformat;
376     unsigned long  remain;
377     Atom*          targetList=NULL;
378     Window         w;
379     Window         ownerSelection = 0;
380         
381     TRACE("enter\n");
382     /*
383      * Empty the clipboard cache 
384      */
385     CLIPBOARD_EmptyCache(TRUE);
386
387     cSelectionTargets = 0;
388     selectionCacheSrc = SelectionName;
389     
390     hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
391
392     ownerSelection = TSXGetSelectionOwner(display, SelectionName);
393     if ( !hWnd || (ownerSelection == None) )
394         return cSelectionTargets;
395
396     /*
397      * Query the selection owner for the TARGETS property
398      */
399     w = X11DRV_get_whole_window( GetAncestor(hWnd,GA_ROOT) );
400
401     aTargets = TSXInternAtom(display, "TARGETS", False);
402
403     TRACE("Requesting TARGETS selection for '%s' (owner=%08x)...\n",
404           TSXGetAtomName(display, selectionCacheSrc), (unsigned)ownerSelection );
405     wine_tsx11_lock();
406     XConvertSelection(display, selectionCacheSrc, aTargets,
407                     TSXInternAtom(display, "SELECTION_DATA", False),
408                     w, CurrentTime);
409
410     /*
411      * Wait until SelectionNotify is received
412      */
413     while( TRUE )
414     {
415        if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
416            if( xe.xselection.selection == selectionCacheSrc )
417                break;
418     }
419     wine_tsx11_unlock();
420
421     /* Verify that the selection returned a valid TARGETS property */
422     if ( (xe.xselection.target != aTargets)
423           || (xe.xselection.property == None) )
424     {
425         TRACE("\tExit, could not retrieve TARGETS\n");
426         return cSelectionTargets;
427     }
428
429     /* Read the TARGETS property contents */
430     if(TSXGetWindowProperty(display, xe.xselection.requestor, xe.xselection.property,
431                             0, 0x3FFF, True, AnyPropertyType/*XA_ATOM*/, &atype, &aformat,
432                             &cSelectionTargets, &remain, (unsigned char**)&targetList) != Success)
433         TRACE("\tCouldn't read TARGETS property\n");
434     else
435     {
436        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
437              TSXGetAtomName(display,atype),aformat,cSelectionTargets, remain);
438        /*
439         * The TARGETS property should have returned us a list of atoms
440         * corresponding to each selection target format supported.
441         */
442        if( (atype == XA_ATOM || atype == aTargets) && aformat == 32 )
443        {
444           int i;
445           LPWINE_CLIPFORMAT lpFormat;
446           
447           /* Cache these formats in the clipboard cache */
448
449           for (i = 0; i < cSelectionTargets; i++)
450           {
451               char *itemFmtName = TSXGetAtomName(display, targetList[i]);
452               UINT wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
453
454               /*
455                * If the clipboard format maps to a Windows format, simply store
456                * the atom identifier and record its availablity status
457                * in the clipboard cache.
458                */
459               if (wFormat)
460               {
461                   lpFormat = CLIPBOARD_LookupFormat( wFormat );
462                   
463                   /* Don't replace if the property already cached is a native format,
464                    * or if a PIXMAP is being replaced by a BITMAP.
465                    */
466                   if (lpFormat->wDataPresent &&
467                         ( X11DRV_CLIPBOARD_IsNativeProperty(lpFormat->drvData)
468                           || (lpFormat->drvData == XA_PIXMAP && targetList[i] == XA_BITMAP) )
469                      )
470                   {
471                       TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s (Skipped)\n",
472                             i, itemFmtName, wFormat, lpFormat->Name);
473                   }
474                   else
475                   {
476                       lpFormat->wDataPresent = 1;
477                       lpFormat->drvData = targetList[i];
478                       TRACE("\tAtom# %d: '%s' --> FormatID(%d) %s\n",
479                             i, itemFmtName, wFormat, lpFormat->Name);
480                   }
481               }
482               
483               TSXFree(itemFmtName);
484           }
485        }
486
487        /* Free the list of targets */
488        TSXFree(targetList);
489     }
490
491     return cSelectionTargets;
492 }
493
494 /**************************************************************************
495  *              X11DRV_CLIPBOARD_ReadSelection
496  *  Reads the contents of the X selection property into the WINE clipboard cache
497  *  converting the selection into a format compatible with the windows clipboard
498  *  if possible.
499  *  This method is invoked only to read the contents of a the selection owned
500  *  by an external application. i.e. when we do not own the X selection.
501  */
502 static BOOL X11DRV_CLIPBOARD_ReadSelection(UINT wFormat, Window w, Atom prop, Atom reqType)
503 {
504     Display *display = thread_display();
505     Atom              atype=AnyPropertyType;
506     int               aformat;
507     unsigned long     total,nitems,remain,itemSize,val_cnt;
508     long              lRequestLength,bwc;
509     unsigned char*    val;
510     unsigned char*    buffer;
511     LPWINE_CLIPFORMAT lpFormat;
512     BOOL              bRet = FALSE;
513     HWND              hWndClipWindow = GetOpenClipboardWindow();
514
515     
516     if(prop == None)
517         return bRet;
518
519     TRACE("Reading X selection...\n");
520
521     TRACE("\tretrieving property %s from window %ld into %s\n",
522           TSXGetAtomName(display,reqType), (long)w, TSXGetAtomName(display,prop) );
523
524     /*
525      * First request a zero length in order to figure out the request size.
526      */
527     if(TSXGetWindowProperty(display,w,prop,0,0,False, AnyPropertyType/*reqType*/,
528                             &atype, &aformat, &nitems, &itemSize, &val) != Success)
529     {
530         WARN("\tcouldn't get property size\n");
531         return bRet;
532     }
533
534     /* Free zero length return data if any */
535     if ( val )
536     {
537        TSXFree(val);
538        val = NULL;
539     }
540     
541     TRACE("\tretrieving %ld bytes...\n", itemSize * aformat/8);
542     lRequestLength = (itemSize * aformat/8)/4  + 1;
543
544    bwc = aformat/8; 
545    /* we want to read the property, but not it too large of chunks or 
546       we could hang the cause problems. Lets go for 4k blocks */
547
548     if(TSXGetWindowProperty(display,w,prop,0,4096,False,
549                             AnyPropertyType/*reqType*/,
550                             &atype, &aformat, &nitems, &remain, &buffer) 
551         != Success)
552     {
553         WARN("\tcouldn't read property\n");
554         return bRet;
555     }
556    val = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
557                           nitems*bwc);
558    memcpy(val,buffer,nitems*bwc);
559    TSXFree(buffer);
560
561    for (total = nitems*bwc,val_cnt=0; remain;)
562    {
563        val_cnt +=nitems*bwc;
564        TSXGetWindowProperty(display, w, prop,
565                           (total / 4), 4096, False,
566                           AnyPropertyType, &atype,
567                           &aformat, &nitems, &remain,
568                           &buffer);
569
570        total += nitems*bwc;
571        HeapReAlloc(GetProcessHeap(),0,val, total);
572        memcpy(&val[val_cnt], buffer, nitems*(aformat/8));
573        TSXFree(buffer);
574    }
575    nitems = total;
576
577     /*
578      * Translate the X property into the appropriate Windows clipboard
579      * format, if possible.
580      */
581     if ( (reqType == XA_STRING)
582          && (atype == XA_STRING) && (aformat == 8) )
583     /* convert Unix text to CF_UNICODETEXT */
584     {
585       int          i,inlcount = 0;
586       char*      lpstr;
587  
588       for(i=0; i <= nitems; i++)
589           if( val[i] == '\n' ) inlcount++;
590  
591       if( (lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nitems + inlcount + 1)) )
592       {
593           static UINT text_cp = (UINT)-1;
594           UINT count;
595           HANDLE hUnicodeText;
596
597           for(i=0,inlcount=0; i <= nitems; i++)
598           {
599              if( val[i] == '\n' ) lpstr[inlcount++]='\r';
600              lpstr[inlcount++]=val[i];
601           }
602
603           if(text_cp == (UINT)-1)
604           {
605               HKEY hkey;
606               /* default value */
607               text_cp = CP_ACP;
608               if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", &hkey))
609               {
610                   char buf[20];
611                   DWORD type, count = sizeof(buf);
612                   if(!RegQueryValueExA(hkey, "TextCP", 0, &type, buf, &count))
613                       text_cp = atoi(buf);
614                   RegCloseKey(hkey);
615               }
616           }
617
618           count = MultiByteToWideChar(text_cp, 0, lpstr, -1, NULL, 0);
619           hUnicodeText = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, count * sizeof(WCHAR));
620           if(hUnicodeText)
621           {
622               WCHAR *textW = GlobalLock(hUnicodeText);
623               MultiByteToWideChar(text_cp, 0, lpstr, -1, textW, count);
624               GlobalUnlock(hUnicodeText);
625               if (!SetClipboardData(CF_UNICODETEXT, hUnicodeText))
626               {
627             ERR("Not SET! Need to free our own block\n");
628                     GlobalFree(hUnicodeText);
629           }
630               bRet = TRUE;
631           }
632           HeapFree(GetProcessHeap(), 0, lpstr);
633       }
634     }
635     else if ( reqType == XA_PIXMAP || reqType == XA_BITMAP ) /* treat PIXMAP as CF_DIB or CF_BITMAP */
636     {
637       /* Get the first pixmap handle passed to us */
638       Pixmap *pPixmap = (Pixmap *)val;
639       HANDLE hTargetImage = 0;  /* Handle to store the converted bitmap or DIB */
640       
641       if (aformat != 32 || nitems < 1 || atype != XA_PIXMAP
642           || (wFormat != CF_BITMAP && wFormat != CF_DIB))
643       {
644           WARN("\tUnimplemented format conversion request\n");
645           goto END;
646       }
647           
648       if ( wFormat == CF_BITMAP )
649       {
650         /* For CF_BITMAP requests we must return an HBITMAP */
651         hTargetImage = X11DRV_BITMAP_CreateBitmapFromPixmap(*pPixmap, TRUE);
652       }
653       else if (wFormat == CF_DIB)
654       {
655         HWND hwnd = GetOpenClipboardWindow();
656         HDC hdc = GetDC(hwnd);
657         
658         /* For CF_DIB requests we must return an HGLOBAL storing a packed DIB */
659         hTargetImage = X11DRV_DIB_CreateDIBFromPixmap(*pPixmap, hdc, TRUE);
660         
661         ReleaseDC(hwnd, hdc);
662       }
663
664       if (!hTargetImage)
665       {
666           WARN("PIXMAP conversion failed!\n" );
667           goto END;
668       }
669
670       /* Delete previous clipboard data */
671       lpFormat = CLIPBOARD_LookupFormat(wFormat);
672       if (lpFormat->wDataPresent && (lpFormat->hData16 || lpFormat->hData32))
673           CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
674       
675       /* Update the clipboard record */
676       lpFormat->wDataPresent = 1;
677       lpFormat->hData32 = hTargetImage;
678       lpFormat->hData16 = 0;
679
680       bRet = TRUE;
681     }
682  
683     /* For native properties simply copy the X data without conversion */
684     else if (X11DRV_CLIPBOARD_IsNativeProperty(reqType)) /* <WCF>* */
685     {
686       HANDLE hClipData = 0;
687       void*  lpClipData;
688       int cBytes = nitems * aformat/8;
689
690       if( cBytes )
691       {
692         /* Turn on the DDESHARE flag to enable shared 32 bit memory */
693         hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cBytes );
694         if( (lpClipData = GlobalLock(hClipData)) )
695         {
696             memcpy(lpClipData, val, cBytes);
697             GlobalUnlock(hClipData);
698         }
699         else
700             hClipData = 0;
701       }
702       
703       if( hClipData )
704       {
705           /* delete previous clipboard record if any */
706           lpFormat = CLIPBOARD_LookupFormat(wFormat);
707           if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) 
708               CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
709           
710           /* Update the clipboard record */
711           lpFormat->wDataPresent = 1;
712           lpFormat->hData32 = hClipData;
713           lpFormat->hData16 = 0;
714
715           bRet = TRUE;
716       }
717     }
718     else
719     {
720         WARN("\tUnimplemented format conversion request\n");
721         goto END;
722     }
723
724 END:
725     /* Delete the property on the window now that we are done
726      * This will send a PropertyNotify event to the selection owner. */
727     TSXDeleteProperty(display,w,prop);
728     
729     /* Free the retrieved property data */
730     HeapFree(GetProcessHeap(),0,val);
731     return bRet;
732 }
733
734 /**************************************************************************
735  *              X11DRV_CLIPBOARD_ReleaseSelection
736  *
737  * Release an XA_PRIMARY or XA_CLIPBOARD selection that we own, in response
738  * to a SelectionClear event.
739  * This can occur in response to another client grabbing the X selection.
740  * If the XA_CLIPBOARD selection is lost, we relinquish XA_PRIMARY as well.
741  */
742 void X11DRV_CLIPBOARD_ReleaseSelection(Atom selType, Window w, HWND hwnd)
743 {
744     Display *display = thread_display();
745     Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
746     int clearAllSelections = 0;
747     HKEY hkey;
748
749     if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Clipboard", &hkey))
750     {
751         char buffer[20];
752         DWORD type, count = sizeof(buffer);
753         if(!RegQueryValueExA(hkey, "ClearAllSelections", 0, &type, buffer, &count))
754             clearAllSelections = atoi(buffer);
755         RegCloseKey(hkey);
756     }
757     
758     /* w is the window that lost the selection
759      * selectionPrevWindow is nonzero if CheckSelection() was called. 
760      */
761
762     TRACE("\tevent->window = %08x (sw = %08x, spw=%08x)\n", 
763           (unsigned)w, (unsigned)selectionWindow, (unsigned)selectionPrevWindow );
764
765     if( selectionAcquired )
766     {
767         if( w == selectionWindow || selectionPrevWindow == None)
768         {
769             /* If we're losing the CLIPBOARD selection, or if the preferences in .winerc
770              * dictate that *all* selections should be cleared on loss of a selection,
771              * we must give up all the selections we own.
772              */
773             if ( clearAllSelections || (selType == xaClipboard) )
774             {
775               /* completely give up the selection */
776               TRACE("Lost CLIPBOARD (+PRIMARY) selection\n");
777
778               /* We are completely giving up the selection.
779                * Make sure we can open the windows clipboard first. */
780               
781               if ( !OpenClipboard(hwnd) )
782               {
783                   /*
784                    * We can't empty the clipboard if we cant open it so abandon.
785                    * Wine will think that it still owns the selection but this is
786                    * safer than losing the selection without properly emptying
787                    * the clipboard. Perhaps we should forcibly re-assert ownership
788                    * of the CLIPBOARD selection in this case...
789                    */
790                   ERR("\tClipboard is busy. Could not give up selection!\n");
791                   return;
792               }
793
794               /* We really lost CLIPBOARD but want to voluntarily lose PRIMARY */
795               if ( (selType == xaClipboard)
796                    && (selectionAcquired & S_PRIMARY) )
797               {
798                   XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
799               }
800               
801               /* We really lost PRIMARY but want to voluntarily lose CLIPBOARD  */
802               if ( (selType == XA_PRIMARY)
803                    && (selectionAcquired & S_CLIPBOARD) )
804               {
805                   XSetSelectionOwner(display, xaClipboard, None, CurrentTime);
806               }
807               
808               selectionWindow = None;
809               PrimarySelectionOwner = ClipboardSelectionOwner = 0;
810               
811               /* Empty the windows clipboard.
812                * We should pretend that we still own the selection BEFORE calling
813                * EmptyClipboard() since otherwise this has the side effect of
814                * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection
815                * to be re-acquired by us!
816                */
817               selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
818               EmptyClipboard();
819               CloseClipboard();
820
821               /* Give up ownership of the windows clipboard */
822               CLIPBOARD_ReleaseOwner();
823
824               /* Reset the selection flags now that we are done */
825               selectionAcquired = S_NOSELECTION;
826             }
827             else if ( selType == XA_PRIMARY ) /* Give up only PRIMARY selection */
828             {
829                 TRACE("Lost PRIMARY selection\n");
830                 PrimarySelectionOwner = 0;
831                 selectionAcquired &= ~S_PRIMARY;  /* clear S_PRIMARY mask */
832             }
833
834             cSelectionTargets = 0;
835         }
836         /* but we'll keep existing data for internal use */
837         else if( w == selectionPrevWindow )
838         {
839             Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
840             
841             w = TSXGetSelectionOwner(display, XA_PRIMARY);
842             if( w == None )
843                 TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
844
845             w = TSXGetSelectionOwner(display, xaClipboard);
846             if( w == None )
847                 TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime);
848         }
849     }
850
851     /* Signal to a selectionClearEvent listener if the selection is completely lost */
852     if (selectionClearEvent && !selectionAcquired)
853     {
854         TRACE("Lost all selections, signalling to selectionClearEvent listener\n");
855         SetEvent(selectionClearEvent);
856     }
857     
858     selectionPrevWindow = None;
859 }
860
861 /**************************************************************************
862  *              ReleaseClipboard (X11DRV.@)
863  *  Voluntarily release all currently owned X selections
864  */
865 void X11DRV_ReleaseClipboard(void)
866 {
867     Display *display = thread_display();
868     if( selectionAcquired )
869     {
870         XEvent xe;
871         Window savePrevWindow = selectionWindow;
872         Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
873         BOOL bHasPrimarySelection = selectionAcquired & S_PRIMARY;
874
875         selectionAcquired   = S_NOSELECTION;
876         selectionPrevWindow = selectionWindow;
877         selectionWindow     = None;
878       
879         TRACE("\tgiving up selection (spw = %08x)\n", 
880              (unsigned)selectionPrevWindow);
881       
882         wine_tsx11_lock();
883
884         TRACE("Releasing CLIPBOARD selection\n");
885         XSetSelectionOwner(display, xaClipboard, None, CurrentTime);
886         if( selectionPrevWindow )
887             while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
888                                             SelectionClear, &xe ) );
889
890         if ( bHasPrimarySelection )
891         {
892             TRACE("Releasing XA_PRIMARY selection\n");
893             selectionPrevWindow = savePrevWindow; /* May be cleared in X11DRV_CLIPBOARD_ReleaseSelection */
894             XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
895     
896             if( selectionPrevWindow )
897                 while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
898                                                 SelectionClear, &xe ) );
899         }
900         wine_tsx11_unlock();
901     }
902
903     /* Get rid of any Pixmap resources we may still have */
904     while (prop_head)
905     {
906         PROPERTY *prop = prop_head;
907         prop_head = prop->next;
908         XFreePixmap( gdi_display, prop->pixmap );
909         HeapFree( GetProcessHeap(), 0, prop );
910     }
911 }
912
913 /**************************************************************************
914  *              AcquireClipboard (X11DRV.@)
915  */
916 void X11DRV_AcquireClipboard(void)
917 {
918     Display *display = thread_display();
919     Window       owner;
920     HWND         hWndClipWindow = GetOpenClipboardWindow();
921
922     /*
923      * Acquire X selection if we don't already own it.
924      * Note that we only acquire the selection if it hasn't been already
925      * acquired by us, and ignore the fact that another X window may be
926      * asserting ownership. The reason for this is we need *any* top level
927      * X window to hold selection ownership. The actual clipboard data requests
928      * are made via GetClipboardData from EVENT_SelectionRequest and this
929      * ensures that the real HWND owner services the request.
930      * If the owning X window gets destroyed the selection ownership is
931      * re-cycled to another top level X window in X11DRV_CLIPBOARD_ResetOwner.
932      *
933      */
934
935     if ( !(selectionAcquired == (S_PRIMARY | S_CLIPBOARD)) )
936     {
937         Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
938         owner = X11DRV_get_whole_window( GetAncestor( hWndClipWindow ? hWndClipWindow : AnyPopup(),
939                                                       GA_ROOT ) );
940
941         /* Grab PRIMARY selection if not owned */
942         if ( !(selectionAcquired & S_PRIMARY) )
943             TSXSetSelectionOwner(display, XA_PRIMARY, owner, CurrentTime);
944         
945         /* Grab CLIPBOARD selection if not owned */
946         if ( !(selectionAcquired & S_CLIPBOARD) )
947             TSXSetSelectionOwner(display, xaClipboard, owner, CurrentTime);
948
949         if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner )
950             selectionAcquired |= S_PRIMARY;
951
952         if( TSXGetSelectionOwner(display,xaClipboard) == owner)
953             selectionAcquired |= S_CLIPBOARD;
954
955         if (selectionAcquired)
956         {
957             selectionWindow = owner;
958             TRACE("Grabbed X selection, owner=(%08x)\n", (unsigned) owner);
959         }
960     }
961 }
962
963 /**************************************************************************
964  *              IsClipboardFormatAvailable (X11DRV.@)
965  *
966  * Checks if the specified format is available in the current selection
967  * Only invoked when WINE is not the selection owner
968  */
969 BOOL X11DRV_IsClipboardFormatAvailable(UINT wFormat)
970 {
971     Display *display = thread_display();
972     Atom xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
973     Window ownerPrimary = TSXGetSelectionOwner(display,XA_PRIMARY);
974     Window ownerClipboard = TSXGetSelectionOwner(display,xaClipboard);
975
976     TRACE("enter for %d\n", wFormat);
977
978     /*
979      * If the selection has not been previously cached, or the selection has changed,
980      * try and cache the list of available selection targets from the current selection.
981      */
982     if ( !cSelectionTargets || (PrimarySelectionOwner != ownerPrimary)
983                          || (ClipboardSelectionOwner != ownerClipboard) )
984     {
985         /*
986          * First try caching the CLIPBOARD selection.
987          * If unavailable try PRIMARY.
988          */
989         if ( X11DRV_CLIPBOARD_CacheDataFormats(xaClipboard) == 0 )
990         {
991             X11DRV_CLIPBOARD_CacheDataFormats(XA_PRIMARY);
992         }
993
994         ClipboardSelectionOwner = ownerClipboard;
995         PrimarySelectionOwner = ownerPrimary;
996     }
997
998     /* Exit if there is no selection */
999     if ( !ownerClipboard && !ownerPrimary )
1000     {
1001         TRACE("There is no selection owner\n");
1002         return FALSE;
1003     }
1004    
1005     /* Check if the format is available in the clipboard cache */
1006     if ( CLIPBOARD_IsPresent(wFormat) )
1007         return TRUE;
1008
1009     /*
1010      * Many X client apps (such as XTerminal) don't support being queried
1011      * for the "TARGETS" target atom. To handle such clients we must actually
1012      * try to convert the selection to the requested type.
1013      */
1014     if ( !cSelectionTargets )
1015         return X11DRV_GetClipboardData( wFormat );
1016         
1017     TRACE("There is no selection\n");
1018     return FALSE;
1019 }
1020
1021 /**************************************************************************
1022  *              RegisterClipboardFormat (X11DRV.@)
1023  *
1024  * Registers a custom X clipboard format
1025  * Returns: TRUE - success,  FALSE - failure
1026  */
1027 BOOL X11DRV_RegisterClipboardFormat( LPCSTR FormatName )
1028 {
1029     Display *display = thread_display();
1030     Atom prop = None;
1031     char str[256];
1032     
1033     /*
1034      * If an X atom is registered for this format, return that
1035      * Otherwise register a new atom.
1036      */
1037     if (FormatName)
1038     {
1039         /* Add a WINE specific prefix to the format */
1040         strcpy(str, FMT_PREFIX);
1041         strncat(str, FormatName, sizeof(str) - strlen(FMT_PREFIX));
1042         prop = TSXInternAtom(display, str, False);
1043     }
1044     
1045     return (prop) ? TRUE : FALSE;
1046 }
1047
1048 /**************************************************************************
1049  *              IsSelectionOwner (X11DRV.@)
1050  *
1051  * Returns: TRUE - We(WINE) own the selection, FALSE - Selection not owned by us
1052  */
1053 BOOL X11DRV_IsSelectionOwner(void)
1054 {
1055     return selectionAcquired;
1056 }
1057
1058 /**************************************************************************
1059  *              SetClipboardData (X11DRV.@)
1060  *
1061  * We don't need to do anything special here since the clipboard code
1062  * maintains the cache. 
1063  *
1064  */
1065 void X11DRV_SetClipboardData(UINT wFormat)
1066 {
1067     /* Make sure we have acquired the X selection */
1068     X11DRV_AcquireClipboard();
1069 }
1070
1071 /**************************************************************************
1072  *              GetClipboardData (X11DRV.@)
1073  *
1074  * This method is invoked only when we DO NOT own the X selection
1075  *
1076  * NOTE: Clipboard driver get requests only for CF_UNICODETEXT data.
1077  * We always get the data from the selection client each time,
1078  * since we have no way of determining if the data in our cache is stale.
1079  */
1080 BOOL X11DRV_GetClipboardData(UINT wFormat)
1081 {
1082     Display *display = thread_display();
1083     BOOL bRet = selectionAcquired;
1084     HWND hWndClipWindow = GetOpenClipboardWindow();
1085     HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
1086     LPWINE_CLIPFORMAT lpFormat;
1087
1088     TRACE("%d\n", wFormat);
1089
1090     if (!selectionAcquired)
1091     {
1092         XEvent xe;
1093         Atom propRequest;
1094         Window w = X11DRV_get_whole_window( GetAncestor( hWnd, GA_ROOT ));
1095         if(!w)
1096         {
1097             FIXME("No parent win found %x %x\n", hWnd, hWndClipWindow);
1098             return FALSE;
1099         }
1100
1101         /* Map the format ID requested to an X selection property.
1102          * If the format is in the cache, use the atom associated
1103          * with it.
1104          */
1105         
1106         lpFormat = CLIPBOARD_LookupFormat( wFormat );
1107         if (lpFormat && lpFormat->wDataPresent && lpFormat->drvData)
1108             propRequest = (Atom)lpFormat->drvData;
1109         else
1110             propRequest = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat);
1111
1112         if (propRequest)
1113         {
1114             TRACE("Requesting %s selection from %s...\n",
1115                   TSXGetAtomName(display, propRequest),
1116                   TSXGetAtomName(display, selectionCacheSrc) );
1117             wine_tsx11_lock();
1118             XConvertSelection(display, selectionCacheSrc, propRequest,
1119                             TSXInternAtom(display, "SELECTION_DATA", False),
1120                             w, CurrentTime);
1121         
1122             /* wait until SelectionNotify is received */
1123     
1124             while( TRUE )
1125             {
1126                if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
1127                    if( xe.xselection.selection == selectionCacheSrc )
1128                        break;
1129             }
1130             wine_tsx11_unlock();
1131
1132             /*
1133              *  Read the contents of the X selection property into WINE's
1134              *  clipboard cache converting the selection to be compatible if possible.
1135              */
1136             bRet = X11DRV_CLIPBOARD_ReadSelection( wFormat,
1137                                                    xe.xselection.requestor,
1138                                                    xe.xselection.property,
1139                                                    xe.xselection.target);
1140         }
1141         else
1142             bRet = FALSE;
1143
1144         TRACE("\tpresent %s = %i\n", CLIPBOARD_GetFormatName(wFormat), bRet );
1145     }
1146
1147     TRACE("Returning %d\n", bRet);
1148     
1149     return bRet;
1150 }
1151
1152 /**************************************************************************
1153  *              ResetSelectionOwner (X11DRV.@)
1154  *
1155  * Called from DestroyWindow() to prevent X selection from being lost when
1156  * a top level window is destroyed, by switching ownership to another top
1157  * level window.
1158  * Any top level window can own the selection. See X11DRV_CLIPBOARD_Acquire
1159  * for a more detailed description of this.
1160  */
1161 void X11DRV_ResetSelectionOwner(HWND hwnd, BOOL bFooBar)
1162 {
1163     Display *display = thread_display();
1164     HWND hWndClipOwner = 0;
1165     HWND tmp;
1166     Window XWnd = X11DRV_get_whole_window(hwnd);
1167     Atom xaClipboard;
1168     BOOL bLostSelection = FALSE;
1169
1170     /* There is nothing to do if we don't own the selection,
1171      * or if the X window which currently owns the selecion is different
1172      * from the one passed in.
1173      */
1174     if ( !selectionAcquired || XWnd != selectionWindow
1175          || selectionWindow == None )
1176        return;
1177
1178     if ( (bFooBar && XWnd) || (!bFooBar && !XWnd) )
1179        return;
1180
1181     hWndClipOwner = GetClipboardOwner();
1182     xaClipboard = TSXInternAtom(display, _CLIPBOARD, False);
1183     
1184     TRACE("clipboard owner = %04x, selection window = %08x\n",
1185           hWndClipOwner, (unsigned)selectionWindow);
1186
1187     /* now try to salvage current selection from being destroyed by X */
1188
1189     TRACE("\tchecking %08x\n", (unsigned) XWnd);
1190
1191     selectionPrevWindow = selectionWindow;
1192     selectionWindow = None;
1193
1194     if (!(tmp = GetWindow( hwnd, GW_HWNDNEXT ))) tmp = GetWindow( hwnd, GW_HWNDFIRST );
1195     if (tmp && tmp != hwnd) selectionWindow = X11DRV_get_whole_window(tmp);
1196
1197     if( selectionWindow != None )
1198     {
1199         /* We must pretend that we don't own the selection while making the switch
1200          * since a SelectionClear event will be sent to the last owner.
1201          * If there is no owner X11DRV_CLIPBOARD_ReleaseSelection will do nothing.
1202          */
1203         int saveSelectionState = selectionAcquired;
1204         selectionAcquired = False;
1205
1206         TRACE("\tswitching selection from %08x to %08x\n", 
1207                     (unsigned)selectionPrevWindow, (unsigned)selectionWindow);
1208     
1209         /* Assume ownership for the PRIMARY and CLIPBOARD selection */
1210         if ( saveSelectionState & S_PRIMARY )
1211             TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
1212         
1213         TSXSetSelectionOwner(display, xaClipboard, selectionWindow, CurrentTime);
1214
1215         /* Restore the selection masks */
1216         selectionAcquired = saveSelectionState;
1217
1218         /* Lose the selection if something went wrong */
1219         if ( ( (saveSelectionState & S_PRIMARY) &&
1220                (TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow) )
1221              || (TSXGetSelectionOwner(display, xaClipboard) != selectionWindow) )
1222         {
1223             bLostSelection = TRUE;
1224             goto END;
1225         }
1226         else
1227         {
1228             /* Update selection state */
1229             if (saveSelectionState & S_PRIMARY)
1230                PrimarySelectionOwner = selectionWindow;
1231             
1232             ClipboardSelectionOwner = selectionWindow;
1233         }
1234     }
1235     else
1236     {
1237         bLostSelection = TRUE;
1238         goto END;
1239     }
1240
1241 END:
1242     if (bLostSelection)
1243     {
1244       /* Launch the clipboard server if the selection can no longer be recyled
1245        * to another top level window. */
1246   
1247       if ( !X11DRV_CLIPBOARD_LaunchServer() )
1248       {
1249          /* Empty the windows clipboard if the server was not launched.
1250           * We should pretend that we still own the selection BEFORE calling
1251           * EmptyClipboard() since otherwise this has the side effect of
1252           * triggering X11DRV_CLIPBOARD_Acquire() and causing the X selection
1253           * to be re-acquired by us!
1254           */
1255   
1256          TRACE("\tLost the selection! Emptying the clipboard...\n");
1257       
1258          OpenClipboard( 0 );
1259          selectionAcquired = (S_PRIMARY | S_CLIPBOARD);
1260          EmptyClipboard();
1261          
1262          CloseClipboard();
1263    
1264          /* Give up ownership of the windows clipboard */
1265          CLIPBOARD_ReleaseOwner();
1266       }
1267
1268       selectionAcquired = S_NOSELECTION;
1269       ClipboardSelectionOwner = PrimarySelectionOwner = 0;
1270       selectionWindow = 0;
1271     }
1272 }
1273
1274 /**************************************************************************
1275  *              X11DRV_CLIPBOARD_RegisterPixmapResource
1276  * Registers a Pixmap resource which is to be associated with a property Atom.
1277  * When the property is destroyed we also destroy the Pixmap through the
1278  * PropertyNotify event.
1279  */
1280 BOOL X11DRV_CLIPBOARD_RegisterPixmapResource( Atom property, Pixmap pixmap )
1281 {
1282     PROPERTY *prop = HeapAlloc( GetProcessHeap(), 0, sizeof(*prop) );
1283     if (!prop) return FALSE;
1284     prop->atom = property;
1285     prop->pixmap = pixmap;
1286     prop->next = prop_head;
1287     prop_head = prop;
1288     return TRUE;
1289 }
1290
1291 /**************************************************************************
1292  *              X11DRV_CLIPBOARD_FreeResources
1293  *
1294  * Called from EVENT_PropertyNotify() to give us a chance to destroy
1295  * any resources associated with this property.
1296  */
1297 void X11DRV_CLIPBOARD_FreeResources( Atom property )
1298 {
1299     /* Do a simple linear search to see if we have a Pixmap resource
1300      * associated with this property and release it.
1301      */
1302     PROPERTY **prop = &prop_head;
1303
1304     while (*prop)
1305     {
1306         if ((*prop)->atom == property)
1307         {
1308             PROPERTY *next = (*prop)->next;
1309             XFreePixmap( gdi_display, (*prop)->pixmap );
1310             HeapFree( GetProcessHeap(), 0, *prop );
1311             *prop = next;
1312         }
1313         else prop = &(*prop)->next;
1314     }
1315 }