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