SDL_clipboard.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #include "SDL_clipboard_c.h"
  20. #include "SDL_sysvideo.h"
  21. #include "../events/SDL_clipboardevents_c.h"
  22. void SDL_CancelClipboardData(Uint32 sequence)
  23. {
  24. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  25. size_t i;
  26. if (sequence != _this->clipboard_sequence) {
  27. // This clipboard data was already canceled
  28. return;
  29. }
  30. if (_this->clipboard_cleanup) {
  31. _this->clipboard_cleanup(_this->clipboard_userdata);
  32. }
  33. if (_this->clipboard_mime_types) {
  34. for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
  35. SDL_free(_this->clipboard_mime_types[i]);
  36. }
  37. SDL_free(_this->clipboard_mime_types);
  38. _this->clipboard_mime_types = NULL;
  39. _this->num_clipboard_mime_types = 0;
  40. }
  41. _this->clipboard_callback = NULL;
  42. _this->clipboard_cleanup = NULL;
  43. _this->clipboard_userdata = NULL;
  44. }
  45. SDL_bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types)
  46. {
  47. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  48. size_t i;
  49. if (!_this) {
  50. return SDL_SetError("Video subsystem must be initialized to set clipboard text");
  51. }
  52. // Parameter validation
  53. if (!((callback && mime_types && num_mime_types > 0) ||
  54. (!callback && !mime_types && num_mime_types == 0))) {
  55. return SDL_SetError("Invalid parameters");
  56. }
  57. if (!callback && !_this->clipboard_callback) {
  58. // Nothing to do, don't modify the system clipboard
  59. return true;
  60. }
  61. SDL_CancelClipboardData(_this->clipboard_sequence);
  62. ++_this->clipboard_sequence;
  63. if (!_this->clipboard_sequence) {
  64. _this->clipboard_sequence = 1;
  65. }
  66. _this->clipboard_callback = callback;
  67. _this->clipboard_cleanup = cleanup;
  68. _this->clipboard_userdata = userdata;
  69. if (mime_types && num_mime_types > 0) {
  70. size_t num_allocated = 0;
  71. _this->clipboard_mime_types = (char **)SDL_malloc(num_mime_types * sizeof(char *));
  72. if (_this->clipboard_mime_types) {
  73. for (i = 0; i < num_mime_types; ++i) {
  74. _this->clipboard_mime_types[i] = SDL_strdup(mime_types[i]);
  75. if (_this->clipboard_mime_types[i]) {
  76. ++num_allocated;
  77. }
  78. }
  79. }
  80. if (num_allocated < num_mime_types) {
  81. SDL_ClearClipboardData();
  82. return false;
  83. }
  84. _this->num_clipboard_mime_types = num_mime_types;
  85. }
  86. if (_this->SetClipboardData) {
  87. if (!_this->SetClipboardData(_this)) {
  88. return false;
  89. }
  90. } else if (_this->SetClipboardText) {
  91. char *text = NULL;
  92. size_t size;
  93. for (i = 0; i < num_mime_types; ++i) {
  94. const char *mime_type = _this->clipboard_mime_types[i];
  95. if (SDL_IsTextMimeType(mime_type)) {
  96. const void *data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &size);
  97. if (data) {
  98. text = (char *)SDL_malloc(size + 1);
  99. SDL_memcpy(text, data, size);
  100. text[size] = '\0';
  101. if (!_this->SetClipboardText(_this, text)) {
  102. SDL_free(text);
  103. return false;
  104. }
  105. break;
  106. }
  107. }
  108. }
  109. if (text) {
  110. SDL_free(text);
  111. } else {
  112. if (!_this->SetClipboardText(_this, "")) {
  113. return false;
  114. }
  115. }
  116. }
  117. SDL_SendClipboardUpdate();
  118. return true;
  119. }
  120. SDL_bool SDL_ClearClipboardData(void)
  121. {
  122. return SDL_SetClipboardData(NULL, NULL, NULL, NULL, 0);
  123. }
  124. void *SDL_GetInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
  125. {
  126. void *data = NULL;
  127. if (_this->clipboard_callback) {
  128. const void *provided_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, size);
  129. if (provided_data) {
  130. // Make a copy of it for the caller and guarantee null termination
  131. data = SDL_malloc(*size + sizeof(Uint32));
  132. if (data) {
  133. SDL_memcpy(data, provided_data, *size);
  134. SDL_memset((Uint8 *)data + *size, 0, sizeof(Uint32));
  135. }
  136. }
  137. }
  138. return data;
  139. }
  140. void *SDL_GetClipboardData(const char *mime_type, size_t *size)
  141. {
  142. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  143. if (!_this) {
  144. SDL_SetError("Video subsystem must be initialized to get clipboard data");
  145. return NULL;
  146. }
  147. if (!mime_type) {
  148. SDL_InvalidParamError("mime_type");
  149. return NULL;
  150. }
  151. if (!size) {
  152. SDL_InvalidParamError("size");
  153. return NULL;
  154. }
  155. // Initialize size to empty, so implementations don't have to worry about it
  156. *size = 0;
  157. if (_this->GetClipboardData) {
  158. return _this->GetClipboardData(_this, mime_type, size);
  159. } else if (_this->GetClipboardText && SDL_IsTextMimeType(mime_type)) {
  160. char *text = _this->GetClipboardText(_this);
  161. if (text && *text == '\0') {
  162. SDL_free(text);
  163. text = NULL;
  164. }
  165. return text;
  166. } else {
  167. return SDL_GetInternalClipboardData(_this, mime_type, size);
  168. }
  169. }
  170. bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type)
  171. {
  172. size_t i;
  173. for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
  174. if (SDL_strcmp(mime_type, _this->clipboard_mime_types[i]) == 0) {
  175. return true;
  176. }
  177. }
  178. return false;
  179. }
  180. SDL_bool SDL_HasClipboardData(const char *mime_type)
  181. {
  182. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  183. if (!_this) {
  184. SDL_SetError("Video subsystem must be initialized to check clipboard data");
  185. return false;
  186. }
  187. if (!mime_type) {
  188. SDL_InvalidParamError("mime_type");
  189. return false;
  190. }
  191. if (_this->HasClipboardData) {
  192. return _this->HasClipboardData(_this, mime_type);
  193. } else if (_this->HasClipboardText && SDL_IsTextMimeType(mime_type)) {
  194. return _this->HasClipboardText(_this);
  195. } else {
  196. return SDL_HasInternalClipboardData(_this, mime_type);
  197. }
  198. }
  199. // Clipboard text
  200. bool SDL_IsTextMimeType(const char *mime_type)
  201. {
  202. return (SDL_strncmp(mime_type, "text", 4) == 0);
  203. }
  204. static const char **SDL_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types)
  205. {
  206. if (_this->GetTextMimeTypes) {
  207. return _this->GetTextMimeTypes(_this, num_mime_types);
  208. } else {
  209. static const char *text_mime_types[] = {
  210. "text/plain;charset=utf-8"
  211. };
  212. *num_mime_types = SDL_arraysize(text_mime_types);
  213. return text_mime_types;
  214. }
  215. }
  216. const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size)
  217. {
  218. char *text = (char *)userdata;
  219. if (text) {
  220. *size = SDL_strlen(text);
  221. } else {
  222. *size = 0;
  223. }
  224. return text;
  225. }
  226. SDL_bool SDL_SetClipboardText(const char *text)
  227. {
  228. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  229. size_t num_mime_types;
  230. const char **text_mime_types;
  231. if (!_this) {
  232. return SDL_SetError("Video subsystem must be initialized to set clipboard text");
  233. }
  234. if (text && *text) {
  235. text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types);
  236. return SDL_SetClipboardData(SDL_ClipboardTextCallback, SDL_free, SDL_strdup(text), text_mime_types, num_mime_types);
  237. }
  238. return SDL_ClearClipboardData();
  239. }
  240. char *SDL_GetClipboardText(void)
  241. {
  242. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  243. size_t i, num_mime_types;
  244. const char **text_mime_types;
  245. size_t length;
  246. char *text = NULL;
  247. if (!_this) {
  248. SDL_SetError("Video subsystem must be initialized to get clipboard text");
  249. return SDL_strdup("");
  250. }
  251. text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types);
  252. for (i = 0; i < num_mime_types; ++i) {
  253. void *clipdata = SDL_GetClipboardData(text_mime_types[i], &length);
  254. if (clipdata) {
  255. text = (char *)clipdata;
  256. break;
  257. }
  258. }
  259. if (!text) {
  260. text = SDL_strdup("");
  261. }
  262. return text;
  263. }
  264. SDL_bool SDL_HasClipboardText(void)
  265. {
  266. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  267. size_t i, num_mime_types;
  268. const char **text_mime_types;
  269. if (!_this) {
  270. SDL_SetError("Video subsystem must be initialized to check clipboard text");
  271. return false;
  272. }
  273. text_mime_types = SDL_GetTextMimeTypes(_this, &num_mime_types);
  274. for (i = 0; i < num_mime_types; ++i) {
  275. if (SDL_HasClipboardData(text_mime_types[i])) {
  276. return true;
  277. }
  278. }
  279. return false;
  280. }
  281. // Primary selection text
  282. SDL_bool SDL_SetPrimarySelectionText(const char *text)
  283. {
  284. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  285. if (!_this) {
  286. return SDL_SetError("Video subsystem must be initialized to set primary selection text");
  287. }
  288. if (!text) {
  289. text = "";
  290. }
  291. if (_this->SetPrimarySelectionText) {
  292. if (!_this->SetPrimarySelectionText(_this, text)) {
  293. return false;
  294. }
  295. } else {
  296. SDL_free(_this->primary_selection_text);
  297. _this->primary_selection_text = SDL_strdup(text);
  298. }
  299. SDL_SendClipboardUpdate();
  300. return true;
  301. }
  302. char *SDL_GetPrimarySelectionText(void)
  303. {
  304. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  305. if (!_this) {
  306. SDL_SetError("Video subsystem must be initialized to get primary selection text");
  307. return SDL_strdup("");
  308. }
  309. if (_this->GetPrimarySelectionText) {
  310. return _this->GetPrimarySelectionText(_this);
  311. } else {
  312. const char *text = _this->primary_selection_text;
  313. if (!text) {
  314. text = "";
  315. }
  316. return SDL_strdup(text);
  317. }
  318. }
  319. SDL_bool SDL_HasPrimarySelectionText(void)
  320. {
  321. SDL_VideoDevice *_this = SDL_GetVideoDevice();
  322. if (!_this) {
  323. SDL_SetError("Video subsystem must be initialized to check primary selection text");
  324. return false;
  325. }
  326. if (_this->HasPrimarySelectionText) {
  327. return _this->HasPrimarySelectionText(_this);
  328. } else {
  329. if (_this->primary_selection_text && _this->primary_selection_text[0] != '\0') {
  330. return true;
  331. } else {
  332. return false;
  333. }
  334. }
  335. }