clipboard.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. /*
  2. * Server-side clipboard management
  3. *
  4. * Copyright 2002 Ulrich Czekalla
  5. * Copyright 2016 Alexandre Julliard
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  20. */
  21. #include "config.h"
  22. #include <assert.h>
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include "ntstatus.h"
  27. #define WIN32_NO_STATUS
  28. #include "request.h"
  29. #include "object.h"
  30. #include "file.h"
  31. #include "process.h"
  32. #include "user.h"
  33. #include "winuser.h"
  34. #include "winternl.h"
  35. struct clip_format
  36. {
  37. struct list entry; /* entry in format list */
  38. unsigned int id; /* format id */
  39. unsigned int from; /* for synthesized data, format to generate it from */
  40. unsigned int seqno; /* sequence number when the data was set */
  41. data_size_t size; /* size of the data block */
  42. void *data; /* data contents, or NULL for delay-rendered */
  43. };
  44. struct clipboard
  45. {
  46. struct object obj; /* object header */
  47. struct thread *open_thread; /* thread id that has clipboard open */
  48. user_handle_t open_win; /* window that has clipboard open */
  49. user_handle_t owner; /* window that owns the clipboard */
  50. user_handle_t viewer; /* first window in clipboard viewer list */
  51. unsigned int lcid; /* locale id to use for synthesizing text formats */
  52. unsigned int seqno; /* clipboard change sequence number */
  53. unsigned int open_seqno; /* sequence number at open time */
  54. unsigned int rendering; /* format rendering recursion counter */
  55. struct list formats; /* list of data formats */
  56. unsigned int format_count; /* count of data formats */
  57. unsigned int format_map; /* existence bitmap for formats < CF_MAX */
  58. unsigned int listen_size; /* size of listeners array */
  59. unsigned int listen_count; /* count of listeners */
  60. user_handle_t *listeners; /* array of listener windows */
  61. };
  62. static void clipboard_dump( struct object *obj, int verbose );
  63. static void clipboard_destroy( struct object *obj );
  64. static const struct object_ops clipboard_ops =
  65. {
  66. sizeof(struct clipboard), /* size */
  67. &no_type, /* type */
  68. clipboard_dump, /* dump */
  69. no_add_queue, /* add_queue */
  70. NULL, /* remove_queue */
  71. NULL, /* signaled */
  72. NULL, /* satisfied */
  73. no_signal, /* signal */
  74. no_get_fd, /* get_fd */
  75. default_map_access, /* map_access */
  76. default_get_sd, /* get_sd */
  77. default_set_sd, /* set_sd */
  78. no_get_full_name, /* get_full_name */
  79. no_lookup_name, /* lookup_name */
  80. no_link_name, /* link_name */
  81. NULL, /* unlink_name */
  82. no_open_file, /* open_file */
  83. no_kernel_obj_list, /* get_kernel_obj_list */
  84. no_close_handle, /* close_handle */
  85. clipboard_destroy /* destroy */
  86. };
  87. #define HAS_FORMAT(map,id) ((map) & (1 << (id))) /* only for formats < CF_MAX */
  88. /* find a data format in the clipboard */
  89. static struct clip_format *get_format( struct clipboard *clipboard, unsigned int id )
  90. {
  91. struct clip_format *format;
  92. LIST_FOR_EACH_ENTRY( format, &clipboard->formats, struct clip_format, entry )
  93. if (format->id == id) return format;
  94. return NULL;
  95. }
  96. /* add a data format to the clipboard */
  97. static struct clip_format *add_format( struct clipboard *clipboard, unsigned int id )
  98. {
  99. struct clip_format *format;
  100. if (!(format = mem_alloc( sizeof(*format )))) return NULL;
  101. format->id = id;
  102. format->from = 0;
  103. format->size = 0;
  104. format->data = NULL;
  105. list_add_tail( &clipboard->formats, &format->entry );
  106. clipboard->format_count++;
  107. if (id < CF_MAX) clipboard->format_map |= 1 << id;
  108. return format;
  109. }
  110. /* free all clipboard formats */
  111. static void free_clipboard_formats( struct clipboard *clipboard )
  112. {
  113. struct clip_format *format, *next;
  114. LIST_FOR_EACH_ENTRY_SAFE( format, next, &clipboard->formats, struct clip_format, entry )
  115. {
  116. list_remove( &format->entry );
  117. free( format->data );
  118. free( format );
  119. }
  120. clipboard->format_count = 0;
  121. clipboard->format_map = 0;
  122. }
  123. /* dump a clipboard object */
  124. static void clipboard_dump( struct object *obj, int verbose )
  125. {
  126. struct clipboard *clipboard = (struct clipboard *)obj;
  127. fprintf( stderr, "Clipboard open_thread=%p open_win=%08x owner=%08x viewer=%08x seq=%u\n",
  128. clipboard->open_thread, clipboard->open_win,
  129. clipboard->owner, clipboard->viewer, clipboard->seqno );
  130. }
  131. static void clipboard_destroy( struct object *obj )
  132. {
  133. struct clipboard *clipboard = (struct clipboard *)obj;
  134. free( clipboard->listeners );
  135. free_clipboard_formats( clipboard );
  136. }
  137. /* retrieve the clipboard info for the current process, allocating it if needed */
  138. static struct clipboard *get_process_clipboard(void)
  139. {
  140. struct clipboard *clipboard;
  141. struct winstation *winstation = get_process_winstation( current->process, WINSTA_ACCESSCLIPBOARD );
  142. if (!winstation) return NULL;
  143. if (!(clipboard = winstation->clipboard))
  144. {
  145. if ((clipboard = alloc_object( &clipboard_ops )))
  146. {
  147. clipboard->open_thread = NULL;
  148. clipboard->open_win = 0;
  149. clipboard->owner = 0;
  150. clipboard->viewer = 0;
  151. clipboard->seqno = 0;
  152. clipboard->format_count = 0;
  153. clipboard->format_map = 0;
  154. clipboard->listen_size = 0;
  155. clipboard->listen_count = 0;
  156. clipboard->listeners = NULL;
  157. list_init( &clipboard->formats );
  158. winstation->clipboard = clipboard;
  159. }
  160. }
  161. release_object( winstation );
  162. return clipboard;
  163. }
  164. /* add synthesized formats upon clipboard close */
  165. static int synthesize_formats( struct clipboard *clipboard )
  166. {
  167. static const unsigned int formats[][3] =
  168. {
  169. { CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT },
  170. { CF_OEMTEXT, CF_UNICODETEXT, CF_TEXT },
  171. { CF_UNICODETEXT, CF_TEXT, CF_OEMTEXT },
  172. { CF_METAFILEPICT, CF_ENHMETAFILE },
  173. { CF_ENHMETAFILE, CF_METAFILEPICT },
  174. { CF_BITMAP, CF_DIB, CF_DIBV5 },
  175. { CF_DIB, CF_BITMAP, CF_DIBV5 },
  176. { CF_DIBV5, CF_BITMAP, CF_DIB }
  177. };
  178. unsigned int i, from, total = 0, map = clipboard->format_map;
  179. struct clip_format *format;
  180. if (!HAS_FORMAT( map, CF_LOCALE ) &&
  181. (HAS_FORMAT( map, CF_TEXT ) || HAS_FORMAT( map, CF_OEMTEXT ) || HAS_FORMAT( map, CF_UNICODETEXT )))
  182. {
  183. void *data = memdup( &clipboard->lcid, sizeof(clipboard->lcid) );
  184. if (data && (format = add_format( clipboard, CF_LOCALE )))
  185. {
  186. format->seqno = clipboard->seqno++;
  187. format->data = data;
  188. format->size = sizeof(clipboard->lcid);
  189. }
  190. else free( data );
  191. }
  192. for (i = 0; i < ARRAY_SIZE( formats ); i++)
  193. {
  194. if (HAS_FORMAT( map, formats[i][0] )) continue;
  195. if (HAS_FORMAT( map, formats[i][1] )) from = formats[i][1];
  196. else if (HAS_FORMAT( map, formats[i][2] )) from = formats[i][2];
  197. else continue;
  198. if (!(format = add_format( clipboard, formats[i][0] ))) continue;
  199. format->from = from;
  200. format->seqno = clipboard->seqno;
  201. total++;
  202. }
  203. return total;
  204. }
  205. /* add a clipboard listener */
  206. static void add_listener( struct clipboard *clipboard, user_handle_t window )
  207. {
  208. unsigned int i;
  209. for (i = 0; i < clipboard->listen_count; i++)
  210. {
  211. if (clipboard->listeners[i] != window) continue;
  212. set_error( STATUS_INVALID_PARAMETER ); /* already set */
  213. return;
  214. }
  215. if (clipboard->listen_size == clipboard->listen_count)
  216. {
  217. unsigned int new_size = max( 8, clipboard->listen_size * 2 );
  218. user_handle_t *new = realloc( clipboard->listeners, new_size * sizeof(*new) );
  219. if (!new)
  220. {
  221. set_error( STATUS_NO_MEMORY );
  222. return;
  223. }
  224. clipboard->listeners = new;
  225. clipboard->listen_size = new_size;
  226. }
  227. clipboard->listeners[clipboard->listen_count++] = window;
  228. }
  229. /* remove a clipboard listener */
  230. static int remove_listener( struct clipboard *clipboard, user_handle_t window )
  231. {
  232. unsigned int i;
  233. for (i = 0; i < clipboard->listen_count; i++)
  234. {
  235. if (clipboard->listeners[i] != window) continue;
  236. memmove( clipboard->listeners + i, clipboard->listeners + i + 1,
  237. (clipboard->listen_count - i - 1) * sizeof(*clipboard->listeners) );
  238. clipboard->listen_count--;
  239. return 1;
  240. }
  241. return 0;
  242. }
  243. /* notify all listeners, and return the viewer window that should be notified if any */
  244. static user_handle_t notify_listeners( struct clipboard *clipboard )
  245. {
  246. unsigned int i;
  247. for (i = 0; i < clipboard->listen_count; i++)
  248. post_message( clipboard->listeners[i], WM_CLIPBOARDUPDATE, 0, 0 );
  249. return clipboard->viewer;
  250. }
  251. /* close the clipboard, and return the viewer window that should be notified if any */
  252. static user_handle_t close_clipboard( struct clipboard *clipboard )
  253. {
  254. clipboard->open_win = 0;
  255. clipboard->open_thread = NULL;
  256. if (clipboard->seqno == clipboard->open_seqno) return 0; /* unchanged */
  257. if (synthesize_formats( clipboard )) clipboard->seqno++;
  258. return notify_listeners( clipboard );
  259. }
  260. /* release the clipboard owner, and return the viewer window that should be notified if any */
  261. static user_handle_t release_clipboard( struct clipboard *clipboard )
  262. {
  263. struct clip_format *format, *next;
  264. int changed = 0;
  265. clipboard->owner = 0;
  266. /* free the delayed-rendered formats, since we no longer have an owner to render them */
  267. LIST_FOR_EACH_ENTRY_SAFE( format, next, &clipboard->formats, struct clip_format, entry )
  268. {
  269. if (format->data) continue;
  270. /* format->from is earlier in the list and thus has already been
  271. * removed if not available anymore (it is also < CF_MAX)
  272. */
  273. if (format->from && HAS_FORMAT( clipboard->format_map, format->from )) continue;
  274. list_remove( &format->entry );
  275. if (format->id < CF_MAX) clipboard->format_map &= ~(1 << format->id);
  276. clipboard->format_count--;
  277. free( format );
  278. changed = 1;
  279. }
  280. if (!changed) return 0;
  281. clipboard->seqno++;
  282. return notify_listeners( clipboard );
  283. }
  284. /* cleanup clipboard information upon window destruction */
  285. void cleanup_clipboard_window( struct desktop *desktop, user_handle_t window )
  286. {
  287. struct clipboard *clipboard = desktop->winstation->clipboard;
  288. if (!clipboard) return;
  289. remove_listener( clipboard, window );
  290. if (clipboard->viewer == window) clipboard->viewer = 0;
  291. if (clipboard->owner == window) release_clipboard( clipboard );
  292. if (clipboard->open_win == window)
  293. {
  294. user_handle_t viewer = close_clipboard( clipboard );
  295. if (viewer) send_notify_message( viewer, WM_DRAWCLIPBOARD, clipboard->owner, 0 );
  296. }
  297. }
  298. /* Called when thread terminates to allow release of clipboard */
  299. void cleanup_clipboard_thread(struct thread *thread)
  300. {
  301. struct clipboard *clipboard;
  302. struct winstation *winstation;
  303. if (!thread->process->winstation) return;
  304. if (!(winstation = get_process_winstation( thread->process, WINSTA_ACCESSCLIPBOARD ))) return;
  305. if ((clipboard = winstation->clipboard))
  306. {
  307. if (thread == clipboard->open_thread)
  308. {
  309. user_handle_t viewer = close_clipboard( clipboard );
  310. if (viewer) send_notify_message( viewer, WM_DRAWCLIPBOARD, clipboard->owner, 0 );
  311. }
  312. }
  313. release_object( winstation );
  314. }
  315. /* open the clipboard */
  316. DECL_HANDLER(open_clipboard)
  317. {
  318. struct clipboard *clipboard = get_process_clipboard();
  319. user_handle_t win = 0;
  320. if (!clipboard) return;
  321. if (req->window && !(win = get_valid_window_handle( req->window ))) return;
  322. if (clipboard->open_thread && clipboard->open_win != win)
  323. {
  324. set_error( STATUS_INVALID_LOCK_SEQUENCE );
  325. return;
  326. }
  327. if (!clipboard->open_thread) clipboard->open_seqno = clipboard->seqno; /* first open */
  328. if (clipboard->open_thread != current) clipboard->rendering = 0;
  329. clipboard->open_win = win;
  330. clipboard->open_thread = current;
  331. reply->owner = clipboard->owner;
  332. }
  333. /* close the clipboard */
  334. DECL_HANDLER(close_clipboard)
  335. {
  336. struct clipboard *clipboard = get_process_clipboard();
  337. if (!clipboard) return;
  338. if (clipboard->open_thread != current)
  339. {
  340. set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
  341. return;
  342. }
  343. reply->viewer = close_clipboard( clipboard );
  344. reply->owner = clipboard->owner;
  345. }
  346. /* add a data format to the clipboard */
  347. DECL_HANDLER(set_clipboard_data)
  348. {
  349. struct clip_format *format;
  350. struct clipboard *clipboard = get_process_clipboard();
  351. void *data = NULL;
  352. if (!clipboard) return;
  353. if (!req->format || !clipboard->open_thread)
  354. {
  355. set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
  356. return;
  357. }
  358. if (get_req_data_size() && !(data = memdup( get_req_data(), get_req_data_size() ))) return;
  359. if (!(format = get_format( clipboard, req->format )))
  360. {
  361. if (!(format = add_format( clipboard, req->format )))
  362. {
  363. free( data );
  364. return;
  365. }
  366. }
  367. free( format->data );
  368. format->from = 0;
  369. format->seqno = clipboard->seqno;
  370. format->size = get_req_data_size();
  371. format->data = data;
  372. if (!clipboard->rendering) clipboard->seqno++;
  373. if (req->format == CF_TEXT || req->format == CF_OEMTEXT || req->format == CF_UNICODETEXT)
  374. clipboard->lcid = req->lcid;
  375. reply->seqno = format->seqno;
  376. }
  377. /* fetch a data format from the clipboard */
  378. DECL_HANDLER(get_clipboard_data)
  379. {
  380. struct clip_format *format;
  381. struct clipboard *clipboard = get_process_clipboard();
  382. if (!clipboard) return;
  383. if (clipboard->open_thread != current)
  384. {
  385. set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
  386. return;
  387. }
  388. if (!(format = get_format( clipboard, req->format )))
  389. {
  390. set_error( STATUS_OBJECT_NAME_NOT_FOUND );
  391. goto done;
  392. }
  393. reply->from = format->from;
  394. reply->total = format->size;
  395. reply->seqno = format->seqno;
  396. reply->owner = clipboard->owner;
  397. if (!format->data && req->render) /* try rendering it client-side */
  398. {
  399. if (format->from || clipboard->owner) clipboard->rendering++;
  400. return;
  401. }
  402. if (req->cached && req->seqno == format->seqno) goto done; /* client-side cache still valid */
  403. if (format->size > get_reply_max_size())
  404. {
  405. set_error( STATUS_BUFFER_OVERFLOW );
  406. return;
  407. }
  408. set_reply_data( format->data, format->size );
  409. done:
  410. if (!req->render) clipboard->rendering--;
  411. }
  412. /* retrieve a list of available formats */
  413. DECL_HANDLER(get_clipboard_formats)
  414. {
  415. struct clipboard *clipboard = get_process_clipboard();
  416. if (!clipboard) return;
  417. if (!req->format)
  418. {
  419. struct clip_format *format;
  420. unsigned int i = 0, *ptr;
  421. data_size_t size = clipboard->format_count * sizeof(unsigned int);
  422. reply->count = clipboard->format_count;
  423. if (size <= get_reply_max_size())
  424. {
  425. if ((ptr = mem_alloc( size )))
  426. {
  427. LIST_FOR_EACH_ENTRY( format, &clipboard->formats, struct clip_format, entry )
  428. ptr[i++] = format->id;
  429. assert( i == clipboard->format_count );
  430. set_reply_data_ptr( ptr, size );
  431. }
  432. }
  433. else set_error( STATUS_BUFFER_TOO_SMALL );
  434. }
  435. else reply->count = (get_format( clipboard, req->format ) != NULL); /* query a single format */
  436. }
  437. /* retrieve the next available format */
  438. DECL_HANDLER(enum_clipboard_formats)
  439. {
  440. struct list *ptr;
  441. struct clipboard *clipboard = get_process_clipboard();
  442. if (!clipboard) return;
  443. if (clipboard->open_thread != current)
  444. {
  445. set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
  446. return;
  447. }
  448. ptr = list_head( &clipboard->formats );
  449. if (req->previous)
  450. {
  451. while (ptr && LIST_ENTRY( ptr, struct clip_format, entry )->id != req->previous)
  452. ptr = list_next( &clipboard->formats, ptr );
  453. if (ptr) ptr = list_next( &clipboard->formats, ptr );
  454. }
  455. if (ptr) reply->format = LIST_ENTRY( ptr, struct clip_format, entry )->id;
  456. }
  457. /* empty the clipboard and grab ownership */
  458. DECL_HANDLER(empty_clipboard)
  459. {
  460. struct clipboard *clipboard = get_process_clipboard();
  461. if (!clipboard) return;
  462. if (clipboard->open_thread != current)
  463. {
  464. set_win32_error( ERROR_CLIPBOARD_NOT_OPEN );
  465. return;
  466. }
  467. free_clipboard_formats( clipboard );
  468. clipboard->owner = clipboard->open_win;
  469. clipboard->seqno++;
  470. }
  471. /* release ownership of the clipboard */
  472. DECL_HANDLER(release_clipboard)
  473. {
  474. struct clipboard *clipboard = get_process_clipboard();
  475. user_handle_t owner;
  476. if (!clipboard) return;
  477. if (!(owner = get_valid_window_handle( req->owner ))) return;
  478. if (clipboard->owner == owner)
  479. {
  480. reply->viewer = release_clipboard( clipboard );
  481. reply->owner = clipboard->owner;
  482. }
  483. else set_error( STATUS_INVALID_OWNER );
  484. }
  485. /* get clipboard information */
  486. DECL_HANDLER(get_clipboard_info)
  487. {
  488. struct clipboard *clipboard = get_process_clipboard();
  489. if (!clipboard) return;
  490. reply->window = clipboard->open_win;
  491. reply->owner = clipboard->owner;
  492. reply->viewer = clipboard->viewer;
  493. reply->seqno = clipboard->seqno;
  494. }
  495. /* set the clipboard viewer window */
  496. DECL_HANDLER(set_clipboard_viewer)
  497. {
  498. struct clipboard *clipboard = get_process_clipboard();
  499. user_handle_t viewer = 0, previous = 0;
  500. if (!clipboard) return;
  501. if (req->viewer && !(viewer = get_valid_window_handle( req->viewer ))) return;
  502. if (req->previous && !(previous = get_valid_window_handle( req->previous ))) return;
  503. reply->old_viewer = clipboard->viewer;
  504. reply->owner = clipboard->owner;
  505. if (!previous || clipboard->viewer == previous)
  506. clipboard->viewer = viewer;
  507. else
  508. set_error( STATUS_PENDING ); /* need to send message instead */
  509. }
  510. /* add a clipboard listener window */
  511. DECL_HANDLER(add_clipboard_listener)
  512. {
  513. struct clipboard *clipboard = get_process_clipboard();
  514. user_handle_t win;
  515. if (!clipboard) return;
  516. if (!(win = get_valid_window_handle( req->window ))) return;
  517. add_listener( clipboard, win );
  518. }
  519. /* remove a clipboard listener window */
  520. DECL_HANDLER(remove_clipboard_listener)
  521. {
  522. struct clipboard *clipboard = get_process_clipboard();
  523. user_handle_t win;
  524. if (!clipboard) return;
  525. if (!(win = get_valid_window_handle( req->window ))) return;
  526. if (!remove_listener( clipboard, win )) set_error( STATUS_INVALID_PARAMETER );
  527. }