2 * Copyright © 2012 Intel Corporation
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 writable_callback(int fd, uint32_t mask, void *data)
35 struct weston_wm *wm = data;
36 unsigned char *property;
39 property = xcb_get_property_value(wm->property_reply);
40 remainder = xcb_get_property_value_length(wm->property_reply) -
43 len = write(fd, property + wm->property_start, remainder);
45 free(wm->property_reply);
46 wm->property_reply = NULL;
47 if (wm->property_source)
48 wl_event_source_remove(wm->property_source);
50 weston_log("write error to target fd: %m\n");
54 weston_log("wrote %d (chunk size %d) of %d bytes\n",
55 wm->property_start + len,
56 len, xcb_get_property_value_length(wm->property_reply));
58 wm->property_start += len;
59 if (len == remainder) {
60 free(wm->property_reply);
61 wm->property_reply = NULL;
62 if (wm->property_source)
63 wl_event_source_remove(wm->property_source);
66 xcb_delete_property(wm->conn,
68 wm->atom.wl_selection);
70 weston_log("transfer complete\n");
79 weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply)
81 wm->property_start = 0;
82 wm->property_reply = reply;
83 writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm);
85 if (wm->property_reply)
87 wl_event_loop_add_fd(wm->server->loop,
90 writable_callback, wm);
94 weston_wm_get_incr_chunk(struct weston_wm *wm)
96 xcb_get_property_cookie_t cookie;
97 xcb_get_property_reply_t *reply;
99 cookie = xcb_get_property(wm->conn,
101 wm->selection_window,
102 wm->atom.wl_selection,
103 XCB_GET_PROPERTY_TYPE_ANY,
105 0x1fffffff /* length */);
107 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
109 dump_property(wm, wm->atom.wl_selection, reply);
111 if (xcb_get_property_value_length(reply) > 0) {
112 weston_wm_write_property(wm, reply);
114 weston_log("transfer complete\n");
115 close(wm->data_source_fd);
120 struct x11_data_source {
121 struct weston_data_source base;
122 struct weston_wm *wm;
126 data_source_accept(struct weston_data_source *source,
127 uint32_t time, const char *mime_type)
132 data_source_send(struct weston_data_source *base,
133 const char *mime_type, int32_t fd)
135 struct x11_data_source *source = (struct x11_data_source *) base;
136 struct weston_wm *wm = source->wm;
138 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
139 /* Get data for the utf8_string target */
140 xcb_convert_selection(wm->conn,
141 wm->selection_window,
143 wm->atom.utf8_string,
144 wm->atom.wl_selection,
145 XCB_TIME_CURRENT_TIME);
149 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
150 wm->data_source_fd = fd;
155 data_source_cancel(struct weston_data_source *source)
160 weston_wm_get_selection_targets(struct weston_wm *wm)
162 struct x11_data_source *source;
163 struct weston_compositor *compositor;
164 struct weston_seat *seat = weston_wm_pick_seat(wm);
165 xcb_get_property_cookie_t cookie;
166 xcb_get_property_reply_t *reply;
171 cookie = xcb_get_property(wm->conn,
173 wm->selection_window,
174 wm->atom.wl_selection,
175 XCB_GET_PROPERTY_TYPE_ANY,
179 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
181 dump_property(wm, wm->atom.wl_selection, reply);
183 if (reply->type != XCB_ATOM_ATOM) {
188 source = malloc(sizeof *source);
192 wl_signal_init(&source->base.destroy_signal);
193 source->base.accept = data_source_accept;
194 source->base.send = data_source_send;
195 source->base.cancel = data_source_cancel;
198 wl_array_init(&source->base.mime_types);
199 value = xcb_get_property_value(reply);
200 for (i = 0; i < reply->value_len; i++) {
201 if (value[i] == wm->atom.utf8_string) {
202 p = wl_array_add(&source->base.mime_types, sizeof *p);
204 *p = strdup("text/plain;charset=utf-8");
208 compositor = wm->server->compositor;
209 weston_seat_set_selection(seat, &source->base,
210 wl_display_next_serial(compositor->wl_display));
216 weston_wm_get_selection_data(struct weston_wm *wm)
218 xcb_get_property_cookie_t cookie;
219 xcb_get_property_reply_t *reply;
221 cookie = xcb_get_property(wm->conn,
223 wm->selection_window,
224 wm->atom.wl_selection,
225 XCB_GET_PROPERTY_TYPE_ANY,
227 0x1fffffff /* length */);
229 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
231 if (reply->type == wm->atom.incr) {
232 dump_property(wm, wm->atom.wl_selection, reply);
236 dump_property(wm, wm->atom.wl_selection, reply);
238 weston_wm_write_property(wm, reply);
243 weston_wm_handle_selection_notify(struct weston_wm *wm,
244 xcb_generic_event_t *event)
246 xcb_selection_notify_event_t *selection_notify =
247 (xcb_selection_notify_event_t *) event;
249 if (selection_notify->property == XCB_ATOM_NONE) {
250 /* convert selection failed */
251 } else if (selection_notify->target == wm->atom.targets) {
252 weston_wm_get_selection_targets(wm);
254 weston_wm_get_selection_data(wm);
258 static const size_t incr_chunk_size = 64 * 1024;
261 weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
263 xcb_selection_notify_event_t selection_notify;
265 memset(&selection_notify, 0, sizeof selection_notify);
266 selection_notify.response_type = XCB_SELECTION_NOTIFY;
267 selection_notify.sequence = 0;
268 selection_notify.time = wm->selection_request.time;
269 selection_notify.requestor = wm->selection_request.requestor;
270 selection_notify.selection = wm->selection_request.selection;
271 selection_notify.target = wm->selection_request.target;
272 selection_notify.property = property;
274 xcb_send_event(wm->conn, 0, /* propagate */
275 wm->selection_request.requestor,
276 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
280 weston_wm_send_targets(struct weston_wm *wm)
282 xcb_atom_t targets[] = {
285 wm->atom.utf8_string,
286 /* wm->atom.compound_text, */
288 /* wm->atom.string */
291 xcb_change_property(wm->conn,
292 XCB_PROP_MODE_REPLACE,
293 wm->selection_request.requestor,
294 wm->selection_request.property,
297 ARRAY_LENGTH(targets), targets);
299 weston_wm_send_selection_notify(wm, wm->selection_request.property);
303 weston_wm_send_timestamp(struct weston_wm *wm)
305 xcb_change_property(wm->conn,
306 XCB_PROP_MODE_REPLACE,
307 wm->selection_request.requestor,
308 wm->selection_request.property,
311 1, &wm->selection_timestamp);
313 weston_wm_send_selection_notify(wm, wm->selection_request.property);
317 weston_wm_flush_source_data(struct weston_wm *wm)
321 xcb_change_property(wm->conn,
322 XCB_PROP_MODE_REPLACE,
323 wm->selection_request.requestor,
324 wm->selection_request.property,
325 wm->selection_target,
327 wm->source_data.size,
328 wm->source_data.data);
329 wm->selection_property_set = 1;
330 length = wm->source_data.size;
331 wm->source_data.size = 0;
337 weston_wm_read_data_source(int fd, uint32_t mask, void *data)
339 struct weston_wm *wm = data;
340 int len, current, available;
343 current = wm->source_data.size;
344 if (wm->source_data.size < incr_chunk_size)
345 p = wl_array_add(&wm->source_data, incr_chunk_size);
347 p = (char *) wm->source_data.data + wm->source_data.size;
348 available = wm->source_data.alloc - current;
350 len = read(fd, p, available);
352 weston_log("read error from data source: %m\n");
353 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
354 wl_event_source_remove(wm->property_source);
356 wl_array_release(&wm->source_data);
359 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
360 len, available, mask, len, (char *) p);
362 wm->source_data.size = current + len;
363 if (wm->source_data.size >= incr_chunk_size) {
365 weston_log("got %zu bytes, starting incr\n",
366 wm->source_data.size);
368 xcb_change_property(wm->conn,
369 XCB_PROP_MODE_REPLACE,
370 wm->selection_request.requestor,
371 wm->selection_request.property,
374 1, &incr_chunk_size);
375 wm->selection_property_set = 1;
376 wm->flush_property_on_delete = 1;
377 wl_event_source_remove(wm->property_source);
378 weston_wm_send_selection_notify(wm, wm->selection_request.property);
379 } else if (wm->selection_property_set) {
380 weston_log("got %zu bytes, waiting for "
381 "property delete\n", wm->source_data.size);
383 wm->flush_property_on_delete = 1;
384 wl_event_source_remove(wm->property_source);
386 weston_log("got %zu bytes, "
387 "property deleted, seting new property\n",
388 wm->source_data.size);
389 weston_wm_flush_source_data(wm);
391 } else if (len == 0 && !wm->incr) {
392 weston_log("non-incr transfer complete\n");
393 /* Non-incr transfer all done. */
394 weston_wm_flush_source_data(wm);
395 weston_wm_send_selection_notify(wm, wm->selection_request.property);
397 wl_event_source_remove(wm->property_source);
399 wl_array_release(&wm->source_data);
400 wm->selection_request.requestor = XCB_NONE;
401 } else if (len == 0 && wm->incr) {
402 weston_log("incr transfer complete\n");
404 wm->flush_property_on_delete = 1;
405 if (wm->selection_property_set) {
406 weston_log("got %zu bytes, waiting for "
407 "property delete\n", wm->source_data.size);
409 weston_log("got %zu bytes, "
410 "property deleted, seting new property\n",
411 wm->source_data.size);
412 weston_wm_flush_source_data(wm);
415 wl_event_source_remove(wm->property_source);
416 close(wm->data_source_fd);
417 wm->data_source_fd = -1;
420 weston_log("nothing happened, buffered the bytes\n");
427 weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
429 struct weston_data_source *source;
430 struct weston_seat *seat = weston_wm_pick_seat(wm);
433 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
434 weston_log("pipe2 failed: %m\n");
435 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
439 wl_array_init(&wm->source_data);
440 wm->selection_target = target;
441 wm->data_source_fd = p[0];
442 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
445 weston_wm_read_data_source,
448 source = seat->selection_data_source;
449 source->send(source, mime_type, p[1]);
454 weston_wm_send_incr_chunk(struct weston_wm *wm)
458 weston_log("property deleted\n");
460 wm->selection_property_set = 0;
461 if (wm->flush_property_on_delete) {
462 weston_log("setting new property, %zu bytes\n",
463 wm->source_data.size);
464 wm->flush_property_on_delete = 0;
465 length = weston_wm_flush_source_data(wm);
467 if (wm->data_source_fd >= 0) {
468 wm->property_source =
469 wl_event_loop_add_fd(wm->server->loop,
472 weston_wm_read_data_source,
474 } else if (length > 0) {
475 /* Transfer is all done, but queue a flush for
476 * the delete of the last chunk so we can set
477 * the 0 sized propert to signal the end of
479 wm->flush_property_on_delete = 1;
480 wl_array_release(&wm->source_data);
482 wm->selection_request.requestor = XCB_NONE;
488 weston_wm_handle_selection_property_notify(struct weston_wm *wm,
489 xcb_generic_event_t *event)
491 xcb_property_notify_event_t *property_notify =
492 (xcb_property_notify_event_t *) event;
494 if (property_notify->window == wm->selection_window) {
495 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
496 property_notify->atom == wm->atom.wl_selection &&
498 weston_wm_get_incr_chunk(wm);
500 } else if (property_notify->window == wm->selection_request.requestor) {
501 if (property_notify->state == XCB_PROPERTY_DELETE &&
502 property_notify->atom == wm->selection_request.property &&
504 weston_wm_send_incr_chunk(wm);
512 weston_wm_handle_selection_request(struct weston_wm *wm,
513 xcb_generic_event_t *event)
515 xcb_selection_request_event_t *selection_request =
516 (xcb_selection_request_event_t *) event;
518 weston_log("selection request, %s, ",
519 get_atom_name(wm->conn, selection_request->selection));
520 weston_log_continue("target %s, ",
521 get_atom_name(wm->conn, selection_request->target));
522 weston_log_continue("property %s\n",
523 get_atom_name(wm->conn, selection_request->property));
525 wm->selection_request = *selection_request;
527 wm->flush_property_on_delete = 0;
529 if (selection_request->selection == wm->atom.clipboard_manager) {
530 /* The weston clipboard should already have grabbed
531 * the first target, so just send selection notify
532 * now. This isn't synchronized with the clipboard
533 * finishing getting the data, so there's a race here. */
534 weston_wm_send_selection_notify(wm, wm->selection_request.property);
538 if (selection_request->target == wm->atom.targets) {
539 weston_wm_send_targets(wm);
540 } else if (selection_request->target == wm->atom.timestamp) {
541 weston_wm_send_timestamp(wm);
542 } else if (selection_request->target == wm->atom.utf8_string ||
543 selection_request->target == wm->atom.text) {
544 weston_wm_send_data(wm, wm->atom.utf8_string,
545 "text/plain;charset=utf-8");
547 weston_log("can only handle UTF8_STRING targets...\n");
548 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
553 weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
554 xcb_generic_event_t *event)
556 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
557 (xcb_xfixes_selection_notify_event_t *) event;
558 struct weston_compositor *compositor;
559 struct weston_seat *seat = weston_wm_pick_seat(wm);
562 if (xfixes_selection_notify->selection != wm->atom.clipboard)
565 weston_log("xfixes selection notify event: owner %d\n",
566 xfixes_selection_notify->owner);
568 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
569 if (wm->selection_owner != wm->selection_window) {
570 /* A real X client selection went away, not our
571 * proxy selection. Clear the wayland selection. */
572 compositor = wm->server->compositor;
573 serial = wl_display_next_serial(compositor->wl_display);
574 weston_seat_set_selection(seat, NULL, serial);
577 wm->selection_owner = XCB_WINDOW_NONE;
582 wm->selection_owner = xfixes_selection_notify->owner;
584 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
585 * selection, so grab the actual timestamp here so we can
586 * answer TIMESTAMP conversion requests correctly. */
587 if (xfixes_selection_notify->owner == wm->selection_window) {
588 wm->selection_timestamp = xfixes_selection_notify->timestamp;
589 weston_log("our window, skipping\n");
594 xcb_convert_selection(wm->conn, wm->selection_window,
597 wm->atom.wl_selection,
598 xfixes_selection_notify->timestamp);
606 weston_wm_handle_selection_event(struct weston_wm *wm,
607 xcb_generic_event_t *event)
609 switch (event->response_type & ~0x80) {
610 case XCB_SELECTION_NOTIFY:
611 weston_wm_handle_selection_notify(wm, event);
613 case XCB_PROPERTY_NOTIFY:
614 return weston_wm_handle_selection_property_notify(wm, event);
615 case XCB_SELECTION_REQUEST:
616 weston_wm_handle_selection_request(wm, event);
620 switch (event->response_type - wm->xfixes->first_event) {
621 case XCB_XFIXES_SELECTION_NOTIFY:
622 return weston_wm_handle_xfixes_selection_notify(wm, event);
629 weston_wm_set_selection(struct wl_listener *listener, void *data)
631 struct weston_seat *seat = data;
632 struct weston_wm *wm =
633 container_of(listener, struct weston_wm, selection_listener);
634 struct weston_data_source *source = seat->selection_data_source;
635 const char **p, **end;
636 int has_text_plain = 0;
638 if (source == NULL) {
639 if (wm->selection_owner == wm->selection_window)
640 xcb_set_selection_owner(wm->conn,
643 wm->selection_timestamp);
647 if (source->send == data_source_send)
650 p = source->mime_types.data;
651 end = (const char **)
652 ((char *) source->mime_types.data + source->mime_types.size);
654 weston_log(" %s\n", *p);
655 if (strcmp(*p, "text/plain") == 0 ||
656 strcmp(*p, "text/plain;charset=utf-8") == 0)
661 if (has_text_plain) {
662 xcb_set_selection_owner(wm->conn,
663 wm->selection_window,
665 XCB_TIME_CURRENT_TIME);
667 xcb_set_selection_owner(wm->conn,
670 XCB_TIME_CURRENT_TIME);
675 weston_wm_selection_init(struct weston_wm *wm)
677 struct weston_seat *seat;
678 uint32_t values[1], mask;
680 wm->selection_request.requestor = XCB_NONE;
682 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
683 wm->selection_window = xcb_generate_id(wm->conn);
684 xcb_create_window(wm->conn,
685 XCB_COPY_FROM_PARENT,
686 wm->selection_window,
691 XCB_WINDOW_CLASS_INPUT_OUTPUT,
692 wm->screen->root_visual,
693 XCB_CW_EVENT_MASK, values);
695 xcb_set_selection_owner(wm->conn,
696 wm->selection_window,
697 wm->atom.clipboard_manager,
698 XCB_TIME_CURRENT_TIME);
701 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
702 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
703 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
704 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
705 wm->atom.clipboard, mask);
707 seat = weston_wm_pick_seat(wm);
708 wm->selection_listener.notify = weston_wm_set_selection;
709 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
711 weston_wm_set_selection(&wm->selection_listener, seat);