| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "SDL_internal.h" |
| |
| #include "SDL_clipboard_c.h" |
| #include "SDL_sysvideo.h" |
| #include "../events/SDL_clipboardevents_c.h" |
| |
| |
| void SDL_CancelClipboardData(Uint32 sequence) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| size_t i; |
| |
| if (sequence != _this->clipboard_sequence) { |
| /* This clipboard data was already canceled */ |
| return; |
| } |
| |
| if (_this->clipboard_cleanup) { |
| _this->clipboard_cleanup(_this->clipboard_userdata); |
| } |
| |
| if (_this->clipboard_mime_types) { |
| for (i = 0; i < _this->num_clipboard_mime_types; ++i) { |
| SDL_free(_this->clipboard_mime_types[i]); |
| } |
| SDL_free(_this->clipboard_mime_types); |
| _this->clipboard_mime_types = NULL; |
| _this->num_clipboard_mime_types = 0; |
| } |
| |
| _this->clipboard_callback = NULL; |
| _this->clipboard_cleanup = NULL; |
| _this->clipboard_userdata = NULL; |
| } |
| |
| int SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| size_t i; |
| |
| if (!_this) { |
| return SDL_SetError("Video subsystem must be initialized to set clipboard text"); |
| } |
| |
| /* Parameter validation */ |
| if (!((callback && mime_types && num_mime_types > 0) || |
| (!callback && !mime_types && num_mime_types == 0))) { |
| return SDL_SetError("Invalid parameters"); |
| } |
| |
| if (!callback && !_this->clipboard_callback) { |
| /* Nothing to do, don't modify the system clipboard */ |
| return 0; |
| } |
| |
| SDL_CancelClipboardData(_this->clipboard_sequence); |
| |
| ++_this->clipboard_sequence; |
| if (!_this->clipboard_sequence) { |
| _this->clipboard_sequence = 1; |
| } |
| _this->clipboard_callback = callback; |
| _this->clipboard_cleanup = cleanup; |
| _this->clipboard_userdata = userdata; |
| |
| if (mime_types && num_mime_types > 0) { |
| size_t num_allocated = 0; |
| |
| _this->clipboard_mime_types = (char **)SDL_malloc(num_mime_types * sizeof(char *)); |
| if (_this->clipboard_mime_types) { |
| for (i = 0; i < num_mime_types; ++i) { |
| _this->clipboard_mime_types[i] = SDL_strdup(mime_types[i]); |
| if (_this->clipboard_mime_types[i]) { |
| ++num_allocated; |
| } |
| } |
| } |
| if (num_allocated < num_mime_types) { |
| SDL_ClearClipboardData(); |
| return -1; |
| } |
| _this->num_clipboard_mime_types = num_mime_types; |
| } |
| |
| if (_this->SetClipboardData) { |
| if (_this->SetClipboardData(_this) < 0) { |
| return -1; |
| } |
| } else if (_this->SetClipboardText) { |
| char *text = NULL; |
| size_t size; |
| |
| for (i = 0; i < num_mime_types; ++i) { |
| const char *mime_type = _this->clipboard_mime_types[i]; |
| if (SDL_IsTextMimeType(mime_type)) { |
| const void *data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &size); |
| if (data) { |
| text = (char *)SDL_malloc(size + 1); |
| SDL_memcpy(text, data, size); |
| text[size] = '\0'; |
| if (_this->SetClipboardText(_this, text) < 0) { |
| SDL_free(text); |
| return -1; |
| } |
| break; |
| } |
| } |
| } |
| if (text) { |
| SDL_free(text); |
| } else { |
| if (_this->SetClipboardText(_this, "") < 0) { |
| return -1; |
| } |
| } |
| } |
| |
| SDL_SendClipboardUpdate(); |
| return 0; |
| } |
| |
| int SDL_ClearClipboardData(void) |
| { |
| return SDL_SetClipboardData(NULL, NULL, NULL, NULL, 0); |
| } |
| |
| void *SDL_GetInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size) |
| { |
| void *data = NULL; |
| |
| if (_this->clipboard_callback) { |
| const void *provided_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, size); |
| if (provided_data) { |
| /* Make a copy of it for the caller and guarantee null termination */ |
| data = SDL_malloc(*size + sizeof(Uint32)); |
| if (data) { |
| SDL_memcpy(data, provided_data, *size); |
| SDL_memset((Uint8 *)data + *size, 0, sizeof(Uint32)); |
| } |
| } |
| } |
| return data; |
| } |
| |
| void *SDL_GetClipboardData(const char *mime_type, size_t *size) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| |
| if (!_this) { |
| SDL_SetError("Video subsystem must be initialized to get clipboard data"); |
| return NULL; |
| } |
| |
| if (!mime_type) { |
| SDL_InvalidParamError("mime_type"); |
| return NULL; |
| } |
| if (!size) { |
| SDL_InvalidParamError("size"); |
| return NULL; |
| } |
| |
| /* Initialize size to empty, so implementations don't have to worry about it */ |
| *size = 0; |
| |
| if (_this->GetClipboardData) { |
| return _this->GetClipboardData(_this, mime_type, size); |
| } else if (_this->GetClipboardText && SDL_IsTextMimeType(mime_type)) { |
| void *data = _this->GetClipboardText(_this); |
| if (data && *(char *)data == '\0') { |
| SDL_free(data); |
| data = NULL; |
| } |
| return data; |
| } else { |
| return SDL_GetInternalClipboardData(_this, mime_type, size); |
| } |
| } |
| |
| SDL_bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type) |
| { |
| size_t i; |
| |
| for (i = 0; i < _this->num_clipboard_mime_types; ++i) { |
| if (SDL_strcmp(mime_type, _this->clipboard_mime_types[i]) == 0) { |
| return SDL_TRUE; |
| } |
| } |
| return SDL_FALSE; |
| } |
| |
| SDL_bool SDL_HasClipboardData(const char *mime_type) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| |
| if (!_this) { |
| SDL_SetError("Video subsystem must be initialized to check clipboard data"); |
| return SDL_FALSE; |
| } |
| |
| if (!mime_type) { |
| SDL_InvalidParamError("mime_type"); |
| return SDL_FALSE; |
| } |
| |
| if (_this->HasClipboardData) { |
| return _this->HasClipboardData(_this, mime_type); |
| } else if (_this->HasClipboardText && SDL_IsTextMimeType(mime_type)) { |
| return _this->HasClipboardText(_this); |
| } else { |
| return SDL_HasInternalClipboardData(_this, mime_type); |
| } |
| } |
| |
| /* Clipboard text */ |
| |
| SDL_bool SDL_IsTextMimeType(const char *mime_type) |
| { |
| return (SDL_strncmp(mime_type, "text", 4) == 0); |
| } |
| |
| static const char **SDL_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) |
| { |
| if (_this->GetTextMimeTypes) { |
| return _this->GetTextMimeTypes(_this, num_mime_types); |
| } else { |
| static const char *text_mime_types[] = { |
| "text/plain;charset=utf-8" |
| }; |
| |
| *num_mime_types = SDL_arraysize(text_mime_types); |
| return text_mime_types; |
| } |
| } |
| |
| const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size) |
| { |
| char *text = (char *)userdata; |
| if (text) { |
| *size = SDL_strlen(text); |
| } else { |
| *size = 0; |
| } |
| return text; |
| } |
| |
| int SDL_SetClipboardText(const char *text) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| size_t num_mime_types; |
| const char **text_mime_types; |
| |
| if (!_this) { |
| return SDL_SetError("Video subsystem must be initialized to set clipboard text"); |
| } |
| |
| if (text && *text) { |
| text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types); |
| |
| return SDL_SetClipboardData(SDL_ClipboardTextCallback, SDL_free, SDL_strdup(text), text_mime_types, num_mime_types); |
| } |
| return SDL_ClearClipboardData(); |
| } |
| |
| char *SDL_GetClipboardText(void) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| size_t i, num_mime_types; |
| const char **text_mime_types; |
| size_t length; |
| char *text = NULL; |
| |
| if (!_this) { |
| SDL_SetError("Video subsystem must be initialized to get clipboard text"); |
| return SDL_strdup(""); |
| } |
| |
| text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types); |
| for (i = 0; i < num_mime_types; ++i) { |
| text = (char *)SDL_GetClipboardData(text_mime_types[i], &length); |
| if (text) { |
| break; |
| } |
| } |
| |
| if (!text) { |
| text = SDL_strdup(""); |
| } |
| return text; |
| } |
| |
| SDL_bool SDL_HasClipboardText(void) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| size_t i, num_mime_types; |
| const char **text_mime_types; |
| |
| if (!_this) { |
| SDL_SetError("Video subsystem must be initialized to check clipboard text"); |
| return SDL_FALSE; |
| } |
| |
| text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types); |
| for (i = 0; i < num_mime_types; ++i) { |
| if (SDL_HasClipboardData(text_mime_types[i])) { |
| return SDL_TRUE; |
| } |
| } |
| return SDL_FALSE; |
| } |
| |
| /* Primary selection text */ |
| |
| int SDL_SetPrimarySelectionText(const char *text) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| |
| if (!_this) { |
| return SDL_SetError("Video subsystem must be initialized to set primary selection text"); |
| } |
| |
| if (!text) { |
| text = ""; |
| } |
| if (_this->SetPrimarySelectionText) { |
| if (_this->SetPrimarySelectionText(_this, text) < 0) { |
| return -1; |
| } |
| } else { |
| SDL_free(_this->primary_selection_text); |
| _this->primary_selection_text = SDL_strdup(text); |
| } |
| |
| SDL_SendClipboardUpdate(); |
| return 0; |
| } |
| |
| char *SDL_GetPrimarySelectionText(void) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| |
| if (!_this) { |
| SDL_SetError("Video subsystem must be initialized to get primary selection text"); |
| return SDL_strdup(""); |
| } |
| |
| if (_this->GetPrimarySelectionText) { |
| return _this->GetPrimarySelectionText(_this); |
| } else { |
| const char *text = _this->primary_selection_text; |
| if (!text) { |
| text = ""; |
| } |
| return SDL_strdup(text); |
| } |
| } |
| |
| SDL_bool SDL_HasPrimarySelectionText(void) |
| { |
| SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| |
| if (!_this) { |
| SDL_SetError("Video subsystem must be initialized to check primary selection text"); |
| return SDL_FALSE; |
| } |
| |
| if (_this->HasPrimarySelectionText) { |
| return _this->HasPrimarySelectionText(_this); |
| } else { |
| if (_this->primary_selection_text && _this->primary_selection_text[0] != '\0') { |
| return SDL_TRUE; |
| } else { |
| return SDL_FALSE; |
| } |
| } |
| } |
| |