Made access to the wnd struct thread-safe.
[wine] / windows / x11drv / clipboard.c
1 /*
2  * X11 windows driver
3  *
4  * Copyright 1994 Martin Ayotte
5  *           1996 Alex Korobka
6  */
7
8 #include "config.h"
9
10 #ifndef X_DISPLAY_MISSING
11
12 #include <X11/Xatom.h>
13 #include "ts_xlib.h"
14
15 #include "clipboard.h"
16 #include "debug.h"
17 #include "message.h"
18 #include "win.h"
19 #include "windef.h"
20 #include "x11drv.h"
21
22 extern HWND hWndClipOwner;
23 extern HWND hWndClipWindow;
24 extern WINE_CLIPFORMAT ClipFormats[];
25
26 static Bool   selectionWait = False;
27 static Bool   selectionAcquired = False;
28 static Window selectionWindow = None;
29 static Window selectionPrevWindow = None;
30
31 /**************************************************************************
32  *              X11DRV_CLIPBOARD_CheckSelection [Internal]
33  *
34  * Prevent X selection from being lost when a top level window is
35  * destroyed.
36  */
37 static void X11DRV_CLIPBOARD_CheckSelection(WND* pWnd)
38 {
39     TRACE(clipboard,"\tchecking %08x\n",
40         (unsigned) X11DRV_WND_GetXWindow(pWnd)
41     );
42
43     if( selectionAcquired && selectionWindow != None &&
44         X11DRV_WND_GetXWindow(pWnd) == selectionWindow )
45     {
46         selectionPrevWindow = selectionWindow;
47         selectionWindow = None;
48
49         if( pWnd->next ) 
50             selectionWindow = X11DRV_WND_GetXWindow(pWnd->next);
51         else if( pWnd->parent )
52              if( pWnd->parent->child != pWnd ) 
53                  selectionWindow = X11DRV_WND_GetXWindow(pWnd->parent->child);
54
55         TRACE(clipboard,"\tswitching selection from %08x to %08x\n", 
56                     (unsigned)selectionPrevWindow, (unsigned)selectionWindow);
57
58         if( selectionWindow != None )
59         {
60             TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
61             if( TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow )
62                 selectionWindow = None;
63         }
64     }
65 }
66
67 /**************************************************************************
68  *              X11DRV_CLIPBOARD_ReadSelection
69  *
70  * Called from the SelectionNotify event handler. 
71  */
72 void X11DRV_CLIPBOARD_ReadSelection(Window w,Atom prop)
73 {
74     HANDLE       hText = 0;
75     LPWINE_CLIPFORMAT lpFormat = ClipFormats; 
76
77     TRACE(clipboard,"ReadSelection callback\n");
78
79     if(prop != None)
80     {
81         Atom            atype=AnyPropertyType;
82         int             aformat;
83         unsigned long   nitems,remain;
84         unsigned char*  val=NULL;
85
86         TRACE(clipboard,"\tgot property %s\n",TSXGetAtomName(display,prop));
87
88         /* TODO: Properties longer than 64K */
89
90         if(TSXGetWindowProperty(display,w,prop,0,0x3FFF,True,XA_STRING,
91             &atype, &aformat, &nitems, &remain, &val) != Success)
92             WARN(clipboard, "\tcouldn't read property\n");
93         else
94         {
95            TRACE(clipboard,"\tType %s,Format %d,nitems %ld,value %s\n",
96                              TSXGetAtomName(display,atype),aformat,nitems,val);
97
98            if(atype == XA_STRING && aformat == 8)
99            {
100               int       i,inlcount = 0;
101               char*     lpstr;
102
103               TRACE(clipboard,"\tselection is '%s'\n",val);
104
105               for(i=0; i <= nitems; i++)
106                   if( val[i] == '\n' ) inlcount++;
107
108               if( nitems )
109               {
110                 hText=GlobalAlloc(GMEM_MOVEABLE, nitems + inlcount + 1);
111                 if( (lpstr = (char*)GlobalLock(hText)) )
112                   for(i=0,inlcount=0; i <= nitems; i++)
113                   {
114                      if( val[i] == '\n' ) lpstr[inlcount++]='\r';
115                      lpstr[inlcount++]=val[i];
116                   }
117                 else hText = 0;
118               }
119            }
120            TSXFree(val);
121         }
122    }
123
124    /* delete previous CF_TEXT and CF_OEMTEXT data */
125
126    if( hText )
127    {
128      lpFormat = &ClipFormats[CF_TEXT-1];
129      if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32) 
130          CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
131      lpFormat = &ClipFormats[CF_OEMTEXT-1];
132      if (lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32)  
133          CLIPBOARD_DeleteRecord(lpFormat, !(hWndClipWindow));
134
135      lpFormat->wDataPresent = 1;
136      lpFormat->hData32 = hText;
137      lpFormat->hData16 = 0;
138    }
139
140    selectionWait=False;
141 }
142
143 /**************************************************************************
144  *              X11DRV_CLIPBOARD_ReleaseSelection
145  *
146  * Wine might have lost XA_PRIMARY selection because of
147  * EmptyClipboard() or other client. 
148  */
149 void X11DRV_CLIPBOARD_ReleaseSelection(Window w, HWND hwnd)
150 {
151     /* w is the window that lost selection,
152      * 
153      * selectionPrevWindow is nonzero if CheckSelection() was called. 
154      */
155
156     TRACE(clipboard,"\tevent->window = %08x (sw = %08x, spw=%08x)\n", 
157           (unsigned)w, (unsigned)selectionWindow, (unsigned)selectionPrevWindow );
158
159     if( selectionAcquired )
160     {
161         if( w == selectionWindow || selectionPrevWindow == None)
162         {
163             /* alright, we really lost it */
164
165             selectionAcquired = False;
166             selectionWindow = None; 
167
168             /* but we'll keep existing data for internal use */
169         }
170         else if( w == selectionPrevWindow )
171         {
172             w = TSXGetSelectionOwner(display, XA_PRIMARY);
173             if( w == None )
174                 TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
175         }
176     }
177
178     selectionPrevWindow = None;
179 }
180
181 /**************************************************************************
182  *              X11DRV_CLIPBOARD_EmptyClipboard
183  */
184 void X11DRV_CLIPBOARD_EmptyClipboard()
185 {
186   if(selectionAcquired)
187     {
188       selectionAcquired = False;
189       selectionPrevWindow       = selectionWindow;
190       selectionWindow   = None;
191       
192       TRACE(clipboard, "\tgiving up selection (spw = %08x)\n", 
193             (unsigned)selectionPrevWindow);
194       
195       TSXSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
196     }
197 }
198
199 /**************************************************************************
200  *              X11DRV_CLIPBOARD_SetClipboardData
201  */
202 void X11DRV_CLIPBOARD_SetClipboardData(UINT wFormat)
203 {
204     Window       owner;
205
206     /* Acquire X selection if text format */
207
208     if( !selectionAcquired && 
209         (wFormat == CF_TEXT || wFormat == CF_OEMTEXT) )
210     {
211         WND *tmpWnd = WIN_FindWndPtr( hWndClipWindow ? hWndClipWindow : AnyPopup() );
212         owner = X11DRV_WND_FindXWindow(tmpWnd );
213
214         TSXSetSelectionOwner(display,XA_PRIMARY, owner, CurrentTime);
215         if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner )
216         {
217             selectionAcquired = True;
218             selectionWindow = owner;
219
220             TRACE(clipboard,"Grabbed X selection, owner=(%08x)\n", 
221                                                 (unsigned) owner);
222         }
223         WIN_ReleaseWndPtr(tmpWnd);
224     }
225 }
226
227 /**************************************************************************
228  *              X11DRV_CLIPBOARD_RequestSelection
229  */
230 BOOL X11DRV_CLIPBOARD_RequestSelection()
231 {
232     HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
233     WND *tmpWnd = WIN_FindWndPtr(hWnd);
234
235     if( selectionAcquired )
236       return TRUE;
237
238     if( !hWnd ) return FALSE;
239
240     TRACE(clipboard,"Requesting selection...\n");
241
242   /* request data type XA_STRING, later
243    * CLIPBOARD_ReadSelection() will be invoked 
244    * from the SelectionNotify event handler */
245
246     TSXConvertSelection(display, XA_PRIMARY, XA_STRING,
247                         TSXInternAtom(display, "PRIMARY_TEXT", False),
248                         X11DRV_WND_FindXWindow(tmpWnd ),
249                         CurrentTime);
250     
251     WIN_ReleaseWndPtr(tmpWnd);
252
253   /* wait until SelectionNotify is processed 
254    *
255    * FIXME: Use TSXCheckTypedWindowEvent() instead ( same in the 
256    *        CLIPBOARD_CheckSelection() ). 
257    */
258
259     selectionWait=True;
260     while(selectionWait) EVENT_WaitNetEvent( TRUE, FALSE );
261
262   /* we treat Unix text as CF_OEMTEXT */
263     TRACE(clipboard,"\tgot CF_OEMTEXT = %i\n", 
264                       ClipFormats[CF_OEMTEXT-1].wDataPresent);
265
266     return (BOOL)ClipFormats[CF_OEMTEXT-1].wDataPresent;
267 }
268
269 /**************************************************************************
270  *              X11DRV_CLIPBOARD_ResetOwner
271  *
272  * Called from DestroyWindow().
273  */
274 void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
275 {
276     LPWINE_CLIPFORMAT lpFormat = ClipFormats;
277
278     if(bFooBar && X11DRV_WND_GetXWindow(pWnd))
279       return;
280
281     if(!bFooBar && !X11DRV_WND_GetXWindow(pWnd))
282       return;
283
284     TRACE(clipboard,"clipboard owner = %04x, selection = %08x\n", 
285                                 hWndClipOwner, (unsigned)selectionWindow);
286
287     if( pWnd->hwndSelf == hWndClipOwner)
288     {
289         SendMessage16(hWndClipOwner,WM_RENDERALLFORMATS,0,0L);
290
291         /* check if all formats were rendered */
292
293         while(lpFormat)
294         {
295             if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 )
296             {
297                 TRACE(clipboard,"\tdata missing for clipboard format %i\n", 
298                                    lpFormat->wFormatID); 
299                 lpFormat->wDataPresent = 0;
300             }
301             lpFormat = lpFormat->NextFormat;
302         }
303         hWndClipOwner = 0;
304     }
305
306     /* now try to salvage current selection from being destroyed by X */
307
308     if( X11DRV_WND_GetXWindow(pWnd) ) X11DRV_CLIPBOARD_CheckSelection(pWnd);
309 }
310
311 #endif /* !defined(X_DISPLAY_MISSING) */