blob: 442b37312e5fe4de651533fffb3319fb3341cc74 [file] [log] [blame]
Kristian Høgsberg102bf032012-05-21 15:52:02 -04001/*
2 * Copyright © 2012 Intel Corporation
3 *
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.
13 *
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.
21 */
22
23#define _GNU_SOURCE
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <unistd.h>
29#include <fcntl.h>
30
31#include "xwayland.h"
32
33static int
34weston_wm_write_property(int fd, uint32_t mask, void *data)
35{
36 struct weston_wm *wm = data;
37 unsigned char *property;
38 int len, remainder;
39
40 property = xcb_get_property_value(wm->property_reply);
41 remainder = xcb_get_property_value_length(wm->property_reply) -
42 wm->property_start;
43
44 len = write(fd, property + wm->property_start, remainder);
45 if (len == -1) {
46 free(wm->property_reply);
47 wl_event_source_remove(wm->property_source);
48 close(fd);
49 fprintf(stderr, "write error to target fd: %m\n");
50 return 1;
51 }
52
53 fprintf(stderr, "wrote %d (chunk size %d) of %d bytes\n",
54 wm->property_start + len,
55 len, xcb_get_property_value_length(wm->property_reply));
56
57 wm->property_start += len;
58 if (len == remainder) {
59 free(wm->property_reply);
60 wl_event_source_remove(wm->property_source);
61
62 if (wm->incr) {
63 xcb_delete_property(wm->conn,
64 wm->selection_window,
65 wm->atom.wl_selection);
66 } else {
67 fprintf(stderr, "transfer complete\n");
68 close(fd);
69 }
70 }
71
72 return 1;
73}
74
75static void
76weston_wm_get_incr_chunk(struct weston_wm *wm)
77{
78 xcb_get_property_cookie_t cookie;
79 xcb_get_property_reply_t *reply;
80
81 cookie = xcb_get_property(wm->conn,
82 0, /* delete */
83 wm->selection_window,
84 wm->atom.wl_selection,
85 XCB_GET_PROPERTY_TYPE_ANY,
86 0, /* offset */
87 0x1fffffff /* length */);
88
89 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
90
91 dump_property(wm, wm->atom.wl_selection, reply);
92
93 if (xcb_get_property_value_length(reply) > 0) {
94 wm->property_start = 0;
95 wm->property_source =
96 wl_event_loop_add_fd(wm->server->loop,
97 wm->data_source_fd,
98 WL_EVENT_WRITABLE,
99 weston_wm_write_property,
100 wm);
101 wm->property_reply = reply;
102 } else {
103 fprintf(stderr, "transfer complete\n");
104 close(wm->data_source_fd);
105 free(reply);
106 }
107}
108
109static void
110data_offer_accept(struct wl_client *client, struct wl_resource *resource,
111 uint32_t time, const char *mime_type)
112{
113}
114
115static void
116data_offer_receive(struct wl_client *client, struct wl_resource *resource,
117 const char *mime_type, int32_t fd)
118{
119 struct wl_data_offer *offer = resource->data;
120 struct weston_wm *wm = offer->source->resource.data;
121
122 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
123 /* Get data for the utf8_string target */
124 xcb_convert_selection(wm->conn,
125 wm->selection_window,
126 wm->atom.clipboard,
127 wm->atom.utf8_string,
128 wm->atom.wl_selection,
129 XCB_TIME_CURRENT_TIME);
130
131 xcb_flush(wm->conn);
132
133 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
134 wm->data_source_fd = fd;
135 } else {
136 close(fd);
137 }
138}
139
140static void
141data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
142{
143 wl_resource_destroy(resource);
144}
145
146static const struct wl_data_offer_interface data_offer_interface = {
147 data_offer_accept,
148 data_offer_receive,
149 data_offer_destroy,
150};
151
152static void
153data_source_cancel(struct wl_data_source *source)
154{
155}
156
157static void
158weston_wm_get_selection_targets(struct weston_wm *wm)
159{
160 struct wl_data_source *source;
161 struct weston_compositor *compositor;
162 xcb_get_property_cookie_t cookie;
163 xcb_get_property_reply_t *reply;
164 xcb_atom_t *value;
165 char **p;
166 uint32_t i;
167
168 cookie = xcb_get_property(wm->conn,
169 1, /* delete */
170 wm->selection_window,
171 wm->atom.wl_selection,
172 XCB_GET_PROPERTY_TYPE_ANY,
173 0, /* offset */
174 4096 /* length */);
175
176 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
177
178 dump_property(wm, wm->atom.wl_selection, reply);
179
180 if (reply->type != XCB_ATOM_ATOM) {
181 free(reply);
182 return;
183 }
184
185 source = malloc(sizeof *source);
186 if (source == NULL)
187 return;
188
189 wl_signal_init(&source->resource.destroy_signal);
190 source->offer_interface = &data_offer_interface;
191 source->cancel = data_source_cancel;
192 source->resource.data = wm;
193
194 wl_array_init(&source->mime_types);
195 value = xcb_get_property_value(reply);
196 for (i = 0; i < reply->value_len; i++) {
197 if (value[i] == wm->atom.utf8_string) {
198 p = wl_array_add(&source->mime_types, sizeof *p);
199 if (p)
200 *p = strdup("text/plain;charset=utf-8");
201 }
202 }
203
204 compositor = wm->server->compositor;
205 wl_seat_set_selection(&compositor->seat->seat, source,
206 wl_display_next_serial(compositor->wl_display));
207
208 free(reply);
209}
210
211static void
212weston_wm_get_selection_data(struct weston_wm *wm)
213{
214 xcb_get_property_cookie_t cookie;
215 xcb_get_property_reply_t *reply;
216
217 cookie = xcb_get_property(wm->conn,
218 1, /* delete */
219 wm->selection_window,
220 wm->atom.wl_selection,
221 XCB_GET_PROPERTY_TYPE_ANY,
222 0, /* offset */
223 0x1fffffff /* length */);
224
225 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
226
227 if (reply->type == wm->atom.incr) {
228 dump_property(wm, wm->atom.wl_selection, reply);
229 wm->incr = 1;
230 free(reply);
231 } else {
232 dump_property(wm, wm->atom.wl_selection, reply);
233 wm->incr = 0;
234 wm->property_start = 0;
235 wm->property_source =
236 wl_event_loop_add_fd(wm->server->loop,
237 wm->data_source_fd,
238 WL_EVENT_WRITABLE,
239 weston_wm_write_property,
240 wm);
241 wm->property_reply = reply;
242 }
243}
244
245static void
246weston_wm_handle_selection_notify(struct weston_wm *wm,
247 xcb_generic_event_t *event)
248{
249 xcb_selection_notify_event_t *selection_notify =
250 (xcb_selection_notify_event_t *) event;
251
252 if (selection_notify->property == XCB_ATOM_NONE) {
253 /* convert selection failed */
254 } else if (selection_notify->target == wm->atom.targets) {
255 weston_wm_get_selection_targets(wm);
256 } else {
257 weston_wm_get_selection_data(wm);
258 }
259}
260
261static const size_t incr_chunk_size = 64 * 1024;
262
263static void
264weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
265{
266 xcb_selection_notify_event_t selection_notify;
267
268 memset(&selection_notify, 0, sizeof selection_notify);
269 selection_notify.response_type = XCB_SELECTION_NOTIFY;
270 selection_notify.sequence = 0;
271 selection_notify.time = wm->selection_request.time;
272 selection_notify.requestor = wm->selection_request.requestor;
273 selection_notify.selection = wm->selection_request.selection;
274 selection_notify.target = wm->selection_request.target;
275 selection_notify.property = property;
276
277 xcb_send_event(wm->conn, 0, /* propagate */
278 wm->selection_request.requestor,
279 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
280}
281
282static void
283weston_wm_send_targets(struct weston_wm *wm)
284{
285 xcb_atom_t targets[] = {
286 wm->atom.timestamp,
287 wm->atom.targets,
288 wm->atom.utf8_string,
289 /* wm->atom.compound_text, */
290 wm->atom.text,
291 /* wm->atom.string */
292 };
293
294 xcb_change_property(wm->conn,
295 XCB_PROP_MODE_REPLACE,
296 wm->selection_request.requestor,
297 wm->selection_request.property,
298 XCB_ATOM_ATOM,
299 32, /* format */
300 ARRAY_LENGTH(targets), targets);
301
302 weston_wm_send_selection_notify(wm, wm->selection_request.property);
303}
304
305static void
306weston_wm_send_timestamp(struct weston_wm *wm)
307{
308 xcb_change_property(wm->conn,
309 XCB_PROP_MODE_REPLACE,
310 wm->selection_request.requestor,
311 wm->selection_request.property,
312 XCB_ATOM_INTEGER,
313 32, /* format */
314 1, &wm->selection_timestamp);
315
316 weston_wm_send_selection_notify(wm, wm->selection_request.property);
317}
318
319static int
320weston_wm_flush_source_data(struct weston_wm *wm)
321{
322 int length;
323
324 xcb_change_property(wm->conn,
325 XCB_PROP_MODE_REPLACE,
326 wm->selection_request.requestor,
327 wm->selection_request.property,
328 wm->selection_target,
329 8, /* format */
330 wm->source_data.size,
331 wm->source_data.data);
332 wm->selection_property_set = 1;
333 length = wm->source_data.size;
334 wm->source_data.size = 0;
335
336 return length;
337}
338
339static int
340weston_wm_read_data_source(int fd, uint32_t mask, void *data)
341{
342 struct weston_wm *wm = data;
343 int len, current, available;
344 void *p;
345
346 current = wm->source_data.size;
347 if (wm->source_data.size < incr_chunk_size)
348 p = wl_array_add(&wm->source_data, incr_chunk_size);
349 else
350 p = (char *) wm->source_data.data + wm->source_data.size;
351 available = wm->source_data.alloc - current;
352
353 len = read(fd, p, available);
354 if (len == -1) {
355 fprintf(stderr, "read error from data source: %m\n");
356 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
357 wl_event_source_remove(wm->property_source);
358 close(fd);
359 wl_array_release(&wm->source_data);
360 }
361
362 fprintf(stderr, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
363 len, available, mask, len, (char *) p);
364
365 wm->source_data.size = current + len;
366 if (wm->source_data.size >= incr_chunk_size) {
367 if (!wm->incr) {
368 fprintf(stderr, "got %zu bytes, starting incr\n",
369 wm->source_data.size);
370 wm->incr = 1;
371 xcb_change_property(wm->conn,
372 XCB_PROP_MODE_REPLACE,
373 wm->selection_request.requestor,
374 wm->selection_request.property,
375 wm->atom.incr,
376 32, /* format */
377 1, &incr_chunk_size);
378 wm->selection_property_set = 1;
379 wm->flush_property_on_delete = 1;
380 wl_event_source_remove(wm->property_source);
381 weston_wm_send_selection_notify(wm, wm->selection_request.property);
382 } else if (wm->selection_property_set) {
383 fprintf(stderr, "got %zu bytes, waiting for "
384 "property delete\n", wm->source_data.size);
385
386 wm->flush_property_on_delete = 1;
387 wl_event_source_remove(wm->property_source);
388 } else {
389 fprintf(stderr, "got %zu bytes, "
390 "property deleted, seting new property\n",
391 wm->source_data.size);
392 weston_wm_flush_source_data(wm);
393 }
394 } else if (len == 0 && !wm->incr) {
395 fprintf(stderr, "non-incr transfer complete\n");
396 /* Non-incr transfer all done. */
397 weston_wm_flush_source_data(wm);
398 weston_wm_send_selection_notify(wm, wm->selection_request.property);
399 xcb_flush(wm->conn);
400 wl_event_source_remove(wm->property_source);
401 close(fd);
402 wl_array_release(&wm->source_data);
403 wm->selection_request.requestor = XCB_NONE;
404 } else if (len == 0 && wm->incr) {
405 fprintf(stderr, "incr transfer complete\n");
406
407 wm->flush_property_on_delete = 1;
408 if (wm->selection_property_set) {
409 fprintf(stderr, "got %zu bytes, waiting for "
410 "property delete\n", wm->source_data.size);
411 } else {
412 fprintf(stderr, "got %zu bytes, "
413 "property deleted, seting new property\n",
414 wm->source_data.size);
415 weston_wm_flush_source_data(wm);
416 }
417 xcb_flush(wm->conn);
418 wl_event_source_remove(wm->property_source);
419 wm->data_source_fd = -1;
420 close(fd);
421 } else {
422 fprintf(stderr, "nothing happened, buffered the bytes\n");
423 }
424
425 return 1;
426}
427
428static void
429weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
430{
431 struct wl_seat *seat = &wm->server->compositor->seat->seat;
432 int p[2];
433
434 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
435 fprintf(stderr, "pipe2 failed: %m\n");
436 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
437 return;
438 }
439
440 wl_array_init(&wm->source_data);
441 wm->selection_target = target;
442 wm->data_source_fd = p[0];
443 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
444 wm->data_source_fd,
445 WL_EVENT_READABLE,
446 weston_wm_read_data_source,
447 wm);
448
449 wl_data_source_send_send(&seat->selection_data_source->resource,
450 mime_type, p[1]);
451 close(p[1]);
452}
453
454static void
455weston_wm_send_incr_chunk(struct weston_wm *wm)
456{
457 int length;
458
459 fprintf(stderr, "property deleted\n");
460
461 wm->selection_property_set = 0;
462 if (wm->flush_property_on_delete) {
463 fprintf(stderr, "setting new property, %zu bytes\n",
464 wm->source_data.size);
465 wm->flush_property_on_delete = 0;
466 length = weston_wm_flush_source_data(wm);
467
468 if (wm->data_source_fd >= 0) {
469 wm->property_source =
470 wl_event_loop_add_fd(wm->server->loop,
471 wm->data_source_fd,
472 WL_EVENT_READABLE,
473 weston_wm_read_data_source,
474 wm);
475 } else if (length > 0) {
476 /* Transfer is all done, but queue a flush for
477 * the delete of the last chunk so we can set
478 * the 0 sized propert to signal the end of
479 * the transfer. */
480 wm->flush_property_on_delete = 1;
481 wl_array_release(&wm->source_data);
482 } else {
483 wm->selection_request.requestor = XCB_NONE;
484 }
485 }
486}
487
488static int
489weston_wm_handle_selection_property_notify(struct weston_wm *wm,
490 xcb_generic_event_t *event)
491{
492 xcb_property_notify_event_t *property_notify =
493 (xcb_property_notify_event_t *) event;
494
495 if (property_notify->window == wm->selection_window) {
496 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
497 property_notify->atom == wm->atom.wl_selection &&
498 wm->incr)
499 weston_wm_get_incr_chunk(wm);
500 return 1;
501 } else if (property_notify->window == wm->selection_request.requestor) {
502 if (property_notify->state == XCB_PROPERTY_DELETE &&
503 property_notify->atom == wm->selection_request.property &&
504 wm->incr)
505 weston_wm_send_incr_chunk(wm);
506 return 1;
507 }
508
509 return 0;
510}
511
512static void
513weston_wm_handle_selection_request(struct weston_wm *wm,
514 xcb_generic_event_t *event)
515{
516 xcb_selection_request_event_t *selection_request =
517 (xcb_selection_request_event_t *) event;
518
519 fprintf(stderr, "selection request, %s, ",
520 get_atom_name(wm->conn, selection_request->selection));
521 fprintf(stderr, "target %s, ",
522 get_atom_name(wm->conn, selection_request->target));
523 fprintf(stderr, "property %s\n",
524 get_atom_name(wm->conn, selection_request->property));
525
526 wm->selection_request = *selection_request;
527 wm->incr = 0;
528 wm->flush_property_on_delete = 0;
529
530 if (selection_request->target == wm->atom.targets) {
531 weston_wm_send_targets(wm);
532 } else if (selection_request->target == wm->atom.timestamp) {
533 weston_wm_send_timestamp(wm);
534 } else if (selection_request->target == wm->atom.utf8_string ||
535 selection_request->target == wm->atom.text) {
536 weston_wm_send_data(wm, wm->atom.utf8_string,
537 "text/plain;charset=utf-8");
538 } else {
539 fprintf(stderr, "can only handle UTF8_STRING targets...\n");
540 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
541 }
542}
543
544static void
545weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
546 xcb_generic_event_t *event)
547{
548 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
549 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400550 struct weston_compositor *compositor;
551 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400552
553 printf("xfixes selection notify event: owner %d\n",
554 xfixes_selection_notify->owner);
555
Kristian Høgsberg80566732012-06-01 00:08:12 -0400556 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
557 if (wm->selection_owner != wm->selection_window) {
558 /* A real X client selection went away, not our
559 * proxy selection. Clear the wayland selection. */
560 compositor = wm->server->compositor;
561 serial = wl_display_next_serial(compositor->wl_display);
562 wl_seat_set_selection(&compositor->seat->seat, NULL, serial);
563 }
564
565 wm->selection_owner = XCB_WINDOW_NONE;
566
567 return;
568 }
569
570 wm->selection_owner = xfixes_selection_notify->owner;
571
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400572 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
573 * selection, so grab the actual timestamp here so we can
574 * answer TIMESTAMP conversion requests correctly. */
575 if (xfixes_selection_notify->owner == wm->selection_window) {
576 wm->selection_timestamp = xfixes_selection_notify->timestamp;
577 fprintf(stderr, "our window, skipping\n");
578 return;
579 }
580
581 wm->incr = 0;
582 xcb_convert_selection(wm->conn, wm->selection_window,
583 wm->atom.clipboard,
584 wm->atom.targets,
585 wm->atom.wl_selection,
586 xfixes_selection_notify->timestamp);
587
588 xcb_flush(wm->conn);
589}
590
591int
592weston_wm_handle_selection_event(struct weston_wm *wm,
593 xcb_generic_event_t *event)
594{
595 switch (event->response_type & ~0x80) {
596 case XCB_SELECTION_NOTIFY:
597 weston_wm_handle_selection_notify(wm, event);
598 return 1;
599 case XCB_PROPERTY_NOTIFY:
600 return weston_wm_handle_selection_property_notify(wm, event);
601 case XCB_SELECTION_REQUEST:
602 weston_wm_handle_selection_request(wm, event);
603 return 1;
604 }
605
606 switch (event->response_type - wm->xfixes->first_event) {
607 case XCB_XFIXES_SELECTION_NOTIFY:
608 weston_wm_handle_xfixes_selection_notify(wm, event);
609 return 1;
610 }
611
612 return 0;
613}
614
615void
616weston_wm_set_selection(struct wl_listener *listener, void *data)
617{
618 struct wl_seat *seat = data;
619 struct weston_wm *wm =
620 container_of(listener, struct weston_wm, selection_listener);
621 struct wl_data_source *source = seat->selection_data_source;
622 const char **p, **end;
623 int has_text_plain = 0;
624
Kristian Høgsberg80566732012-06-01 00:08:12 -0400625 if (source == NULL) {
626 if (wm->selection_owner == wm->selection_window)
627 xcb_set_selection_owner(wm->conn,
628 XCB_ATOM_NONE,
629 wm->atom.clipboard,
630 wm->selection_timestamp);
631 return;
632 }
633
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400634 if (source->offer_interface == &data_offer_interface)
635 return;
636
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400637 p = source->mime_types.data;
638 end = (const char **)
639 ((char *) source->mime_types.data + source->mime_types.size);
640 while (p < end) {
641 fprintf(stderr, " %s\n", *p);
642 if (strcmp(*p, "text/plain") == 0 ||
643 strcmp(*p, "text/plain;charset=utf-8") == 0)
644 has_text_plain = 1;
645 p++;
646 }
647
648 if (has_text_plain) {
649 xcb_set_selection_owner(wm->conn,
650 wm->selection_window,
651 wm->atom.clipboard,
652 XCB_TIME_CURRENT_TIME);
653 } else {
654 xcb_set_selection_owner(wm->conn,
655 XCB_ATOM_NONE,
656 wm->atom.clipboard,
657 XCB_TIME_CURRENT_TIME);
658 }
659}