summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/windows/SDL_windowsclipboard.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/windows/SDL_windowsclipboard.c')
-rw-r--r--contrib/SDL-3.2.8/src/video/windows/SDL_windowsclipboard.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/windows/SDL_windowsclipboard.c b/contrib/SDL-3.2.8/src/video/windows/SDL_windowsclipboard.c
new file mode 100644
index 0000000..39f86ef
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/windows/SDL_windowsclipboard.c
@@ -0,0 +1,442 @@
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#include "SDL_internal.h"
22
23#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
24
25#include "SDL_windowsvideo.h"
26#include "SDL_windowswindow.h"
27#include "../SDL_clipboard_c.h"
28#include "../../events/SDL_events_c.h"
29#include "../../events/SDL_clipboardevents_c.h"
30
31#ifdef UNICODE
32#define TEXT_FORMAT CF_UNICODETEXT
33#else
34#define TEXT_FORMAT CF_TEXT
35#endif
36
37#define IMAGE_FORMAT CF_DIB
38#define IMAGE_MIME_TYPE "image/bmp"
39#define BFT_BITMAP 0x4d42 // 'BM'
40
41// Assume we can directly read and write BMP fields without byte swapping
42SDL_COMPILE_TIME_ASSERT(verify_byte_order, SDL_BYTEORDER == SDL_LIL_ENDIAN);
43
44static BOOL WIN_OpenClipboard(SDL_VideoDevice *_this)
45{
46 // Retry to open the clipboard in case another application has it open
47 const int MAX_ATTEMPTS = 3;
48 int attempt;
49 HWND hwnd = NULL;
50
51 if (_this->windows) {
52 hwnd = _this->windows->internal->hwnd;
53 }
54 for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
55 if (OpenClipboard(hwnd)) {
56 return TRUE;
57 }
58 SDL_Delay(10);
59 }
60 return FALSE;
61}
62
63static void WIN_CloseClipboard(void)
64{
65 CloseClipboard();
66}
67
68static HANDLE WIN_ConvertBMPtoDIB(const void *bmp, size_t bmp_size)
69{
70 HANDLE hMem = NULL;
71
72 if (bmp && bmp_size > sizeof(BITMAPFILEHEADER) && ((BITMAPFILEHEADER *)bmp)->bfType == BFT_BITMAP) {
73 BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp;
74 BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)((Uint8 *)bmp + sizeof(BITMAPFILEHEADER));
75 size_t bih_size = pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
76 size_t pixels_size = pbih->biSizeImage;
77
78 if (pbfh->bfOffBits >= (sizeof(BITMAPFILEHEADER) + bih_size) &&
79 (pbfh->bfOffBits + pixels_size) <= bmp_size) {
80 const Uint8 *pixels = (const Uint8 *)bmp + pbfh->bfOffBits;
81 size_t dib_size = bih_size + pixels_size;
82 hMem = GlobalAlloc(GMEM_MOVEABLE, dib_size);
83 if (hMem) {
84 LPVOID dst = GlobalLock(hMem);
85 if (dst) {
86 SDL_memcpy(dst, pbih, bih_size);
87 SDL_memcpy((Uint8 *)dst + bih_size, pixels, pixels_size);
88 GlobalUnlock(hMem);
89 } else {
90 WIN_SetError("GlobalLock()");
91 GlobalFree(hMem);
92 hMem = NULL;
93 }
94 } else {
95 SDL_OutOfMemory();
96 }
97 } else {
98 SDL_SetError("Invalid BMP data");
99 }
100 } else {
101 SDL_SetError("Invalid BMP data");
102 }
103 return hMem;
104}
105
106static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size)
107{
108 void *bmp = NULL;
109 size_t mem_size = GlobalSize(hMem);
110
111 if (mem_size > sizeof(BITMAPINFOHEADER)) {
112 LPVOID dib = GlobalLock(hMem);
113 if (dib) {
114 BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)dib;
115
116 // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#color-tables
117 size_t color_table_size;
118 switch (pbih->biCompression) {
119 case BI_RGB:
120 if (pbih->biBitCount <= 8) {
121 color_table_size = sizeof(RGBQUAD) * (pbih->biClrUsed == 0 ? 1 << pbih->biBitCount : pbih->biClrUsed);
122 } else {
123 color_table_size = 0;
124 }
125 break;
126 case BI_BITFIELDS:
127 color_table_size = 3 * sizeof(DWORD);
128 break;
129 case 6 /* BI_ALPHABITFIELDS */:
130 // https://learn.microsoft.com/en-us/previous-versions/windows/embedded/aa452885(v=msdn.10)
131 color_table_size = 4 * sizeof(DWORD);
132 break;
133 default: // FOURCC
134 color_table_size = sizeof(RGBQUAD) * pbih->biClrUsed;
135 }
136
137 size_t bih_size = pbih->biSize + color_table_size;
138 size_t dib_size = bih_size + pbih->biSizeImage;
139 if (dib_size <= mem_size) {
140 size_t bmp_size = sizeof(BITMAPFILEHEADER) + mem_size;
141 bmp = SDL_malloc(bmp_size);
142 if (bmp) {
143 BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp;
144 pbfh->bfType = BFT_BITMAP;
145 pbfh->bfSize = (DWORD)bmp_size;
146 pbfh->bfReserved1 = 0;
147 pbfh->bfReserved2 = 0;
148 pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + pbih->biSize + color_table_size);
149 SDL_memcpy((Uint8 *)bmp + sizeof(BITMAPFILEHEADER), dib, dib_size);
150 *size = bmp_size;
151 }
152 } else {
153 SDL_SetError("Invalid BMP data");
154 }
155 GlobalUnlock(hMem);
156 } else {
157 WIN_SetError("GlobalLock()");
158 }
159 } else {
160 SDL_SetError("Invalid BMP data");
161 }
162 return bmp;
163}
164
165static bool WIN_SetClipboardImage(SDL_VideoDevice *_this)
166{
167 HANDLE hMem;
168 size_t clipboard_data_size;
169 const void *clipboard_data;
170 bool result = true;
171
172 clipboard_data = _this->clipboard_callback(_this->clipboard_userdata, IMAGE_MIME_TYPE, &clipboard_data_size);
173 hMem = WIN_ConvertBMPtoDIB(clipboard_data, clipboard_data_size);
174 if (hMem) {
175 // Save the image to the clipboard
176 if (!SetClipboardData(IMAGE_FORMAT, hMem)) {
177 result = WIN_SetError("Couldn't set clipboard data");
178 }
179 } else {
180 // WIN_ConvertBMPtoDIB() set the error
181 result = false;
182 }
183 return result;
184}
185
186static bool WIN_SetClipboardText(SDL_VideoDevice *_this, const char *mime_type)
187{
188 HANDLE hMem;
189 size_t clipboard_data_size;
190 const void *clipboard_data;
191 bool result = true;
192
193 clipboard_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &clipboard_data_size);
194 if (clipboard_data && clipboard_data_size > 0) {
195 SIZE_T i, size;
196 LPTSTR tstr = (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)clipboard_data, clipboard_data_size);
197 if (!tstr) {
198 return SDL_SetError("Couldn't convert text from UTF-8");
199 }
200
201 // Find out the size of the data
202 for (size = 0, i = 0; tstr[i]; ++i, ++size) {
203 if (tstr[i] == '\n' && (i == 0 || tstr[i - 1] != '\r')) {
204 // We're going to insert a carriage return
205 ++size;
206 }
207 }
208 size = (size + 1) * sizeof(*tstr);
209
210 // Save the data to the clipboard
211 hMem = GlobalAlloc(GMEM_MOVEABLE, size);
212 if (hMem) {
213 LPTSTR dst = (LPTSTR)GlobalLock(hMem);
214 if (dst) {
215 // Copy the text over, adding carriage returns as necessary
216 for (i = 0; tstr[i]; ++i) {
217 if (tstr[i] == '\n' && (i == 0 || tstr[i - 1] != '\r')) {
218 *dst++ = '\r';
219 }
220 *dst++ = tstr[i];
221 }
222 *dst = 0;
223 GlobalUnlock(hMem);
224 }
225
226 if (!SetClipboardData(TEXT_FORMAT, hMem)) {
227 result = WIN_SetError("Couldn't set clipboard data");
228 }
229 } else {
230 result = SDL_OutOfMemory();
231 }
232 SDL_free(tstr);
233 }
234 return result;
235}
236
237bool WIN_SetClipboardData(SDL_VideoDevice *_this)
238{
239 SDL_VideoData *data = _this->internal;
240 size_t i;
241 bool result = true;
242
243 /* I investigated delayed clipboard rendering, and at least with text and image
244 * formats you have to use an output window, not SDL_HelperWindow, and the system
245 * requests them being rendered immediately, so there isn't any benefit.
246 */
247
248 if (WIN_OpenClipboard(_this)) {
249 EmptyClipboard();
250
251 // Set the clipboard text
252 for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
253 const char *mime_type = _this->clipboard_mime_types[i];
254
255 if (SDL_IsTextMimeType(mime_type)) {
256 if (!WIN_SetClipboardText(_this, mime_type)) {
257 result = false;
258 }
259 // Only set the first clipboard text
260 break;
261 }
262 }
263
264 // Set the clipboard image
265 for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
266 const char *mime_type = _this->clipboard_mime_types[i];
267
268 if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
269 if (!WIN_SetClipboardImage(_this)) {
270 result = false;
271 }
272 break;
273 }
274 }
275
276 data->clipboard_count = GetClipboardSequenceNumber();
277 WIN_CloseClipboard();
278 } else {
279 result = WIN_SetError("Couldn't open clipboard");
280 }
281 return result;
282}
283
284void *WIN_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
285{
286 void *data = NULL;
287
288 if (SDL_IsTextMimeType(mime_type)) {
289 char *text = NULL;
290
291 if (IsClipboardFormatAvailable(TEXT_FORMAT)) {
292 if (WIN_OpenClipboard(_this)) {
293 HANDLE hMem;
294 LPTSTR tstr;
295
296 hMem = GetClipboardData(TEXT_FORMAT);
297 if (hMem) {
298 tstr = (LPTSTR)GlobalLock(hMem);
299 if (tstr) {
300 text = WIN_StringToUTF8(tstr);
301 GlobalUnlock(hMem);
302 } else {
303 WIN_SetError("Couldn't lock clipboard data");
304 }
305 } else {
306 WIN_SetError("Couldn't get clipboard data");
307 }
308 WIN_CloseClipboard();
309 }
310 }
311 if (!text) {
312 text = SDL_strdup("");
313 }
314 data = text;
315 *size = SDL_strlen(text);
316
317 } else if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
318 if (IsClipboardFormatAvailable(IMAGE_FORMAT)) {
319 if (WIN_OpenClipboard(_this)) {
320 HANDLE hMem;
321
322 hMem = GetClipboardData(IMAGE_FORMAT);
323 if (hMem) {
324 data = WIN_ConvertDIBtoBMP(hMem, size);
325 } else {
326 WIN_SetError("Couldn't get clipboard data");
327 }
328 WIN_CloseClipboard();
329 }
330 }
331 } else {
332 data = SDL_GetInternalClipboardData(_this, mime_type, size);
333 }
334 return data;
335}
336
337bool WIN_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
338{
339 if (SDL_IsTextMimeType(mime_type)) {
340 if (IsClipboardFormatAvailable(TEXT_FORMAT)) {
341 return true;
342 }
343 } else if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
344 if (IsClipboardFormatAvailable(IMAGE_FORMAT)) {
345 return true;
346 }
347 } else {
348 if (SDL_HasInternalClipboardData(_this, mime_type)) {
349 return true;
350 }
351 }
352 return false;
353}
354
355static int GetClipboardFormatMimeType(UINT format, char *name)
356{
357 static struct
358 {
359 UINT format;
360 const char *mime_type;
361 } mime_types[] = {
362 { TEXT_FORMAT, "text/plain;charset=utf-8" },
363 { IMAGE_FORMAT, IMAGE_MIME_TYPE },
364 };
365
366 for (int i = 0; i < SDL_arraysize(mime_types); ++i) {
367 if (format == mime_types[i].format) {
368 size_t len = SDL_strlen(mime_types[i].mime_type) + 1;
369 if (name) {
370 SDL_memcpy(name, mime_types[i].mime_type, len);
371 }
372 return (int)len;
373 }
374 }
375 return 0;
376}
377
378static char **GetMimeTypes(int *pnformats)
379{
380 char **new_mime_types = NULL;
381
382 *pnformats = 0;
383
384 if (WIN_OpenClipboard(SDL_GetVideoDevice())) {
385 int nformats = 0;
386 UINT format = 0;
387 int formatsSz = 0;
388 for ( ; ; ) {
389 format = EnumClipboardFormats(format);
390 if (!format) {
391 break;
392 }
393
394 int len = GetClipboardFormatMimeType(format, NULL);
395 if (len > 0) {
396 ++nformats;
397 formatsSz += len;
398 }
399 }
400
401 new_mime_types = SDL_AllocateTemporaryMemory((nformats + 1) * sizeof(char *) + formatsSz);
402 if (new_mime_types) {
403 format = 0;
404 char *strPtr = (char *)(new_mime_types + nformats + 1);
405 int i = 0;
406 for ( ; ; ) {
407 format = EnumClipboardFormats(format);
408 if (!format) {
409 break;
410 }
411
412 int len = GetClipboardFormatMimeType(format, strPtr);
413 if (len > 0) {
414 new_mime_types[i++] = strPtr;
415 strPtr += len;
416 }
417 }
418
419 new_mime_types[nformats] = NULL;
420 *pnformats = nformats;
421 }
422 WIN_CloseClipboard();
423 }
424 return new_mime_types;
425}
426
427void WIN_CheckClipboardUpdate(struct SDL_VideoData *data)
428{
429 DWORD count = GetClipboardSequenceNumber();
430 if (count != data->clipboard_count) {
431 if (count) {
432 int nformats = 0;
433 char **new_mime_types = GetMimeTypes(&nformats);
434 if (new_mime_types) {
435 SDL_SendClipboardUpdate(false, new_mime_types, nformats);
436 }
437 }
438 data->clipboard_count = count;
439 }
440}
441
442#endif // SDL_VIDEO_DRIVER_WINDOWS