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