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