blob: 572a0b59fd187063ee5b79dc47174306e22911ec [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);
114
115 dump_property(wm, wm->atom.wl_selection, reply);
116
117 if (xcb_get_property_value_length(reply) > 0) {
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700118 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400119 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200120 weston_log("transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400121 close(wm->data_source_fd);
122 free(reply);
123 }
124}
125
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400126struct x11_data_source {
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700127 struct weston_data_source base;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400128 struct weston_wm *wm;
129};
130
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400131static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700132data_source_accept(struct weston_data_source *source,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400133 uint32_t time, const char *mime_type)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400134{
135}
136
137static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700138data_source_send(struct weston_data_source *base,
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400139 const char *mime_type, int32_t fd)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400140{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400141 struct x11_data_source *source = (struct x11_data_source *) base;
142 struct weston_wm *wm = source->wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400143
144 if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
145 /* Get data for the utf8_string target */
146 xcb_convert_selection(wm->conn,
147 wm->selection_window,
148 wm->atom.clipboard,
149 wm->atom.utf8_string,
150 wm->atom.wl_selection,
151 XCB_TIME_CURRENT_TIME);
152
153 xcb_flush(wm->conn);
154
155 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
Kristian Høgsberg668fc0d2013-09-04 20:48:46 -0700156 wm->data_source_fd = fd;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400157 }
158}
159
160static void
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700161data_source_cancel(struct weston_data_source *source)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400162{
163}
164
165static void
166weston_wm_get_selection_targets(struct weston_wm *wm)
167{
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400168 struct x11_data_source *source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400169 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400170 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400171 xcb_get_property_cookie_t cookie;
172 xcb_get_property_reply_t *reply;
173 xcb_atom_t *value;
174 char **p;
175 uint32_t i;
176
177 cookie = xcb_get_property(wm->conn,
178 1, /* delete */
179 wm->selection_window,
180 wm->atom.wl_selection,
181 XCB_GET_PROPERTY_TYPE_ANY,
182 0, /* offset */
183 4096 /* length */);
184
185 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
186
187 dump_property(wm, wm->atom.wl_selection, reply);
188
189 if (reply->type != XCB_ATOM_ATOM) {
190 free(reply);
191 return;
192 }
193
194 source = malloc(sizeof *source);
195 if (source == NULL)
196 return;
197
Jason Ekstrand8a4a9eb2013-06-14 10:07:55 -0500198 wl_signal_init(&source->base.destroy_signal);
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400199 source->base.accept = data_source_accept;
200 source->base.send = data_source_send;
201 source->base.cancel = data_source_cancel;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400202 source->wm = wm;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400203
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400204 wl_array_init(&source->base.mime_types);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400205 value = xcb_get_property_value(reply);
206 for (i = 0; i < reply->value_len; i++) {
207 if (value[i] == wm->atom.utf8_string) {
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400208 p = wl_array_add(&source->base.mime_types, sizeof *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400209 if (p)
210 *p = strdup("text/plain;charset=utf-8");
211 }
212 }
213
214 compositor = wm->server->compositor;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400215 weston_seat_set_selection(seat, &source->base,
216 wl_display_next_serial(compositor->wl_display));
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400217
218 free(reply);
219}
220
221static void
222weston_wm_get_selection_data(struct weston_wm *wm)
223{
224 xcb_get_property_cookie_t cookie;
225 xcb_get_property_reply_t *reply;
226
227 cookie = xcb_get_property(wm->conn,
228 1, /* delete */
229 wm->selection_window,
230 wm->atom.wl_selection,
231 XCB_GET_PROPERTY_TYPE_ANY,
232 0, /* offset */
233 0x1fffffff /* length */);
234
235 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
236
237 if (reply->type == wm->atom.incr) {
238 dump_property(wm, wm->atom.wl_selection, reply);
239 wm->incr = 1;
240 free(reply);
241 } else {
242 dump_property(wm, wm->atom.wl_selection, reply);
243 wm->incr = 0;
Kristian Høgsberg3f7fcf82013-09-04 22:12:28 -0700244 weston_wm_write_property(wm, reply);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400245 }
246}
247
248static void
249weston_wm_handle_selection_notify(struct weston_wm *wm,
250 xcb_generic_event_t *event)
251{
252 xcb_selection_notify_event_t *selection_notify =
253 (xcb_selection_notify_event_t *) event;
254
255 if (selection_notify->property == XCB_ATOM_NONE) {
256 /* convert selection failed */
257 } else if (selection_notify->target == wm->atom.targets) {
258 weston_wm_get_selection_targets(wm);
259 } else {
260 weston_wm_get_selection_data(wm);
261 }
262}
263
264static const size_t incr_chunk_size = 64 * 1024;
265
266static void
267weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
268{
269 xcb_selection_notify_event_t selection_notify;
270
271 memset(&selection_notify, 0, sizeof selection_notify);
272 selection_notify.response_type = XCB_SELECTION_NOTIFY;
273 selection_notify.sequence = 0;
274 selection_notify.time = wm->selection_request.time;
275 selection_notify.requestor = wm->selection_request.requestor;
276 selection_notify.selection = wm->selection_request.selection;
277 selection_notify.target = wm->selection_request.target;
278 selection_notify.property = property;
279
280 xcb_send_event(wm->conn, 0, /* propagate */
281 wm->selection_request.requestor,
282 XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
283}
284
285static void
286weston_wm_send_targets(struct weston_wm *wm)
287{
288 xcb_atom_t targets[] = {
289 wm->atom.timestamp,
290 wm->atom.targets,
291 wm->atom.utf8_string,
292 /* wm->atom.compound_text, */
293 wm->atom.text,
294 /* wm->atom.string */
295 };
296
297 xcb_change_property(wm->conn,
298 XCB_PROP_MODE_REPLACE,
299 wm->selection_request.requestor,
300 wm->selection_request.property,
301 XCB_ATOM_ATOM,
302 32, /* format */
303 ARRAY_LENGTH(targets), targets);
304
305 weston_wm_send_selection_notify(wm, wm->selection_request.property);
306}
307
308static void
309weston_wm_send_timestamp(struct weston_wm *wm)
310{
311 xcb_change_property(wm->conn,
312 XCB_PROP_MODE_REPLACE,
313 wm->selection_request.requestor,
314 wm->selection_request.property,
315 XCB_ATOM_INTEGER,
316 32, /* format */
317 1, &wm->selection_timestamp);
318
319 weston_wm_send_selection_notify(wm, wm->selection_request.property);
320}
321
322static int
323weston_wm_flush_source_data(struct weston_wm *wm)
324{
325 int length;
326
327 xcb_change_property(wm->conn,
328 XCB_PROP_MODE_REPLACE,
329 wm->selection_request.requestor,
330 wm->selection_request.property,
331 wm->selection_target,
332 8, /* format */
333 wm->source_data.size,
334 wm->source_data.data);
335 wm->selection_property_set = 1;
336 length = wm->source_data.size;
337 wm->source_data.size = 0;
338
339 return length;
340}
341
342static int
343weston_wm_read_data_source(int fd, uint32_t mask, void *data)
344{
345 struct weston_wm *wm = data;
346 int len, current, available;
347 void *p;
348
349 current = wm->source_data.size;
350 if (wm->source_data.size < incr_chunk_size)
351 p = wl_array_add(&wm->source_data, incr_chunk_size);
352 else
353 p = (char *) wm->source_data.data + wm->source_data.size;
354 available = wm->source_data.alloc - current;
355
356 len = read(fd, p, available);
357 if (len == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200358 weston_log("read error from data source: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400359 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
360 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200361 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400362 close(fd);
363 wl_array_release(&wm->source_data);
364 }
365
Martin Minarik6d118362012-06-07 18:01:59 +0200366 weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400367 len, available, mask, len, (char *) p);
368
369 wm->source_data.size = current + len;
370 if (wm->source_data.size >= incr_chunk_size) {
371 if (!wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200372 weston_log("got %zu bytes, starting incr\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400373 wm->source_data.size);
374 wm->incr = 1;
375 xcb_change_property(wm->conn,
376 XCB_PROP_MODE_REPLACE,
377 wm->selection_request.requestor,
378 wm->selection_request.property,
379 wm->atom.incr,
380 32, /* format */
381 1, &incr_chunk_size);
382 wm->selection_property_set = 1;
383 wm->flush_property_on_delete = 1;
384 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200385 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400386 weston_wm_send_selection_notify(wm, wm->selection_request.property);
387 } else if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200388 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400389 "property delete\n", wm->source_data.size);
390
391 wm->flush_property_on_delete = 1;
392 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200393 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400394 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200395 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400396 "property deleted, seting new property\n",
397 wm->source_data.size);
398 weston_wm_flush_source_data(wm);
399 }
400 } else if (len == 0 && !wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200401 weston_log("non-incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400402 /* Non-incr transfer all done. */
403 weston_wm_flush_source_data(wm);
404 weston_wm_send_selection_notify(wm, wm->selection_request.property);
405 xcb_flush(wm->conn);
406 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200407 wm->property_source = NULL;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400408 close(fd);
409 wl_array_release(&wm->source_data);
410 wm->selection_request.requestor = XCB_NONE;
411 } else if (len == 0 && wm->incr) {
Martin Minarik6d118362012-06-07 18:01:59 +0200412 weston_log("incr transfer complete\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400413
414 wm->flush_property_on_delete = 1;
415 if (wm->selection_property_set) {
Martin Minarik6d118362012-06-07 18:01:59 +0200416 weston_log("got %zu bytes, waiting for "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400417 "property delete\n", wm->source_data.size);
418 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200419 weston_log("got %zu bytes, "
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400420 "property deleted, seting new property\n",
421 wm->source_data.size);
422 weston_wm_flush_source_data(wm);
423 }
424 xcb_flush(wm->conn);
425 wl_event_source_remove(wm->property_source);
Giulio Camuffo9e1aeb82014-12-26 18:10:35 +0200426 wm->property_source = NULL;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400427 close(wm->data_source_fd);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400428 wm->data_source_fd = -1;
429 close(fd);
430 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200431 weston_log("nothing happened, buffered the bytes\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400432 }
433
434 return 1;
435}
436
437static void
438weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
439{
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700440 struct weston_data_source *source;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400441 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400442 int p[2];
443
444 if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
Martin Minarik6d118362012-06-07 18:01:59 +0200445 weston_log("pipe2 failed: %m\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400446 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
447 return;
448 }
449
450 wl_array_init(&wm->source_data);
451 wm->selection_target = target;
452 wm->data_source_fd = p[0];
453 wm->property_source = wl_event_loop_add_fd(wm->server->loop,
454 wm->data_source_fd,
455 WL_EVENT_READABLE,
456 weston_wm_read_data_source,
457 wm);
458
Kristian Høgsberge3148752013-05-06 23:19:49 -0400459 source = seat->selection_data_source;
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400460 source->send(source, mime_type, p[1]);
Kristian Høgsberg73bdc0c2013-09-04 22:32:50 -0700461 close(p[1]);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400462}
463
464static void
465weston_wm_send_incr_chunk(struct weston_wm *wm)
466{
467 int length;
468
Martin Minarik6d118362012-06-07 18:01:59 +0200469 weston_log("property deleted\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400470
471 wm->selection_property_set = 0;
472 if (wm->flush_property_on_delete) {
Martin Minarik6d118362012-06-07 18:01:59 +0200473 weston_log("setting new property, %zu bytes\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400474 wm->source_data.size);
475 wm->flush_property_on_delete = 0;
476 length = weston_wm_flush_source_data(wm);
477
478 if (wm->data_source_fd >= 0) {
479 wm->property_source =
480 wl_event_loop_add_fd(wm->server->loop,
481 wm->data_source_fd,
482 WL_EVENT_READABLE,
483 weston_wm_read_data_source,
484 wm);
485 } else if (length > 0) {
486 /* Transfer is all done, but queue a flush for
487 * the delete of the last chunk so we can set
488 * the 0 sized propert to signal the end of
489 * the transfer. */
490 wm->flush_property_on_delete = 1;
491 wl_array_release(&wm->source_data);
492 } else {
493 wm->selection_request.requestor = XCB_NONE;
494 }
495 }
496}
497
498static int
499weston_wm_handle_selection_property_notify(struct weston_wm *wm,
500 xcb_generic_event_t *event)
501{
502 xcb_property_notify_event_t *property_notify =
503 (xcb_property_notify_event_t *) event;
504
505 if (property_notify->window == wm->selection_window) {
506 if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
507 property_notify->atom == wm->atom.wl_selection &&
508 wm->incr)
509 weston_wm_get_incr_chunk(wm);
510 return 1;
511 } else if (property_notify->window == wm->selection_request.requestor) {
512 if (property_notify->state == XCB_PROPERTY_DELETE &&
513 property_notify->atom == wm->selection_request.property &&
514 wm->incr)
515 weston_wm_send_incr_chunk(wm);
516 return 1;
517 }
518
519 return 0;
520}
521
522static void
523weston_wm_handle_selection_request(struct weston_wm *wm,
524 xcb_generic_event_t *event)
525{
526 xcb_selection_request_event_t *selection_request =
527 (xcb_selection_request_event_t *) event;
528
Martin Minarik6d118362012-06-07 18:01:59 +0200529 weston_log("selection request, %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400530 get_atom_name(wm->conn, selection_request->selection));
Martin Minarik6d118362012-06-07 18:01:59 +0200531 weston_log_continue("target %s, ",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400532 get_atom_name(wm->conn, selection_request->target));
Martin Minarik6d118362012-06-07 18:01:59 +0200533 weston_log_continue("property %s\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400534 get_atom_name(wm->conn, selection_request->property));
535
536 wm->selection_request = *selection_request;
537 wm->incr = 0;
538 wm->flush_property_on_delete = 0;
539
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400540 if (selection_request->selection == wm->atom.clipboard_manager) {
541 /* The weston clipboard should already have grabbed
542 * the first target, so just send selection notify
543 * now. This isn't synchronized with the clipboard
544 * finishing getting the data, so there's a race here. */
545 weston_wm_send_selection_notify(wm, wm->selection_request.property);
546 return;
547 }
548
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400549 if (selection_request->target == wm->atom.targets) {
550 weston_wm_send_targets(wm);
551 } else if (selection_request->target == wm->atom.timestamp) {
552 weston_wm_send_timestamp(wm);
553 } else if (selection_request->target == wm->atom.utf8_string ||
554 selection_request->target == wm->atom.text) {
555 weston_wm_send_data(wm, wm->atom.utf8_string,
556 "text/plain;charset=utf-8");
557 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200558 weston_log("can only handle UTF8_STRING targets...\n");
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400559 weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
560 }
561}
562
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700563static int
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400564weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
565 xcb_generic_event_t *event)
566{
567 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
568 (xcb_xfixes_selection_notify_event_t *) event;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400569 struct weston_compositor *compositor;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400570 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400571 uint32_t serial;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400572
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700573 if (xfixes_selection_notify->selection != wm->atom.clipboard)
574 return 0;
575
Martin Minarik6d118362012-06-07 18:01:59 +0200576 weston_log("xfixes selection notify event: owner %d\n",
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400577 xfixes_selection_notify->owner);
578
Kristian Høgsberg80566732012-06-01 00:08:12 -0400579 if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) {
580 if (wm->selection_owner != wm->selection_window) {
581 /* A real X client selection went away, not our
582 * proxy selection. Clear the wayland selection. */
583 compositor = wm->server->compositor;
584 serial = wl_display_next_serial(compositor->wl_display);
Kristian Høgsberge3148752013-05-06 23:19:49 -0400585 weston_seat_set_selection(seat, NULL, serial);
Kristian Høgsberg80566732012-06-01 00:08:12 -0400586 }
587
588 wm->selection_owner = XCB_WINDOW_NONE;
589
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700590 return 1;
Kristian Høgsberg80566732012-06-01 00:08:12 -0400591 }
592
593 wm->selection_owner = xfixes_selection_notify->owner;
594
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400595 /* We have to use XCB_TIME_CURRENT_TIME when we claim the
596 * selection, so grab the actual timestamp here so we can
597 * answer TIMESTAMP conversion requests correctly. */
598 if (xfixes_selection_notify->owner == wm->selection_window) {
599 wm->selection_timestamp = xfixes_selection_notify->timestamp;
Martin Minarik6d118362012-06-07 18:01:59 +0200600 weston_log("our window, skipping\n");
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700601 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400602 }
603
604 wm->incr = 0;
605 xcb_convert_selection(wm->conn, wm->selection_window,
606 wm->atom.clipboard,
607 wm->atom.targets,
608 wm->atom.wl_selection,
609 xfixes_selection_notify->timestamp);
610
611 xcb_flush(wm->conn);
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700612
613 return 1;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400614}
615
616int
617weston_wm_handle_selection_event(struct weston_wm *wm,
618 xcb_generic_event_t *event)
619{
620 switch (event->response_type & ~0x80) {
621 case XCB_SELECTION_NOTIFY:
622 weston_wm_handle_selection_notify(wm, event);
623 return 1;
624 case XCB_PROPERTY_NOTIFY:
625 return weston_wm_handle_selection_property_notify(wm, event);
626 case XCB_SELECTION_REQUEST:
627 weston_wm_handle_selection_request(wm, event);
628 return 1;
629 }
630
631 switch (event->response_type - wm->xfixes->first_event) {
632 case XCB_XFIXES_SELECTION_NOTIFY:
Kristian Høgsberg9466e502013-09-04 21:09:24 -0700633 return weston_wm_handle_xfixes_selection_notify(wm, event);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400634 }
635
636 return 0;
637}
638
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200639static void
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400640weston_wm_set_selection(struct wl_listener *listener, void *data)
641{
Kristian Høgsberge3148752013-05-06 23:19:49 -0400642 struct weston_seat *seat = data;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400643 struct weston_wm *wm =
644 container_of(listener, struct weston_wm, selection_listener);
Kristian Høgsberg7ff3bdb2013-07-25 15:52:14 -0700645 struct weston_data_source *source = seat->selection_data_source;
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400646 const char **p, **end;
647 int has_text_plain = 0;
648
Kristian Høgsberg80566732012-06-01 00:08:12 -0400649 if (source == NULL) {
650 if (wm->selection_owner == wm->selection_window)
651 xcb_set_selection_owner(wm->conn,
652 XCB_ATOM_NONE,
653 wm->atom.clipboard,
654 wm->selection_timestamp);
655 return;
656 }
657
Kristian Høgsbergc65d56a2012-06-02 21:23:01 -0400658 if (source->send == data_source_send)
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400659 return;
660
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400661 p = source->mime_types.data;
662 end = (const char **)
663 ((char *) source->mime_types.data + source->mime_types.size);
664 while (p < end) {
Martin Minarik6d118362012-06-07 18:01:59 +0200665 weston_log(" %s\n", *p);
Kristian Høgsberg102bf032012-05-21 15:52:02 -0400666 if (strcmp(*p, "text/plain") == 0 ||
667 strcmp(*p, "text/plain;charset=utf-8") == 0)
668 has_text_plain = 1;
669 p++;
670 }
671
672 if (has_text_plain) {
673 xcb_set_selection_owner(wm->conn,
674 wm->selection_window,
675 wm->atom.clipboard,
676 XCB_TIME_CURRENT_TIME);
677 } else {
678 xcb_set_selection_owner(wm->conn,
679 XCB_ATOM_NONE,
680 wm->atom.clipboard,
681 XCB_TIME_CURRENT_TIME);
682 }
683}
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400684
685void
686weston_wm_selection_init(struct weston_wm *wm)
687{
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400688 struct weston_seat *seat;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400689 uint32_t values[1], mask;
690
691 wm->selection_request.requestor = XCB_NONE;
692
693 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
694 wm->selection_window = xcb_generate_id(wm->conn);
695 xcb_create_window(wm->conn,
696 XCB_COPY_FROM_PARENT,
697 wm->selection_window,
698 wm->screen->root,
699 0, 0,
700 10, 10,
701 0,
702 XCB_WINDOW_CLASS_INPUT_OUTPUT,
703 wm->screen->root_visual,
704 XCB_CW_EVENT_MASK, values);
705
Kristian Høgsbergcba022a2012-06-04 10:11:45 -0400706 xcb_set_selection_owner(wm->conn,
707 wm->selection_window,
708 wm->atom.clipboard_manager,
709 XCB_TIME_CURRENT_TIME);
710
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400711 mask =
712 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
713 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
714 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
715 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
716 wm->atom.clipboard, mask);
717
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400718 seat = weston_wm_pick_seat(wm);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400719 wm->selection_listener.notify = weston_wm_set_selection;
Kristian Høgsberge3148752013-05-06 23:19:49 -0400720 wl_signal_add(&seat->selection_signal, &wm->selection_listener);
Kristian Høgsberge2203272012-06-03 10:32:48 -0400721
722 weston_wm_set_selection(&wm->selection_listener, seat);
Kristian Høgsberg4dec0112012-06-03 09:18:06 -0400723}