blob: 6f5328d2d32acb5980220e39f1c25680ffe26dc7 [file] [log] [blame]
Kristian Høgsberg102bf032012-05-21 15:52:02 -04001/*
2 * Copyright © 2012 Intel Corporation
3 *
Bryce Harrington0a007dd2015-06-11 16:22:34 -07004 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
Kristian Høgsberg102bf032012-05-21 15:52:02 -040011 *
Bryce Harrington0a007dd2015-06-11 16:22:34 -070012 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
Kristian Høgsberg102bf032012-05-21 15:52:02 -040024 */
25
Daniel Stonec228e232013-05-22 18:03:19 +030026#include "config.h"
Kristian Høgsberg102bf032012-05-21 15:52:02 -040027
28#include <stdlib.h>
Kristian Høgsberg102bf032012-05-21 15:52:02 -040029#include <string.h>
30#include <unistd.h>
31#include <fcntl.h>
32
33#include "xwayland.h"
Jon Cruz35b2eaa2015-06-15 15:37:08 -070034#include "shared/helpers.h"
Kristian Høgsberg102bf032012-05-21 15:52:02 -040035
36static int
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070037writable_callback(int fd, uint32_t mask, void *data)
Kristian Høgsberg102bf032012-05-21 15:52:02 -040038{
39 struct weston_wm *wm = data;
40 unsigned char *property;
41 int len, remainder;
42
43 property = xcb_get_property_value(wm->property_reply);
44 remainder = xcb_get_property_value_length(wm->property_reply) -
45 wm->property_start;
46
47 len = write(fd, property + wm->property_start, remainder);
48 if (len == -1) {
49 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070050 wm->property_reply = NULL;
51 if (wm->property_source)
52 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020053 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040054 close(fd);
Martin Minarik6d118362012-06-07 18:01:59 +020055 weston_log("write error to target fd: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040056 return 1;
57 }
58
Martin Minarik6d118362012-06-07 18:01:59 +020059 weston_log("wrote %d (chunk size %d) of %d bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -040060 wm->property_start + len,
61 len, xcb_get_property_value_length(wm->property_reply));
62
63 wm->property_start += len;
64 if (len == remainder) {
65 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070066 wm->property_reply = NULL;
67 if (wm->property_source)
68 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020069 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040070
71 if (wm->incr) {
72 xcb_delete_property(wm->conn,
73 wm->selection_window,
74 wm->atom.wl_selection);
75 } else {
Martin Minarik6d118362012-06-07 18:01:59 +020076 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040077 close(fd);
78 }
79 }
80
81 return 1;
82}
83
84static void
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070085weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply)
86{
87 wm->property_start = 0;
88 wm->property_reply = reply;
89 writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm);
90
91 if (wm->property_reply)
92 wm->property_source =
93 wl_event_loop_add_fd(wm->server->loop,
94 wm->data_source_fd,
95 WL_EVENT_WRITABLE,
96 writable_callback, wm);
97}
98
99static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400100weston_wm_get_incr_chunk(struct weston_wm *wm)
101{
102 xcb_get_property_cookie_t cookie;
103 xcb_get_property_reply_t *reply;
104
105 cookie = xcb_get_property(wm->conn,
106 0, /* delete */
107 wm->selection_window,
108 wm->atom.wl_selection,
109 XCB_GET_PROPERTY_TYPE_ANY,
110 0, /* offset */
111 0x1fffffff /* length */);
112
113 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700114 if (reply == NULL)
115 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400116
117 dump_property(wm, wm->atom.wl_selection, reply);
118
119 if (xcb_get_property_value_length(reply) > 0) {
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200120 /* reply's ownership is transfered to wm, which is responsible
121 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700122 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400123 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200124 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400125 close(wm->data_source_fd);
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200126 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400127 }
128}
129
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400130struct x11_data_source {
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700131 struct weston_data_source base;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400132 struct weston_wm *wm;
133};
134
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400135static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700136data_source_accept(struct weston_data_source *source,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400137 uint32_t time, const char *mime_type)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400138{
139}
140
141static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700142data_source_send(struct weston_data_source *base,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400143 const char *mime_type, int32_t fd)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400144{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400145 struct x11_data_source *source = (struct x11_data_source *) base;
146 struct weston_wm *wm = source->wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400147
148 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
149 /* Get data for the utf8_string target */
150 xcb_convert_selection(wm->conn,
151 wm->selection_window,
152 wm->atom.clipboard,
153 wm->atom.utf8_string,
154 wm->atom.wl_selection,
155 XCB_TIME_CURRENT_TIME);
156
157 xcb_flush(wm->conn);
158
159 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
Kristian Høgsberg668fc0d2013-09-04 20:48:46 -0700160 wm->data_source_fd = fd;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400161 }
162}
163
164static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700165data_source_cancel(struct weston_data_source *source)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400166{
167}
168
169static void
170weston_wm_get_selection_targets(struct weston_wm *wm)
171{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400172 struct x11_data_source *source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400173 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400174 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400175 xcb_get_property_cookie_t cookie;
176 xcb_get_property_reply_t *reply;
177 xcb_atom_t *value;
178 char **p;
179 uint32_t i;
180
181 cookie = xcb_get_property(wm->conn,
182 1, /* delete */
183 wm->selection_window,
184 wm->atom.wl_selection,
185 XCB_GET_PROPERTY_TYPE_ANY,
186 0, /* offset */
187 4096 /* length */);
188
189 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700190 if (reply == NULL)
191 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400192
193 dump_property(wm, wm->atom.wl_selection, reply);
194
195 if (reply->type != XCB_ATOM_ATOM) {
196 free(reply);
197 return;
198 }
199
Carlos Garnachobeb7a9f2016-02-01 20:28:15 +0100200 source = zalloc(sizeof *source);
Bryce Harringtond3553c72015-06-30 21:35:43 -0700201 if (source == NULL) {
202 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400203 return;
Bryce Harringtond3553c72015-06-30 21:35:43 -0700204 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400205
Jason Ekstrand8a4a9eb2013-06-14 10:07:55 -0500206 wl_signal_init(&source->base.destroy_signal);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400207 source->base.accept = data_source_accept;
208 source->base.send = data_source_send;
209 source->base.cancel = data_source_cancel;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400210 source->wm = wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400211
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400212 wl_array_init(&source->base.mime_types);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400213 value = xcb_get_property_value(reply);
214 for (i = 0; i < reply->value_len; i++) {
215 if (value[i] == wm->atom.utf8_string) {
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400216 p = wl_array_add(&source->base.mime_types, sizeof *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400217 if (p)
218 *p = strdup("text/plain;charset=utf-8");
219 }
220 }
221
222 compositor = wm->server->compositor;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400223 weston_seat_set_selection(seat, &source->base,
224 wl_display_next_serial(compositor->wl_display));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400225
226 free(reply);
227}
228
229static void
230weston_wm_get_selection_data(struct weston_wm *wm)
231{
232 xcb_get_property_cookie_t cookie;
233 xcb_get_property_reply_t *reply;
234
235 cookie = xcb_get_property(wm->conn,
236 1, /* delete */
237 wm->selection_window,
238 wm->atom.wl_selection,
239 XCB_GET_PROPERTY_TYPE_ANY,
240 0, /* offset */
241 0x1fffffff /* length */);
242
243 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
244
Bryce Harringtonda41b572015-07-10 18:13:48 -0700245 dump_property(wm, wm->atom.wl_selection, reply);
246
247 if (reply == NULL) {
248 return;
249 } else if (reply->type == wm->atom.incr) {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400250 wm->incr = 1;
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200251 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400252 } else {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400253 wm->incr = 0;
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200254 /* reply's ownership is transfered to wm, which is responsible
255 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700256 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400257 }
258}
259
260static void
261weston_wm_handle_selection_notify(struct weston_wm *wm,
262 xcb_generic_event_t *event)
263{
264 xcb_selection_notify_event_t *selection_notify =
265 (xcb_selection_notify_event_t *) event;
266
267 if (selection_notify->property == XCB_ATOM_NONE) {
268 /* convert selection failed */
269 } else if (selection_notify->target == wm->atom.targets) {
270 weston_wm_get_selection_targets(wm);
271 } else {
272 weston_wm_get_selection_data(wm);
273 }
274}
275
276static const size_t incr_chunk_size = 64 * 1024;
277
278static void
279weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
280{
281 xcb_selection_notify_event_t selection_notify;
282
283 memset(&selection_notify, 0, sizeof selection_notify);
284 selection_notify.response_type = XCB_SELECTION_NOTIFY;
285 selection_notify.sequence = 0;
286 selection_notify.time = wm->selection_request.time;
287 selection_notify.requestor = wm->selection_request.requestor;
288 selection_notify.selection = wm->selection_request.selection;
289 selection_notify.target = wm->selection_request.target;
290 selection_notify.property = property;
291
292 xcb_send_event(wm->conn, 0, /* propagate */
293 wm->selection_request.requestor,
294 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
295}
296
297static void
298weston_wm_send_targets(struct weston_wm *wm)
299{
300 xcb_atom_t targets[] = {
301 wm->atom.timestamp,
302 wm->atom.targets,
303 wm->atom.utf8_string,
304 /* wm->atom.compound_text, */
305 wm->atom.text,
306 /* wm->atom.string */
307 };
308
309 xcb_change_property(wm->conn,
310 XCB_PROP_MODE_REPLACE,
311 wm->selection_request.requestor,
312 wm->selection_request.property,
313 XCB_ATOM_ATOM,
314 32, /* format */
315 ARRAY_LENGTH(targets), targets);
316
317 weston_wm_send_selection_notify(wm, wm->selection_request.property);
318}
319
320static void
321weston_wm_send_timestamp(struct weston_wm *wm)
322{
323 xcb_change_property(wm->conn,
324 XCB_PROP_MODE_REPLACE,
325 wm->selection_request.requestor,
326 wm->selection_request.property,
327 XCB_ATOM_INTEGER,
328 32, /* format */
329 1, &wm->selection_timestamp);
330
331 weston_wm_send_selection_notify(wm, wm->selection_request.property);
332}
333
334static int
335weston_wm_flush_source_data(struct weston_wm *wm)
336{
337 int length;
338
339 xcb_change_property(wm->conn,
340 XCB_PROP_MODE_REPLACE,
341 wm->selection_request.requestor,
342 wm->selection_request.property,
343 wm->selection_target,
344 8, /* format */
345 wm->source_data.size,
346 wm->source_data.data);
347 wm->selection_property_set = 1;
348 length = wm->source_data.size;
349 wm->source_data.size = 0;
350
351 return length;
352}
353
354static int
355weston_wm_read_data_source(int fd, uint32_t mask, void *data)
356{
357 struct weston_wm *wm = data;
358 int len, current, available;
359 void *p;
360
361 current = wm->source_data.size;
362 if (wm->source_data.size < incr_chunk_size)
363 p = wl_array_add(&wm->source_data, incr_chunk_size);
364 else
365 p = (char *) wm->source_data.data + wm->source_data.size;
366 available = wm->source_data.alloc - current;
367
368 len = read(fd, p, available);
369 if (len == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200370 weston_log("read error from data source: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400371 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
372 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200373 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400374 close(fd);
375 wl_array_release(&wm->source_data);
376 }
377
Martin Minarik6d118362012-06-07 18:01:59 +0200378 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400379 len, available, mask, len, (char *) p);
380
381 wm->source_data.size = current + len;
382 if (wm->source_data.size >= incr_chunk_size) {
383 if (!wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200384 weston_log("got %zu bytes, starting incr\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400385 wm->source_data.size);
386 wm->incr = 1;
387 xcb_change_property(wm->conn,
388 XCB_PROP_MODE_REPLACE,
389 wm->selection_request.requestor,
390 wm->selection_request.property,
391 wm->atom.incr,
392 32, /* format */
393 1, &incr_chunk_size);
394 wm->selection_property_set = 1;
395 wm->flush_property_on_delete = 1;
396 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200397 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400398 weston_wm_send_selection_notify(wm, wm->selection_request.property);
399 } else if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200400 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400401 "property delete\n", wm->source_data.size);
402
403 wm->flush_property_on_delete = 1;
404 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200405 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400406 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200407 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400408 "property deleted, seting new property\n",
409 wm->source_data.size);
410 weston_wm_flush_source_data(wm);
411 }
412 } else if (len == 0 && !wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200413 weston_log("non-incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400414 /* Non-incr transfer all done. */
415 weston_wm_flush_source_data(wm);
416 weston_wm_send_selection_notify(wm, wm->selection_request.property);
417 xcb_flush(wm->conn);
418 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200419 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400420 close(fd);
421 wl_array_release(&wm->source_data);
422 wm->selection_request.requestor = XCB_NONE;
423 } else if (len == 0 && wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200424 weston_log("incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400425
426 wm->flush_property_on_delete = 1;
427 if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200428 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400429 "property delete\n", wm->source_data.size);
430 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200431 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400432 "property deleted, seting new property\n",
433 wm->source_data.size);
434 weston_wm_flush_source_data(wm);
435 }
436 xcb_flush(wm->conn);
437 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200438 wm->property_source = NULL;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400439 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400440 wm->data_source_fd = -1;
441 close(fd);
442 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200443 weston_log("nothing happened, buffered the bytes\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400444 }
445
446 return 1;
447}
448
449static void
450weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
451{
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700452 struct weston_data_source *source;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400453 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400454 int p[2];
455
456 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200457 weston_log("pipe2 failed: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400458 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
459 return;
460 }
461
462 wl_array_init(&wm->source_data);
463 wm->selection_target = target;
464 wm->data_source_fd = p[0];
465 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
466 wm->data_source_fd,
467 WL_EVENT_READABLE,
468 weston_wm_read_data_source,
469 wm);
470
Kristian Høgsberge3148752013-05-06 23:19:49 -0400471 source = seat->selection_data_source;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400472 source->send(source, mime_type, p[1]);
Kristian Høgsberg73bdc0c2013-09-04 22:32:50 -0700473 close(p[1]);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400474}
475
476static void
477weston_wm_send_incr_chunk(struct weston_wm *wm)
478{
479 int length;
480
Martin Minarik6d118362012-06-07 18:01:59 +0200481 weston_log("property deleted\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400482
483 wm->selection_property_set = 0;
484 if (wm->flush_property_on_delete) {
Martin Minarik6d118362012-06-07 18:01:59 +0200485 weston_log("setting new property, %zu bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400486 wm->source_data.size);
487 wm->flush_property_on_delete = 0;
488 length = weston_wm_flush_source_data(wm);
489
490 if (wm->data_source_fd >= 0) {
491 wm->property_source =
492 wl_event_loop_add_fd(wm->server->loop,
493 wm->data_source_fd,
494 WL_EVENT_READABLE,
495 weston_wm_read_data_source,
496 wm);
497 } else if (length > 0) {
498 /* Transfer is all done, but queue a flush for
499 * the delete of the last chunk so we can set
500 * the 0 sized propert to signal the end of
501 * the transfer. */
502 wm->flush_property_on_delete = 1;
503 wl_array_release(&wm->source_data);
504 } else {
505 wm->selection_request.requestor = XCB_NONE;
506 }
507 }
508}
509
510static int
511weston_wm_handle_selection_property_notify(struct weston_wm *wm,
512 xcb_generic_event_t *event)
513{
514 xcb_property_notify_event_t *property_notify =
515 (xcb_property_notify_event_t *) event;
516
517 if (property_notify->window == wm->selection_window) {
518 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
519 property_notify->atom == wm->atom.wl_selection &&
520 wm->incr)
521 weston_wm_get_incr_chunk(wm);
522 return 1;
523 } else if (property_notify->window == wm->selection_request.requestor) {
524 if (property_notify->state == XCB_PROPERTY_DELETE &&
525 property_notify->atom == wm->selection_request.property &&
526 wm->incr)
527 weston_wm_send_incr_chunk(wm);
528 return 1;
529 }
530
531 return 0;
532}
533
534static void
535weston_wm_handle_selection_request(struct weston_wm *wm,
536 xcb_generic_event_t *event)
537{
538 xcb_selection_request_event_t *selection_request =
539 (xcb_selection_request_event_t *) event;
540
Martin Minarik6d118362012-06-07 18:01:59 +0200541 weston_log("selection request, %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400542 get_atom_name(wm->conn, selection_request->selection));
Martin Minarik6d118362012-06-07 18:01:59 +0200543 weston_log_continue("target %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400544 get_atom_name(wm->conn, selection_request->target));
Martin Minarik6d118362012-06-07 18:01:59 +0200545 weston_log_continue("property %s\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400546 get_atom_name(wm->conn, selection_request->property));
547
548 wm->selection_request = *selection_request;
549 wm->incr = 0;
550 wm->flush_property_on_delete = 0;
551
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400552 if (selection_request->selection == wm->atom.clipboard_manager) {
553 /* The weston clipboard should already have grabbed
554 * the first target, so just send selection notify
555 * now. This isn't synchronized with the clipboard
556 * finishing getting the data, so there's a race here. */
557 weston_wm_send_selection_notify(wm, wm->selection_request.property);
558 return;
559 }
560
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400561 if (selection_request->target == wm->atom.targets) {
562 weston_wm_send_targets(wm);
563 } else if (selection_request->target == wm->atom.timestamp) {
564 weston_wm_send_timestamp(wm);
565 } else if (selection_request->target == wm->atom.utf8_string ||
566 selection_request->target == wm->atom.text) {
567 weston_wm_send_data(wm, wm->atom.utf8_string,
568 "text/plain;charset=utf-8");
569 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200570 weston_log("can only handle UTF8_STRING targets...\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400571 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
572 }
573}
574
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700575static int
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400576weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
577 xcb_generic_event_t *event)
578{
579 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
580 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400581 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400582 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400583 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400584
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700585 if (xfixes_selection_notify->selection != wm->atom.clipboard)
586 return 0;
587
Martin Minarik6d118362012-06-07 18:01:59 +0200588 weston_log("xfixes selection notify event: owner %d\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400589 xfixes_selection_notify->owner);
590
Kristian Høgsberg80566732012-06-01 00:08:12 -0400591 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
592 if (wm->selection_owner != wm->selection_window) {
593 /* A real X client selection went away, not our
594 * proxy selection. Clear the wayland selection. */
595 compositor = wm->server->compositor;
596 serial = wl_display_next_serial(compositor->wl_display);
Kristian Høgsberge3148752013-05-06 23:19:49 -0400597 weston_seat_set_selection(seat, NULL, serial);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400598 }
599
600 wm->selection_owner = XCB_WINDOW_NONE;
601
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700602 return 1;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400603 }
604
605 wm->selection_owner = xfixes_selection_notify->owner;
606
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400607 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
608 * selection, so grab the actual timestamp here so we can
609 * answer TIMESTAMP conversion requests correctly. */
610 if (xfixes_selection_notify->owner == wm->selection_window) {
611 wm->selection_timestamp = xfixes_selection_notify->timestamp;
Martin Minarik6d118362012-06-07 18:01:59 +0200612 weston_log("our window, skipping\n");
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700613 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400614 }
615
616 wm->incr = 0;
617 xcb_convert_selection(wm->conn, wm->selection_window,
618 wm->atom.clipboard,
619 wm->atom.targets,
620 wm->atom.wl_selection,
621 xfixes_selection_notify->timestamp);
622
623 xcb_flush(wm->conn);
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700624
625 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400626}
627
628int
629weston_wm_handle_selection_event(struct weston_wm *wm,
630 xcb_generic_event_t *event)
631{
632 switch (event->response_type & ~0x80) {
633 case XCB_SELECTION_NOTIFY:
634 weston_wm_handle_selection_notify(wm, event);
635 return 1;
636 case XCB_PROPERTY_NOTIFY:
637 return weston_wm_handle_selection_property_notify(wm, event);
638 case XCB_SELECTION_REQUEST:
639 weston_wm_handle_selection_request(wm, event);
640 return 1;
641 }
642
643 switch (event->response_type - wm->xfixes->first_event) {
644 case XCB_XFIXES_SELECTION_NOTIFY:
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700645 return weston_wm_handle_xfixes_selection_notify(wm, event);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400646 }
647
648 return 0;
649}
650
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200651static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400652weston_wm_set_selection(struct wl_listener *listener, void *data)
653{
Kristian Høgsberge3148752013-05-06 23:19:49 -0400654 struct weston_seat *seat = data;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400655 struct weston_wm *wm =
656 container_of(listener, struct weston_wm, selection_listener);
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700657 struct weston_data_source *source = seat->selection_data_source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400658
Kristian Høgsberg80566732012-06-01 00:08:12 -0400659 if (source == NULL) {
660 if (wm->selection_owner == wm->selection_window)
661 xcb_set_selection_owner(wm->conn,
662 XCB_ATOM_NONE,
663 wm->atom.clipboard,
664 wm->selection_timestamp);
665 return;
666 }
667
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400668 if (source->send == data_source_send)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400669 return;
670
Derek Foreman4e184482016-02-01 14:36:57 -0600671 xcb_set_selection_owner(wm->conn,
672 wm->selection_window,
673 wm->atom.clipboard,
674 XCB_TIME_CURRENT_TIME);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400675}
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400676
677void
678weston_wm_selection_init(struct weston_wm *wm)
679{
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400680 struct weston_seat *seat;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400681 uint32_t values[1], mask;
682
683 wm->selection_request.requestor = XCB_NONE;
684
685 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
686 wm->selection_window = xcb_generate_id(wm->conn);
687 xcb_create_window(wm->conn,
688 XCB_COPY_FROM_PARENT,
689 wm->selection_window,
690 wm->screen->root,
691 0, 0,
692 10, 10,
693 0,
694 XCB_WINDOW_CLASS_INPUT_OUTPUT,
695 wm->screen->root_visual,
696 XCB_CW_EVENT_MASK, values);
697
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400698 xcb_set_selection_owner(wm->conn,
699 wm->selection_window,
700 wm->atom.clipboard_manager,
701 XCB_TIME_CURRENT_TIME);
702
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400703 mask =
704 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
705 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
706 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
707 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
708 wm->atom.clipboard, mask);
709
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400710 seat = weston_wm_pick_seat(wm);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400711 wm->selection_listener.notify = weston_wm_set_selection;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400712 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
Kristian Høgsberge2203272012-06-03 10:32:48 -0400713
714 weston_wm_set_selection(&wm->selection_listener, seat);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400715}