While handling the X FocusIn message in managed mode, if the window
[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 "wine/winuser16.h"
16 #include "clipboard.h"
17 #include "debugtools.h"
18 #include "message.h"
19 #include "win.h"
20 #include "windef.h"
21 #include "x11drv.h"
22
23 DEFAULT_DEBUG_CHANNEL(clipboard)
24
25 extern HWND hWndClipOwner;
26 extern HWND hWndClipWindow;
27 extern WINE_CLIPFORMAT ClipFormats[];
28
29 static Bool   selectionAcquired = False;
30 static Window selectionWindow = None;
31 static Window selectionPrevWindow = None;
32
33 /**************************************************************************
34  *              X11DRV_CLIPBOARD_CheckSelection [Internal]
35  *
36  * Prevent X selection from being lost when a top level window is
37  * destroyed.
38  */
39 static void X11DRV_CLIPBOARD_CheckSelection(WND* pWnd)
40 {
41     TRACE("\tchecking %08x\n",
42         (unsigned) X11DRV_WND_GetXWindow(pWnd)
43     );
44
45     if( selectionAcquired && selectionWindow != None &&
46         X11DRV_WND_GetXWindow(pWnd) == selectionWindow )
47     {
48         selectionPrevWindow = selectionWindow;
49         selectionWindow = None;
50
51         if( pWnd->next ) 
52             selectionWindow = X11DRV_WND_GetXWindow(pWnd->next);
53         else if( pWnd->parent )
54              if( pWnd->parent->child != pWnd ) 
55                  selectionWindow = X11DRV_WND_GetXWindow(pWnd->parent->child);
56
57         TRACE("\tswitching selection from %08x to %08x\n", 
58                     (unsigned)selectionPrevWindow, (unsigned)selectionWindow);
59
60         if( selectionWindow != None )
61         {
62             TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
63             if( TSXGetSelectionOwner(display, XA_PRIMARY) != selectionWindow )
64                 selectionWindow = None;
65         }
66     }
67 }
68
69 /**************************************************************************
70  *              X11DRV_CLIPBOARD_ReadSelection
71  */
72 static void X11DRV_CLIPBOARD_ReadSelection(Window w, Atom prop)
73 {
74     HANDLE       hText = 0;
75     LPWINE_CLIPFORMAT lpFormat = ClipFormats; 
76
77     TRACE("Reading X selection...\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("\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("\tcouldn't read property\n");
93         else
94         {
95            TRACE("\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("\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        lpFormat->wDataPresent = 1;
135        lpFormat->hData32 = hText;
136        lpFormat->hData16 = 0;
137    }
138 }
139
140 /**************************************************************************
141  *              X11DRV_CLIPBOARD_ReleaseSelection
142  *
143  * Wine might have lost XA_PRIMARY selection because of
144  * EmptyClipboard() or other client. 
145  */
146 void X11DRV_CLIPBOARD_ReleaseSelection(Window w, HWND hwnd)
147 {
148     /* w is the window that lost selection,
149      * 
150      * selectionPrevWindow is nonzero if CheckSelection() was called. 
151      */
152
153     TRACE("\tevent->window = %08x (sw = %08x, spw=%08x)\n", 
154           (unsigned)w, (unsigned)selectionWindow, (unsigned)selectionPrevWindow );
155
156     if( selectionAcquired )
157     {
158         if( w == selectionWindow || selectionPrevWindow == None)
159         {
160             /* alright, we really lost it */
161
162             selectionAcquired = False;
163             selectionWindow = None; 
164
165             /* but we'll keep existing data for internal use */
166         }
167         else if( w == selectionPrevWindow )
168         {
169             w = TSXGetSelectionOwner(display, XA_PRIMARY);
170             if( w == None )
171                 TSXSetSelectionOwner(display, XA_PRIMARY, selectionWindow, CurrentTime);
172         }
173     }
174
175     selectionPrevWindow = None;
176 }
177
178 /**************************************************************************
179  *              X11DRV_CLIPBOARD_Empty
180  */
181 void X11DRV_CLIPBOARD_Empty()
182 {
183     if( selectionAcquired )
184     {
185         XEvent xe;
186
187         selectionAcquired   = False;
188         selectionPrevWindow = selectionWindow;
189         selectionWindow     = None;
190       
191         TRACE("\tgiving up selection (spw = %08x)\n", 
192              (unsigned)selectionPrevWindow);
193       
194         EnterCriticalSection(&X11DRV_CritSection);
195         XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
196
197         if( selectionPrevWindow )
198             while( !XCheckTypedWindowEvent( display, selectionPrevWindow,
199                                             SelectionClear, &xe ) );
200         LeaveCriticalSection(&X11DRV_CritSection);
201     }
202 }
203
204 /**************************************************************************
205  *              X11DRV_CLIPBOARD_SetData
206  */
207 void X11DRV_CLIPBOARD_SetData(UINT wFormat)
208 {
209     Window       owner;
210
211     /* Acquire X selection if text format */
212
213     if( !selectionAcquired && 
214         (wFormat == CF_TEXT || wFormat == CF_OEMTEXT) )
215     {
216         WND *tmpWnd = WIN_FindWndPtr( hWndClipWindow ? hWndClipWindow : AnyPopup() );
217         owner = X11DRV_WND_FindXWindow(tmpWnd );
218
219         TSXSetSelectionOwner(display,XA_PRIMARY, owner, CurrentTime);
220         if( TSXGetSelectionOwner(display,XA_PRIMARY) == owner )
221         {
222             selectionAcquired = True;
223             selectionWindow = owner;
224
225             TRACE("Grabbed X selection, owner=(%08x)\n", 
226                                                 (unsigned) owner);
227         }
228         WIN_ReleaseWndPtr(tmpWnd);
229     }
230 }
231
232 /**************************************************************************
233  *              X11DRV_CLIPBOARD_GetData
234  *
235  * NOTE: Clipboard driver doesn't get requests for CF_TEXT data, only
236  *       for CF_OEMTEXT.
237  */
238 BOOL X11DRV_CLIPBOARD_GetData(UINT wFormat)
239 {
240     BOOL bRet = selectionAcquired;
241     HWND hWnd = (hWndClipWindow) ? hWndClipWindow : GetActiveWindow();
242     WND* wnd = NULL;
243
244     if( wFormat != CF_OEMTEXT ) return FALSE;
245
246     if( !bRet && (wnd = WIN_FindWndPtr(hWnd)) )
247     {
248         XEvent xe;
249         Window w = X11DRV_WND_FindXWindow(wnd);
250
251         TRACE("Requesting XA_STRING selection...\n");
252
253         EnterCriticalSection( &X11DRV_CritSection );
254         XConvertSelection(display, XA_PRIMARY, XA_STRING,
255                         XInternAtom(display, "PRIMARY_TEXT", False),
256                         w, CurrentTime);
257     
258         /* wait until SelectionNotify is received */
259
260         while( TRUE )
261         {
262            if( XCheckTypedWindowEvent(display, w, SelectionNotify, &xe) )
263                if( xe.xselection.selection == XA_PRIMARY )
264                    break;
265         }
266         LeaveCriticalSection( &X11DRV_CritSection );
267
268         if (xe.xselection.target != XA_STRING) 
269             X11DRV_CLIPBOARD_ReadSelection( 0, None );
270         else 
271             X11DRV_CLIPBOARD_ReadSelection( xe.xselection.requestor, 
272                                             xe.xselection.property );
273
274         /* treat Unix text as CF_OEMTEXT */
275
276         bRet = (BOOL)ClipFormats[CF_OEMTEXT-1].wDataPresent;
277
278         TRACE("\tpresent CF_OEMTEXT = %i\n", bRet );
279         WIN_ReleaseWndPtr(wnd);
280     }
281     return bRet;
282 }
283
284 /**************************************************************************
285  *              X11DRV_CLIPBOARD_ResetOwner
286  *
287  * Called from DestroyWindow().
288  */
289 void X11DRV_CLIPBOARD_ResetOwner(WND *pWnd, BOOL bFooBar)
290 {
291     LPWINE_CLIPFORMAT lpFormat = ClipFormats;
292
293     if(bFooBar && X11DRV_WND_GetXWindow(pWnd))
294       return;
295
296     if(!bFooBar && !X11DRV_WND_GetXWindow(pWnd))
297       return;
298
299     TRACE("clipboard owner = %04x, selection = %08x\n", 
300                                 hWndClipOwner, (unsigned)selectionWindow);
301
302     if( pWnd->hwndSelf == hWndClipOwner)
303     {
304         SendMessage16(hWndClipOwner,WM_RENDERALLFORMATS,0,0L);
305
306         /* check if all formats were rendered */
307
308         while(lpFormat)
309         {
310             if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 )
311             {
312                 TRACE("\tdata missing for clipboard format %i\n", 
313                                    lpFormat->wFormatID); 
314                 lpFormat->wDataPresent = 0;
315             }
316             lpFormat = lpFormat->NextFormat;
317         }
318         hWndClipOwner = 0;
319     }
320
321     /* now try to salvage current selection from being destroyed by X */
322
323     if( X11DRV_WND_GetXWindow(pWnd) ) X11DRV_CLIPBOARD_CheckSelection(pWnd);
324 }
325
326 #endif /* !defined(X_DISPLAY_MISSING) */