blob: 69665b73d5fa65f86daada2c23d53fc072a0ba95 [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>
Kristian Høgsberg102bf032012-05-21 15:52:02 -040026#include <string.h>
27#include <unistd.h>
28#include <fcntl.h>
29
30#include "xwayland.h"
31
32static int
33weston_wm_write_property(int fd, uint32_t mask, void *data)
34{
35 struct weston_wm *wm = data;
36 unsigned char *property;
37 int len, remainder;
38
39 property = xcb_get_property_value(wm->property_reply);
40 remainder = xcb_get_property_value_length(wm->property_reply) -
41 wm->property_start;
42
43 len = write(fd, property + wm->property_start, remainder);
44 if (len == -1) {
45 free(wm->property_reply);
46 wl_event_source_remove(wm->property_source);
47 close(fd);
Martin Minarik6d118362012-06-07 18:01:59 +020048 weston_log("write error to target fd: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040049 return 1;
50 }
51
Martin Minarik6d118362012-06-07 18:01:59 +020052 weston_log("wrote %d (chunk size %d) of %d bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -040053 wm->property_start + len,
54 len, xcb_get_property_value_length(wm->property_reply));
55
56 wm->property_start += len;
57 if (len == remainder) {
58 free(wm->property_reply);
59 wl_event_source_remove(wm->property_source);
60
61 if (wm->incr) {
62 xcb_delete_property(wm->conn,
63 wm->selection_window,
64 wm->atom.wl_selection);
65 } else {
Martin Minarik6d118362012-06-07 18:01:59 +020066 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040067 close(fd);
68 }
69 }
70
71 return 1;
72}
73
74static void
75weston_wm_get_incr_chunk(struct weston_wm *wm)
76{
77 xcb_get_property_cookie_t cookie;
78 xcb_get_property_reply_t *reply;
79
80 cookie = xcb_get_property(wm->conn,
81 0, /* delete */
82 wm->selection_window,
83 wm->atom.wl_selection,
84 XCB_GET_PROPERTY_TYPE_ANY,
85 0, /* offset */
86 0x1fffffff /* length */);
87
88 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
89
90 dump_property(wm, wm->atom.wl_selection, reply);
91
92 if (xcb_get_property_value_length(reply) > 0) {
93 wm->property_start = 0;
94 wm->property_source =
95 wl_event_loop_add_fd(wm->server->loop,
96 wm->data_source_fd,
97 WL_EVENT_WRITABLE,
98 weston_wm_write_property,
99 wm);
100 wm->property_reply = reply;
101 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200102 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400103 close(wm->data_source_fd);
104 free(reply);
105 }
106}
107
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400108struct x11_data_source {
109 struct wl_data_source base;
110 struct weston_wm *wm;
111};
112
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400113static void
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400114data_source_accept(struct wl_data_source *source,
115 uint32_t time, const char *mime_type)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400116{
117}
118
119static void
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400120data_source_send(struct wl_data_source *base,
121 const char *mime_type, int32_t fd)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400122{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400123 struct x11_data_source *source = (struct x11_data_source *) base;
124 struct weston_wm *wm = source->wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400125
126 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
127 /* Get data for the utf8_string target */
128 xcb_convert_selection(wm->conn,
129 wm->selection_window,
130 wm->atom.clipboard,
131 wm->atom.utf8_string,
132 wm->atom.wl_selection,
133 XCB_TIME_CURRENT_TIME);
134
135 xcb_flush(wm->conn);
136
137 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400138 wm->data_source_fd = fcntl(fd, F_DUPFD_CLOEXEC, fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400139 }
140}
141
142static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400143data_source_cancel(struct wl_data_source *source)
144{
145}
146
147static void
148weston_wm_get_selection_targets(struct weston_wm *wm)
149{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400150 struct x11_data_source *source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400151 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400152 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400153 xcb_get_property_cookie_t cookie;
154 xcb_get_property_reply_t *reply;
155 xcb_atom_t *value;
156 char **p;
157 uint32_t i;
158
159 cookie = xcb_get_property(wm->conn,
160 1, /* delete */
161 wm->selection_window,
162 wm->atom.wl_selection,
163 XCB_GET_PROPERTY_TYPE_ANY,
164 0, /* offset */
165 4096 /* length */);
166
167 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
168
169 dump_property(wm, wm->atom.wl_selection, reply);
170
171 if (reply->type != XCB_ATOM_ATOM) {
172 free(reply);
173 return;
174 }
175
176 source = malloc(sizeof *source);
177 if (source == NULL)
178 return;
179
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400180 wl_signal_init(&source->base.resource.destroy_signal);
181 source->base.accept = data_source_accept;
182 source->base.send = data_source_send;
183 source->base.cancel = data_source_cancel;
184 source->base.resource.data = source;
185 source->wm = wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400186
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400187 wl_array_init(&source->base.mime_types);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400188 value = xcb_get_property_value(reply);
189 for (i = 0; i < reply->value_len; i++) {
190 if (value[i] == wm->atom.utf8_string) {
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400191 p = wl_array_add(&source->base.mime_types, sizeof *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400192 if (p)
193 *p = strdup("text/plain;charset=utf-8");
194 }
195 }
196
197 compositor = wm->server->compositor;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400198 weston_seat_set_selection(seat, &source->base,
199 wl_display_next_serial(compositor->wl_display));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400200
201 free(reply);
202}
203
204static void
205weston_wm_get_selection_data(struct weston_wm *wm)
206{
207 xcb_get_property_cookie_t cookie;
208 xcb_get_property_reply_t *reply;
209
210 cookie = xcb_get_property(wm->conn,
211 1, /* delete */
212 wm->selection_window,
213 wm->atom.wl_selection,
214 XCB_GET_PROPERTY_TYPE_ANY,
215 0, /* offset */
216 0x1fffffff /* length */);
217
218 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
219
220 if (reply->type == wm->atom.incr) {
221 dump_property(wm, wm->atom.wl_selection, reply);
222 wm->incr = 1;
223 free(reply);
224 } else {
225 dump_property(wm, wm->atom.wl_selection, reply);
226 wm->incr = 0;
227 wm->property_start = 0;
228 wm->property_source =
229 wl_event_loop_add_fd(wm->server->loop,
230 wm->data_source_fd,
231 WL_EVENT_WRITABLE,
232 weston_wm_write_property,
233 wm);
234 wm->property_reply = reply;
235 }
236}
237
238static void
239weston_wm_handle_selection_notify(struct weston_wm *wm,
240 xcb_generic_event_t *event)
241{
242 xcb_selection_notify_event_t *selection_notify =
243 (xcb_selection_notify_event_t *) event;
244
245 if (selection_notify->property == XCB_ATOM_NONE) {
246 /* convert selection failed */
247 } else if (selection_notify->target == wm->atom.targets) {
248 weston_wm_get_selection_targets(wm);
249 } else {
250 weston_wm_get_selection_data(wm);
251 }
252}
253
254static const size_t incr_chunk_size = 64 * 1024;
255
256static void
257weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
258{
259 xcb_selection_notify_event_t selection_notify;
260
261 memset(&selection_notify, 0, sizeof selection_notify);
262 selection_notify.response_type = XCB_SELECTION_NOTIFY;
263 selection_notify.sequence = 0;
264 selection_notify.time = wm->selection_request.time;
265 selection_notify.requestor = wm->selection_request.requestor;
266 selection_notify.selection = wm->selection_request.selection;
267 selection_notify.target = wm->selection_request.target;
268 selection_notify.property = property;
269
270 xcb_send_event(wm->conn, 0, /* propagate */
271 wm->selection_request.requestor,
272 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
273}
274
275static void
276weston_wm_send_targets(struct weston_wm *wm)
277{
278 xcb_atom_t targets[] = {
279 wm->atom.timestamp,
280 wm->atom.targets,
281 wm->atom.utf8_string,
282 /* wm->atom.compound_text, */
283 wm->atom.text,
284 /* wm->atom.string */
285 };
286
287 xcb_change_property(wm->conn,
288 XCB_PROP_MODE_REPLACE,
289 wm->selection_request.requestor,
290 wm->selection_request.property,
291 XCB_ATOM_ATOM,
292 32, /* format */
293 ARRAY_LENGTH(targets), targets);
294
295 weston_wm_send_selection_notify(wm, wm->selection_request.property);
296}
297
298static void
299weston_wm_send_timestamp(struct weston_wm *wm)
300{
301 xcb_change_property(wm->conn,
302 XCB_PROP_MODE_REPLACE,
303 wm->selection_request.requestor,
304 wm->selection_request.property,
305 XCB_ATOM_INTEGER,
306 32, /* format */
307 1, &wm->selection_timestamp);
308
309 weston_wm_send_selection_notify(wm, wm->selection_request.property);
310}
311
312static int
313weston_wm_flush_source_data(struct weston_wm *wm)
314{
315 int length;
316
317 xcb_change_property(wm->conn,
318 XCB_PROP_MODE_REPLACE,
319 wm->selection_request.requestor,
320 wm->selection_request.property,
321 wm->selection_target,
322 8, /* format */
323 wm->source_data.size,
324 wm->source_data.data);
325 wm->selection_property_set = 1;
326 length = wm->source_data.size;
327 wm->source_data.size = 0;
328
329 return length;
330}
331
332static int
333weston_wm_read_data_source(int fd, uint32_t mask, void *data)
334{
335 struct weston_wm *wm = data;
336 int len, current, available;
337 void *p;
338
339 current = wm->source_data.size;
340 if (wm->source_data.size < incr_chunk_size)
341 p = wl_array_add(&wm->source_data, incr_chunk_size);
342 else
343 p = (char *) wm->source_data.data + wm->source_data.size;
344 available = wm->source_data.alloc - current;
345
346 len = read(fd, p, available);
347 if (len == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200348 weston_log("read error from data source: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400349 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
350 wl_event_source_remove(wm->property_source);
351 close(fd);
352 wl_array_release(&wm->source_data);
353 }
354
Martin Minarik6d118362012-06-07 18:01:59 +0200355 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400356 len, available, mask, len, (char *) p);
357
358 wm->source_data.size = current + len;
359 if (wm->source_data.size >= incr_chunk_size) {
360 if (!wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200361 weston_log("got %zu bytes, starting incr\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400362 wm->source_data.size);
363 wm->incr = 1;
364 xcb_change_property(wm->conn,
365 XCB_PROP_MODE_REPLACE,
366 wm->selection_request.requestor,
367 wm->selection_request.property,
368 wm->atom.incr,
369 32, /* format */
370 1, &incr_chunk_size);
371 wm->selection_property_set = 1;
372 wm->flush_property_on_delete = 1;
373 wl_event_source_remove(wm->property_source);
374 weston_wm_send_selection_notify(wm, wm->selection_request.property);
375 } else if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200376 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400377 "property delete\n", wm->source_data.size);
378
379 wm->flush_property_on_delete = 1;
380 wl_event_source_remove(wm->property_source);
381 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200382 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400383 "property deleted, seting new property\n",
384 wm->source_data.size);
385 weston_wm_flush_source_data(wm);
386 }
387 } else if (len == 0 && !wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200388 weston_log("non-incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400389 /* Non-incr transfer all done. */
390 weston_wm_flush_source_data(wm);
391 weston_wm_send_selection_notify(wm, wm->selection_request.property);
392 xcb_flush(wm->conn);
393 wl_event_source_remove(wm->property_source);
394 close(fd);
395 wl_array_release(&wm->source_data);
396 wm->selection_request.requestor = XCB_NONE;
397 } else if (len == 0 && wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200398 weston_log("incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400399
400 wm->flush_property_on_delete = 1;
401 if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200402 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400403 "property delete\n", wm->source_data.size);
404 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200405 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400406 "property deleted, seting new property\n",
407 wm->source_data.size);
408 weston_wm_flush_source_data(wm);
409 }
410 xcb_flush(wm->conn);
411 wl_event_source_remove(wm->property_source);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400412 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400413 wm->data_source_fd = -1;
414 close(fd);
415 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200416 weston_log("nothing happened, buffered the bytes\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400417 }
418
419 return 1;
420}
421
422static void
423weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
424{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400425 struct wl_data_source *source;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400426 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400427 int p[2];
428
429 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200430 weston_log("pipe2 failed: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400431 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
432 return;
433 }
434
435 wl_array_init(&wm->source_data);
436 wm->selection_target = target;
437 wm->data_source_fd = p[0];
438 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
439 wm->data_source_fd,
440 WL_EVENT_READABLE,
441 weston_wm_read_data_source,
442 wm);
443
Kristian Høgsberge3148752013-05-06 23:19:49 -0400444 source = seat->selection_data_source;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400445 source->send(source, mime_type, p[1]);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400446}
447
448static void
449weston_wm_send_incr_chunk(struct weston_wm *wm)
450{
451 int length;
452
Martin Minarik6d118362012-06-07 18:01:59 +0200453 weston_log("property deleted\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400454
455 wm->selection_property_set = 0;
456 if (wm->flush_property_on_delete) {
Martin Minarik6d118362012-06-07 18:01:59 +0200457 weston_log("setting new property, %zu bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400458 wm->source_data.size);
459 wm->flush_property_on_delete = 0;
460 length = weston_wm_flush_source_data(wm);
461
462 if (wm->data_source_fd >= 0) {
463 wm->property_source =
464 wl_event_loop_add_fd(wm->server->loop,
465 wm->data_source_fd,
466 WL_EVENT_READABLE,
467 weston_wm_read_data_source,
468 wm);
469 } else if (length > 0) {
470 /* Transfer is all done, but queue a flush for
471 * the delete of the last chunk so we can set
472 * the 0 sized propert to signal the end of
473 * the transfer. */
474 wm->flush_property_on_delete = 1;
475 wl_array_release(&wm->source_data);
476 } else {
477 wm->selection_request.requestor = XCB_NONE;
478 }
479 }
480}
481
482static int
483weston_wm_handle_selection_property_notify(struct weston_wm *wm,
484 xcb_generic_event_t *event)
485{
486 xcb_property_notify_event_t *property_notify =
487 (xcb_property_notify_event_t *) event;
488
489 if (property_notify->window == wm->selection_window) {
490 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
491 property_notify->atom == wm->atom.wl_selection &&
492 wm->incr)
493 weston_wm_get_incr_chunk(wm);
494 return 1;
495 } else if (property_notify->window == wm->selection_request.requestor) {
496 if (property_notify->state == XCB_PROPERTY_DELETE &&
497 property_notify->atom == wm->selection_request.property &&
498 wm->incr)
499 weston_wm_send_incr_chunk(wm);
500 return 1;
501 }
502
503 return 0;
504}
505
506static void
507weston_wm_handle_selection_request(struct weston_wm *wm,
508 xcb_generic_event_t *event)
509{
510 xcb_selection_request_event_t *selection_request =
511 (xcb_selection_request_event_t *) event;
512
Martin Minarik6d118362012-06-07 18:01:59 +0200513 weston_log("selection request, %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400514 get_atom_name(wm->conn, selection_request->selection));
Martin Minarik6d118362012-06-07 18:01:59 +0200515 weston_log_continue("target %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400516 get_atom_name(wm->conn, selection_request->target));
Martin Minarik6d118362012-06-07 18:01:59 +0200517 weston_log_continue("property %s\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400518 get_atom_name(wm->conn, selection_request->property));
519
520 wm->selection_request = *selection_request;
521 wm->incr = 0;
522 wm->flush_property_on_delete = 0;
523
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400524 if (selection_request->selection == wm->atom.clipboard_manager) {
525 /* The weston clipboard should already have grabbed
526 * the first target, so just send selection notify
527 * now. This isn't synchronized with the clipboard
528 * finishing getting the data, so there's a race here. */
529 weston_wm_send_selection_notify(wm, wm->selection_request.property);
530 return;
531 }
532
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400533 if (selection_request->target == wm->atom.targets) {
534 weston_wm_send_targets(wm);
535 } else if (selection_request->target == wm->atom.timestamp) {
536 weston_wm_send_timestamp(wm);
537 } else if (selection_request->target == wm->atom.utf8_string ||
538 selection_request->target == wm->atom.text) {
539 weston_wm_send_data(wm, wm->atom.utf8_string,
540 "text/plain;charset=utf-8");
541 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200542 weston_log("can only handle UTF8_STRING targets...\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400543 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
544 }
545}
546
547static void
548weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
549 xcb_generic_event_t *event)
550{
551 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
552 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400553 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400554 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400555 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400556
Martin Minarik6d118362012-06-07 18:01:59 +0200557 weston_log("xfixes selection notify event: owner %d\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400558 xfixes_selection_notify->owner);
559
Kristian Høgsberg80566732012-06-01 00:08:12 -0400560 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
561 if (wm->selection_owner != wm->selection_window) {
562 /* A real X client selection went away, not our
563 * proxy selection. Clear the wayland selection. */
564 compositor = wm->server->compositor;
565 serial = wl_display_next_serial(compositor->wl_display);
Kristian Høgsberge3148752013-05-06 23:19:49 -0400566 weston_seat_set_selection(seat, NULL, serial);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400567 }
568
569 wm->selection_owner = XCB_WINDOW_NONE;
570
571 return;
572 }
573
574 wm->selection_owner = xfixes_selection_notify->owner;
575
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400576 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
577 * selection, so grab the actual timestamp here so we can
578 * answer TIMESTAMP conversion requests correctly. */
579 if (xfixes_selection_notify->owner == wm->selection_window) {
580 wm->selection_timestamp = xfixes_selection_notify->timestamp;
Martin Minarik6d118362012-06-07 18:01:59 +0200581 weston_log("our window, skipping\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400582 return;
583 }
584
585 wm->incr = 0;
586 xcb_convert_selection(wm->conn, wm->selection_window,
587 wm->atom.clipboard,
588 wm->atom.targets,
589 wm->atom.wl_selection,
590 xfixes_selection_notify->timestamp);
591
592 xcb_flush(wm->conn);
593}
594
595int
596weston_wm_handle_selection_event(struct weston_wm *wm,
597 xcb_generic_event_t *event)
598{
599 switch (event->response_type & ~0x80) {
600 case XCB_SELECTION_NOTIFY:
601 weston_wm_handle_selection_notify(wm, event);
602 return 1;
603 case XCB_PROPERTY_NOTIFY:
604 return weston_wm_handle_selection_property_notify(wm, event);
605 case XCB_SELECTION_REQUEST:
606 weston_wm_handle_selection_request(wm, event);
607 return 1;
608 }
609
610 switch (event->response_type - wm->xfixes->first_event) {
611 case XCB_XFIXES_SELECTION_NOTIFY:
612 weston_wm_handle_xfixes_selection_notify(wm, event);
613 return 1;
614 }
615
616 return 0;
617}
618
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200619static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400620weston_wm_set_selection(struct wl_listener *listener, void *data)
621{
Kristian Høgsberge3148752013-05-06 23:19:49 -0400622 struct weston_seat *seat = data;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400623 struct weston_wm *wm =
624 container_of(listener, struct weston_wm, selection_listener);
625 struct wl_data_source *source = seat->selection_data_source;
626 const char **p, **end;
627 int has_text_plain = 0;
628
Kristian Høgsberg80566732012-06-01 00:08:12 -0400629 if (source == NULL) {
630 if (wm->selection_owner == wm->selection_window)
631 xcb_set_selection_owner(wm->conn,
632 XCB_ATOM_NONE,
633 wm->atom.clipboard,
634 wm->selection_timestamp);
635 return;
636 }
637
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400638 if (source->send == data_source_send)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400639 return;
640
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400641 p = source->mime_types.data;
642 end = (const char **)
643 ((char *) source->mime_types.data + source->mime_types.size);
644 while (p < end) {
Martin Minarik6d118362012-06-07 18:01:59 +0200645 weston_log(" %s\n", *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400646 if (strcmp(*p, "text/plain") == 0 ||
647 strcmp(*p, "text/plain;charset=utf-8") == 0)
648 has_text_plain = 1;
649 p++;
650 }
651
652 if (has_text_plain) {
653 xcb_set_selection_owner(wm->conn,
654 wm->selection_window,
655 wm->atom.clipboard,
656 XCB_TIME_CURRENT_TIME);
657 } else {
658 xcb_set_selection_owner(wm->conn,
659 XCB_ATOM_NONE,
660 wm->atom.clipboard,
661 XCB_TIME_CURRENT_TIME);
662 }
663}
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400664
665void
666weston_wm_selection_init(struct weston_wm *wm)
667{
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400668 struct weston_seat *seat;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400669 uint32_t values[1], mask;
670
671 wm->selection_request.requestor = XCB_NONE;
672
673 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
674 wm->selection_window = xcb_generate_id(wm->conn);
675 xcb_create_window(wm->conn,
676 XCB_COPY_FROM_PARENT,
677 wm->selection_window,
678 wm->screen->root,
679 0, 0,
680 10, 10,
681 0,
682 XCB_WINDOW_CLASS_INPUT_OUTPUT,
683 wm->screen->root_visual,
684 XCB_CW_EVENT_MASK, values);
685
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400686 xcb_set_selection_owner(wm->conn,
687 wm->selection_window,
688 wm->atom.clipboard_manager,
689 XCB_TIME_CURRENT_TIME);
690
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400691 mask =
692 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
693 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
694 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
695 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
696 wm->atom.clipboard, mask);
697
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400698 seat = weston_wm_pick_seat(wm);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400699 wm->selection_listener.notify = weston_wm_set_selection;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400700 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
Kristian Høgsberge2203272012-06-03 10:32:48 -0400701
702 weston_wm_set_selection(&wm->selection_listener, seat);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400703}