server: Don't release the clipboard owner window when the owner thread terminates.
[wine.git] / server / clipboard.c
blob323fdb9787c7a0274b43a2b59addff9f5d431f62
1 /*
2 * Server-side clipboard management
4 * Copyright (C) 2002 Ulrich Czekalla
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #include "request.h"
32 #include "object.h"
33 #include "file.h"
34 #include "process.h"
35 #include "user.h"
36 #include "winuser.h"
37 #include "winternl.h"
39 struct clipboard
41 struct object obj; /* object header */
42 struct thread *open_thread; /* thread id that has clipboard open */
43 user_handle_t open_win; /* window that has clipboard open */
44 struct thread *owner_thread; /* thread id that owns the clipboard */
45 user_handle_t owner_win; /* window that owns the clipboard data */
46 user_handle_t viewer; /* first window in clipboard viewer list */
47 unsigned int seqno; /* clipboard change sequence number */
48 unsigned int open_seqno; /* sequence number at open time */
49 timeout_t seqno_timestamp; /* time stamp of last seqno increment */
50 unsigned int listen_size; /* size of listeners array */
51 unsigned int listen_count; /* count of listeners */
52 user_handle_t *listeners; /* array of listener windows */
55 static void clipboard_dump( struct object *obj, int verbose );
56 static void clipboard_destroy( struct object *obj );
58 static const struct object_ops clipboard_ops =
60 sizeof(struct clipboard), /* size */
61 clipboard_dump, /* dump */
62 no_get_type, /* get_type */
63 no_add_queue, /* add_queue */
64 NULL, /* remove_queue */
65 NULL, /* signaled */
66 NULL, /* satisfied */
67 no_signal, /* signal */
68 no_get_fd, /* get_fd */
69 no_map_access, /* map_access */
70 default_get_sd, /* get_sd */
71 default_set_sd, /* set_sd */
72 no_lookup_name, /* lookup_name */
73 no_link_name, /* link_name */
74 NULL, /* unlink_name */
75 no_open_file, /* open_file */
76 no_close_handle, /* close_handle */
77 clipboard_destroy /* destroy */
81 #define MINUPDATELAPSE (2 * TICKS_PER_SEC)
83 /* dump a clipboard object */
84 static void clipboard_dump( struct object *obj, int verbose )
86 struct clipboard *clipboard = (struct clipboard *)obj;
88 fprintf( stderr, "Clipboard open_thread=%p open_win=%08x owner_thread=%p owner_win=%08x viewer=%08x seq=%u\n",
89 clipboard->open_thread, clipboard->open_win, clipboard->owner_thread,
90 clipboard->owner_win, clipboard->viewer, clipboard->seqno );
93 static void clipboard_destroy( struct object *obj )
95 struct clipboard *clipboard = (struct clipboard *)obj;
97 free( clipboard->listeners );
100 /* retrieve the clipboard info for the current process, allocating it if needed */
101 static struct clipboard *get_process_clipboard(void)
103 struct clipboard *clipboard;
104 struct winstation *winstation = get_process_winstation( current->process, WINSTA_ACCESSCLIPBOARD );
106 if (!winstation) return NULL;
108 if (!(clipboard = winstation->clipboard))
110 if ((clipboard = alloc_object( &clipboard_ops )))
112 clipboard->open_thread = NULL;
113 clipboard->open_win = 0;
114 clipboard->owner_thread = NULL;
115 clipboard->owner_win = 0;
116 clipboard->viewer = 0;
117 clipboard->seqno = 0;
118 clipboard->seqno_timestamp = 0;
119 clipboard->listen_size = 0;
120 clipboard->listen_count = 0;
121 clipboard->listeners = NULL;
122 winstation->clipboard = clipboard;
125 release_object( winstation );
126 return clipboard;
129 /* add a clipboard listener */
130 static void add_listener( struct clipboard *clipboard, user_handle_t window )
132 unsigned int i;
134 for (i = 0; i < clipboard->listen_count; i++)
136 if (clipboard->listeners[i] != window) continue;
137 set_error( STATUS_INVALID_PARAMETER ); /* already set */
138 return;
140 if (clipboard->listen_size == clipboard->listen_count)
142 unsigned int new_size = max( 8, clipboard->listen_size * 2 );
143 user_handle_t *new = realloc( clipboard->listeners, new_size * sizeof(*new) );
144 if (!new)
146 set_error( STATUS_NO_MEMORY );
147 return;
149 clipboard->listeners = new;
150 clipboard->listen_size = new_size;
152 clipboard->listeners[clipboard->listen_count++] = window;
155 /* remove a clipboard listener */
156 static int remove_listener( struct clipboard *clipboard, user_handle_t window )
158 unsigned int i;
160 for (i = 0; i < clipboard->listen_count; i++)
162 if (clipboard->listeners[i] != window) continue;
163 memmove( clipboard->listeners + i, clipboard->listeners + i + 1,
164 (clipboard->listen_count - i - 1) * sizeof(*clipboard->listeners) );
165 clipboard->listen_count--;
166 return 1;
168 return 0;
171 /* close the clipboard, and return the viewer window that should be notified if any */
172 static user_handle_t close_clipboard( struct clipboard *clipboard )
174 unsigned int i;
176 clipboard->open_win = 0;
177 clipboard->open_thread = NULL;
179 if (clipboard->seqno == clipboard->open_seqno) return 0; /* unchanged */
181 for (i = 0; i < clipboard->listen_count; i++)
182 post_message( clipboard->listeners[i], WM_CLIPBOARDUPDATE, 0, 0 );
183 return clipboard->viewer;
186 /* release the clipboard owner, and return the viewer window that should be notified if any */
187 static user_handle_t release_clipboard( struct clipboard *clipboard )
189 clipboard->owner_win = 0;
190 clipboard->owner_thread = NULL;
191 /* FIXME: free delay-rendered formats if any and notify listeners */
192 return 0;
195 /* cleanup clipboard information upon window destruction */
196 void cleanup_clipboard_window( struct desktop *desktop, user_handle_t window )
198 struct clipboard *clipboard = desktop->winstation->clipboard;
200 if (!clipboard) return;
202 remove_listener( clipboard, window );
203 if (clipboard->viewer == window) clipboard->viewer = 0;
204 if (clipboard->owner_win == window) release_clipboard( clipboard );
205 if (clipboard->open_win == window)
207 user_handle_t viewer = close_clipboard( clipboard );
208 if (viewer) send_notify_message( viewer, WM_DRAWCLIPBOARD, clipboard->owner_win, 0 );
212 /* Called when thread terminates to allow release of clipboard */
213 void cleanup_clipboard_thread(struct thread *thread)
215 struct clipboard *clipboard;
216 struct winstation *winstation;
218 if (!thread->process->winstation) return;
219 if (!(winstation = get_process_winstation( thread->process, WINSTA_ACCESSCLIPBOARD ))) return;
221 if ((clipboard = winstation->clipboard))
223 if (thread == clipboard->owner_thread) clipboard->owner_thread = NULL;
224 if (thread == clipboard->open_thread)
226 user_handle_t viewer = close_clipboard( clipboard );
227 if (viewer) send_notify_message( viewer, WM_DRAWCLIPBOARD, clipboard->owner_win, 0 );
230 release_object( winstation );
233 static int release_clipboard_owner( struct clipboard *clipboard, user_handle_t win )
235 if ((clipboard->open_thread && clipboard->open_thread->process != current->process) ||
236 (win && clipboard->owner_win != get_user_full_handle( win )))
238 set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
239 return 0;
241 clipboard->owner_win = 0;
242 clipboard->owner_thread = NULL;
243 return 1;
247 static int get_seqno( struct clipboard *clipboard )
249 if (!clipboard->owner_thread && (current_time - clipboard->seqno_timestamp > MINUPDATELAPSE))
251 clipboard->seqno_timestamp = current_time;
252 clipboard->seqno++;
254 return clipboard->seqno;
258 /* open the clipboard */
259 DECL_HANDLER(open_clipboard)
261 struct clipboard *clipboard = get_process_clipboard();
262 user_handle_t win = 0;
264 if (!clipboard) return;
265 if (req->window && !(win = get_valid_window_handle( req->window ))) return;
267 if (clipboard->open_thread && clipboard->open_win != win)
269 set_error( STATUS_INVALID_LOCK_SEQUENCE );
270 return;
273 if (!clipboard->open_thread) clipboard->open_seqno = clipboard->seqno; /* first open */
274 clipboard->open_win = win;
275 clipboard->open_thread = current;
277 reply->owner = (clipboard->owner_thread && clipboard->owner_thread->process == current->process);
281 /* close the clipboard */
282 DECL_HANDLER(close_clipboard)
284 struct clipboard *clipboard = get_process_clipboard();
286 if (!clipboard) return;
288 if (clipboard->open_thread != current)
290 set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
291 return;
293 if (req->changed) clipboard->seqno++;
295 reply->viewer = close_clipboard( clipboard );
296 reply->owner = (clipboard->owner_thread && clipboard->owner_thread->process == current->process);
300 DECL_HANDLER(set_clipboard_info)
302 struct clipboard *clipboard = get_process_clipboard();
304 if (!clipboard) return;
306 reply->old_clipboard = clipboard->open_win;
307 reply->old_owner = clipboard->owner_win;
308 reply->old_viewer = clipboard->viewer;
310 if (req->flags & SET_CB_RELOWNER)
312 if (!release_clipboard_owner( clipboard, req->owner )) return;
315 if (req->flags & SET_CB_SEQNO) clipboard->seqno++;
317 reply->seqno = get_seqno( clipboard );
319 if (clipboard->open_thread) reply->flags |= CB_OPEN_ANY;
320 if (clipboard->open_thread == current) reply->flags |= CB_OPEN;
321 if (clipboard->owner_thread == current) reply->flags |= CB_OWNER;
322 if (clipboard->owner_thread && clipboard->owner_thread->process == current->process)
323 reply->flags |= CB_PROCESS;
327 /* empty the clipboard and grab ownership */
328 DECL_HANDLER(empty_clipboard)
330 struct clipboard *clipboard = get_process_clipboard();
332 if (!clipboard) return;
334 if (clipboard->open_thread != current)
336 set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
337 return;
339 clipboard->owner_win = clipboard->open_win;
340 clipboard->owner_thread = clipboard->open_thread;
341 clipboard->seqno++;
345 /* release ownership of the clipboard */
346 DECL_HANDLER(release_clipboard)
348 struct clipboard *clipboard = get_process_clipboard();
349 user_handle_t owner;
351 if (!clipboard) return;
353 if (!(owner = get_valid_window_handle( req->owner ))) return;
355 if (clipboard->owner_win == owner)
356 reply->viewer = release_clipboard( clipboard );
357 else
358 set_error( STATUS_INVALID_OWNER );
362 /* get clipboard information */
363 DECL_HANDLER(get_clipboard_info)
365 struct clipboard *clipboard = get_process_clipboard();
367 if (!clipboard) return;
369 reply->window = clipboard->open_win;
370 reply->owner = clipboard->owner_win;
371 reply->viewer = clipboard->viewer;
372 reply->seqno = get_seqno( clipboard );
376 /* set the clipboard viewer window */
377 DECL_HANDLER(set_clipboard_viewer)
379 struct clipboard *clipboard = get_process_clipboard();
380 user_handle_t viewer = 0, previous = 0;
382 if (!clipboard) return;
383 if (req->viewer && !(viewer = get_valid_window_handle( req->viewer ))) return;
384 if (req->previous && !(previous = get_valid_window_handle( req->previous ))) return;
386 reply->old_viewer = clipboard->viewer;
387 reply->owner = clipboard->owner_win;
389 if (!previous || clipboard->viewer == previous)
390 clipboard->viewer = viewer;
391 else
392 set_error( STATUS_PENDING ); /* need to send message instead */
396 /* add a clipboard listener window */
397 DECL_HANDLER(add_clipboard_listener)
399 struct clipboard *clipboard = get_process_clipboard();
400 user_handle_t win;
402 if (!clipboard) return;
403 if (!(win = get_valid_window_handle( req->window ))) return;
405 add_listener( clipboard, win );
409 /* remove a clipboard listener window */
410 DECL_HANDLER(remove_clipboard_listener)
412 struct clipboard *clipboard = get_process_clipboard();
413 user_handle_t win;
415 if (!clipboard) return;
416 if (!(win = get_valid_window_handle( req->window ))) return;
418 if (!remove_listener( clipboard, win )) set_error( STATUS_INVALID_PARAMETER );