blob: c4845f20a0f9ed793261907aaa4be293a0135b84 [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
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030028#include <stdint.h>
Kristian Høgsberg102bf032012-05-21 15:52:02 -040029#include <stdlib.h>
Kristian Høgsberg102bf032012-05-21 15:52:02 -040030#include <string.h>
31#include <unistd.h>
32#include <fcntl.h>
Antonio Borneo39578632019-04-26 23:57:31 +020033#include <errno.h>
Kristian Høgsberg102bf032012-05-21 15:52:02 -040034
Guillaume Champagnef1e8fc92020-01-27 20:14:29 -050035#include <libweston/libweston.h>
Kristian Høgsberg102bf032012-05-21 15:52:02 -040036#include "xwayland.h"
Jon Cruz35b2eaa2015-06-15 15:37:08 -070037#include "shared/helpers.h"
Kristian Høgsberg102bf032012-05-21 15:52:02 -040038
Pekka Paalanenb3b00652017-10-12 13:18:13 +020039#ifdef WM_DEBUG
40#define wm_log(...) weston_log(__VA_ARGS__)
41#else
42#define wm_log(...) do {} while (0)
43#endif
44
Kristian Høgsberg102bf032012-05-21 15:52:02 -040045static int
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070046writable_callback(int fd, uint32_t mask, void *data)
Kristian Høgsberg102bf032012-05-21 15:52:02 -040047{
48 struct weston_wm *wm = data;
49 unsigned char *property;
50 int len, remainder;
51
52 property = xcb_get_property_value(wm->property_reply);
53 remainder = xcb_get_property_value_length(wm->property_reply) -
54 wm->property_start;
55
56 len = write(fd, property + wm->property_start, remainder);
57 if (len == -1) {
58 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070059 wm->property_reply = NULL;
60 if (wm->property_source)
61 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020062 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040063 close(fd);
Antonio Borneo39578632019-04-26 23:57:31 +020064 weston_log("write error to target fd: %s\n", strerror(errno));
Kristian Høgsberg102bf032012-05-21 15:52:02 -040065 return 1;
66 }
67
Martin Minarik6d118362012-06-07 18:01:59 +020068 weston_log("wrote %d (chunk size %d) of %d bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -040069 wm->property_start + len,
70 len, xcb_get_property_value_length(wm->property_reply));
71
72 wm->property_start += len;
73 if (len == remainder) {
74 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070075 wm->property_reply = NULL;
76 if (wm->property_source)
77 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020078 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040079
80 if (wm->incr) {
81 xcb_delete_property(wm->conn,
82 wm->selection_window,
83 wm->atom.wl_selection);
84 } else {
Martin Minarik6d118362012-06-07 18:01:59 +020085 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040086 close(fd);
87 }
88 }
89
90 return 1;
91}
92
93static void
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070094weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply)
95{
96 wm->property_start = 0;
97 wm->property_reply = reply;
98 writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm);
99
100 if (wm->property_reply)
101 wm->property_source =
102 wl_event_loop_add_fd(wm->server->loop,
103 wm->data_source_fd,
104 WL_EVENT_WRITABLE,
105 writable_callback, wm);
106}
107
108static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400109weston_wm_get_incr_chunk(struct weston_wm *wm)
110{
111 xcb_get_property_cookie_t cookie;
112 xcb_get_property_reply_t *reply;
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200113 FILE *fp;
114 char *logstr;
115 size_t logsize;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400116
117 cookie = xcb_get_property(wm->conn,
118 0, /* delete */
119 wm->selection_window,
120 wm->atom.wl_selection,
121 XCB_GET_PROPERTY_TYPE_ANY,
122 0, /* offset */
123 0x1fffffff /* length */);
124
125 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700126 if (reply == NULL)
127 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400128
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200129 fp = open_memstream(&logstr, &logsize);
130 if (fp) {
131 dump_property(fp, wm, wm->atom.wl_selection, reply);
132 if (fclose(fp) == 0)
133 wm_log("%s", logstr);
134 free(logstr);
135 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400136
137 if (xcb_get_property_value_length(reply) > 0) {
Eric Engestromf951f822016-04-02 17:03:14 +0100138 /* reply's ownership is transferred to wm, which is responsible
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200139 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700140 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400141 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200142 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400143 close(wm->data_source_fd);
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200144 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400145 }
146}
147
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400148struct x11_data_source {
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700149 struct weston_data_source base;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400150 struct weston_wm *wm;
151};
152
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400153static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700154data_source_accept(struct weston_data_source *source,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400155 uint32_t time, const char *mime_type)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400156{
157}
158
159static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700160data_source_send(struct weston_data_source *base,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400161 const char *mime_type, int32_t fd)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400162{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400163 struct x11_data_source *source = (struct x11_data_source *) base;
164 struct weston_wm *wm = source->wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400165
166 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
167 /* Get data for the utf8_string target */
168 xcb_convert_selection(wm->conn,
169 wm->selection_window,
170 wm->atom.clipboard,
171 wm->atom.utf8_string,
172 wm->atom.wl_selection,
173 XCB_TIME_CURRENT_TIME);
174
175 xcb_flush(wm->conn);
176
177 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
Kristian Høgsberg668fc0d2013-09-04 20:48:46 -0700178 wm->data_source_fd = fd;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400179 }
180}
181
182static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700183data_source_cancel(struct weston_data_source *source)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400184{
185}
186
187static void
188weston_wm_get_selection_targets(struct weston_wm *wm)
189{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400190 struct x11_data_source *source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400191 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400192 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400193 xcb_get_property_cookie_t cookie;
194 xcb_get_property_reply_t *reply;
195 xcb_atom_t *value;
196 char **p;
197 uint32_t i;
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200198 FILE *fp;
199 char *logstr;
200 size_t logsize;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400201
202 cookie = xcb_get_property(wm->conn,
203 1, /* delete */
204 wm->selection_window,
205 wm->atom.wl_selection,
206 XCB_GET_PROPERTY_TYPE_ANY,
207 0, /* offset */
208 4096 /* length */);
209
210 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700211 if (reply == NULL)
212 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400213
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200214 fp = open_memstream(&logstr, &logsize);
215 if (fp) {
216 dump_property(fp, wm, wm->atom.wl_selection, reply);
217 if (fclose(fp) == 0)
218 wm_log("%s", logstr);
219 free(logstr);
220 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400221
222 if (reply->type != XCB_ATOM_ATOM) {
223 free(reply);
224 return;
225 }
226
Carlos Garnachobeb7a9f2016-02-01 20:28:15 +0100227 source = zalloc(sizeof *source);
Bryce Harringtond3553c72015-06-30 21:35:43 -0700228 if (source == NULL) {
229 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400230 return;
Bryce Harringtond3553c72015-06-30 21:35:43 -0700231 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400232
Jason Ekstrand8a4a9eb2013-06-14 10:07:55 -0500233 wl_signal_init(&source->base.destroy_signal);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400234 source->base.accept = data_source_accept;
235 source->base.send = data_source_send;
236 source->base.cancel = data_source_cancel;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400237 source->wm = wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400238
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400239 wl_array_init(&source->base.mime_types);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400240 value = xcb_get_property_value(reply);
241 for (i = 0; i < reply->value_len; i++) {
242 if (value[i] == wm->atom.utf8_string) {
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400243 p = wl_array_add(&source->base.mime_types, sizeof *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400244 if (p)
245 *p = strdup("text/plain;charset=utf-8");
246 }
247 }
248
249 compositor = wm->server->compositor;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400250 weston_seat_set_selection(seat, &source->base,
251 wl_display_next_serial(compositor->wl_display));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400252
253 free(reply);
254}
255
256static void
257weston_wm_get_selection_data(struct weston_wm *wm)
258{
259 xcb_get_property_cookie_t cookie;
260 xcb_get_property_reply_t *reply;
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200261 FILE *fp;
262 char *logstr;
263 size_t logsize;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400264
265 cookie = xcb_get_property(wm->conn,
266 1, /* delete */
267 wm->selection_window,
268 wm->atom.wl_selection,
269 XCB_GET_PROPERTY_TYPE_ANY,
270 0, /* offset */
271 0x1fffffff /* length */);
272
273 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
274
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200275 fp = open_memstream(&logstr, &logsize);
276 if (fp) {
277 dump_property(fp, wm, wm->atom.wl_selection, reply);
278 if (fclose(fp) == 0)
279 wm_log("%s", logstr);
280 free(logstr);
281 }
Bryce Harringtonda41b572015-07-10 18:13:48 -0700282
283 if (reply == NULL) {
284 return;
285 } else if (reply->type == wm->atom.incr) {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400286 wm->incr = 1;
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200287 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400288 } else {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400289 wm->incr = 0;
Eric Engestromf951f822016-04-02 17:03:14 +0100290 /* reply's ownership is transferred to wm, which is responsible
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200291 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700292 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400293 }
294}
295
296static void
297weston_wm_handle_selection_notify(struct weston_wm *wm,
298 xcb_generic_event_t *event)
299{
300 xcb_selection_notify_event_t *selection_notify =
301 (xcb_selection_notify_event_t *) event;
302
303 if (selection_notify->property == XCB_ATOM_NONE) {
304 /* convert selection failed */
305 } else if (selection_notify->target == wm->atom.targets) {
306 weston_wm_get_selection_targets(wm);
307 } else {
308 weston_wm_get_selection_data(wm);
309 }
310}
311
312static const size_t incr_chunk_size = 64 * 1024;
313
314static void
315weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
316{
317 xcb_selection_notify_event_t selection_notify;
318
319 memset(&selection_notify, 0, sizeof selection_notify);
320 selection_notify.response_type = XCB_SELECTION_NOTIFY;
321 selection_notify.sequence = 0;
322 selection_notify.time = wm->selection_request.time;
323 selection_notify.requestor = wm->selection_request.requestor;
324 selection_notify.selection = wm->selection_request.selection;
325 selection_notify.target = wm->selection_request.target;
326 selection_notify.property = property;
327
328 xcb_send_event(wm->conn, 0, /* propagate */
329 wm->selection_request.requestor,
330 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
331}
332
333static void
334weston_wm_send_targets(struct weston_wm *wm)
335{
336 xcb_atom_t targets[] = {
337 wm->atom.timestamp,
338 wm->atom.targets,
339 wm->atom.utf8_string,
340 /* wm->atom.compound_text, */
341 wm->atom.text,
342 /* wm->atom.string */
343 };
344
345 xcb_change_property(wm->conn,
346 XCB_PROP_MODE_REPLACE,
347 wm->selection_request.requestor,
348 wm->selection_request.property,
349 XCB_ATOM_ATOM,
350 32, /* format */
351 ARRAY_LENGTH(targets), targets);
352
353 weston_wm_send_selection_notify(wm, wm->selection_request.property);
354}
355
356static void
357weston_wm_send_timestamp(struct weston_wm *wm)
358{
359 xcb_change_property(wm->conn,
360 XCB_PROP_MODE_REPLACE,
361 wm->selection_request.requestor,
362 wm->selection_request.property,
363 XCB_ATOM_INTEGER,
364 32, /* format */
365 1, &wm->selection_timestamp);
366
367 weston_wm_send_selection_notify(wm, wm->selection_request.property);
368}
369
370static int
371weston_wm_flush_source_data(struct weston_wm *wm)
372{
373 int length;
374
375 xcb_change_property(wm->conn,
376 XCB_PROP_MODE_REPLACE,
377 wm->selection_request.requestor,
378 wm->selection_request.property,
379 wm->selection_target,
380 8, /* format */
381 wm->source_data.size,
382 wm->source_data.data);
383 wm->selection_property_set = 1;
384 length = wm->source_data.size;
385 wm->source_data.size = 0;
386
387 return length;
388}
389
390static int
391weston_wm_read_data_source(int fd, uint32_t mask, void *data)
392{
393 struct weston_wm *wm = data;
394 int len, current, available;
395 void *p;
396
397 current = wm->source_data.size;
398 if (wm->source_data.size < incr_chunk_size)
399 p = wl_array_add(&wm->source_data, incr_chunk_size);
400 else
401 p = (char *) wm->source_data.data + wm->source_data.size;
402 available = wm->source_data.alloc - current;
403
404 len = read(fd, p, available);
405 if (len == -1) {
Antonio Borneo39578632019-04-26 23:57:31 +0200406 weston_log("read error from data source: %s\n",
407 strerror(errno));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400408 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
Greg V3ea54372018-07-22 11:36:21 +0100409 if (wm->property_source)
410 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200411 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400412 close(fd);
413 wl_array_release(&wm->source_data);
Greg V4253f232018-11-08 00:24:15 +0300414 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400415 }
416
Martin Minarik6d118362012-06-07 18:01:59 +0200417 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400418 len, available, mask, len, (char *) p);
419
420 wm->source_data.size = current + len;
421 if (wm->source_data.size >= incr_chunk_size) {
422 if (!wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200423 weston_log("got %zu bytes, starting incr\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400424 wm->source_data.size);
425 wm->incr = 1;
426 xcb_change_property(wm->conn,
427 XCB_PROP_MODE_REPLACE,
428 wm->selection_request.requestor,
429 wm->selection_request.property,
430 wm->atom.incr,
431 32, /* format */
432 1, &incr_chunk_size);
433 wm->selection_property_set = 1;
434 wm->flush_property_on_delete = 1;
Greg V3ea54372018-07-22 11:36:21 +0100435 if (wm->property_source)
436 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200437 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400438 weston_wm_send_selection_notify(wm, wm->selection_request.property);
439 } else if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200440 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400441 "property delete\n", wm->source_data.size);
442
443 wm->flush_property_on_delete = 1;
Greg V3ea54372018-07-22 11:36:21 +0100444 if (wm->property_source)
445 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200446 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400447 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200448 weston_log("got %zu bytes, "
Abdur Rehmanb8b150b2017-01-01 19:46:46 +0500449 "property deleted, setting new property\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400450 wm->source_data.size);
451 weston_wm_flush_source_data(wm);
452 }
453 } else if (len == 0 && !wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200454 weston_log("non-incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400455 /* Non-incr transfer all done. */
456 weston_wm_flush_source_data(wm);
457 weston_wm_send_selection_notify(wm, wm->selection_request.property);
458 xcb_flush(wm->conn);
Greg V3ea54372018-07-22 11:36:21 +0100459 if (wm->property_source)
460 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200461 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400462 close(fd);
463 wl_array_release(&wm->source_data);
464 wm->selection_request.requestor = XCB_NONE;
465 } else if (len == 0 && wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200466 weston_log("incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400467
468 wm->flush_property_on_delete = 1;
469 if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200470 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400471 "property delete\n", wm->source_data.size);
472 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200473 weston_log("got %zu bytes, "
Abdur Rehmanb8b150b2017-01-01 19:46:46 +0500474 "property deleted, setting new property\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400475 wm->source_data.size);
476 weston_wm_flush_source_data(wm);
477 }
478 xcb_flush(wm->conn);
Greg V3ea54372018-07-22 11:36:21 +0100479 if (wm->property_source)
480 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200481 wm->property_source = NULL;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400482 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400483 wm->data_source_fd = -1;
484 close(fd);
485 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200486 weston_log("nothing happened, buffered the bytes\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400487 }
488
489 return 1;
490}
491
492static void
493weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
494{
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700495 struct weston_data_source *source;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400496 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400497 int p[2];
498
499 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
Antonio Borneo39578632019-04-26 23:57:31 +0200500 weston_log("pipe2 failed: %s\n", strerror(errno));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400501 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
502 return;
503 }
504
505 wl_array_init(&wm->source_data);
506 wm->selection_target = target;
507 wm->data_source_fd = p[0];
508 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
509 wm->data_source_fd,
510 WL_EVENT_READABLE,
511 weston_wm_read_data_source,
512 wm);
513
Kristian Høgsberge3148752013-05-06 23:19:49 -0400514 source = seat->selection_data_source;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400515 source->send(source, mime_type, p[1]);
Kristian Høgsberg73bdc0c2013-09-04 22:32:50 -0700516 close(p[1]);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400517}
518
519static void
520weston_wm_send_incr_chunk(struct weston_wm *wm)
521{
522 int length;
523
Martin Minarik6d118362012-06-07 18:01:59 +0200524 weston_log("property deleted\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400525
526 wm->selection_property_set = 0;
527 if (wm->flush_property_on_delete) {
Martin Minarik6d118362012-06-07 18:01:59 +0200528 weston_log("setting new property, %zu bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400529 wm->source_data.size);
530 wm->flush_property_on_delete = 0;
531 length = weston_wm_flush_source_data(wm);
532
533 if (wm->data_source_fd >= 0) {
534 wm->property_source =
535 wl_event_loop_add_fd(wm->server->loop,
536 wm->data_source_fd,
537 WL_EVENT_READABLE,
538 weston_wm_read_data_source,
539 wm);
540 } else if (length > 0) {
541 /* Transfer is all done, but queue a flush for
542 * the delete of the last chunk so we can set
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100543 * the 0 sized property to signal the end of
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400544 * the transfer. */
545 wm->flush_property_on_delete = 1;
546 wl_array_release(&wm->source_data);
547 } else {
548 wm->selection_request.requestor = XCB_NONE;
549 }
550 }
551}
552
553static int
554weston_wm_handle_selection_property_notify(struct weston_wm *wm,
555 xcb_generic_event_t *event)
556{
557 xcb_property_notify_event_t *property_notify =
558 (xcb_property_notify_event_t *) event;
559
560 if (property_notify->window == wm->selection_window) {
561 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
562 property_notify->atom == wm->atom.wl_selection &&
563 wm->incr)
564 weston_wm_get_incr_chunk(wm);
565 return 1;
566 } else if (property_notify->window == wm->selection_request.requestor) {
567 if (property_notify->state == XCB_PROPERTY_DELETE &&
568 property_notify->atom == wm->selection_request.property &&
569 wm->incr)
570 weston_wm_send_incr_chunk(wm);
571 return 1;
572 }
573
574 return 0;
575}
576
577static void
578weston_wm_handle_selection_request(struct weston_wm *wm,
579 xcb_generic_event_t *event)
580{
581 xcb_selection_request_event_t *selection_request =
582 (xcb_selection_request_event_t *) event;
583
Martin Minarik6d118362012-06-07 18:01:59 +0200584 weston_log("selection request, %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400585 get_atom_name(wm->conn, selection_request->selection));
Martin Minarik6d118362012-06-07 18:01:59 +0200586 weston_log_continue("target %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400587 get_atom_name(wm->conn, selection_request->target));
Martin Minarik6d118362012-06-07 18:01:59 +0200588 weston_log_continue("property %s\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400589 get_atom_name(wm->conn, selection_request->property));
590
591 wm->selection_request = *selection_request;
592 wm->incr = 0;
593 wm->flush_property_on_delete = 0;
594
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400595 if (selection_request->selection == wm->atom.clipboard_manager) {
596 /* The weston clipboard should already have grabbed
597 * the first target, so just send selection notify
598 * now. This isn't synchronized with the clipboard
599 * finishing getting the data, so there's a race here. */
600 weston_wm_send_selection_notify(wm, wm->selection_request.property);
601 return;
602 }
603
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400604 if (selection_request->target == wm->atom.targets) {
605 weston_wm_send_targets(wm);
606 } else if (selection_request->target == wm->atom.timestamp) {
607 weston_wm_send_timestamp(wm);
608 } else if (selection_request->target == wm->atom.utf8_string ||
609 selection_request->target == wm->atom.text) {
610 weston_wm_send_data(wm, wm->atom.utf8_string,
611 "text/plain;charset=utf-8");
612 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200613 weston_log("can only handle UTF8_STRING targets...\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400614 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
615 }
616}
617
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700618static int
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400619weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
620 xcb_generic_event_t *event)
621{
622 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
623 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400624 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400625 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400626 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400627
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700628 if (xfixes_selection_notify->selection != wm->atom.clipboard)
629 return 0;
630
Martin Minarik6d118362012-06-07 18:01:59 +0200631 weston_log("xfixes selection notify event: owner %d\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400632 xfixes_selection_notify->owner);
633
Kristian Høgsberg80566732012-06-01 00:08:12 -0400634 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
635 if (wm->selection_owner != wm->selection_window) {
636 /* A real X client selection went away, not our
637 * proxy selection. Clear the wayland selection. */
638 compositor = wm->server->compositor;
639 serial = wl_display_next_serial(compositor->wl_display);
Kristian Høgsberge3148752013-05-06 23:19:49 -0400640 weston_seat_set_selection(seat, NULL, serial);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400641 }
642
643 wm->selection_owner = XCB_WINDOW_NONE;
644
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700645 return 1;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400646 }
647
648 wm->selection_owner = xfixes_selection_notify->owner;
649
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400650 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
651 * selection, so grab the actual timestamp here so we can
652 * answer TIMESTAMP conversion requests correctly. */
653 if (xfixes_selection_notify->owner == wm->selection_window) {
654 wm->selection_timestamp = xfixes_selection_notify->timestamp;
Martin Minarik6d118362012-06-07 18:01:59 +0200655 weston_log("our window, skipping\n");
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700656 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400657 }
658
659 wm->incr = 0;
660 xcb_convert_selection(wm->conn, wm->selection_window,
661 wm->atom.clipboard,
662 wm->atom.targets,
663 wm->atom.wl_selection,
664 xfixes_selection_notify->timestamp);
665
666 xcb_flush(wm->conn);
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700667
668 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400669}
670
671int
672weston_wm_handle_selection_event(struct weston_wm *wm,
673 xcb_generic_event_t *event)
674{
675 switch (event->response_type & ~0x80) {
676 case XCB_SELECTION_NOTIFY:
677 weston_wm_handle_selection_notify(wm, event);
678 return 1;
679 case XCB_PROPERTY_NOTIFY:
680 return weston_wm_handle_selection_property_notify(wm, event);
681 case XCB_SELECTION_REQUEST:
682 weston_wm_handle_selection_request(wm, event);
683 return 1;
684 }
685
686 switch (event->response_type - wm->xfixes->first_event) {
687 case XCB_XFIXES_SELECTION_NOTIFY:
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700688 return weston_wm_handle_xfixes_selection_notify(wm, event);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400689 }
690
691 return 0;
692}
693
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200694static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400695weston_wm_set_selection(struct wl_listener *listener, void *data)
696{
Kristian Høgsberge3148752013-05-06 23:19:49 -0400697 struct weston_seat *seat = data;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400698 struct weston_wm *wm =
699 container_of(listener, struct weston_wm, selection_listener);
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700700 struct weston_data_source *source = seat->selection_data_source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400701
Kristian Høgsberg80566732012-06-01 00:08:12 -0400702 if (source == NULL) {
703 if (wm->selection_owner == wm->selection_window)
704 xcb_set_selection_owner(wm->conn,
705 XCB_ATOM_NONE,
706 wm->atom.clipboard,
707 wm->selection_timestamp);
708 return;
709 }
710
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400711 if (source->send == data_source_send)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400712 return;
713
Derek Foreman4e184482016-02-01 14:36:57 -0600714 xcb_set_selection_owner(wm->conn,
715 wm->selection_window,
716 wm->atom.clipboard,
717 XCB_TIME_CURRENT_TIME);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400718}
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400719
720void
721weston_wm_selection_init(struct weston_wm *wm)
722{
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400723 struct weston_seat *seat;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400724 uint32_t values[1], mask;
725
Derek Foreman4b72ff02018-02-05 15:59:29 -0600726 wl_list_init(&wm->selection_listener.link);
727
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400728 wm->selection_request.requestor = XCB_NONE;
729
730 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
731 wm->selection_window = xcb_generate_id(wm->conn);
732 xcb_create_window(wm->conn,
733 XCB_COPY_FROM_PARENT,
734 wm->selection_window,
735 wm->screen->root,
736 0, 0,
737 10, 10,
738 0,
739 XCB_WINDOW_CLASS_INPUT_OUTPUT,
740 wm->screen->root_visual,
741 XCB_CW_EVENT_MASK, values);
742
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400743 xcb_set_selection_owner(wm->conn,
744 wm->selection_window,
745 wm->atom.clipboard_manager,
746 XCB_TIME_CURRENT_TIME);
747
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400748 mask =
749 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
750 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
751 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
752 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
753 wm->atom.clipboard, mask);
754
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400755 seat = weston_wm_pick_seat(wm);
Tom Hochsteine7fff212016-11-01 14:14:00 -0500756 if (seat == NULL)
757 return;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400758 wm->selection_listener.notify = weston_wm_set_selection;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400759 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
Kristian Høgsberge2203272012-06-03 10:32:48 -0400760
761 weston_wm_set_selection(&wm->selection_listener, seat);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400762}