Atlas - SDL_clipboard.c
Home / ext / SDL / src / video Lines: 1 | Size: 13482 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#include "SDL_internal.h" 22 23#include "SDL_clipboard_c.h" 24#include "SDL_sysvideo.h" 25#include "../events/SDL_events_c.h" 26#include "../events/SDL_clipboardevents_c.h" 27 28void SDL_FreeClipboardMimeTypes(SDL_VideoDevice *_this) 29{ 30 if (_this->clipboard_mime_types) { 31 for (size_t i = 0; i < _this->num_clipboard_mime_types; ++i) { 32 SDL_free(_this->clipboard_mime_types[i]); 33 } 34 SDL_free(_this->clipboard_mime_types); 35 _this->clipboard_mime_types = NULL; 36 _this->num_clipboard_mime_types = 0; 37 } 38} 39 40 41void SDL_CancelClipboardData(Uint32 sequence) 42{ 43 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 44 45 if (!_this) { 46 return; 47 } 48 49 if (sequence && sequence != _this->clipboard_sequence) { 50 // This clipboard data was already canceled 51 return; 52 } 53 54 if (_this->clipboard_cleanup) { 55 _this->clipboard_cleanup(_this->clipboard_userdata); 56 } 57 58 SDL_FreeClipboardMimeTypes(_this); 59 60 _this->clipboard_callback = NULL; 61 _this->clipboard_cleanup = NULL; 62 _this->clipboard_userdata = NULL; 63} 64 65bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types) 66{ 67 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 68 69 if (!_this) { 70 return SDL_UninitializedVideo(); 71 } 72 73 SDL_FreeClipboardMimeTypes(_this); 74 75 if (mime_types && num_mime_types > 0) { 76 size_t num_allocated = 0; 77 78 _this->clipboard_mime_types = (char **)SDL_malloc(num_mime_types * sizeof(char *)); 79 if (_this->clipboard_mime_types) { 80 for (size_t i = 0; i < num_mime_types; ++i) { 81 _this->clipboard_mime_types[i] = SDL_strdup(mime_types[i]); 82 if (_this->clipboard_mime_types[i]) { 83 ++num_allocated; 84 } 85 } 86 } 87 if (num_allocated < num_mime_types) { 88 SDL_FreeClipboardMimeTypes(_this); 89 return false; 90 } 91 _this->num_clipboard_mime_types = num_mime_types; 92 } 93 return true; 94} 95 96bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types) 97{ 98 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 99 100 if (!_this) { 101 return SDL_UninitializedVideo(); 102 } 103 104 // Parameter validation 105 if (!((callback && mime_types && num_mime_types > 0) || 106 (!callback && !mime_types && num_mime_types == 0))) { 107 return SDL_SetError("Invalid parameters"); 108 } 109 110 SDL_CancelClipboardData(0); 111 112 ++_this->clipboard_sequence; 113 if (!_this->clipboard_sequence) { 114 _this->clipboard_sequence = 1; 115 } 116 _this->clipboard_callback = callback; 117 _this->clipboard_cleanup = cleanup; 118 _this->clipboard_userdata = userdata; 119 120 if (!SDL_SaveClipboardMimeTypes(mime_types, num_mime_types)) { 121 SDL_ClearClipboardData(); 122 return false; 123 } 124 125 if (_this->SetClipboardData) { 126 if (!_this->SetClipboardData(_this)) { 127 return false; 128 } 129 } else if (_this->SetClipboardText) { 130 char *text = NULL; 131 size_t size; 132 133 for (size_t i = 0; i < num_mime_types; ++i) { 134 const char *mime_type = _this->clipboard_mime_types[i]; 135 if (SDL_IsTextMimeType(mime_type)) { 136 const void *data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &size); 137 if (data) { 138 text = (char *)SDL_malloc(size + 1); 139 SDL_memcpy(text, data, size); 140 text[size] = '\0'; 141 if (!_this->SetClipboardText(_this, text)) { 142 SDL_free(text); 143 return false; 144 } 145 break; 146 } 147 } 148 } 149 if (text) { 150 SDL_free(text); 151 } else { 152 if (!_this->SetClipboardText(_this, "")) { 153 return false; 154 } 155 } 156 } 157 158 char **mime_types_copy = SDL_CopyClipboardMimeTypes(mime_types, num_mime_types, true); 159 if (!mime_types_copy) { 160 return SDL_SetError("unable to copy current mime types"); 161 } 162 SDL_SendClipboardUpdate(true, mime_types_copy, num_mime_types); 163 return true; 164} 165 166bool SDL_ClearClipboardData(void) 167{ 168 return SDL_SetClipboardData(NULL, NULL, NULL, NULL, 0); 169} 170 171void *SDL_GetInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size) 172{ 173 void *data = NULL; 174 175 if (_this->clipboard_callback) { 176 if (!SDL_HasInternalClipboardData(_this, mime_type)) { 177 // The callback hasn't advertised that it can handle this mime type 178 return NULL; 179 } 180 181 const void *provided_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, size); 182 if (provided_data) { 183 // Make a copy of it for the caller and guarantee null termination 184 data = SDL_malloc(*size + sizeof(Uint32)); 185 if (data) { 186 SDL_memcpy(data, provided_data, *size); 187 SDL_memset((Uint8 *)data + *size, 0, sizeof(Uint32)); 188 } else { 189 *size = 0; 190 } 191 } 192 } 193 return data; 194} 195 196void *SDL_GetClipboardData(const char *mime_type, size_t *size) 197{ 198 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 199 size_t unused; 200 201 if (!_this) { 202 SDL_UninitializedVideo(); 203 return NULL; 204 } 205 206 CHECK_PARAM(!mime_type) { 207 SDL_InvalidParamError("mime_type"); 208 return NULL; 209 } 210 if (!size) { 211 size = &unused; 212 } 213 214 // Initialize size to empty, so implementations don't have to worry about it 215 *size = 0; 216 217 if (_this->GetClipboardData) { 218 return _this->GetClipboardData(_this, mime_type, size); 219 } else if (_this->GetClipboardText && SDL_IsTextMimeType(mime_type)) { 220 char *text = _this->GetClipboardText(_this); 221 if (text) { 222 if (*text == '\0') { 223 SDL_free(text); 224 text = NULL; 225 } else { 226 *size = SDL_strlen(text); 227 } 228 } 229 return text; 230 } else { 231 return SDL_GetInternalClipboardData(_this, mime_type, size); 232 } 233} 234 235bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type) 236{ 237 size_t i; 238 239 for (i = 0; i < _this->num_clipboard_mime_types; ++i) { 240 if (SDL_strcmp(mime_type, _this->clipboard_mime_types[i]) == 0) { 241 return true; 242 } 243 } 244 return false; 245} 246 247bool SDL_HasClipboardData(const char *mime_type) 248{ 249 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 250 251 if (!_this) { 252 return SDL_UninitializedVideo(); 253 } 254 255 CHECK_PARAM(!mime_type) { 256 return SDL_InvalidParamError("mime_type"); 257 } 258 259 if (_this->HasClipboardData) { 260 return _this->HasClipboardData(_this, mime_type); 261 } else if (_this->HasClipboardText && SDL_IsTextMimeType(mime_type)) { 262 return _this->HasClipboardText(_this); 263 } else { 264 return SDL_HasInternalClipboardData(_this, mime_type); 265 } 266} 267 268char **SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary) 269{ 270 size_t allocSize = sizeof(char *); 271 for (size_t i = 0; i < num_mime_types; i++) { 272 allocSize += sizeof(char *) + SDL_strlen(clipboard_mime_types[i]) + 1; 273 } 274 275 char *ret; 276 if (temporary) 277 ret = (char *)SDL_AllocateTemporaryMemory(allocSize); 278 else 279 ret = (char *)SDL_malloc(allocSize); 280 if (!ret) { 281 return NULL; 282 } 283 284 char **result = (char **)ret; 285 ret += sizeof(char *) * (num_mime_types + 1); 286 287 for (size_t i = 0; i < num_mime_types; i++) { 288 result[i] = ret; 289 290 const char *mime_type = clipboard_mime_types[i]; 291 // Copy the whole string including the terminating null char 292 char c; 293 do { 294 c = *ret++ = *mime_type++; 295 } while (c != '\0'); 296 } 297 result[num_mime_types] = NULL; 298 299 return result; 300 301} 302 303char **SDL_GetClipboardMimeTypes(size_t *num_mime_types) 304{ 305 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 306 307 if (num_mime_types) { 308 *num_mime_types = 0; 309 } 310 311 if (!_this) { 312 SDL_UninitializedVideo(); 313 return NULL; 314 } 315 316 if (num_mime_types) { 317 *num_mime_types = _this->num_clipboard_mime_types; 318 } 319 return SDL_CopyClipboardMimeTypes((const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, false); 320} 321 322// Clipboard text 323 324bool SDL_IsTextMimeType(const char *mime_type) 325{ 326 return (SDL_strncmp(mime_type, "text", 4) == 0); 327} 328 329static const char **SDL_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) 330{ 331 if (_this->GetTextMimeTypes) { 332 return _this->GetTextMimeTypes(_this, num_mime_types); 333 } else { 334 static const char *text_mime_types[] = { 335 "text/plain;charset=utf-8" 336 }; 337 338 *num_mime_types = SDL_arraysize(text_mime_types); 339 return text_mime_types; 340 } 341} 342 343const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size) 344{ 345 char *text = (char *)userdata; 346 if (text) { 347 *size = SDL_strlen(text); 348 } else { 349 *size = 0; 350 } 351 return text; 352} 353 354bool SDL_SetClipboardText(const char *text) 355{ 356 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 357 size_t num_mime_types; 358 const char **text_mime_types; 359 360 if (!_this) { 361 return SDL_UninitializedVideo(); 362 } 363 364 if (text && *text) { 365 text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types); 366 367 return SDL_SetClipboardData(SDL_ClipboardTextCallback, SDL_free, SDL_strdup(text), text_mime_types, num_mime_types); 368 } 369 return SDL_ClearClipboardData(); 370} 371 372char *SDL_GetClipboardText(void) 373{ 374 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 375 size_t i, num_mime_types; 376 const char **text_mime_types; 377 size_t length; 378 char *text = NULL; 379 380 if (!_this) { 381 SDL_UninitializedVideo(); 382 return SDL_strdup(""); 383 } 384 385 text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types); 386 for (i = 0; i < num_mime_types; ++i) { 387 void *clipdata = SDL_GetClipboardData(text_mime_types[i], &length); 388 if (clipdata) { 389 text = (char *)clipdata; 390 break; 391 } 392 } 393 394 if (!text) { 395 text = SDL_strdup(""); 396 } 397 return text; 398} 399 400bool SDL_HasClipboardText(void) 401{ 402 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 403 size_t i, num_mime_types; 404 const char **text_mime_types; 405 406 if (!_this) { 407 return SDL_UninitializedVideo(); 408 } 409 410 text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types); 411 for (i = 0; i < num_mime_types; ++i) { 412 if (SDL_HasClipboardData(text_mime_types[i])) { 413 return true; 414 } 415 } 416 return false; 417} 418 419// Primary selection text 420 421bool SDL_SetPrimarySelectionText(const char *text) 422{ 423 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 424 425 if (!_this) { 426 return SDL_UninitializedVideo(); 427 } 428 429 if (!text) { 430 text = ""; 431 } 432 if (_this->SetPrimarySelectionText) { 433 if (!_this->SetPrimarySelectionText(_this, text)) { 434 return false; 435 } 436 } else { 437 SDL_free(_this->primary_selection_text); 438 _this->primary_selection_text = SDL_strdup(text); 439 } 440 441 char **mime_types = SDL_CopyClipboardMimeTypes((const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, true); 442 if (!mime_types) { 443 return SDL_SetError("unable to copy current mime types"); 444 } 445 SDL_SendClipboardUpdate(true, mime_types, _this->num_clipboard_mime_types); 446 return true; 447} 448 449char *SDL_GetPrimarySelectionText(void) 450{ 451 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 452 453 if (!_this) { 454 SDL_UninitializedVideo(); 455 return SDL_strdup(""); 456 } 457 458 if (_this->GetPrimarySelectionText) { 459 return _this->GetPrimarySelectionText(_this); 460 } else { 461 const char *text = _this->primary_selection_text; 462 if (!text) { 463 text = ""; 464 } 465 return SDL_strdup(text); 466 } 467} 468 469bool SDL_HasPrimarySelectionText(void) 470{ 471 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 472 473 if (!_this) { 474 return SDL_UninitializedVideo(); 475 } 476 477 if (_this->HasPrimarySelectionText) { 478 return _this->HasPrimarySelectionText(_this); 479 } else { 480 if (_this->primary_selection_text && _this->primary_selection_text[0] != '\0') { 481 return true; 482 } else { 483 return false; 484 } 485 } 486} 487 488[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.