1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * 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:
11 *
12 * 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.
24 */
25
26 #include "config.h"
27
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <linux/input.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <sys/uio.h>
35
36 #include <libweston/libweston.h>
37 #include "libweston-internal.h"
38 #include "shared/helpers.h"
39
40 struct clipboard_source {
41 struct weston_data_source base;
42 struct wl_array contents;
43 struct clipboard *clipboard;
44 struct wl_event_source *event_source;
45 uint32_t serial;
46 int refcount;
47 int fd;
48 };
49
50 struct clipboard {
51 struct weston_seat *seat;
52 struct wl_listener selection_listener;
53 struct wl_listener destroy_listener;
54 struct clipboard_source *source;
55 };
56
57 static void clipboard_client_create(struct clipboard_source *source, int fd);
58
59 static void
clipboard_source_unref(struct clipboard_source * source)60 clipboard_source_unref(struct clipboard_source *source)
61 {
62 char **s;
63
64 source->refcount--;
65 if (source->refcount > 0)
66 return;
67
68 if (source->event_source) {
69 wl_event_source_remove(source->event_source);
70 close(source->fd);
71 }
72 wl_signal_emit(&source->base.destroy_signal,
73 &source->base);
74 s = source->base.mime_types.data;
75 free(*s);
76 wl_array_release(&source->base.mime_types);
77 wl_array_release(&source->contents);
78 free(source);
79 }
80
81 static int
clipboard_source_data(int fd,uint32_t mask,void * data)82 clipboard_source_data(int fd, uint32_t mask, void *data)
83 {
84 struct clipboard_source *source = data;
85 struct clipboard *clipboard = source->clipboard;
86 char *p;
87 int len, size;
88
89 if (source->contents.alloc - source->contents.size < 1024) {
90 wl_array_add(&source->contents, 1024);
91 source->contents.size -= 1024;
92 }
93
94 p = source->contents.data + source->contents.size;
95 size = source->contents.alloc - source->contents.size;
96 len = read(fd, p, size);
97 if (len == 0) {
98 wl_event_source_remove(source->event_source);
99 close(fd);
100 source->event_source = NULL;
101 } else if (len < 0) {
102 clipboard_source_unref(source);
103 clipboard->source = NULL;
104 } else {
105 source->contents.size += len;
106 }
107
108 return 1;
109 }
110
111 static void
clipboard_source_accept(struct weston_data_source * source,uint32_t time,const char * mime_type)112 clipboard_source_accept(struct weston_data_source *source,
113 uint32_t time, const char *mime_type)
114 {
115 }
116
117 static void
clipboard_source_send(struct weston_data_source * base,const char * mime_type,int32_t fd)118 clipboard_source_send(struct weston_data_source *base,
119 const char *mime_type, int32_t fd)
120 {
121 struct clipboard_source *source =
122 container_of(base, struct clipboard_source, base);
123 char **s;
124
125 s = source->base.mime_types.data;
126 if (strcmp(mime_type, s[0]) == 0)
127 clipboard_client_create(source, fd);
128 else
129 close(fd);
130 }
131
132 static void
clipboard_source_cancel(struct weston_data_source * source)133 clipboard_source_cancel(struct weston_data_source *source)
134 {
135 }
136
137 static struct clipboard_source *
clipboard_source_create(struct clipboard * clipboard,const char * mime_type,uint32_t serial,int fd)138 clipboard_source_create(struct clipboard *clipboard,
139 const char *mime_type, uint32_t serial, int fd)
140 {
141 struct wl_display *display = clipboard->seat->compositor->wl_display;
142 struct wl_event_loop *loop = wl_display_get_event_loop(display);
143 struct clipboard_source *source;
144 char **s;
145
146 source = zalloc(sizeof *source);
147 if (source == NULL)
148 return NULL;
149
150 wl_array_init(&source->contents);
151 wl_array_init(&source->base.mime_types);
152 source->base.resource = NULL;
153 source->base.accept = clipboard_source_accept;
154 source->base.send = clipboard_source_send;
155 source->base.cancel = clipboard_source_cancel;
156 wl_signal_init(&source->base.destroy_signal);
157 source->refcount = 1;
158 source->clipboard = clipboard;
159 source->serial = serial;
160 source->fd = fd;
161
162 s = wl_array_add(&source->base.mime_types, sizeof *s);
163 if (s == NULL)
164 goto err_add;
165 *s = strdup(mime_type);
166 if (*s == NULL)
167 goto err_strdup;
168 source->event_source =
169 wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
170 clipboard_source_data, source);
171 if (source->event_source == NULL)
172 goto err_source;
173
174 return source;
175
176 err_source:
177 free(*s);
178 err_strdup:
179 wl_array_release(&source->base.mime_types);
180 err_add:
181 free(source);
182
183 return NULL;
184 }
185
186 struct clipboard_client {
187 struct wl_event_source *event_source;
188 size_t offset;
189 struct clipboard_source *source;
190 };
191
192 static int
clipboard_client_data(int fd,uint32_t mask,void * data)193 clipboard_client_data(int fd, uint32_t mask, void *data)
194 {
195 struct clipboard_client *client = data;
196 char *p;
197 size_t size;
198 int len;
199
200 size = client->source->contents.size;
201 p = client->source->contents.data;
202 len = write(fd, p + client->offset, size - client->offset);
203 if (len > 0)
204 client->offset += len;
205
206 if (client->offset == size || len <= 0) {
207 close(fd);
208 wl_event_source_remove(client->event_source);
209 clipboard_source_unref(client->source);
210 free(client);
211 }
212
213 return 1;
214 }
215
216 static void
clipboard_client_create(struct clipboard_source * source,int fd)217 clipboard_client_create(struct clipboard_source *source, int fd)
218 {
219 struct weston_seat *seat = source->clipboard->seat;
220 struct clipboard_client *client;
221 struct wl_event_loop *loop =
222 wl_display_get_event_loop(seat->compositor->wl_display);
223
224 client = zalloc(sizeof *client);
225 if (client == NULL)
226 return;
227
228 client->source = source;
229 source->refcount++;
230 client->event_source =
231 wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE,
232 clipboard_client_data, client);
233 }
234
235 static void
clipboard_set_selection(struct wl_listener * listener,void * data)236 clipboard_set_selection(struct wl_listener *listener, void *data)
237 {
238 struct clipboard *clipboard =
239 container_of(listener, struct clipboard, selection_listener);
240 struct weston_seat *seat = data;
241 struct weston_data_source *source = seat->selection_data_source;
242 const char **mime_types;
243 int p[2];
244
245 if (source == NULL) {
246 if (clipboard->source)
247 weston_seat_set_selection(seat,
248 &clipboard->source->base,
249 clipboard->source->serial);
250 return;
251 } else if (source->accept == clipboard_source_accept) {
252 /* Callback for our data source. */
253 return;
254 }
255
256 if (clipboard->source)
257 clipboard_source_unref(clipboard->source);
258
259 clipboard->source = NULL;
260
261 mime_types = source->mime_types.data;
262
263 if (!mime_types || pipe2(p, O_CLOEXEC) == -1)
264 return;
265
266 source->send(source, mime_types[0], p[1]);
267
268 clipboard->source =
269 clipboard_source_create(clipboard, mime_types[0],
270 seat->selection_serial, p[0]);
271 if (clipboard->source == NULL) {
272 close(p[0]);
273 return;
274 }
275 }
276
277 static void
clipboard_destroy(struct wl_listener * listener,void * data)278 clipboard_destroy(struct wl_listener *listener, void *data)
279 {
280 struct clipboard *clipboard =
281 container_of(listener, struct clipboard, destroy_listener);
282
283 wl_list_remove(&clipboard->selection_listener.link);
284 wl_list_remove(&clipboard->destroy_listener.link);
285
286 free(clipboard);
287 }
288
289 struct clipboard *
clipboard_create(struct weston_seat * seat)290 clipboard_create(struct weston_seat *seat)
291 {
292 struct clipboard *clipboard;
293
294 clipboard = zalloc(sizeof *clipboard);
295 if (clipboard == NULL)
296 return NULL;
297
298 clipboard->seat = seat;
299 clipboard->selection_listener.notify = clipboard_set_selection;
300 clipboard->destroy_listener.notify = clipboard_destroy;
301
302 wl_signal_add(&seat->selection_signal,
303 &clipboard->selection_listener);
304 wl_signal_add(&seat->destroy_signal,
305 &clipboard->destroy_listener);
306
307 return clipboard;
308 }
309