1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | |
22 | #include "../../SDL_internal.h" |
23 | |
24 | #if SDL_VIDEO_DRIVER_WAYLAND |
25 | |
26 | #include <fcntl.h> |
27 | #include <unistd.h> |
28 | #include <limits.h> |
29 | #include <signal.h> |
30 | |
31 | #include "SDL_stdinc.h" |
32 | #include "../../core/unix/SDL_poll.h" |
33 | |
34 | #include "SDL_waylandvideo.h" |
35 | #include "SDL_waylanddatamanager.h" |
36 | |
37 | #include "SDL_waylanddyn.h" |
38 | |
39 | /* FIXME: This is arbitrary, but we want this to be less than a frame because |
40 | * any longer can potentially spin an infinite loop of PumpEvents (!) |
41 | */ |
42 | #define PIPE_MS_TIMEOUT 10 |
43 | |
44 | static ssize_t |
45 | write_pipe(int fd, const void* buffer, size_t total_length, size_t *pos) |
46 | { |
47 | int ready = 0; |
48 | ssize_t bytes_written = 0; |
49 | ssize_t length = total_length - *pos; |
50 | |
51 | sigset_t sig_set; |
52 | sigset_t old_sig_set; |
53 | struct timespec zerotime = {0}; |
54 | |
55 | ready = SDL_IOReady(fd, SDL_TRUE, PIPE_MS_TIMEOUT); |
56 | |
57 | sigemptyset(&sig_set); |
58 | sigaddset(&sig_set, SIGPIPE); |
59 | |
60 | #if SDL_THREADS_DISABLED |
61 | sigprocmask(SIG_BLOCK, &sig_set, &old_sig_set); |
62 | #else |
63 | pthread_sigmask(SIG_BLOCK, &sig_set, &old_sig_set); |
64 | #endif |
65 | |
66 | if (ready == 0) { |
67 | bytes_written = SDL_SetError("Pipe timeout" ); |
68 | } else if (ready < 0) { |
69 | bytes_written = SDL_SetError("Pipe select error" ); |
70 | } else { |
71 | if (length > 0) { |
72 | bytes_written = write(fd, (Uint8*)buffer + *pos, SDL_min(length, PIPE_BUF)); |
73 | } |
74 | |
75 | if (bytes_written > 0) { |
76 | *pos += bytes_written; |
77 | } |
78 | } |
79 | |
80 | sigtimedwait(&sig_set, 0, &zerotime); |
81 | |
82 | #if SDL_THREADS_DISABLED |
83 | sigprocmask(SIG_SETMASK, &old_sig_set, NULL); |
84 | #else |
85 | pthread_sigmask(SIG_SETMASK, &old_sig_set, NULL); |
86 | #endif |
87 | |
88 | return bytes_written; |
89 | } |
90 | |
91 | static ssize_t |
92 | read_pipe(int fd, void** buffer, size_t* total_length, SDL_bool null_terminate) |
93 | { |
94 | int ready = 0; |
95 | void* output_buffer = NULL; |
96 | char temp[PIPE_BUF]; |
97 | size_t new_buffer_length = 0; |
98 | ssize_t bytes_read = 0; |
99 | size_t pos = 0; |
100 | |
101 | ready = SDL_IOReady(fd, SDL_FALSE, PIPE_MS_TIMEOUT); |
102 | |
103 | if (ready == 0) { |
104 | bytes_read = SDL_SetError("Pipe timeout" ); |
105 | } else if (ready < 0) { |
106 | bytes_read = SDL_SetError("Pipe select error" ); |
107 | } else { |
108 | bytes_read = read(fd, temp, sizeof(temp)); |
109 | } |
110 | |
111 | if (bytes_read > 0) { |
112 | pos = *total_length; |
113 | *total_length += bytes_read; |
114 | |
115 | if (null_terminate == SDL_TRUE) { |
116 | new_buffer_length = *total_length + 1; |
117 | } else { |
118 | new_buffer_length = *total_length; |
119 | } |
120 | |
121 | if (*buffer == NULL) { |
122 | output_buffer = SDL_malloc(new_buffer_length); |
123 | } else { |
124 | output_buffer = SDL_realloc(*buffer, new_buffer_length); |
125 | } |
126 | |
127 | if (output_buffer == NULL) { |
128 | bytes_read = SDL_OutOfMemory(); |
129 | } else { |
130 | SDL_memcpy((Uint8*)output_buffer + pos, temp, bytes_read); |
131 | |
132 | if (null_terminate == SDL_TRUE) { |
133 | SDL_memset((Uint8*)output_buffer + (new_buffer_length - 1), 0, 1); |
134 | } |
135 | |
136 | *buffer = output_buffer; |
137 | } |
138 | } |
139 | |
140 | return bytes_read; |
141 | } |
142 | |
143 | #define MIME_LIST_SIZE 4 |
144 | |
145 | static const char* mime_conversion_list[MIME_LIST_SIZE][2] = { |
146 | {"text/plain" , TEXT_MIME}, |
147 | {"TEXT" , TEXT_MIME}, |
148 | {"UTF8_STRING" , TEXT_MIME}, |
149 | {"STRING" , TEXT_MIME} |
150 | }; |
151 | |
152 | const char* |
153 | Wayland_convert_mime_type(const char *mime_type) |
154 | { |
155 | const char *found = mime_type; |
156 | |
157 | size_t index = 0; |
158 | |
159 | for (index = 0; index < MIME_LIST_SIZE; ++index) { |
160 | if (strcmp(mime_conversion_list[index][0], mime_type) == 0) { |
161 | found = mime_conversion_list[index][1]; |
162 | break; |
163 | } |
164 | } |
165 | |
166 | return found; |
167 | } |
168 | |
169 | static SDL_MimeDataList* |
170 | mime_data_list_find(struct wl_list* list, |
171 | const char* mime_type) |
172 | { |
173 | SDL_MimeDataList *found = NULL; |
174 | |
175 | SDL_MimeDataList *mime_list = NULL; |
176 | wl_list_for_each(mime_list, list, link) { |
177 | if (strcmp(mime_list->mime_type, mime_type) == 0) { |
178 | found = mime_list; |
179 | break; |
180 | } |
181 | } |
182 | return found; |
183 | } |
184 | |
185 | static int |
186 | mime_data_list_add(struct wl_list* list, |
187 | const char* mime_type, |
188 | const void* buffer, size_t length) |
189 | { |
190 | int status = 0; |
191 | size_t mime_type_length = 0; |
192 | SDL_MimeDataList *mime_data = NULL; |
193 | void *internal_buffer = NULL; |
194 | |
195 | if (buffer != NULL) { |
196 | internal_buffer = SDL_malloc(length); |
197 | if (internal_buffer == NULL) { |
198 | return SDL_OutOfMemory(); |
199 | } |
200 | SDL_memcpy(internal_buffer, buffer, length); |
201 | } |
202 | |
203 | mime_data = mime_data_list_find(list, mime_type); |
204 | |
205 | if (mime_data == NULL) { |
206 | mime_data = SDL_calloc(1, sizeof(*mime_data)); |
207 | if (mime_data == NULL) { |
208 | status = SDL_OutOfMemory(); |
209 | } else { |
210 | WAYLAND_wl_list_insert(list, &(mime_data->link)); |
211 | |
212 | mime_type_length = SDL_strlen(mime_type) + 1; |
213 | mime_data->mime_type = SDL_malloc(mime_type_length); |
214 | if (mime_data->mime_type == NULL) { |
215 | status = SDL_OutOfMemory(); |
216 | } else { |
217 | SDL_memcpy(mime_data->mime_type, mime_type, mime_type_length); |
218 | } |
219 | } |
220 | } |
221 | |
222 | if (mime_data != NULL && buffer != NULL && length > 0) { |
223 | if (mime_data->data != NULL) { |
224 | SDL_free(mime_data->data); |
225 | } |
226 | mime_data->data = internal_buffer; |
227 | mime_data->length = length; |
228 | } else { |
229 | SDL_free(internal_buffer); |
230 | } |
231 | |
232 | return status; |
233 | } |
234 | |
235 | static void |
236 | mime_data_list_free(struct wl_list *list) |
237 | { |
238 | SDL_MimeDataList *mime_data = NULL; |
239 | SDL_MimeDataList *next = NULL; |
240 | |
241 | wl_list_for_each_safe(mime_data, next, list, link) { |
242 | if (mime_data->data != NULL) { |
243 | SDL_free(mime_data->data); |
244 | } |
245 | if (mime_data->mime_type != NULL) { |
246 | SDL_free(mime_data->mime_type); |
247 | } |
248 | SDL_free(mime_data); |
249 | } |
250 | } |
251 | |
252 | ssize_t |
253 | Wayland_data_source_send(SDL_WaylandDataSource *source, |
254 | const char *mime_type, int fd) |
255 | { |
256 | size_t written_bytes = 0; |
257 | ssize_t status = 0; |
258 | SDL_MimeDataList *mime_data = NULL; |
259 | |
260 | mime_type = Wayland_convert_mime_type(mime_type); |
261 | mime_data = mime_data_list_find(&source->mimes, |
262 | mime_type); |
263 | |
264 | if (mime_data == NULL || mime_data->data == NULL) { |
265 | status = SDL_SetError("Invalid mime type" ); |
266 | close(fd); |
267 | } else { |
268 | while (write_pipe(fd, mime_data->data, mime_data->length, |
269 | &written_bytes) > 0); |
270 | close(fd); |
271 | status = written_bytes; |
272 | } |
273 | return status; |
274 | } |
275 | |
276 | int Wayland_data_source_add_data(SDL_WaylandDataSource *source, |
277 | const char *mime_type, |
278 | const void *buffer, |
279 | size_t length) |
280 | { |
281 | return mime_data_list_add(&source->mimes, mime_type, buffer, length); |
282 | } |
283 | |
284 | SDL_bool |
285 | Wayland_data_source_has_mime(SDL_WaylandDataSource *source, |
286 | const char *mime_type) |
287 | { |
288 | SDL_bool found = SDL_FALSE; |
289 | |
290 | if (source != NULL) { |
291 | found = mime_data_list_find(&source->mimes, mime_type) != NULL; |
292 | } |
293 | return found; |
294 | } |
295 | |
296 | void* |
297 | Wayland_data_source_get_data(SDL_WaylandDataSource *source, |
298 | size_t *length, const char* mime_type, |
299 | SDL_bool null_terminate) |
300 | { |
301 | SDL_MimeDataList *mime_data = NULL; |
302 | void *buffer = NULL; |
303 | *length = 0; |
304 | |
305 | if (source == NULL) { |
306 | SDL_SetError("Invalid data source" ); |
307 | } else { |
308 | mime_data = mime_data_list_find(&source->mimes, mime_type); |
309 | if (mime_data != NULL && mime_data->length > 0) { |
310 | buffer = SDL_malloc(mime_data->length); |
311 | if (buffer == NULL) { |
312 | *length = SDL_OutOfMemory(); |
313 | } else { |
314 | *length = mime_data->length; |
315 | SDL_memcpy(buffer, mime_data->data, mime_data->length); |
316 | } |
317 | } |
318 | } |
319 | |
320 | return buffer; |
321 | } |
322 | |
323 | void |
324 | Wayland_data_source_destroy(SDL_WaylandDataSource *source) |
325 | { |
326 | if (source != NULL) { |
327 | wl_data_source_destroy(source->source); |
328 | mime_data_list_free(&source->mimes); |
329 | SDL_free(source); |
330 | } |
331 | } |
332 | |
333 | void* |
334 | Wayland_data_offer_receive(SDL_WaylandDataOffer *offer, |
335 | size_t *length, const char* mime_type, |
336 | SDL_bool null_terminate) |
337 | { |
338 | SDL_WaylandDataDevice *data_device = NULL; |
339 | |
340 | int pipefd[2]; |
341 | void *buffer = NULL; |
342 | *length = 0; |
343 | |
344 | if (offer == NULL) { |
345 | SDL_SetError("Invalid data offer" ); |
346 | } else if ((data_device = offer->data_device) == NULL) { |
347 | SDL_SetError("Data device not initialized" ); |
348 | } else if (pipe2(pipefd, O_CLOEXEC|O_NONBLOCK) == -1) { |
349 | SDL_SetError("Could not read pipe" ); |
350 | } else { |
351 | wl_data_offer_receive(offer->offer, mime_type, pipefd[1]); |
352 | |
353 | /* TODO: Needs pump and flush? */ |
354 | WAYLAND_wl_display_flush(data_device->video_data->display); |
355 | |
356 | close(pipefd[1]); |
357 | |
358 | while (read_pipe(pipefd[0], &buffer, length, null_terminate) > 0); |
359 | close(pipefd[0]); |
360 | } |
361 | return buffer; |
362 | } |
363 | |
364 | int |
365 | Wayland_data_offer_add_mime(SDL_WaylandDataOffer *offer, |
366 | const char* mime_type) |
367 | { |
368 | return mime_data_list_add(&offer->mimes, mime_type, NULL, 0); |
369 | } |
370 | |
371 | |
372 | SDL_bool |
373 | Wayland_data_offer_has_mime(SDL_WaylandDataOffer *offer, |
374 | const char *mime_type) |
375 | { |
376 | SDL_bool found = SDL_FALSE; |
377 | |
378 | if (offer != NULL) { |
379 | found = mime_data_list_find(&offer->mimes, mime_type) != NULL; |
380 | } |
381 | return found; |
382 | } |
383 | |
384 | void |
385 | Wayland_data_offer_destroy(SDL_WaylandDataOffer *offer) |
386 | { |
387 | if (offer != NULL) { |
388 | wl_data_offer_destroy(offer->offer); |
389 | mime_data_list_free(&offer->mimes); |
390 | SDL_free(offer); |
391 | } |
392 | } |
393 | |
394 | int |
395 | Wayland_data_device_clear_selection(SDL_WaylandDataDevice *data_device) |
396 | { |
397 | int status = 0; |
398 | |
399 | if (data_device == NULL || data_device->data_device == NULL) { |
400 | status = SDL_SetError("Invalid Data Device" ); |
401 | } else if (data_device->selection_source != 0) { |
402 | wl_data_device_set_selection(data_device->data_device, NULL, 0); |
403 | data_device->selection_source = NULL; |
404 | } |
405 | return status; |
406 | } |
407 | |
408 | int |
409 | Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device, |
410 | SDL_WaylandDataSource *source) |
411 | { |
412 | int status = 0; |
413 | size_t num_offers = 0; |
414 | size_t index = 0; |
415 | |
416 | if (data_device == NULL) { |
417 | status = SDL_SetError("Invalid Data Device" ); |
418 | } else if (source == NULL) { |
419 | status = SDL_SetError("Invalid source" ); |
420 | } else { |
421 | SDL_MimeDataList *mime_data = NULL; |
422 | |
423 | wl_list_for_each(mime_data, &(source->mimes), link) { |
424 | wl_data_source_offer(source->source, |
425 | mime_data->mime_type); |
426 | |
427 | /* TODO - Improve system for multiple mime types to same data */ |
428 | for (index = 0; index < MIME_LIST_SIZE; ++index) { |
429 | if (strcmp(mime_conversion_list[index][1], mime_data->mime_type) == 0) { |
430 | wl_data_source_offer(source->source, |
431 | mime_conversion_list[index][0]); |
432 | } |
433 | } |
434 | /* */ |
435 | |
436 | ++num_offers; |
437 | } |
438 | |
439 | if (num_offers == 0) { |
440 | Wayland_data_device_clear_selection(data_device); |
441 | status = SDL_SetError("No mime data" ); |
442 | } else { |
443 | /* Only set if there is a valid serial if not set it later */ |
444 | if (data_device->selection_serial != 0) { |
445 | wl_data_device_set_selection(data_device->data_device, |
446 | source->source, |
447 | data_device->selection_serial); |
448 | } |
449 | data_device->selection_source = source; |
450 | } |
451 | } |
452 | |
453 | return status; |
454 | } |
455 | |
456 | int |
457 | Wayland_data_device_set_serial(SDL_WaylandDataDevice *data_device, |
458 | uint32_t serial) |
459 | { |
460 | int status = -1; |
461 | if (data_device != NULL) { |
462 | status = 0; |
463 | |
464 | /* If there was no serial and there is a pending selection set it now. */ |
465 | if (data_device->selection_serial == 0 |
466 | && data_device->selection_source != NULL) { |
467 | wl_data_device_set_selection(data_device->data_device, |
468 | data_device->selection_source->source, |
469 | serial); |
470 | } |
471 | |
472 | data_device->selection_serial = serial; |
473 | } |
474 | |
475 | return status; |
476 | } |
477 | |
478 | #endif /* SDL_VIDEO_DRIVER_WAYLAND */ |
479 | |
480 | /* vi: set ts=4 sw=4 expandtab: */ |
481 | |