blob: 641ac49061fbb8ce4a5b2f957eae6f9bacd7d7ad [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>
33
34#include "xwayland.h"
Jon Cruz35b2eaa2015-06-15 15:37:08 -070035#include "shared/helpers.h"
Kristian Høgsberg102bf032012-05-21 15:52:02 -040036
37static int
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070038writable_callback(int fd, uint32_t mask, void *data)
Kristian Høgsberg102bf032012-05-21 15:52:02 -040039{
40 struct weston_wm *wm = data;
41 unsigned char *property;
42 int len, remainder;
43
44 property = xcb_get_property_value(wm->property_reply);
45 remainder = xcb_get_property_value_length(wm->property_reply) -
46 wm->property_start;
47
48 len = write(fd, property + wm->property_start, remainder);
49 if (len == -1) {
50 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070051 wm->property_reply = NULL;
52 if (wm->property_source)
53 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020054 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040055 close(fd);
Martin Minarik6d118362012-06-07 18:01:59 +020056 weston_log("write error to target fd: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040057 return 1;
58 }
59
Martin Minarik6d118362012-06-07 18:01:59 +020060 weston_log("wrote %d (chunk size %d) of %d bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -040061 wm->property_start + len,
62 len, xcb_get_property_value_length(wm->property_reply));
63
64 wm->property_start += len;
65 if (len == remainder) {
66 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070067 wm->property_reply = NULL;
68 if (wm->property_source)
69 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020070 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040071
72 if (wm->incr) {
73 xcb_delete_property(wm->conn,
74 wm->selection_window,
75 wm->atom.wl_selection);
76 } else {
Martin Minarik6d118362012-06-07 18:01:59 +020077 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040078 close(fd);
79 }
80 }
81
82 return 1;
83}
84
85static void
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070086weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply)
87{
88 wm->property_start = 0;
89 wm->property_reply = reply;
90 writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm);
91
92 if (wm->property_reply)
93 wm->property_source =
94 wl_event_loop_add_fd(wm->server->loop,
95 wm->data_source_fd,
96 WL_EVENT_WRITABLE,
97 writable_callback, wm);
98}
99
100static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400101weston_wm_get_incr_chunk(struct weston_wm *wm)
102{
103 xcb_get_property_cookie_t cookie;
104 xcb_get_property_reply_t *reply;
105
106 cookie = xcb_get_property(wm->conn,
107 0, /* delete */
108 wm->selection_window,
109 wm->atom.wl_selection,
110 XCB_GET_PROPERTY_TYPE_ANY,
111 0, /* offset */
112 0x1fffffff /* length */);
113
114 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700115 if (reply == NULL)
116 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400117
118 dump_property(wm, wm->atom.wl_selection, reply);
119
120 if (xcb_get_property_value_length(reply) > 0) {
Eric Engestromf951f822016-04-02 17:03:14 +0100121 /* reply's ownership is transferred to wm, which is responsible
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200122 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700123 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400124 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200125 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400126 close(wm->data_source_fd);
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200127 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400128 }
129}
130
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400131struct x11_data_source {
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700132 struct weston_data_source base;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400133 struct weston_wm *wm;
134};
135
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400136static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700137data_source_accept(struct weston_data_source *source,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400138 uint32_t time, const char *mime_type)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400139{
140}
141
142static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700143data_source_send(struct weston_data_source *base,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400144 const char *mime_type, int32_t fd)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400145{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400146 struct x11_data_source *source = (struct x11_data_source *) base;
147 struct weston_wm *wm = source->wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400148
149 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
150 /* Get data for the utf8_string target */
151 xcb_convert_selection(wm->conn,
152 wm->selection_window,
153 wm->atom.clipboard,
154 wm->atom.utf8_string,
155 wm->atom.wl_selection,
156 XCB_TIME_CURRENT_TIME);
157
158 xcb_flush(wm->conn);
159
160 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
Kristian Høgsberg668fc0d2013-09-04 20:48:46 -0700161 wm->data_source_fd = fd;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400162 }
163}
164
165static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700166data_source_cancel(struct weston_data_source *source)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400167{
168}
169
170static void
171weston_wm_get_selection_targets(struct weston_wm *wm)
172{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400173 struct x11_data_source *source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400174 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400175 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400176 xcb_get_property_cookie_t cookie;
177 xcb_get_property_reply_t *reply;
178 xcb_atom_t *value;
179 char **p;
180 uint32_t i;
181
182 cookie = xcb_get_property(wm->conn,
183 1, /* delete */
184 wm->selection_window,
185 wm->atom.wl_selection,
186 XCB_GET_PROPERTY_TYPE_ANY,
187 0, /* offset */
188 4096 /* length */);
189
190 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700191 if (reply == NULL)
192 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400193
194 dump_property(wm, wm->atom.wl_selection, reply);
195
196 if (reply->type != XCB_ATOM_ATOM) {
197 free(reply);
198 return;
199 }
200
Carlos Garnachobeb7a9f2016-02-01 20:28:15 +0100201 source = zalloc(sizeof *source);
Bryce Harringtond3553c72015-06-30 21:35:43 -0700202 if (source == NULL) {
203 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400204 return;
Bryce Harringtond3553c72015-06-30 21:35:43 -0700205 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400206
Jason Ekstrand8a4a9eb2013-06-14 10:07:55 -0500207 wl_signal_init(&source->base.destroy_signal);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400208 source->base.accept = data_source_accept;
209 source->base.send = data_source_send;
210 source->base.cancel = data_source_cancel;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400211 source->wm = wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400212
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400213 wl_array_init(&source->base.mime_types);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400214 value = xcb_get_property_value(reply);
215 for (i = 0; i < reply->value_len; i++) {
216 if (value[i] == wm->atom.utf8_string) {
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400217 p = wl_array_add(&source->base.mime_types, sizeof *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400218 if (p)
219 *p = strdup("text/plain;charset=utf-8");
220 }
221 }
222
223 compositor = wm->server->compositor;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400224 weston_seat_set_selection(seat, &source->base,
225 wl_display_next_serial(compositor->wl_display));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400226
227 free(reply);
228}
229
230static void
231weston_wm_get_selection_data(struct weston_wm *wm)
232{
233 xcb_get_property_cookie_t cookie;
234 xcb_get_property_reply_t *reply;
235
236 cookie = xcb_get_property(wm->conn,
237 1, /* delete */
238 wm->selection_window,
239 wm->atom.wl_selection,
240 XCB_GET_PROPERTY_TYPE_ANY,
241 0, /* offset */
242 0x1fffffff /* length */);
243
244 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
245
Bryce Harringtonda41b572015-07-10 18:13:48 -0700246 dump_property(wm, wm->atom.wl_selection, reply);
247
248 if (reply == NULL) {
249 return;
250 } else if (reply->type == wm->atom.incr) {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400251 wm->incr = 1;
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200252 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400253 } else {
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400254 wm->incr = 0;
Eric Engestromf951f822016-04-02 17:03:14 +0100255 /* reply's ownership is transferred to wm, which is responsible
Emmanuel Gil Peyrot8c7287f2015-07-15 22:19:04 +0200256 * for freeing it */
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700257 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400258 }
259}
260
261static void
262weston_wm_handle_selection_notify(struct weston_wm *wm,
263 xcb_generic_event_t *event)
264{
265 xcb_selection_notify_event_t *selection_notify =
266 (xcb_selection_notify_event_t *) event;
267
268 if (selection_notify->property == XCB_ATOM_NONE) {
269 /* convert selection failed */
270 } else if (selection_notify->target == wm->atom.targets) {
271 weston_wm_get_selection_targets(wm);
272 } else {
273 weston_wm_get_selection_data(wm);
274 }
275}
276
277static const size_t incr_chunk_size = 64 * 1024;
278
279static void
280weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
281{
282 xcb_selection_notify_event_t selection_notify;
283
284 memset(&selection_notify, 0, sizeof selection_notify);
285 selection_notify.response_type = XCB_SELECTION_NOTIFY;
286 selection_notify.sequence = 0;
287 selection_notify.time = wm->selection_request.time;
288 selection_notify.requestor = wm->selection_request.requestor;
289 selection_notify.selection = wm->selection_request.selection;
290 selection_notify.target = wm->selection_request.target;
291 selection_notify.property = property;
292
293 xcb_send_event(wm->conn, 0, /* propagate */
294 wm->selection_request.requestor,
295 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
296}
297
298static void
299weston_wm_send_targets(struct weston_wm *wm)
300{
301 xcb_atom_t targets[] = {
302 wm->atom.timestamp,
303 wm->atom.targets,
304 wm->atom.utf8_string,
305 /* wm->atom.compound_text, */
306 wm->atom.text,
307 /* wm->atom.string */
308 };
309
310 xcb_change_property(wm->conn,
311 XCB_PROP_MODE_REPLACE,
312 wm->selection_request.requestor,
313 wm->selection_request.property,
314 XCB_ATOM_ATOM,
315 32, /* format */
316 ARRAY_LENGTH(targets), targets);
317
318 weston_wm_send_selection_notify(wm, wm->selection_request.property);
319}
320
321static void
322weston_wm_send_timestamp(struct weston_wm *wm)
323{
324 xcb_change_property(wm->conn,
325 XCB_PROP_MODE_REPLACE,
326 wm->selection_request.requestor,
327 wm->selection_request.property,
328 XCB_ATOM_INTEGER,
329 32, /* format */
330 1, &wm->selection_timestamp);
331
332 weston_wm_send_selection_notify(wm, wm->selection_request.property);
333}
334
335static int
336weston_wm_flush_source_data(struct weston_wm *wm)
337{
338 int length;
339
340 xcb_change_property(wm->conn,
341 XCB_PROP_MODE_REPLACE,
342 wm->selection_request.requestor,
343 wm->selection_request.property,
344 wm->selection_target,
345 8, /* format */
346 wm->source_data.size,
347 wm->source_data.data);
348 wm->selection_property_set = 1;
349 length = wm->source_data.size;
350 wm->source_data.size = 0;
351
352 return length;
353}
354
355static int
356weston_wm_read_data_source(int fd, uint32_t mask, void *data)
357{
358 struct weston_wm *wm = data;
359 int len, current, available;
360 void *p;
361
362 current = wm->source_data.size;
363 if (wm->source_data.size < incr_chunk_size)
364 p = wl_array_add(&wm->source_data, incr_chunk_size);
365 else
366 p = (char *) wm->source_data.data + wm->source_data.size;
367 available = wm->source_data.alloc - current;
368
369 len = read(fd, p, available);
370 if (len == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200371 weston_log("read error from data source: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400372 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
373 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200374 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400375 close(fd);
376 wl_array_release(&wm->source_data);
377 }
378
Martin Minarik6d118362012-06-07 18:01:59 +0200379 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400380 len, available, mask, len, (char *) p);
381
382 wm->source_data.size = current + len;
383 if (wm->source_data.size >= incr_chunk_size) {
384 if (!wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200385 weston_log("got %zu bytes, starting incr\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400386 wm->source_data.size);
387 wm->incr = 1;
388 xcb_change_property(wm->conn,
389 XCB_PROP_MODE_REPLACE,
390 wm->selection_request.requestor,
391 wm->selection_request.property,
392 wm->atom.incr,
393 32, /* format */
394 1, &incr_chunk_size);
395 wm->selection_property_set = 1;
396 wm->flush_property_on_delete = 1;
397 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200398 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400399 weston_wm_send_selection_notify(wm, wm->selection_request.property);
400 } else if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200401 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400402 "property delete\n", wm->source_data.size);
403
404 wm->flush_property_on_delete = 1;
405 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200406 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400407 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200408 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400409 "property deleted, seting new property\n",
410 wm->source_data.size);
411 weston_wm_flush_source_data(wm);
412 }
413 } else if (len == 0 && !wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200414 weston_log("non-incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400415 /* Non-incr transfer all done. */
416 weston_wm_flush_source_data(wm);
417 weston_wm_send_selection_notify(wm, wm->selection_request.property);
418 xcb_flush(wm->conn);
419 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200420 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400421 close(fd);
422 wl_array_release(&wm->source_data);
423 wm->selection_request.requestor = XCB_NONE;
424 } else if (len == 0 && wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200425 weston_log("incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400426
427 wm->flush_property_on_delete = 1;
428 if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200429 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400430 "property delete\n", wm->source_data.size);
431 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200432 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400433 "property deleted, seting new property\n",
434 wm->source_data.size);
435 weston_wm_flush_source_data(wm);
436 }
437 xcb_flush(wm->conn);
438 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200439 wm->property_source = NULL;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400440 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400441 wm->data_source_fd = -1;
442 close(fd);
443 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200444 weston_log("nothing happened, buffered the bytes\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400445 }
446
447 return 1;
448}
449
450static void
451weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
452{
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700453 struct weston_data_source *source;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400454 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400455 int p[2];
456
457 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200458 weston_log("pipe2 failed: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400459 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
460 return;
461 }
462
463 wl_array_init(&wm->source_data);
464 wm->selection_target = target;
465 wm->data_source_fd = p[0];
466 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
467 wm->data_source_fd,
468 WL_EVENT_READABLE,
469 weston_wm_read_data_source,
470 wm);
471
Kristian Høgsberge3148752013-05-06 23:19:49 -0400472 source = seat->selection_data_source;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400473 source->send(source, mime_type, p[1]);
Kristian Høgsberg73bdc0c2013-09-04 22:32:50 -0700474 close(p[1]);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400475}
476
477static void
478weston_wm_send_incr_chunk(struct weston_wm *wm)
479{
480 int length;
481
Martin Minarik6d118362012-06-07 18:01:59 +0200482 weston_log("property deleted\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400483
484 wm->selection_property_set = 0;
485 if (wm->flush_property_on_delete) {
Martin Minarik6d118362012-06-07 18:01:59 +0200486 weston_log("setting new property, %zu bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400487 wm->source_data.size);
488 wm->flush_property_on_delete = 0;
489 length = weston_wm_flush_source_data(wm);
490
491 if (wm->data_source_fd >= 0) {
492 wm->property_source =
493 wl_event_loop_add_fd(wm->server->loop,
494 wm->data_source_fd,
495 WL_EVENT_READABLE,
496 weston_wm_read_data_source,
497 wm);
498 } else if (length > 0) {
499 /* Transfer is all done, but queue a flush for
500 * the delete of the last chunk so we can set
501 * the 0 sized propert to signal the end of
502 * the transfer. */
503 wm->flush_property_on_delete = 1;
504 wl_array_release(&wm->source_data);
505 } else {
506 wm->selection_request.requestor = XCB_NONE;
507 }
508 }
509}
510
511static int
512weston_wm_handle_selection_property_notify(struct weston_wm *wm,
513 xcb_generic_event_t *event)
514{
515 xcb_property_notify_event_t *property_notify =
516 (xcb_property_notify_event_t *) event;
517
518 if (property_notify->window == wm->selection_window) {
519 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
520 property_notify->atom == wm->atom.wl_selection &&
521 wm->incr)
522 weston_wm_get_incr_chunk(wm);
523 return 1;
524 } else if (property_notify->window == wm->selection_request.requestor) {
525 if (property_notify->state == XCB_PROPERTY_DELETE &&
526 property_notify->atom == wm->selection_request.property &&
527 wm->incr)
528 weston_wm_send_incr_chunk(wm);
529 return 1;
530 }
531
532 return 0;
533}
534
535static void
536weston_wm_handle_selection_request(struct weston_wm *wm,
537 xcb_generic_event_t *event)
538{
539 xcb_selection_request_event_t *selection_request =
540 (xcb_selection_request_event_t *) event;
541
Martin Minarik6d118362012-06-07 18:01:59 +0200542 weston_log("selection request, %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400543 get_atom_name(wm->conn, selection_request->selection));
Martin Minarik6d118362012-06-07 18:01:59 +0200544 weston_log_continue("target %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400545 get_atom_name(wm->conn, selection_request->target));
Martin Minarik6d118362012-06-07 18:01:59 +0200546 weston_log_continue("property %s\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400547 get_atom_name(wm->conn, selection_request->property));
548
549 wm->selection_request = *selection_request;
550 wm->incr = 0;
551 wm->flush_property_on_delete = 0;
552
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400553 if (selection_request->selection == wm->atom.clipboard_manager) {
554 /* The weston clipboard should already have grabbed
555 * the first target, so just send selection notify
556 * now. This isn't synchronized with the clipboard
557 * finishing getting the data, so there's a race here. */
558 weston_wm_send_selection_notify(wm, wm->selection_request.property);
559 return;
560 }
561
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400562 if (selection_request->target == wm->atom.targets) {
563 weston_wm_send_targets(wm);
564 } else if (selection_request->target == wm->atom.timestamp) {
565 weston_wm_send_timestamp(wm);
566 } else if (selection_request->target == wm->atom.utf8_string ||
567 selection_request->target == wm->atom.text) {
568 weston_wm_send_data(wm, wm->atom.utf8_string,
569 "text/plain;charset=utf-8");
570 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200571 weston_log("can only handle UTF8_STRING targets...\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400572 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
573 }
574}
575
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700576static int
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400577weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
578 xcb_generic_event_t *event)
579{
580 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
581 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400582 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400583 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400584 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400585
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700586 if (xfixes_selection_notify->selection != wm->atom.clipboard)
587 return 0;
588
Martin Minarik6d118362012-06-07 18:01:59 +0200589 weston_log("xfixes selection notify event: owner %d\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400590 xfixes_selection_notify->owner);
591
Kristian Høgsberg80566732012-06-01 00:08:12 -0400592 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
593 if (wm->selection_owner != wm->selection_window) {
594 /* A real X client selection went away, not our
595 * proxy selection. Clear the wayland selection. */
596 compositor = wm->server->compositor;
597 serial = wl_display_next_serial(compositor->wl_display);
Kristian Høgsberge3148752013-05-06 23:19:49 -0400598 weston_seat_set_selection(seat, NULL, serial);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400599 }
600
601 wm->selection_owner = XCB_WINDOW_NONE;
602
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700603 return 1;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400604 }
605
606 wm->selection_owner = xfixes_selection_notify->owner;
607
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400608 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
609 * selection, so grab the actual timestamp here so we can
610 * answer TIMESTAMP conversion requests correctly. */
611 if (xfixes_selection_notify->owner == wm->selection_window) {
612 wm->selection_timestamp = xfixes_selection_notify->timestamp;
Martin Minarik6d118362012-06-07 18:01:59 +0200613 weston_log("our window, skipping\n");
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700614 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400615 }
616
617 wm->incr = 0;
618 xcb_convert_selection(wm->conn, wm->selection_window,
619 wm->atom.clipboard,
620 wm->atom.targets,
621 wm->atom.wl_selection,
622 xfixes_selection_notify->timestamp);
623
624 xcb_flush(wm->conn);
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700625
626 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400627}
628
629int
630weston_wm_handle_selection_event(struct weston_wm *wm,
631 xcb_generic_event_t *event)
632{
633 switch (event->response_type & ~0x80) {
634 case XCB_SELECTION_NOTIFY:
635 weston_wm_handle_selection_notify(wm, event);
636 return 1;
637 case XCB_PROPERTY_NOTIFY:
638 return weston_wm_handle_selection_property_notify(wm, event);
639 case XCB_SELECTION_REQUEST:
640 weston_wm_handle_selection_request(wm, event);
641 return 1;
642 }
643
644 switch (event->response_type - wm->xfixes->first_event) {
645 case XCB_XFIXES_SELECTION_NOTIFY:
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700646 return weston_wm_handle_xfixes_selection_notify(wm, event);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400647 }
648
649 return 0;
650}
651
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200652static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400653weston_wm_set_selection(struct wl_listener *listener, void *data)
654{
Kristian Høgsberge3148752013-05-06 23:19:49 -0400655 struct weston_seat *seat = data;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400656 struct weston_wm *wm =
657 container_of(listener, struct weston_wm, selection_listener);
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700658 struct weston_data_source *source = seat->selection_data_source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400659
Kristian Høgsberg80566732012-06-01 00:08:12 -0400660 if (source == NULL) {
661 if (wm->selection_owner == wm->selection_window)
662 xcb_set_selection_owner(wm->conn,
663 XCB_ATOM_NONE,
664 wm->atom.clipboard,
665 wm->selection_timestamp);
666 return;
667 }
668
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400669 if (source->send == data_source_send)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400670 return;
671
Derek Foreman4e184482016-02-01 14:36:57 -0600672 xcb_set_selection_owner(wm->conn,
673 wm->selection_window,
674 wm->atom.clipboard,
675 XCB_TIME_CURRENT_TIME);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400676}
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400677
678void
679weston_wm_selection_init(struct weston_wm *wm)
680{
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400681 struct weston_seat *seat;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400682 uint32_t values[1], mask;
683
684 wm->selection_request.requestor = XCB_NONE;
685
686 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
687 wm->selection_window = xcb_generate_id(wm->conn);
688 xcb_create_window(wm->conn,
689 XCB_COPY_FROM_PARENT,
690 wm->selection_window,
691 wm->screen->root,
692 0, 0,
693 10, 10,
694 0,
695 XCB_WINDOW_CLASS_INPUT_OUTPUT,
696 wm->screen->root_visual,
697 XCB_CW_EVENT_MASK, values);
698
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400699 xcb_set_selection_owner(wm->conn,
700 wm->selection_window,
701 wm->atom.clipboard_manager,
702 XCB_TIME_CURRENT_TIME);
703
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400704 mask =
705 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
706 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
707 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
708 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
709 wm->atom.clipboard, mask);
710
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400711 seat = weston_wm_pick_seat(wm);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400712 wm->selection_listener.notify = weston_wm_set_selection;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400713 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
Kristian Høgsberge2203272012-06-03 10:32:48 -0400714
715 weston_wm_set_selection(&wm->selection_listener, seat);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400716}