blob: 2f60c0ecd0b620e655ab981e5edbd494fbf0488c [file] [log] [blame]
Kristian Høgsberg102bf032012-05-21 15:52:02 -04001/*
2 * Copyright © 2012 Intel Corporation
3 *
Bryce Harrington0a007dd2015-06-11 16:22:34 -07004 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
Kristian Høgsberg102bf032012-05-21 15:52:02 -040011 *
Bryce Harrington0a007dd2015-06-11 16:22:34 -070012 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
Kristian Høgsberg102bf032012-05-21 15:52:02 -040024 */
25
Daniel Stonec228e232013-05-22 18:03:19 +030026#include "config.h"
Kristian Høgsberg102bf032012-05-21 15:52:02 -040027
28#include <stdlib.h>
Kristian Høgsberg102bf032012-05-21 15:52:02 -040029#include <string.h>
30#include <unistd.h>
31#include <fcntl.h>
32
33#include "xwayland.h"
Jon Cruz35b2eaa2015-06-15 15:37:08 -070034#include "shared/helpers.h"
Kristian Høgsberg102bf032012-05-21 15:52:02 -040035
36static int
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070037writable_callback(int fd, uint32_t mask, void *data)
Kristian Høgsberg102bf032012-05-21 15:52:02 -040038{
39 struct weston_wm *wm = data;
40 unsigned char *property;
41 int len, remainder;
42
43 property = xcb_get_property_value(wm->property_reply);
44 remainder = xcb_get_property_value_length(wm->property_reply) -
45 wm->property_start;
46
47 len = write(fd, property + wm->property_start, remainder);
48 if (len == -1) {
49 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070050 wm->property_reply = NULL;
51 if (wm->property_source)
52 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020053 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040054 close(fd);
Martin Minarik6d118362012-06-07 18:01:59 +020055 weston_log("write error to target fd: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040056 return 1;
57 }
58
Martin Minarik6d118362012-06-07 18:01:59 +020059 weston_log("wrote %d (chunk size %d) of %d bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -040060 wm->property_start + len,
61 len, xcb_get_property_value_length(wm->property_reply));
62
63 wm->property_start += len;
64 if (len == remainder) {
65 free(wm->property_reply);
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070066 wm->property_reply = NULL;
67 if (wm->property_source)
68 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +020069 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -040070
71 if (wm->incr) {
72 xcb_delete_property(wm->conn,
73 wm->selection_window,
74 wm->atom.wl_selection);
75 } else {
Martin Minarik6d118362012-06-07 18:01:59 +020076 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -040077 close(fd);
78 }
79 }
80
81 return 1;
82}
83
84static void
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -070085weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply)
86{
87 wm->property_start = 0;
88 wm->property_reply = reply;
89 writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm);
90
91 if (wm->property_reply)
92 wm->property_source =
93 wl_event_loop_add_fd(wm->server->loop,
94 wm->data_source_fd,
95 WL_EVENT_WRITABLE,
96 writable_callback, wm);
97}
98
99static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400100weston_wm_get_incr_chunk(struct weston_wm *wm)
101{
102 xcb_get_property_cookie_t cookie;
103 xcb_get_property_reply_t *reply;
104
105 cookie = xcb_get_property(wm->conn,
106 0, /* delete */
107 wm->selection_window,
108 wm->atom.wl_selection,
109 XCB_GET_PROPERTY_TYPE_ANY,
110 0, /* offset */
111 0x1fffffff /* length */);
112
113 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700114 if (reply == NULL)
115 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400116
117 dump_property(wm, wm->atom.wl_selection, reply);
118
119 if (xcb_get_property_value_length(reply) > 0) {
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700120 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400121 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200122 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400123 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400124 }
Bryce Harringtond3553c72015-06-30 21:35:43 -0700125
126 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400127}
128
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400129struct x11_data_source {
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700130 struct weston_data_source base;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400131 struct weston_wm *wm;
132};
133
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400134static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700135data_source_accept(struct weston_data_source *source,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400136 uint32_t time, const char *mime_type)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400137{
138}
139
140static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700141data_source_send(struct weston_data_source *base,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400142 const char *mime_type, int32_t fd)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400143{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400144 struct x11_data_source *source = (struct x11_data_source *) base;
145 struct weston_wm *wm = source->wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400146
147 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
148 /* Get data for the utf8_string target */
149 xcb_convert_selection(wm->conn,
150 wm->selection_window,
151 wm->atom.clipboard,
152 wm->atom.utf8_string,
153 wm->atom.wl_selection,
154 XCB_TIME_CURRENT_TIME);
155
156 xcb_flush(wm->conn);
157
158 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
Kristian Høgsberg668fc0d2013-09-04 20:48:46 -0700159 wm->data_source_fd = fd;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400160 }
161}
162
163static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700164data_source_cancel(struct weston_data_source *source)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400165{
166}
167
168static void
169weston_wm_get_selection_targets(struct weston_wm *wm)
170{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400171 struct x11_data_source *source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400172 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400173 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400174 xcb_get_property_cookie_t cookie;
175 xcb_get_property_reply_t *reply;
176 xcb_atom_t *value;
177 char **p;
178 uint32_t i;
179
180 cookie = xcb_get_property(wm->conn,
181 1, /* delete */
182 wm->selection_window,
183 wm->atom.wl_selection,
184 XCB_GET_PROPERTY_TYPE_ANY,
185 0, /* offset */
186 4096 /* length */);
187
188 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700189 if (reply == NULL)
190 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400191
192 dump_property(wm, wm->atom.wl_selection, reply);
193
194 if (reply->type != XCB_ATOM_ATOM) {
195 free(reply);
196 return;
197 }
198
199 source = malloc(sizeof *source);
Bryce Harringtond3553c72015-06-30 21:35:43 -0700200 if (source == NULL) {
201 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400202 return;
Bryce Harringtond3553c72015-06-30 21:35:43 -0700203 }
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400204
Jason Ekstrand8a4a9eb2013-06-14 10:07:55 -0500205 wl_signal_init(&source->base.destroy_signal);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400206 source->base.accept = data_source_accept;
207 source->base.send = data_source_send;
208 source->base.cancel = data_source_cancel;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400209 source->wm = wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400210
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400211 wl_array_init(&source->base.mime_types);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400212 value = xcb_get_property_value(reply);
213 for (i = 0; i < reply->value_len; i++) {
214 if (value[i] == wm->atom.utf8_string) {
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400215 p = wl_array_add(&source->base.mime_types, sizeof *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400216 if (p)
217 *p = strdup("text/plain;charset=utf-8");
218 }
219 }
220
221 compositor = wm->server->compositor;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400222 weston_seat_set_selection(seat, &source->base,
223 wl_display_next_serial(compositor->wl_display));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400224
225 free(reply);
226}
227
228static void
229weston_wm_get_selection_data(struct weston_wm *wm)
230{
231 xcb_get_property_cookie_t cookie;
232 xcb_get_property_reply_t *reply;
233
234 cookie = xcb_get_property(wm->conn,
235 1, /* delete */
236 wm->selection_window,
237 wm->atom.wl_selection,
238 XCB_GET_PROPERTY_TYPE_ANY,
239 0, /* offset */
240 0x1fffffff /* length */);
241
242 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Bryce Harringtonb2d79d52015-07-09 21:30:16 -0700243 if (reply == NULL)
244 return;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400245
246 if (reply->type == wm->atom.incr) {
247 dump_property(wm, wm->atom.wl_selection, reply);
248 wm->incr = 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400249 } else {
250 dump_property(wm, wm->atom.wl_selection, reply);
251 wm->incr = 0;
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700252 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400253 }
Bryce Harringtond3553c72015-06-30 21:35:43 -0700254
255 free(reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400256}
257
258static void
259weston_wm_handle_selection_notify(struct weston_wm *wm,
260 xcb_generic_event_t *event)
261{
262 xcb_selection_notify_event_t *selection_notify =
263 (xcb_selection_notify_event_t *) event;
264
265 if (selection_notify->property == XCB_ATOM_NONE) {
266 /* convert selection failed */
267 } else if (selection_notify->target == wm->atom.targets) {
268 weston_wm_get_selection_targets(wm);
269 } else {
270 weston_wm_get_selection_data(wm);
271 }
272}
273
274static const size_t incr_chunk_size = 64 * 1024;
275
276static void
277weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
278{
279 xcb_selection_notify_event_t selection_notify;
280
281 memset(&selection_notify, 0, sizeof selection_notify);
282 selection_notify.response_type = XCB_SELECTION_NOTIFY;
283 selection_notify.sequence = 0;
284 selection_notify.time = wm->selection_request.time;
285 selection_notify.requestor = wm->selection_request.requestor;
286 selection_notify.selection = wm->selection_request.selection;
287 selection_notify.target = wm->selection_request.target;
288 selection_notify.property = property;
289
290 xcb_send_event(wm->conn, 0, /* propagate */
291 wm->selection_request.requestor,
292 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
293}
294
295static void
296weston_wm_send_targets(struct weston_wm *wm)
297{
298 xcb_atom_t targets[] = {
299 wm->atom.timestamp,
300 wm->atom.targets,
301 wm->atom.utf8_string,
302 /* wm->atom.compound_text, */
303 wm->atom.text,
304 /* wm->atom.string */
305 };
306
307 xcb_change_property(wm->conn,
308 XCB_PROP_MODE_REPLACE,
309 wm->selection_request.requestor,
310 wm->selection_request.property,
311 XCB_ATOM_ATOM,
312 32, /* format */
313 ARRAY_LENGTH(targets), targets);
314
315 weston_wm_send_selection_notify(wm, wm->selection_request.property);
316}
317
318static void
319weston_wm_send_timestamp(struct weston_wm *wm)
320{
321 xcb_change_property(wm->conn,
322 XCB_PROP_MODE_REPLACE,
323 wm->selection_request.requestor,
324 wm->selection_request.property,
325 XCB_ATOM_INTEGER,
326 32, /* format */
327 1, &wm->selection_timestamp);
328
329 weston_wm_send_selection_notify(wm, wm->selection_request.property);
330}
331
332static int
333weston_wm_flush_source_data(struct weston_wm *wm)
334{
335 int length;
336
337 xcb_change_property(wm->conn,
338 XCB_PROP_MODE_REPLACE,
339 wm->selection_request.requestor,
340 wm->selection_request.property,
341 wm->selection_target,
342 8, /* format */
343 wm->source_data.size,
344 wm->source_data.data);
345 wm->selection_property_set = 1;
346 length = wm->source_data.size;
347 wm->source_data.size = 0;
348
349 return length;
350}
351
352static int
353weston_wm_read_data_source(int fd, uint32_t mask, void *data)
354{
355 struct weston_wm *wm = data;
356 int len, current, available;
357 void *p;
358
359 current = wm->source_data.size;
360 if (wm->source_data.size < incr_chunk_size)
361 p = wl_array_add(&wm->source_data, incr_chunk_size);
362 else
363 p = (char *) wm->source_data.data + wm->source_data.size;
364 available = wm->source_data.alloc - current;
365
366 len = read(fd, p, available);
367 if (len == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200368 weston_log("read error from data source: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400369 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
370 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200371 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400372 close(fd);
373 wl_array_release(&wm->source_data);
374 }
375
Martin Minarik6d118362012-06-07 18:01:59 +0200376 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400377 len, available, mask, len, (char *) p);
378
379 wm->source_data.size = current + len;
380 if (wm->source_data.size >= incr_chunk_size) {
381 if (!wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200382 weston_log("got %zu bytes, starting incr\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400383 wm->source_data.size);
384 wm->incr = 1;
385 xcb_change_property(wm->conn,
386 XCB_PROP_MODE_REPLACE,
387 wm->selection_request.requestor,
388 wm->selection_request.property,
389 wm->atom.incr,
390 32, /* format */
391 1, &incr_chunk_size);
392 wm->selection_property_set = 1;
393 wm->flush_property_on_delete = 1;
394 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200395 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400396 weston_wm_send_selection_notify(wm, wm->selection_request.property);
397 } else if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200398 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400399 "property delete\n", wm->source_data.size);
400
401 wm->flush_property_on_delete = 1;
402 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200403 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400404 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200405 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400406 "property deleted, seting new property\n",
407 wm->source_data.size);
408 weston_wm_flush_source_data(wm);
409 }
410 } else if (len == 0 && !wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200411 weston_log("non-incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400412 /* Non-incr transfer all done. */
413 weston_wm_flush_source_data(wm);
414 weston_wm_send_selection_notify(wm, wm->selection_request.property);
415 xcb_flush(wm->conn);
416 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200417 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400418 close(fd);
419 wl_array_release(&wm->source_data);
420 wm->selection_request.requestor = XCB_NONE;
421 } else if (len == 0 && wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200422 weston_log("incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400423
424 wm->flush_property_on_delete = 1;
425 if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200426 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400427 "property delete\n", wm->source_data.size);
428 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200429 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400430 "property deleted, seting new property\n",
431 wm->source_data.size);
432 weston_wm_flush_source_data(wm);
433 }
434 xcb_flush(wm->conn);
435 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200436 wm->property_source = NULL;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400437 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400438 wm->data_source_fd = -1;
439 close(fd);
440 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200441 weston_log("nothing happened, buffered the bytes\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400442 }
443
444 return 1;
445}
446
447static void
448weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
449{
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700450 struct weston_data_source *source;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400451 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400452 int p[2];
453
454 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200455 weston_log("pipe2 failed: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400456 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
457 return;
458 }
459
460 wl_array_init(&wm->source_data);
461 wm->selection_target = target;
462 wm->data_source_fd = p[0];
463 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
464 wm->data_source_fd,
465 WL_EVENT_READABLE,
466 weston_wm_read_data_source,
467 wm);
468
Kristian Høgsberge3148752013-05-06 23:19:49 -0400469 source = seat->selection_data_source;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400470 source->send(source, mime_type, p[1]);
Kristian Høgsberg73bdc0c2013-09-04 22:32:50 -0700471 close(p[1]);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400472}
473
474static void
475weston_wm_send_incr_chunk(struct weston_wm *wm)
476{
477 int length;
478
Martin Minarik6d118362012-06-07 18:01:59 +0200479 weston_log("property deleted\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400480
481 wm->selection_property_set = 0;
482 if (wm->flush_property_on_delete) {
Martin Minarik6d118362012-06-07 18:01:59 +0200483 weston_log("setting new property, %zu bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400484 wm->source_data.size);
485 wm->flush_property_on_delete = 0;
486 length = weston_wm_flush_source_data(wm);
487
488 if (wm->data_source_fd >= 0) {
489 wm->property_source =
490 wl_event_loop_add_fd(wm->server->loop,
491 wm->data_source_fd,
492 WL_EVENT_READABLE,
493 weston_wm_read_data_source,
494 wm);
495 } else if (length > 0) {
496 /* Transfer is all done, but queue a flush for
497 * the delete of the last chunk so we can set
498 * the 0 sized propert to signal the end of
499 * the transfer. */
500 wm->flush_property_on_delete = 1;
501 wl_array_release(&wm->source_data);
502 } else {
503 wm->selection_request.requestor = XCB_NONE;
504 }
505 }
506}
507
508static int
509weston_wm_handle_selection_property_notify(struct weston_wm *wm,
510 xcb_generic_event_t *event)
511{
512 xcb_property_notify_event_t *property_notify =
513 (xcb_property_notify_event_t *) event;
514
515 if (property_notify->window == wm->selection_window) {
516 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
517 property_notify->atom == wm->atom.wl_selection &&
518 wm->incr)
519 weston_wm_get_incr_chunk(wm);
520 return 1;
521 } else if (property_notify->window == wm->selection_request.requestor) {
522 if (property_notify->state == XCB_PROPERTY_DELETE &&
523 property_notify->atom == wm->selection_request.property &&
524 wm->incr)
525 weston_wm_send_incr_chunk(wm);
526 return 1;
527 }
528
529 return 0;
530}
531
532static void
533weston_wm_handle_selection_request(struct weston_wm *wm,
534 xcb_generic_event_t *event)
535{
536 xcb_selection_request_event_t *selection_request =
537 (xcb_selection_request_event_t *) event;
538
Martin Minarik6d118362012-06-07 18:01:59 +0200539 weston_log("selection request, %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400540 get_atom_name(wm->conn, selection_request->selection));
Martin Minarik6d118362012-06-07 18:01:59 +0200541 weston_log_continue("target %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400542 get_atom_name(wm->conn, selection_request->target));
Martin Minarik6d118362012-06-07 18:01:59 +0200543 weston_log_continue("property %s\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400544 get_atom_name(wm->conn, selection_request->property));
545
546 wm->selection_request = *selection_request;
547 wm->incr = 0;
548 wm->flush_property_on_delete = 0;
549
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400550 if (selection_request->selection == wm->atom.clipboard_manager) {
551 /* The weston clipboard should already have grabbed
552 * the first target, so just send selection notify
553 * now. This isn't synchronized with the clipboard
554 * finishing getting the data, so there's a race here. */
555 weston_wm_send_selection_notify(wm, wm->selection_request.property);
556 return;
557 }
558
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400559 if (selection_request->target == wm->atom.targets) {
560 weston_wm_send_targets(wm);
561 } else if (selection_request->target == wm->atom.timestamp) {
562 weston_wm_send_timestamp(wm);
563 } else if (selection_request->target == wm->atom.utf8_string ||
564 selection_request->target == wm->atom.text) {
565 weston_wm_send_data(wm, wm->atom.utf8_string,
566 "text/plain;charset=utf-8");
567 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200568 weston_log("can only handle UTF8_STRING targets...\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400569 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
570 }
571}
572
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700573static int
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400574weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
575 xcb_generic_event_t *event)
576{
577 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
578 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400579 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400580 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400581 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400582
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700583 if (xfixes_selection_notify->selection != wm->atom.clipboard)
584 return 0;
585
Martin Minarik6d118362012-06-07 18:01:59 +0200586 weston_log("xfixes selection notify event: owner %d\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400587 xfixes_selection_notify->owner);
588
Kristian Høgsberg80566732012-06-01 00:08:12 -0400589 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
590 if (wm->selection_owner != wm->selection_window) {
591 /* A real X client selection went away, not our
592 * proxy selection. Clear the wayland selection. */
593 compositor = wm->server->compositor;
594 serial = wl_display_next_serial(compositor->wl_display);
Kristian Høgsberge3148752013-05-06 23:19:49 -0400595 weston_seat_set_selection(seat, NULL, serial);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400596 }
597
598 wm->selection_owner = XCB_WINDOW_NONE;
599
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700600 return 1;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400601 }
602
603 wm->selection_owner = xfixes_selection_notify->owner;
604
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400605 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
606 * selection, so grab the actual timestamp here so we can
607 * answer TIMESTAMP conversion requests correctly. */
608 if (xfixes_selection_notify->owner == wm->selection_window) {
609 wm->selection_timestamp = xfixes_selection_notify->timestamp;
Martin Minarik6d118362012-06-07 18:01:59 +0200610 weston_log("our window, skipping\n");
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700611 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400612 }
613
614 wm->incr = 0;
615 xcb_convert_selection(wm->conn, wm->selection_window,
616 wm->atom.clipboard,
617 wm->atom.targets,
618 wm->atom.wl_selection,
619 xfixes_selection_notify->timestamp);
620
621 xcb_flush(wm->conn);
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700622
623 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400624}
625
626int
627weston_wm_handle_selection_event(struct weston_wm *wm,
628 xcb_generic_event_t *event)
629{
630 switch (event->response_type & ~0x80) {
631 case XCB_SELECTION_NOTIFY:
632 weston_wm_handle_selection_notify(wm, event);
633 return 1;
634 case XCB_PROPERTY_NOTIFY:
635 return weston_wm_handle_selection_property_notify(wm, event);
636 case XCB_SELECTION_REQUEST:
637 weston_wm_handle_selection_request(wm, event);
638 return 1;
639 }
640
641 switch (event->response_type - wm->xfixes->first_event) {
642 case XCB_XFIXES_SELECTION_NOTIFY:
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700643 return weston_wm_handle_xfixes_selection_notify(wm, event);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400644 }
645
646 return 0;
647}
648
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200649static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400650weston_wm_set_selection(struct wl_listener *listener, void *data)
651{
Kristian Høgsberge3148752013-05-06 23:19:49 -0400652 struct weston_seat *seat = data;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400653 struct weston_wm *wm =
654 container_of(listener, struct weston_wm, selection_listener);
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700655 struct weston_data_source *source = seat->selection_data_source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400656 const char **p, **end;
657 int has_text_plain = 0;
658
Kristian Høgsberg80566732012-06-01 00:08:12 -0400659 if (source == NULL) {
660 if (wm->selection_owner == wm->selection_window)
661 xcb_set_selection_owner(wm->conn,
662 XCB_ATOM_NONE,
663 wm->atom.clipboard,
664 wm->selection_timestamp);
665 return;
666 }
667
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400668 if (source->send == data_source_send)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400669 return;
670
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400671 p = source->mime_types.data;
672 end = (const char **)
673 ((char *) source->mime_types.data + source->mime_types.size);
674 while (p < end) {
Martin Minarik6d118362012-06-07 18:01:59 +0200675 weston_log(" %s\n", *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400676 if (strcmp(*p, "text/plain") == 0 ||
677 strcmp(*p, "text/plain;charset=utf-8") == 0)
678 has_text_plain = 1;
679 p++;
680 }
681
682 if (has_text_plain) {
683 xcb_set_selection_owner(wm->conn,
684 wm->selection_window,
685 wm->atom.clipboard,
686 XCB_TIME_CURRENT_TIME);
687 } else {
688 xcb_set_selection_owner(wm->conn,
689 XCB_ATOM_NONE,
690 wm->atom.clipboard,
691 XCB_TIME_CURRENT_TIME);
692 }
693}
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400694
695void
696weston_wm_selection_init(struct weston_wm *wm)
697{
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400698 struct weston_seat *seat;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400699 uint32_t values[1], mask;
700
701 wm->selection_request.requestor = XCB_NONE;
702
703 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
704 wm->selection_window = xcb_generate_id(wm->conn);
705 xcb_create_window(wm->conn,
706 XCB_COPY_FROM_PARENT,
707 wm->selection_window,
708 wm->screen->root,
709 0, 0,
710 10, 10,
711 0,
712 XCB_WINDOW_CLASS_INPUT_OUTPUT,
713 wm->screen->root_visual,
714 XCB_CW_EVENT_MASK, values);
715
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400716 xcb_set_selection_owner(wm->conn,
717 wm->selection_window,
718 wm->atom.clipboard_manager,
719 XCB_TIME_CURRENT_TIME);
720
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400721 mask =
722 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
723 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
724 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
725 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
726 wm->atom.clipboard, mask);
727
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400728 seat = weston_wm_pick_seat(wm);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400729 wm->selection_listener.notify = weston_wm_set_selection;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400730 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
Kristian Høgsberge2203272012-06-03 10:32:48 -0400731
732 weston_wm_set_selection(&wm->selection_listener, seat);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400733}