blob: e4d797aec82710de8ec46daaf8175383141bda64 [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
35#include "xwayland.h"
Jon Cruz35b2eaa2015-06-15 15:37:08 -070036#include "shared/helpers.h"
Kristian Høgsberg102bf032012-05-21 15:52:02 -040037
Pekka Paalanenb3b00652017-10-12 13:18:13 +020038#ifdef WM_DEBUG
39#define wm_log(...) weston_log(__VA_ARGS__)
40#else
41#define wm_log(...) do {} while (0)
42#endif
43
Kristian Høgsberg102bf032012-05-21 15:52:02 -040044static int
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070045writable_callback(int fd, uint32_t mask, void *data)
Kristian Høgsberg102bf032012-05-21 15:52:02 -040046{
47 struct weston_wm *wm = data;
48 unsigned char *property;
49 int len, remainder;
50
51 property = xcb_get_property_value(wm->property_reply);
52 remainder = xcb_get_property_value_length(wm->property_reply) -
53 wm->property_start;
54
55 len = write(fd, property + wm->property_start, remainder);
56 if (len == -1) {
57 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070058 wm->property_reply = NULL;
59 if (wm->property_source)
60 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020061 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040062 close(fd);
Antonio Borneo39578632019-04-26 23:57:31 +020063 weston_log("write error to target fd: %s\n", strerror(errno));
Kristian Høgsberg102bf032012-05-21 15:52:02 -040064 return 1;
65 }
66
Martin Minarik6d118362012-06-07 18:01:59 +020067 weston_log("wrote %d (chunk size %d) of %d bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -040068 wm->property_start + len,
69 len, xcb_get_property_value_length(wm->property_reply));
70
71 wm->property_start += len;
72 if (len == remainder) {
73 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070074 wm->property_reply = NULL;
75 if (wm->property_source)
76 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020077 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040078
79 if (wm->incr) {
80 xcb_delete_property(wm->conn,
81 wm->selection_window,
82 wm->atom.wl_selection);
83 } else {
Martin Minarik6d118362012-06-07 18:01:59 +020084 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040085 close(fd);
86 }
87 }
88
89 return 1;
90}
91
92static void
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070093weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply)
94{
95 wm->property_start = 0;
96 wm->property_reply = reply;
97 writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm);
98
99 if (wm->property_reply)
100 wm->property_source =
101 wl_event_loop_add_fd(wm->server->loop,
102 wm->data_source_fd,
103 WL_EVENT_WRITABLE,
104 writable_callback, wm);
105}
106
107static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400108weston_wm_get_incr_chunk(struct weston_wm *wm)
109{
110 xcb_get_property_cookie_t cookie;
111 xcb_get_property_reply_t *reply;
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200112 FILE *fp;
113 char *logstr;
114 size_t logsize;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400115
116 cookie = xcb_get_property(wm->conn,
117 0, /* delete */
118 wm->selection_window,
119 wm->atom.wl_selection,
120 XCB_GET_PROPERTY_TYPE_ANY,
121 0, /* offset */
122 0x1fffffff /* length */);
123
124 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700125 if (reply == NULL)
126 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400127
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200128 fp = open_memstream(&logstr, &logsize);
129 if (fp) {
130 dump_property(fp, wm, wm->atom.wl_selection, reply);
131 if (fclose(fp) == 0)
132 wm_log("%s", logstr);
133 free(logstr);
134 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400135
136 if (xcb_get_property_value_length(reply) > 0) {
Eric Engestromf951f822016-04-02 17:03:14 +0100137 /* reply's ownership is transferred to wm, which is responsible
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200138 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700139 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400140 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200141 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400142 close(wm->data_source_fd);
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200143 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400144 }
145}
146
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400147struct x11_data_source {
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700148 struct weston_data_source base;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400149 struct weston_wm *wm;
150};
151
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400152static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700153data_source_accept(struct weston_data_source *source,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400154 uint32_t time, const char *mime_type)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400155{
156}
157
158static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700159data_source_send(struct weston_data_source *base,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400160 const char *mime_type, int32_t fd)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400161{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400162 struct x11_data_source *source = (struct x11_data_source *) base;
163 struct weston_wm *wm = source->wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400164
165 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
166 /* Get data for the utf8_string target */
167 xcb_convert_selection(wm->conn,
168 wm->selection_window,
169 wm->atom.clipboard,
170 wm->atom.utf8_string,
171 wm->atom.wl_selection,
172 XCB_TIME_CURRENT_TIME);
173
174 xcb_flush(wm->conn);
175
176 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
Kristian Høgsberg668fc0d2013-09-04 20:48:46 -0700177 wm->data_source_fd = fd;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400178 }
179}
180
181static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700182data_source_cancel(struct weston_data_source *source)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400183{
184}
185
186static void
187weston_wm_get_selection_targets(struct weston_wm *wm)
188{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400189 struct x11_data_source *source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400190 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400191 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400192 xcb_get_property_cookie_t cookie;
193 xcb_get_property_reply_t *reply;
194 xcb_atom_t *value;
195 char **p;
196 uint32_t i;
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200197 FILE *fp;
198 char *logstr;
199 size_t logsize;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400200
201 cookie = xcb_get_property(wm->conn,
202 1, /* delete */
203 wm->selection_window,
204 wm->atom.wl_selection,
205 XCB_GET_PROPERTY_TYPE_ANY,
206 0, /* offset */
207 4096 /* length */);
208
209 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700210 if (reply == NULL)
211 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400212
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200213 fp = open_memstream(&logstr, &logsize);
214 if (fp) {
215 dump_property(fp, wm, wm->atom.wl_selection, reply);
216 if (fclose(fp) == 0)
217 wm_log("%s", logstr);
218 free(logstr);
219 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400220
221 if (reply->type != XCB_ATOM_ATOM) {
222 free(reply);
223 return;
224 }
225
Carlos Garnachobeb7a9f2016-02-01 20:28:15 +0100226 source = zalloc(sizeof *source);
Bryce Harringtond3553c72015-06-30 21:35:43 -0700227 if (source == NULL) {
228 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400229 return;
Bryce Harringtond3553c72015-06-30 21:35:43 -0700230 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400231
Jason Ekstrand8a4a9eb2013-06-14 10:07:55 -0500232 wl_signal_init(&source->base.destroy_signal);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400233 source->base.accept = data_source_accept;
234 source->base.send = data_source_send;
235 source->base.cancel = data_source_cancel;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400236 source->wm = wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400237
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400238 wl_array_init(&source->base.mime_types);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400239 value = xcb_get_property_value(reply);
240 for (i = 0; i < reply->value_len; i++) {
241 if (value[i] == wm->atom.utf8_string) {
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400242 p = wl_array_add(&source->base.mime_types, sizeof *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400243 if (p)
244 *p = strdup("text/plain;charset=utf-8");
245 }
246 }
247
248 compositor = wm->server->compositor;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400249 weston_seat_set_selection(seat, &source->base,
250 wl_display_next_serial(compositor->wl_display));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400251
252 free(reply);
253}
254
255static void
256weston_wm_get_selection_data(struct weston_wm *wm)
257{
258 xcb_get_property_cookie_t cookie;
259 xcb_get_property_reply_t *reply;
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200260 FILE *fp;
261 char *logstr;
262 size_t logsize;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400263
264 cookie = xcb_get_property(wm->conn,
265 1, /* delete */
266 wm->selection_window,
267 wm->atom.wl_selection,
268 XCB_GET_PROPERTY_TYPE_ANY,
269 0, /* offset */
270 0x1fffffff /* length */);
271
272 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
273
Pekka Paalanenb3b00652017-10-12 13:18:13 +0200274 fp = open_memstream(&logstr, &logsize);
275 if (fp) {
276 dump_property(fp, wm, wm->atom.wl_selection, reply);
277 if (fclose(fp) == 0)
278 wm_log("%s", logstr);
279 free(logstr);
280 }
Bryce Harringtonda41b572015-07-10 18:13:48 -0700281
282 if (reply == NULL) {
283 return;
284 } else if (reply->type == wm->atom.incr) {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400285 wm->incr = 1;
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200286 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400287 } else {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400288 wm->incr = 0;
Eric Engestromf951f822016-04-02 17:03:14 +0100289 /* reply's ownership is transferred to wm, which is responsible
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200290 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700291 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400292 }
293}
294
295static void
296weston_wm_handle_selection_notify(struct weston_wm *wm,
297 xcb_generic_event_t *event)
298{
299 xcb_selection_notify_event_t *selection_notify =
300 (xcb_selection_notify_event_t *) event;
301
302 if (selection_notify->property == XCB_ATOM_NONE) {
303 /* convert selection failed */
304 } else if (selection_notify->target == wm->atom.targets) {
305 weston_wm_get_selection_targets(wm);
306 } else {
307 weston_wm_get_selection_data(wm);
308 }
309}
310
311static const size_t incr_chunk_size = 64 * 1024;
312
313static void
314weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
315{
316 xcb_selection_notify_event_t selection_notify;
317
318 memset(&selection_notify, 0, sizeof selection_notify);
319 selection_notify.response_type = XCB_SELECTION_NOTIFY;
320 selection_notify.sequence = 0;
321 selection_notify.time = wm->selection_request.time;
322 selection_notify.requestor = wm->selection_request.requestor;
323 selection_notify.selection = wm->selection_request.selection;
324 selection_notify.target = wm->selection_request.target;
325 selection_notify.property = property;
326
327 xcb_send_event(wm->conn, 0, /* propagate */
328 wm->selection_request.requestor,
329 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
330}
331
332static void
333weston_wm_send_targets(struct weston_wm *wm)
334{
335 xcb_atom_t targets[] = {
336 wm->atom.timestamp,
337 wm->atom.targets,
338 wm->atom.utf8_string,
339 /* wm->atom.compound_text, */
340 wm->atom.text,
341 /* wm->atom.string */
342 };
343
344 xcb_change_property(wm->conn,
345 XCB_PROP_MODE_REPLACE,
346 wm->selection_request.requestor,
347 wm->selection_request.property,
348 XCB_ATOM_ATOM,
349 32, /* format */
350 ARRAY_LENGTH(targets), targets);
351
352 weston_wm_send_selection_notify(wm, wm->selection_request.property);
353}
354
355static void
356weston_wm_send_timestamp(struct weston_wm *wm)
357{
358 xcb_change_property(wm->conn,
359 XCB_PROP_MODE_REPLACE,
360 wm->selection_request.requestor,
361 wm->selection_request.property,
362 XCB_ATOM_INTEGER,
363 32, /* format */
364 1, &wm->selection_timestamp);
365
366 weston_wm_send_selection_notify(wm, wm->selection_request.property);
367}
368
369static int
370weston_wm_flush_source_data(struct weston_wm *wm)
371{
372 int length;
373
374 xcb_change_property(wm->conn,
375 XCB_PROP_MODE_REPLACE,
376 wm->selection_request.requestor,
377 wm->selection_request.property,
378 wm->selection_target,
379 8, /* format */
380 wm->source_data.size,
381 wm->source_data.data);
382 wm->selection_property_set = 1;
383 length = wm->source_data.size;
384 wm->source_data.size = 0;
385
386 return length;
387}
388
389static int
390weston_wm_read_data_source(int fd, uint32_t mask, void *data)
391{
392 struct weston_wm *wm = data;
393 int len, current, available;
394 void *p;
395
396 current = wm->source_data.size;
397 if (wm->source_data.size < incr_chunk_size)
398 p = wl_array_add(&wm->source_data, incr_chunk_size);
399 else
400 p = (char *) wm->source_data.data + wm->source_data.size;
401 available = wm->source_data.alloc - current;
402
403 len = read(fd, p, available);
404 if (len == -1) {
Antonio Borneo39578632019-04-26 23:57:31 +0200405 weston_log("read error from data source: %s\n",
406 strerror(errno));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400407 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
Greg V3ea54372018-07-22 11:36:21 +0100408 if (wm->property_source)
409 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200410 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400411 close(fd);
412 wl_array_release(&wm->source_data);
Greg V4253f232018-11-08 00:24:15 +0300413 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400414 }
415
Martin Minarik6d118362012-06-07 18:01:59 +0200416 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400417 len, available, mask, len, (char *) p);
418
419 wm->source_data.size = current + len;
420 if (wm->source_data.size >= incr_chunk_size) {
421 if (!wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200422 weston_log("got %zu bytes, starting incr\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400423 wm->source_data.size);
424 wm->incr = 1;
425 xcb_change_property(wm->conn,
426 XCB_PROP_MODE_REPLACE,
427 wm->selection_request.requestor,
428 wm->selection_request.property,
429 wm->atom.incr,
430 32, /* format */
431 1, &incr_chunk_size);
432 wm->selection_property_set = 1;
433 wm->flush_property_on_delete = 1;
Greg V3ea54372018-07-22 11:36:21 +0100434 if (wm->property_source)
435 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200436 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400437 weston_wm_send_selection_notify(wm, wm->selection_request.property);
438 } else if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200439 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400440 "property delete\n", wm->source_data.size);
441
442 wm->flush_property_on_delete = 1;
Greg V3ea54372018-07-22 11:36:21 +0100443 if (wm->property_source)
444 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200445 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400446 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200447 weston_log("got %zu bytes, "
Abdur Rehmanb8b150b2017-01-01 19:46:46 +0500448 "property deleted, setting new property\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400449 wm->source_data.size);
450 weston_wm_flush_source_data(wm);
451 }
452 } else if (len == 0 && !wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200453 weston_log("non-incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400454 /* Non-incr transfer all done. */
455 weston_wm_flush_source_data(wm);
456 weston_wm_send_selection_notify(wm, wm->selection_request.property);
457 xcb_flush(wm->conn);
Greg V3ea54372018-07-22 11:36:21 +0100458 if (wm->property_source)
459 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200460 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400461 close(fd);
462 wl_array_release(&wm->source_data);
463 wm->selection_request.requestor = XCB_NONE;
464 } else if (len == 0 && wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200465 weston_log("incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400466
467 wm->flush_property_on_delete = 1;
468 if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200469 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400470 "property delete\n", wm->source_data.size);
471 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200472 weston_log("got %zu bytes, "
Abdur Rehmanb8b150b2017-01-01 19:46:46 +0500473 "property deleted, setting new property\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400474 wm->source_data.size);
475 weston_wm_flush_source_data(wm);
476 }
477 xcb_flush(wm->conn);
Greg V3ea54372018-07-22 11:36:21 +0100478 if (wm->property_source)
479 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200480 wm->property_source = NULL;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400481 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400482 wm->data_source_fd = -1;
483 close(fd);
484 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200485 weston_log("nothing happened, buffered the bytes\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400486 }
487
488 return 1;
489}
490
491static void
492weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
493{
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700494 struct weston_data_source *source;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400495 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400496 int p[2];
497
498 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
Antonio Borneo39578632019-04-26 23:57:31 +0200499 weston_log("pipe2 failed: %s\n", strerror(errno));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400500 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
501 return;
502 }
503
504 wl_array_init(&wm->source_data);
505 wm->selection_target = target;
506 wm->data_source_fd = p[0];
507 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
508 wm->data_source_fd,
509 WL_EVENT_READABLE,
510 weston_wm_read_data_source,
511 wm);
512
Kristian Høgsberge3148752013-05-06 23:19:49 -0400513 source = seat->selection_data_source;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400514 source->send(source, mime_type, p[1]);
Kristian Høgsberg73bdc0c2013-09-04 22:32:50 -0700515 close(p[1]);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400516}
517
518static void
519weston_wm_send_incr_chunk(struct weston_wm *wm)
520{
521 int length;
522
Martin Minarik6d118362012-06-07 18:01:59 +0200523 weston_log("property deleted\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400524
525 wm->selection_property_set = 0;
526 if (wm->flush_property_on_delete) {
Martin Minarik6d118362012-06-07 18:01:59 +0200527 weston_log("setting new property, %zu bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400528 wm->source_data.size);
529 wm->flush_property_on_delete = 0;
530 length = weston_wm_flush_source_data(wm);
531
532 if (wm->data_source_fd >= 0) {
533 wm->property_source =
534 wl_event_loop_add_fd(wm->server->loop,
535 wm->data_source_fd,
536 WL_EVENT_READABLE,
537 weston_wm_read_data_source,
538 wm);
539 } else if (length > 0) {
540 /* Transfer is all done, but queue a flush for
541 * the delete of the last chunk so we can set
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100542 * the 0 sized property to signal the end of
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400543 * the transfer. */
544 wm->flush_property_on_delete = 1;
545 wl_array_release(&wm->source_data);
546 } else {
547 wm->selection_request.requestor = XCB_NONE;
548 }
549 }
550}
551
552static int
553weston_wm_handle_selection_property_notify(struct weston_wm *wm,
554 xcb_generic_event_t *event)
555{
556 xcb_property_notify_event_t *property_notify =
557 (xcb_property_notify_event_t *) event;
558
559 if (property_notify->window == wm->selection_window) {
560 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
561 property_notify->atom == wm->atom.wl_selection &&
562 wm->incr)
563 weston_wm_get_incr_chunk(wm);
564 return 1;
565 } else if (property_notify->window == wm->selection_request.requestor) {
566 if (property_notify->state == XCB_PROPERTY_DELETE &&
567 property_notify->atom == wm->selection_request.property &&
568 wm->incr)
569 weston_wm_send_incr_chunk(wm);
570 return 1;
571 }
572
573 return 0;
574}
575
576static void
577weston_wm_handle_selection_request(struct weston_wm *wm,
578 xcb_generic_event_t *event)
579{
580 xcb_selection_request_event_t *selection_request =
581 (xcb_selection_request_event_t *) event;
582
Martin Minarik6d118362012-06-07 18:01:59 +0200583 weston_log("selection request, %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400584 get_atom_name(wm->conn, selection_request->selection));
Martin Minarik6d118362012-06-07 18:01:59 +0200585 weston_log_continue("target %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400586 get_atom_name(wm->conn, selection_request->target));
Martin Minarik6d118362012-06-07 18:01:59 +0200587 weston_log_continue("property %s\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400588 get_atom_name(wm->conn, selection_request->property));
589
590 wm->selection_request = *selection_request;
591 wm->incr = 0;
592 wm->flush_property_on_delete = 0;
593
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400594 if (selection_request->selection == wm->atom.clipboard_manager) {
595 /* The weston clipboard should already have grabbed
596 * the first target, so just send selection notify
597 * now. This isn't synchronized with the clipboard
598 * finishing getting the data, so there's a race here. */
599 weston_wm_send_selection_notify(wm, wm->selection_request.property);
600 return;
601 }
602
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400603 if (selection_request->target == wm->atom.targets) {
604 weston_wm_send_targets(wm);
605 } else if (selection_request->target == wm->atom.timestamp) {
606 weston_wm_send_timestamp(wm);
607 } else if (selection_request->target == wm->atom.utf8_string ||
608 selection_request->target == wm->atom.text) {
609 weston_wm_send_data(wm, wm->atom.utf8_string,
610 "text/plain;charset=utf-8");
611 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200612 weston_log("can only handle UTF8_STRING targets...\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400613 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
614 }
615}
616
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700617static int
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400618weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
619 xcb_generic_event_t *event)
620{
621 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
622 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400623 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400624 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400625 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400626
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700627 if (xfixes_selection_notify->selection != wm->atom.clipboard)
628 return 0;
629
Martin Minarik6d118362012-06-07 18:01:59 +0200630 weston_log("xfixes selection notify event: owner %d\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400631 xfixes_selection_notify->owner);
632
Kristian Høgsberg80566732012-06-01 00:08:12 -0400633 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
634 if (wm->selection_owner != wm->selection_window) {
635 /* A real X client selection went away, not our
636 * proxy selection. Clear the wayland selection. */
637 compositor = wm->server->compositor;
638 serial = wl_display_next_serial(compositor->wl_display);
Kristian Høgsberge3148752013-05-06 23:19:49 -0400639 weston_seat_set_selection(seat, NULL, serial);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400640 }
641
642 wm->selection_owner = XCB_WINDOW_NONE;
643
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700644 return 1;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400645 }
646
647 wm->selection_owner = xfixes_selection_notify->owner;
648
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400649 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
650 * selection, so grab the actual timestamp here so we can
651 * answer TIMESTAMP conversion requests correctly. */
652 if (xfixes_selection_notify->owner == wm->selection_window) {
653 wm->selection_timestamp = xfixes_selection_notify->timestamp;
Martin Minarik6d118362012-06-07 18:01:59 +0200654 weston_log("our window, skipping\n");
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700655 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400656 }
657
658 wm->incr = 0;
659 xcb_convert_selection(wm->conn, wm->selection_window,
660 wm->atom.clipboard,
661 wm->atom.targets,
662 wm->atom.wl_selection,
663 xfixes_selection_notify->timestamp);
664
665 xcb_flush(wm->conn);
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700666
667 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400668}
669
670int
671weston_wm_handle_selection_event(struct weston_wm *wm,
672 xcb_generic_event_t *event)
673{
674 switch (event->response_type & ~0x80) {
675 case XCB_SELECTION_NOTIFY:
676 weston_wm_handle_selection_notify(wm, event);
677 return 1;
678 case XCB_PROPERTY_NOTIFY:
679 return weston_wm_handle_selection_property_notify(wm, event);
680 case XCB_SELECTION_REQUEST:
681 weston_wm_handle_selection_request(wm, event);
682 return 1;
683 }
684
685 switch (event->response_type - wm->xfixes->first_event) {
686 case XCB_XFIXES_SELECTION_NOTIFY:
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700687 return weston_wm_handle_xfixes_selection_notify(wm, event);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400688 }
689
690 return 0;
691}
692
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200693static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400694weston_wm_set_selection(struct wl_listener *listener, void *data)
695{
Kristian Høgsberge3148752013-05-06 23:19:49 -0400696 struct weston_seat *seat = data;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400697 struct weston_wm *wm =
698 container_of(listener, struct weston_wm, selection_listener);
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700699 struct weston_data_source *source = seat->selection_data_source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400700
Kristian Høgsberg80566732012-06-01 00:08:12 -0400701 if (source == NULL) {
702 if (wm->selection_owner == wm->selection_window)
703 xcb_set_selection_owner(wm->conn,
704 XCB_ATOM_NONE,
705 wm->atom.clipboard,
706 wm->selection_timestamp);
707 return;
708 }
709
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400710 if (source->send == data_source_send)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400711 return;
712
Derek Foreman4e184482016-02-01 14:36:57 -0600713 xcb_set_selection_owner(wm->conn,
714 wm->selection_window,
715 wm->atom.clipboard,
716 XCB_TIME_CURRENT_TIME);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400717}
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400718
719void
720weston_wm_selection_init(struct weston_wm *wm)
721{
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400722 struct weston_seat *seat;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400723 uint32_t values[1], mask;
724
Derek Foreman4b72ff02018-02-05 15:59:29 -0600725 wl_list_init(&wm->selection_listener.link);
726
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400727 wm->selection_request.requestor = XCB_NONE;
728
729 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
730 wm->selection_window = xcb_generate_id(wm->conn);
731 xcb_create_window(wm->conn,
732 XCB_COPY_FROM_PARENT,
733 wm->selection_window,
734 wm->screen->root,
735 0, 0,
736 10, 10,
737 0,
738 XCB_WINDOW_CLASS_INPUT_OUTPUT,
739 wm->screen->root_visual,
740 XCB_CW_EVENT_MASK, values);
741
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400742 xcb_set_selection_owner(wm->conn,
743 wm->selection_window,
744 wm->atom.clipboard_manager,
745 XCB_TIME_CURRENT_TIME);
746
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400747 mask =
748 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
749 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
750 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
751 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
752 wm->atom.clipboard, mask);
753
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400754 seat = weston_wm_pick_seat(wm);
Tom Hochsteine7fff212016-11-01 14:14:00 -0500755 if (seat == NULL)
756 return;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400757 wm->selection_listener.notify = weston_wm_set_selection;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400758 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
Kristian Høgsberge2203272012-06-03 10:32:48 -0400759
760 weston_wm_set_selection(&wm->selection_listener, seat);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400761}