summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/wayland/SDL_waylanddatamanager.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/video/wayland/SDL_waylanddatamanager.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/wayland/SDL_waylanddatamanager.c')
-rw-r--r--contrib/SDL-3.2.8/src/video/wayland/SDL_waylanddatamanager.c619
1 files changed, 619 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/wayland/SDL_waylanddatamanager.c b/contrib/SDL-3.2.8/src/video/wayland/SDL_waylanddatamanager.c
new file mode 100644
index 0000000..6f5b32b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/wayland/SDL_waylanddatamanager.c
@@ -0,0 +1,619 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#ifdef SDL_VIDEO_DRIVER_WAYLAND
25
26#include <fcntl.h>
27#include <unistd.h>
28#include <limits.h>
29#include <signal.h>
30
31#include "../../core/unix/SDL_poll.h"
32#include "../../events/SDL_events_c.h"
33#include "../SDL_clipboard_c.h"
34
35#include "SDL_waylandvideo.h"
36#include "SDL_waylanddatamanager.h"
37#include "primary-selection-unstable-v1-client-protocol.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_TIMEOUT_NS SDL_MS_TO_NS(14)
43
44static ssize_t write_pipe(int fd, const void *buffer, size_t total_length, size_t *pos)
45{
46 int ready = 0;
47 ssize_t bytes_written = 0;
48 ssize_t length = total_length - *pos;
49
50 sigset_t sig_set;
51 sigset_t old_sig_set;
52 struct timespec zerotime = { 0 };
53
54 ready = SDL_IOReady(fd, SDL_IOR_WRITE, PIPE_TIMEOUT_NS);
55
56 sigemptyset(&sig_set);
57 sigaddset(&sig_set, SIGPIPE);
58
59#ifdef SDL_THREADS_DISABLED
60 sigprocmask(SIG_BLOCK, &sig_set, &old_sig_set);
61#else
62 pthread_sigmask(SIG_BLOCK, &sig_set, &old_sig_set);
63#endif
64
65 if (ready == 0) {
66 bytes_written = SDL_SetError("Pipe timeout");
67 } else if (ready < 0) {
68 bytes_written = SDL_SetError("Pipe select error");
69 } else {
70 if (length > 0) {
71 bytes_written = write(fd, (Uint8 *)buffer + *pos, SDL_min(length, PIPE_BUF));
72 }
73
74 if (bytes_written > 0) {
75 *pos += bytes_written;
76 }
77 }
78
79 sigtimedwait(&sig_set, 0, &zerotime);
80
81#ifdef SDL_THREADS_DISABLED
82 sigprocmask(SIG_SETMASK, &old_sig_set, NULL);
83#else
84 pthread_sigmask(SIG_SETMASK, &old_sig_set, NULL);
85#endif
86
87 return bytes_written;
88}
89
90static ssize_t read_pipe(int fd, void **buffer, size_t *total_length)
91{
92 int ready = 0;
93 void *output_buffer = NULL;
94 char temp[PIPE_BUF];
95 size_t new_buffer_length = 0;
96 ssize_t bytes_read = 0;
97 size_t pos = 0;
98
99 ready = SDL_IOReady(fd, SDL_IOR_READ, PIPE_TIMEOUT_NS);
100
101 if (ready == 0) {
102 bytes_read = SDL_SetError("Pipe timeout");
103 } else if (ready < 0) {
104 bytes_read = SDL_SetError("Pipe select error");
105 } else {
106 bytes_read = read(fd, temp, sizeof(temp));
107 }
108
109 if (bytes_read > 0) {
110 pos = *total_length;
111 *total_length += bytes_read;
112
113 new_buffer_length = *total_length + sizeof(Uint32);
114
115 if (!*buffer) {
116 output_buffer = SDL_malloc(new_buffer_length);
117 } else {
118 output_buffer = SDL_realloc(*buffer, new_buffer_length);
119 }
120
121 if (!output_buffer) {
122 bytes_read = -1;
123 } else {
124 SDL_memcpy((Uint8 *)output_buffer + pos, temp, bytes_read);
125 SDL_memset((Uint8 *)output_buffer + (new_buffer_length - sizeof(Uint32)), 0, sizeof(Uint32));
126
127 *buffer = output_buffer;
128 }
129 }
130
131 return bytes_read;
132}
133
134static SDL_MimeDataList *mime_data_list_find(struct wl_list *list,
135 const char *mime_type)
136{
137 SDL_MimeDataList *found = NULL;
138
139 SDL_MimeDataList *mime_list = NULL;
140 wl_list_for_each (mime_list, list, link) {
141 if (SDL_strcmp(mime_list->mime_type, mime_type) == 0) {
142 found = mime_list;
143 break;
144 }
145 }
146 return found;
147}
148
149static bool mime_data_list_add(struct wl_list *list,
150 const char *mime_type,
151 const void *buffer, size_t length)
152{
153 bool result = true;
154 size_t mime_type_length = 0;
155 SDL_MimeDataList *mime_data = NULL;
156 void *internal_buffer = NULL;
157
158 if (buffer) {
159 internal_buffer = SDL_malloc(length);
160 if (!internal_buffer) {
161 return false;
162 }
163 SDL_memcpy(internal_buffer, buffer, length);
164 }
165
166 mime_data = mime_data_list_find(list, mime_type);
167
168 if (!mime_data) {
169 mime_data = SDL_calloc(1, sizeof(*mime_data));
170 if (!mime_data) {
171 result = false;
172 } else {
173 WAYLAND_wl_list_insert(list, &(mime_data->link));
174
175 mime_type_length = SDL_strlen(mime_type) + 1;
176 mime_data->mime_type = SDL_malloc(mime_type_length);
177 if (!mime_data->mime_type) {
178 result = false;
179 } else {
180 SDL_memcpy(mime_data->mime_type, mime_type, mime_type_length);
181 }
182 }
183 }
184
185 if (mime_data && buffer && length > 0) {
186 if (mime_data->data) {
187 SDL_free(mime_data->data);
188 }
189 mime_data->data = internal_buffer;
190 mime_data->length = length;
191 } else {
192 SDL_free(internal_buffer);
193 }
194
195 return result;
196}
197
198static void mime_data_list_free(struct wl_list *list)
199{
200 SDL_MimeDataList *mime_data = NULL;
201 SDL_MimeDataList *next = NULL;
202
203 wl_list_for_each_safe (mime_data, next, list, link) {
204 if (mime_data->data) {
205 SDL_free(mime_data->data);
206 }
207 if (mime_data->mime_type) {
208 SDL_free(mime_data->mime_type);
209 }
210 SDL_free(mime_data);
211 }
212}
213
214static size_t Wayland_send_data(const void *data, size_t length, int fd)
215{
216 size_t result = 0;
217
218 if (length > 0 && data) {
219 while (write_pipe(fd, data, length, &result) > 0) {
220 // Just keep spinning
221 }
222 }
223 close(fd);
224
225 return result;
226}
227
228ssize_t Wayland_data_source_send(SDL_WaylandDataSource *source, const char *mime_type, int fd)
229{
230 const void *data = NULL;
231 size_t length = 0;
232
233 if (source->callback) {
234 data = source->callback(source->userdata.data, mime_type, &length);
235 }
236
237 return Wayland_send_data(data, length, fd);
238}
239
240ssize_t Wayland_primary_selection_source_send(SDL_WaylandPrimarySelectionSource *source, const char *mime_type, int fd)
241{
242 const void *data = NULL;
243 size_t length = 0;
244
245 if (source->callback) {
246 data = source->callback(source->userdata.data, mime_type, &length);
247 }
248
249 return Wayland_send_data(data, length, fd);
250}
251
252void Wayland_data_source_set_callback(SDL_WaylandDataSource *source,
253 SDL_ClipboardDataCallback callback,
254 void *userdata,
255 Uint32 sequence)
256{
257 if (source) {
258 source->callback = callback;
259 source->userdata.sequence = sequence;
260 source->userdata.data = userdata;
261 }
262}
263
264void Wayland_primary_selection_source_set_callback(SDL_WaylandPrimarySelectionSource *source,
265 SDL_ClipboardDataCallback callback,
266 void *userdata)
267{
268 if (source) {
269 source->callback = callback;
270 source->userdata.sequence = 0;
271 source->userdata.data = userdata;
272 }
273}
274
275static void *Wayland_clone_data_buffer(const void *buffer, const size_t *len)
276{
277 void *clone = NULL;
278 if (*len > 0 && buffer) {
279 clone = SDL_malloc((*len)+sizeof(Uint32));
280 if (clone) {
281 SDL_memcpy(clone, buffer, *len);
282 SDL_memset((Uint8 *)clone + *len, 0, sizeof(Uint32));
283 }
284 }
285 return clone;
286}
287
288void *Wayland_data_source_get_data(SDL_WaylandDataSource *source,
289 const char *mime_type, size_t *length)
290{
291 void *buffer = NULL;
292 const void *internal_buffer;
293 *length = 0;
294
295 if (!source) {
296 SDL_SetError("Invalid data source");
297 } else if (source->callback) {
298 internal_buffer = source->callback(source->userdata.data, mime_type, length);
299 buffer = Wayland_clone_data_buffer(internal_buffer, length);
300 }
301
302 return buffer;
303}
304
305void *Wayland_primary_selection_source_get_data(SDL_WaylandPrimarySelectionSource *source,
306 const char *mime_type, size_t *length)
307{
308 void *buffer = NULL;
309 const void *internal_buffer;
310 *length = 0;
311
312 if (!source) {
313 SDL_SetError("Invalid primary selection source");
314 } else if (source->callback) {
315 internal_buffer = source->callback(source->userdata.data, mime_type, length);
316 buffer = Wayland_clone_data_buffer(internal_buffer, length);
317 }
318
319 return buffer;
320}
321
322void Wayland_data_source_destroy(SDL_WaylandDataSource *source)
323{
324 if (source) {
325 SDL_WaylandDataDevice *data_device = (SDL_WaylandDataDevice *)source->data_device;
326 if (data_device && (data_device->selection_source == source)) {
327 data_device->selection_source = NULL;
328 }
329 wl_data_source_destroy(source->source);
330 if (source->userdata.sequence) {
331 SDL_CancelClipboardData(source->userdata.sequence);
332 } else {
333 SDL_free(source->userdata.data);
334 }
335 SDL_free(source);
336 }
337}
338
339void Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelectionSource *source)
340{
341 if (source) {
342 SDL_WaylandPrimarySelectionDevice *primary_selection_device = (SDL_WaylandPrimarySelectionDevice *)source->primary_selection_device;
343 if (primary_selection_device && (primary_selection_device->selection_source == source)) {
344 primary_selection_device->selection_source = NULL;
345 }
346 zwp_primary_selection_source_v1_destroy(source->source);
347 if (source->userdata.sequence == 0) {
348 SDL_free(source->userdata.data);
349 }
350 SDL_free(source);
351 }
352}
353
354void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
355 const char *mime_type, size_t *length)
356{
357 SDL_WaylandDataDevice *data_device = NULL;
358
359 int pipefd[2];
360 void *buffer = NULL;
361 *length = 0;
362
363 if (!offer) {
364 SDL_SetError("Invalid data offer");
365 return NULL;
366 }
367 data_device = offer->data_device;
368 if (!data_device) {
369 SDL_SetError("Data device not initialized");
370 } else if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) {
371 SDL_SetError("Could not read pipe");
372 } else {
373 wl_data_offer_receive(offer->offer, mime_type, pipefd[1]);
374
375 // TODO: Needs pump and flush?
376 WAYLAND_wl_display_flush(data_device->video_data->display);
377
378 close(pipefd[1]);
379
380 while (read_pipe(pipefd[0], &buffer, length) > 0) {
381 }
382 close(pipefd[0]);
383 }
384 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
385 ". In Wayland_data_offer_receive for '%s', buffer (%zu) at %p",
386 mime_type, *length, buffer);
387 return buffer;
388}
389
390void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer,
391 const char *mime_type, size_t *length)
392{
393 SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
394
395 int pipefd[2];
396 void *buffer = NULL;
397 *length = 0;
398
399 if (!offer) {
400 SDL_SetError("Invalid data offer");
401 return NULL;
402 }
403 primary_selection_device = offer->primary_selection_device;
404 if (!primary_selection_device) {
405 SDL_SetError("Primary selection device not initialized");
406 } else if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == -1) {
407 SDL_SetError("Could not read pipe");
408 } else {
409 zwp_primary_selection_offer_v1_receive(offer->offer, mime_type, pipefd[1]);
410
411 // TODO: Needs pump and flush?
412 WAYLAND_wl_display_flush(primary_selection_device->video_data->display);
413
414 close(pipefd[1]);
415
416 while (read_pipe(pipefd[0], &buffer, length) > 0) {
417 }
418 close(pipefd[0]);
419 }
420 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
421 ". In Wayland_primary_selection_offer_receive for '%s', buffer (%zu) at %p",
422 mime_type, *length, buffer);
423 return buffer;
424}
425
426bool Wayland_data_offer_add_mime(SDL_WaylandDataOffer *offer,
427 const char *mime_type)
428{
429 return mime_data_list_add(&offer->mimes, mime_type, NULL, 0);
430}
431
432bool Wayland_primary_selection_offer_add_mime(SDL_WaylandPrimarySelectionOffer *offer,
433 const char *mime_type)
434{
435 return mime_data_list_add(&offer->mimes, mime_type, NULL, 0);
436}
437
438bool Wayland_data_offer_has_mime(SDL_WaylandDataOffer *offer,
439 const char *mime_type)
440{
441 bool found = false;
442
443 if (offer) {
444 found = mime_data_list_find(&offer->mimes, mime_type) != NULL;
445 }
446 return found;
447}
448
449bool Wayland_primary_selection_offer_has_mime(SDL_WaylandPrimarySelectionOffer *offer,
450 const char *mime_type)
451{
452 bool found = false;
453
454 if (offer) {
455 found = mime_data_list_find(&offer->mimes, mime_type) != NULL;
456 }
457 return found;
458}
459
460void Wayland_data_offer_destroy(SDL_WaylandDataOffer *offer)
461{
462 if (offer) {
463 wl_data_offer_destroy(offer->offer);
464 mime_data_list_free(&offer->mimes);
465 SDL_free(offer);
466 }
467}
468
469void Wayland_primary_selection_offer_destroy(SDL_WaylandPrimarySelectionOffer *offer)
470{
471 if (offer) {
472 zwp_primary_selection_offer_v1_destroy(offer->offer);
473 mime_data_list_free(&offer->mimes);
474 SDL_free(offer);
475 }
476}
477
478bool Wayland_data_device_clear_selection(SDL_WaylandDataDevice *data_device)
479{
480 bool result = true;
481
482 if (!data_device || !data_device->data_device) {
483 result = SDL_SetError("Invalid Data Device");
484 } else if (data_device->selection_source) {
485 wl_data_device_set_selection(data_device->data_device, NULL, 0);
486 Wayland_data_source_destroy(data_device->selection_source);
487 data_device->selection_source = NULL;
488 }
489 return result;
490}
491
492bool Wayland_primary_selection_device_clear_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device)
493{
494 bool result = true;
495
496 if (!primary_selection_device || !primary_selection_device->primary_selection_device) {
497 result = SDL_SetError("Invalid Primary Selection Device");
498 } else if (primary_selection_device->selection_source) {
499 zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
500 NULL, 0);
501 Wayland_primary_selection_source_destroy(primary_selection_device->selection_source);
502 primary_selection_device->selection_source = NULL;
503 }
504 return result;
505}
506
507bool Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
508 SDL_WaylandDataSource *source,
509 const char **mime_types,
510 size_t mime_count)
511{
512 bool result = true;
513
514 if (!data_device) {
515 result = SDL_SetError("Invalid Data Device");
516 } else if (!source) {
517 result = SDL_SetError("Invalid source");
518 } else {
519 size_t index = 0;
520 const char *mime_type;
521
522 for (index = 0; index < mime_count; ++index) {
523 mime_type = mime_types[index];
524 wl_data_source_offer(source->source,
525 mime_type);
526 }
527
528 if (index == 0) {
529 Wayland_data_device_clear_selection(data_device);
530 result = SDL_SetError("No mime data");
531 } else {
532 // Only set if there is a valid serial if not set it later
533 if (data_device->selection_serial != 0) {
534 wl_data_device_set_selection(data_device->data_device,
535 source->source,
536 data_device->selection_serial);
537 }
538 if (data_device->selection_source) {
539 Wayland_data_source_destroy(data_device->selection_source);
540 }
541 data_device->selection_source = source;
542 source->data_device = data_device;
543 }
544 }
545
546 return result;
547}
548
549bool Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device,
550 SDL_WaylandPrimarySelectionSource *source,
551 const char **mime_types,
552 size_t mime_count)
553{
554 bool result = true;
555
556 if (!primary_selection_device) {
557 result = SDL_SetError("Invalid Primary Selection Device");
558 } else if (!source) {
559 result = SDL_SetError("Invalid source");
560 } else {
561 size_t index = 0;
562 const char *mime_type = mime_types[index];
563
564 for (index = 0; index < mime_count; ++index) {
565 mime_type = mime_types[index];
566 zwp_primary_selection_source_v1_offer(source->source, mime_type);
567 }
568
569 if (index == 0) {
570 Wayland_primary_selection_device_clear_selection(primary_selection_device);
571 result = SDL_SetError("No mime data");
572 } else {
573 // Only set if there is a valid serial if not set it later
574 if (primary_selection_device->selection_serial != 0) {
575 zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
576 source->source,
577 primary_selection_device->selection_serial);
578 }
579 if (primary_selection_device->selection_source) {
580 Wayland_primary_selection_source_destroy(primary_selection_device->selection_source);
581 }
582 primary_selection_device->selection_source = source;
583 source->primary_selection_device = primary_selection_device;
584 }
585 }
586
587 return result;
588}
589
590void Wayland_data_device_set_serial(SDL_WaylandDataDevice *data_device, uint32_t serial)
591{
592 if (data_device) {
593 // If there was no serial and there is a pending selection set it now.
594 if (data_device->selection_serial == 0 && data_device->selection_source) {
595 wl_data_device_set_selection(data_device->data_device,
596 data_device->selection_source->source,
597 data_device->selection_serial);
598 }
599
600 data_device->selection_serial = serial;
601 }
602}
603
604void Wayland_primary_selection_device_set_serial(SDL_WaylandPrimarySelectionDevice *primary_selection_device,
605 uint32_t serial)
606{
607 if (primary_selection_device) {
608 // If there was no serial and there is a pending selection set it now.
609 if (primary_selection_device->selection_serial == 0 && primary_selection_device->selection_source) {
610 zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
611 primary_selection_device->selection_source->source,
612 primary_selection_device->selection_serial);
613 }
614
615 primary_selection_device->selection_serial = serial;
616 }
617}
618
619#endif // SDL_VIDEO_DRIVER_WAYLAND