Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved. | |
3 | *Copyright (C) Colin Harrison 2005-2008 | |
4 | * | |
5 | *Permission is hereby granted, free of charge, to any person obtaining | |
6 | * a copy of this software and associated documentation files (the | |
7 | *"Software"), to deal in the Software without restriction, including | |
8 | *without limitation the rights to use, copy, modify, merge, publish, | |
9 | *distribute, sublicense, and/or sell copies of the Software, and to | |
10 | *permit persons to whom the Software is furnished to do so, subject to | |
11 | *the following conditions: | |
12 | * | |
13 | *The above copyright notice and this permission notice shall be | |
14 | *included in all copies or substantial portions of the Software. | |
15 | * | |
16 | *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
17 | *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
18 | *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
19 | *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR | |
20 | *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
21 | *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
22 | *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
23 | * | |
24 | *Except as contained in this notice, the name of the copyright holder(s) | |
25 | *and author(s) shall not be used in advertising or otherwise to promote | |
26 | *the sale, use or other dealings in this Software without prior written | |
27 | *authorization from the copyright holder(s) and author(s). | |
28 | * | |
29 | * Authors: Harold L Hunt II | |
30 | * Colin Harrison | |
31 | */ | |
32 | ||
33 | #ifdef HAVE_XWIN_CONFIG_H | |
34 | #include <xwin-config.h> | |
35 | #endif | |
36 | #include "win.h" | |
37 | #include "dixstruct.h" | |
38 | #include <X11/Xatom.h> | |
39 | ||
40 | /* | |
41 | * Constants | |
42 | */ | |
43 | ||
44 | #define CLIP_NUM_SELECTIONS 2 | |
45 | #define CLIP_OWN_PRIMARY 0 | |
46 | #define CLIP_OWN_CLIPBOARD 1 | |
47 | ||
48 | /* | |
49 | * Local function prototypes | |
50 | */ | |
51 | ||
52 | DISPATCH_PROC(winProcEstablishConnection); | |
53 | DISPATCH_PROC(winProcSetSelectionOwner); | |
54 | ||
55 | /* | |
56 | * References to external symbols | |
57 | */ | |
58 | ||
59 | extern Bool g_fClipboardLaunched; | |
60 | extern Bool g_fClipboardStarted; | |
61 | extern Bool g_fClipboard; | |
62 | extern Window g_iClipboardWindow; | |
63 | extern Atom g_atomLastOwnedSelection; | |
64 | extern HWND g_hwndClipboard; | |
65 | ||
66 | ||
67 | /* | |
68 | * Wrapper for internal EstablishConnection function. | |
69 | * Initializes internal clients that must not be started until | |
70 | * an external client has connected. | |
71 | */ | |
72 | ||
73 | int | |
74 | winProcEstablishConnection(ClientPtr client) | |
75 | { | |
76 | int iReturn; | |
77 | static int s_iCallCount = 0; | |
78 | static unsigned long s_ulServerGeneration = 0; | |
79 | ||
80 | if (s_iCallCount == 0) | |
81 | winDebug("winProcEstablishConnection - Hello\n"); | |
82 | ||
83 | /* Do nothing if clipboard is not enabled */ | |
84 | if (!g_fClipboard) { | |
85 | ErrorF("winProcEstablishConnection - Clipboard is not enabled, " | |
86 | "returning.\n"); | |
87 | ||
88 | /* Unwrap the original function, call it, and return */ | |
89 | InitialVector[2] = winProcEstablishConnectionOrig; | |
90 | iReturn = (*winProcEstablishConnectionOrig) (client); | |
91 | winProcEstablishConnectionOrig = NULL; | |
92 | return iReturn; | |
93 | } | |
94 | ||
95 | /* Watch for server reset */ | |
96 | if (s_ulServerGeneration != serverGeneration) { | |
97 | /* Save new generation number */ | |
98 | s_ulServerGeneration = serverGeneration; | |
99 | ||
100 | /* Reset call count */ | |
101 | s_iCallCount = 0; | |
102 | } | |
103 | ||
104 | /* Increment call count */ | |
105 | ++s_iCallCount; | |
106 | ||
107 | /* | |
108 | * This procedure is only used for initialization. | |
109 | * We can unwrap the original procedure at this point | |
110 | * so that this function is no longer called until the | |
111 | * server resets and the function is wrapped again. | |
112 | */ | |
113 | InitialVector[2] = winProcEstablishConnectionOrig; | |
114 | ||
115 | /* | |
116 | * Call original function and bail if it fails. | |
117 | * NOTE: We must do this first, since we need XdmcpOpenDisplay | |
118 | * to be called before we initialize our clipboard client. | |
119 | */ | |
120 | iReturn = (*winProcEstablishConnectionOrig) (client); | |
121 | if (iReturn != 0) { | |
122 | ErrorF("winProcEstablishConnection - ProcEstablishConnection " | |
123 | "failed, bailing.\n"); | |
124 | return iReturn; | |
125 | } | |
126 | ||
127 | /* Clear original function pointer */ | |
128 | winProcEstablishConnectionOrig = NULL; | |
129 | ||
130 | /* If the clipboard client has already been started, abort */ | |
131 | if (g_fClipboardLaunched) { | |
132 | ErrorF("winProcEstablishConnection - Clipboard client already " | |
133 | "launched, returning.\n"); | |
134 | return iReturn; | |
135 | } | |
136 | ||
137 | /* Startup the clipboard client if clipboard mode is being used */ | |
138 | if (g_fClipboard) { | |
139 | /* | |
140 | * NOTE: The clipboard client is started here for a reason: | |
141 | * 1) Assume you are using XDMCP (e.g. XWin -query %hostname%) | |
142 | * 2) If the clipboard client attaches during X Server startup, | |
143 | * then it becomes the "magic client" that causes the X Server | |
144 | * to reset if it exits. | |
145 | * 3) XDMCP calls KillAllClients when it starts up. | |
146 | * 4) The clipboard client is a client, so it is killed. | |
147 | * 5) The clipboard client is the "magic client", so the X Server | |
148 | * resets itself. | |
149 | * 6) This repeats ad infinitum. | |
150 | * 7) We avoid this by waiting until at least one client (could | |
151 | * be XDM, could be another client) connects, which makes it | |
152 | * almost certain that the clipboard client will not connect | |
153 | * until after XDM when using XDMCP. | |
154 | */ | |
155 | ||
156 | /* Create the clipboard client thread */ | |
157 | if (!winInitClipboard()) { | |
158 | ErrorF("winProcEstablishConnection - winClipboardInit " | |
159 | "failed.\n"); | |
160 | return iReturn; | |
161 | } | |
162 | ||
163 | ErrorF("winProcEstablishConnection - winInitClipboard returned.\n"); | |
164 | } | |
165 | ||
166 | /* Flag that clipboard client has been launched */ | |
167 | g_fClipboardLaunched = TRUE; | |
168 | ||
169 | return iReturn; | |
170 | } | |
171 | ||
172 | /* | |
173 | * Wrapper for internal SetSelectionOwner function. | |
174 | * Grabs ownership of Windows clipboard when X11 clipboard owner changes. | |
175 | */ | |
176 | ||
177 | int | |
178 | winProcSetSelectionOwner(ClientPtr client) | |
179 | { | |
180 | int i; | |
181 | DrawablePtr pDrawable; | |
182 | WindowPtr pWindow = None; | |
183 | Bool fOwnedToNotOwned = FALSE; | |
184 | static Window s_iOwners[CLIP_NUM_SELECTIONS] = { None }; | |
185 | static unsigned long s_ulServerGeneration = 0; | |
186 | ||
187 | REQUEST(xSetSelectionOwnerReq); | |
188 | ||
189 | REQUEST_SIZE_MATCH(xSetSelectionOwnerReq); | |
190 | ||
191 | winDebug("winProcSetSelectionOwner - Hello.\n"); | |
192 | ||
193 | /* Watch for server reset */ | |
194 | if (s_ulServerGeneration != serverGeneration) { | |
195 | /* Save new generation number */ | |
196 | s_ulServerGeneration = serverGeneration; | |
197 | ||
198 | /* Initialize static variables */ | |
199 | for (i = 0; i < CLIP_NUM_SELECTIONS; ++i) | |
200 | s_iOwners[i] = None; | |
201 | } | |
202 | ||
203 | /* Abort if clipboard not completely initialized yet */ | |
204 | if (!g_fClipboardStarted) { | |
205 | /* ErrorF ("winProcSetSelectionOwner - Clipboard not yet started, " | |
206 | "aborting.\n"); */ | |
207 | goto winProcSetSelectionOwner_Done; | |
208 | } | |
209 | ||
210 | /* Grab window if we have one */ | |
211 | if (None != stuff->window) { | |
212 | /* Grab the Window from the request */ | |
213 | int rc = | |
214 | dixLookupWindow(&pWindow, stuff->window, client, DixReadAccess); | |
215 | if (rc != Success) { | |
216 | ErrorF("winProcSetSelectionOwner - Found BadWindow, aborting.\n"); | |
217 | goto winProcSetSelectionOwner_Done; | |
218 | } | |
219 | } | |
220 | ||
221 | /* Now we either have a valid window or None */ | |
222 | ||
223 | /* Save selection owners for monitored selections, ignore other selections */ | |
224 | if (XA_PRIMARY == stuff->selection) { | |
225 | /* Look for owned -> not owned transition */ | |
226 | if (None == stuff->window && None != s_iOwners[CLIP_OWN_PRIMARY]) { | |
227 | fOwnedToNotOwned = TRUE; | |
228 | ||
229 | winDebug("winProcSetSelectionOwner - PRIMARY - Going from " | |
230 | "owned to not owned.\n"); | |
231 | ||
232 | /* Adjust last owned selection */ | |
233 | if (None != s_iOwners[CLIP_OWN_CLIPBOARD]) | |
234 | g_atomLastOwnedSelection = MakeAtom("CLIPBOARD", 9, TRUE); | |
235 | else | |
236 | g_atomLastOwnedSelection = None; | |
237 | } | |
238 | ||
239 | /* Save new selection owner or None */ | |
240 | s_iOwners[CLIP_OWN_PRIMARY] = stuff->window; | |
241 | ||
242 | winDebug("winProcSetSelectionOwner - PRIMARY - Now owned by: %d\n", | |
243 | stuff->window); | |
244 | } | |
245 | else if (MakeAtom("CLIPBOARD", 9, TRUE) == stuff->selection) { | |
246 | /* Look for owned -> not owned transition */ | |
247 | if (None == stuff->window && None != s_iOwners[CLIP_OWN_CLIPBOARD]) { | |
248 | fOwnedToNotOwned = TRUE; | |
249 | ||
250 | winDebug("winProcSetSelectionOwner - CLIPBOARD - Going from " | |
251 | "owned to not owned.\n"); | |
252 | ||
253 | /* Adjust last owned selection */ | |
254 | if (None != s_iOwners[CLIP_OWN_PRIMARY]) | |
255 | g_atomLastOwnedSelection = XA_PRIMARY; | |
256 | else | |
257 | g_atomLastOwnedSelection = None; | |
258 | } | |
259 | ||
260 | /* Save new selection owner or None */ | |
261 | s_iOwners[CLIP_OWN_CLIPBOARD] = stuff->window; | |
262 | ||
263 | winDebug("winProcSetSelectionOwner - CLIPBOARD - Now owned by: %d\n", | |
264 | stuff->window); | |
265 | ||
266 | } | |
267 | else | |
268 | goto winProcSetSelectionOwner_Done; | |
269 | ||
270 | /* | |
271 | * At this point, if one of the selections is still owned by the | |
272 | * clipboard manager then it should be marked as unowned since | |
273 | * we will be taking ownership of the Win32 clipboard. | |
274 | */ | |
275 | if (g_iClipboardWindow == s_iOwners[CLIP_OWN_PRIMARY]) | |
276 | s_iOwners[CLIP_OWN_PRIMARY] = None; | |
277 | if (g_iClipboardWindow == s_iOwners[CLIP_OWN_CLIPBOARD]) | |
278 | s_iOwners[CLIP_OWN_CLIPBOARD] = None; | |
279 | ||
280 | /* | |
281 | * Handle case when selection is being disowned, | |
282 | * WM_DRAWCLIPBOARD did not do the disowning, | |
283 | * both monitored selections are no longer owned, | |
284 | * an owned to not owned transition was detected, | |
285 | * and we currently own the Win32 clipboard. | |
286 | */ | |
287 | if (stuff->window == None | |
288 | && s_iOwners[CLIP_OWN_PRIMARY] == None | |
289 | && s_iOwners[CLIP_OWN_CLIPBOARD] == None | |
290 | && fOwnedToNotOwned | |
291 | && g_hwndClipboard != NULL && g_hwndClipboard == GetClipboardOwner()) { | |
292 | winDebug("winProcSetSelectionOwner - We currently own the " | |
293 | "clipboard and neither the PRIMARY nor the CLIPBOARD " | |
294 | "selections are owned, releasing ownership of Win32 " | |
295 | "clipboard.\n"); | |
296 | ||
297 | /* Release ownership of the Windows clipboard */ | |
298 | OpenClipboard(NULL); | |
299 | EmptyClipboard(); | |
300 | CloseClipboard(); | |
301 | ||
302 | goto winProcSetSelectionOwner_Done; | |
303 | } | |
304 | ||
305 | /* Abort if no window at this point */ | |
306 | if (None == stuff->window) { | |
307 | winDebug("winProcSetSelectionOwner - No window, returning.\n"); | |
308 | goto winProcSetSelectionOwner_Done; | |
309 | } | |
310 | ||
311 | /* Abort if invalid selection */ | |
312 | if (!ValidAtom(stuff->selection)) { | |
313 | ErrorF("winProcSetSelectionOwner - Found BadAtom, aborting.\n"); | |
314 | goto winProcSetSelectionOwner_Done; | |
315 | } | |
316 | ||
317 | /* Cast Window to Drawable */ | |
318 | pDrawable = (DrawablePtr) pWindow; | |
319 | ||
320 | /* Abort if clipboard manager is owning the selection */ | |
321 | if (pDrawable->id == g_iClipboardWindow) { | |
322 | winDebug("winProcSetSelectionOwner - We changed ownership, " | |
323 | "aborting.\n"); | |
324 | goto winProcSetSelectionOwner_Done; | |
325 | } | |
326 | ||
327 | /* Abort if root window is taking ownership */ | |
328 | if (pDrawable->id == 0) { | |
329 | ErrorF("winProcSetSelectionOwner - Root window taking ownership, " | |
330 | "aborting\n"); | |
331 | goto winProcSetSelectionOwner_Done; | |
332 | } | |
333 | ||
334 | /* Close clipboard if we have it open already */ | |
335 | if (GetOpenClipboardWindow() == g_hwndClipboard) { | |
336 | CloseClipboard(); | |
337 | } | |
338 | ||
339 | /* Access the Windows clipboard */ | |
340 | if (!OpenClipboard(g_hwndClipboard)) { | |
341 | ErrorF("winProcSetSelectionOwner - OpenClipboard () failed: %08x\n", | |
342 | (int) GetLastError()); | |
343 | goto winProcSetSelectionOwner_Done; | |
344 | } | |
345 | ||
346 | /* Take ownership of the Windows clipboard */ | |
347 | if (!EmptyClipboard()) { | |
348 | ErrorF("winProcSetSelectionOwner - EmptyClipboard () failed: %08x\n", | |
349 | (int) GetLastError()); | |
350 | goto winProcSetSelectionOwner_Done; | |
351 | } | |
352 | ||
353 | /* Advertise regular text and unicode */ | |
354 | SetClipboardData(CF_UNICODETEXT, NULL); | |
355 | SetClipboardData(CF_TEXT, NULL); | |
356 | ||
357 | /* Save handle to last owned selection */ | |
358 | g_atomLastOwnedSelection = stuff->selection; | |
359 | ||
360 | /* Release the clipboard */ | |
361 | if (!CloseClipboard()) { | |
362 | ErrorF("winProcSetSelectionOwner - CloseClipboard () failed: " | |
363 | "%08x\n", (int) GetLastError()); | |
364 | goto winProcSetSelectionOwner_Done; | |
365 | } | |
366 | ||
367 | winProcSetSelectionOwner_Done: | |
368 | return (*winProcSetSelectionOwnerOrig) (client); | |
369 | } |