1 /*
2 pygame - Python Game Library
3 Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen
4
5 Originally written and put in the public domain by Sam Lantinga.
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with this library; if not, write to the Free
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 #include <Windows.h>
22
23 #if !defined(CF_DIBV5)
24 /* Missing from the MinGW win32api-3.11 winuser.h header */
25 #define CF_DIBV5 17
26 #endif
27
28 static HWND window_handle;
29 #define MAX_CHUNK_SIZE INT_MAX
30
31 static UINT _format_MIME_PLAIN;
32
33 /**
34 * \brief Converts the passed type into a system specific clipboard type
35 * to use for the clipboard.
36 *
37 * \param type The type to convert.
38 * \return A system specific type.
39 */
40 static UINT
_convert_format(char * type)41 _convert_format(char *type)
42 {
43 return RegisterClipboardFormat(type);
44 }
45
46 /**
47 * \brief Gets a system specific clipboard format type for a certain type.
48 *
49 * \param type The name of the format to get the mapped format type for.
50 * \return The format type or -1 if no such type was found.
51 */
52 static UINT
_convert_internal_type(char * type)53 _convert_internal_type(char *type)
54 {
55 if (strcmp(type, PYGAME_SCRAP_TEXT) == 0)
56 return CF_TEXT;
57 if (strcmp(type, "text/plain;charset=utf-8") == 0)
58 return CF_UNICODETEXT;
59 if (strcmp(type, "image/tiff") == 0)
60 return CF_TIFF;
61 if (strcmp(type, PYGAME_SCRAP_BMP) == 0)
62 return CF_DIB;
63 if (strcmp(type, "audio/wav") == 0)
64 return CF_WAVE;
65 return -1;
66 }
67
68 /**
69 * \brief Looks up the name for the specific clipboard format type.
70 *
71 * \param format The format to get the name for.
72 * \param buf The buffer to copy the name into.
73 * \param size The size of the buffer.
74 * \return The length of the format name.
75 */
76 static int
_lookup_clipboard_format(UINT format,char * buf,int size)77 _lookup_clipboard_format(UINT format, char *buf, int size)
78 {
79 int len;
80 char *cpy;
81
82 memset(buf, 0, size);
83 switch (format) {
84 case CF_TEXT:
85 len = strlen(PYGAME_SCRAP_TEXT);
86 cpy = PYGAME_SCRAP_TEXT;
87 break;
88 case CF_UNICODETEXT:
89 len = 24;
90 cpy = "text/plain;charset=utf-8";
91 break;
92 case CF_TIFF:
93 len = 10;
94 cpy = "image/tiff";
95 break;
96 case CF_DIB:
97 len = strlen(PYGAME_SCRAP_BMP);
98 cpy = PYGAME_SCRAP_BMP;
99 break;
100 case CF_WAVE:
101 len = 9;
102 cpy = "audio/wav";
103 break;
104 default:
105 len = GetClipboardFormatName(format, buf, size);
106 return len;
107 }
108 if (len != 0)
109 memcpy(buf, cpy, len);
110 return len;
111 }
112
113 /**
114 * \brief Creates a BMP character buffer with all headers from a DIB
115 * HANDLE. The caller has to free the returned buffer.
116 * \param data The DIB handle data.
117 * \param count The size of the DIB handle.
118 * \return The character buffer containing the BMP information.
119 */
120 static char *
_create_dib_buffer(char * data,unsigned long * count)121 _create_dib_buffer(char *data, unsigned long *count)
122 {
123 BITMAPFILEHEADER hdr;
124 LPBITMAPINFOHEADER bihdr;
125 char *buf;
126
127 if (!data)
128 return NULL;
129 bihdr = (LPBITMAPINFOHEADER)data;
130
131 /* Create the BMP header. */
132 hdr.bfType = 'M' << 8 | 'B'; /* Specs say, it is always BM */
133 hdr.bfReserved1 = 0;
134 hdr.bfReserved2 = 0;
135 hdr.bfSize =
136 (DWORD)(sizeof(BITMAPFILEHEADER) + bihdr->biSize +
137 bihdr->biClrUsed * sizeof(RGBQUAD) + bihdr->biSizeImage);
138 hdr.bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + bihdr->biSize +
139 bihdr->biClrUsed * sizeof(RGBQUAD));
140
141 /* Copy both to the buffer. */
142 buf = malloc(sizeof(hdr) + (*count));
143 if (!buf)
144 return NULL;
145 memcpy(buf, &hdr, sizeof(hdr));
146 memcpy(buf + sizeof(BITMAPFILEHEADER), data, *count);
147
148 /* Increase count for the correct size. */
149 *count += sizeof(hdr);
150 return buf;
151 }
152
153 int
pygame_scrap_init(void)154 pygame_scrap_init(void)
155 {
156 SDL_SysWMinfo info;
157 int retval = 0;
158
159 /* Grab the window manager specific information */
160 SDL_SetError("SDL is not running on known window manager");
161
162 SDL_VERSION(&info.version);
163
164 #if IS_SDLv1
165 if (SDL_GetWMInfo(&info)) {
166 /* Save the information for later use */
167 window_handle = info.window;
168 retval = 1;
169 }
170 #else
171 if (SDL_GetWindowWMInfo(pg_GetDefaultWindow(), &info)) {
172 /* Save the information for later use */
173 window_handle = info.info.win.window;
174 retval = 1;
175 }
176 #endif
177
178 if (retval)
179 _scrapinitialized = 1;
180
181 _format_MIME_PLAIN = RegisterClipboardFormat(PYGAME_SCRAP_TEXT);
182 return retval;
183 }
184
185 int
pygame_scrap_lost(void)186 pygame_scrap_lost(void)
187 {
188 if (!pygame_scrap_initialized()) {
189 PyErr_SetString(pgExc_SDLError, "scrap system not initialized.");
190 return 0;
191 }
192 return (GetClipboardOwner() != window_handle);
193 }
194
195 int
pygame_scrap_put(char * type,int srclen,char * src)196 pygame_scrap_put(char *type, int srclen, char *src)
197 {
198 UINT format;
199 int nulledlen = srclen + 1;
200 HANDLE hMem;
201
202 if (!pygame_scrap_initialized()) {
203 PyErr_SetString(pgExc_SDLError, "scrap system not initialized.");
204 return 0;
205 }
206
207 format = _convert_internal_type(type);
208 if (format == -1)
209 format = _convert_format(type);
210
211 if (!OpenClipboard(window_handle))
212 return 0; /* Could not open the clipboard. */
213
214 if (format == CF_DIB || format == CF_DIBV5)
215 nulledlen -= sizeof(BITMAPFILEHEADER); /* We won't copy the header */
216
217 hMem = GlobalAlloc((GMEM_MOVEABLE | GMEM_DDESHARE), nulledlen);
218 if (hMem) {
219 char *dst = GlobalLock(hMem);
220
221 memset(dst, 0, nulledlen);
222 if (format == CF_DIB || format == CF_DIBV5)
223 memcpy(dst, src + sizeof(BITMAPFILEHEADER), nulledlen - 1);
224 else
225 memcpy(dst, src, srclen);
226
227 GlobalUnlock(hMem);
228 EmptyClipboard();
229 SetClipboardData(format, hMem);
230
231 if (format == _format_MIME_PLAIN) {
232 /* Setting SCRAP_TEXT, also set CF_TEXT. */
233 SetClipboardData(CF_TEXT, hMem);
234 }
235 }
236 else {
237 /* Could not access the clipboard, raise an error. */
238 CloseClipboard();
239 return 0;
240 }
241
242 CloseClipboard();
243 return 1;
244 }
245
246 char *
pygame_scrap_get(char * type,unsigned long * count)247 pygame_scrap_get(char *type, unsigned long *count)
248 {
249 UINT format = _convert_format(type);
250 char *retval = NULL;
251
252 if (!pygame_scrap_initialized()) {
253 PyErr_SetString(pgExc_SDLError, "scrap system not initialized.");
254 return NULL;
255 }
256
257 if (!pygame_scrap_lost())
258 return Bytes_AsString(PyDict_GetItemString(_clipdata, type));
259
260 if (!OpenClipboard(window_handle))
261 return NULL;
262
263 if (!IsClipboardFormatAvailable(format)) {
264 /* The format was not found - was it a mapped type? */
265 format = _convert_internal_type(type);
266 if (format == -1) {
267 CloseClipboard();
268 return NULL;
269 }
270 }
271
272 if (IsClipboardFormatAvailable(format)) {
273 HANDLE hMem;
274 char *src;
275 src = NULL;
276
277 hMem = GetClipboardData(format);
278 if (hMem) {
279 *count = 0;
280
281 /* CF_BITMAP is not a global, so do not lock it. */
282 if (format != CF_BITMAP) {
283 src = GlobalLock(hMem);
284 if (!src) {
285 CloseClipboard();
286 return NULL;
287 }
288 *count = GlobalSize(hMem);
289 }
290
291 if (format == CF_DIB || format == CF_DIBV5) {
292 /* Count will be increased accordingly in
293 * _create_dib_buffer.
294 */
295 src = _create_dib_buffer(src, count);
296 GlobalUnlock(hMem);
297 CloseClipboard();
298 return src;
299 }
300 else if (*count != 0) {
301 /* weird error, shouldn't get here. */
302 if (!src) {
303 return NULL;
304 }
305
306 retval = malloc(*count);
307 if (retval) {
308 memset(retval, 0, *count);
309 memcpy(retval, src, *count);
310 }
311 }
312 GlobalUnlock(hMem);
313 }
314 }
315
316 CloseClipboard();
317 return retval;
318 }
319
320 char **
pygame_scrap_get_types(void)321 pygame_scrap_get_types(void)
322 {
323 UINT format = 0;
324 char **types = NULL;
325 char **tmptypes;
326 int i = 0;
327 int count = -1;
328 int len;
329 char tmp[100] = {'\0'};
330 int size = 0;
331
332 if (!OpenClipboard(window_handle))
333 return NULL;
334
335 size = CountClipboardFormats();
336 if (size == 0) {
337 CloseClipboard();
338 return NULL; /* No clipboard data. */
339 }
340
341 for (i = 0; i < size; i++) {
342 format = EnumClipboardFormats(format);
343 if (format == 0) {
344 /* Something wicked happened. */
345 while (i > 0)
346 free(types[i]);
347 free(types);
348 CloseClipboard();
349 return NULL;
350 }
351
352 /* No predefined name, get the (truncated) name. */
353 len = _lookup_clipboard_format(format, tmp, 100);
354 if (len == 0)
355 continue;
356 count++;
357
358 tmptypes = realloc(types, sizeof(char *) * (count + 1));
359 if (!tmptypes) {
360 while (count > 0) {
361 free(types[count]);
362 count--;
363 }
364 free(types);
365 CloseClipboard();
366 return NULL;
367 }
368 types = tmptypes;
369 types[count] = malloc(sizeof(char) * (len + 1));
370 if (!types[count]) {
371 while (count > 0) {
372 free(types[count]);
373 count--;
374 }
375 free(types);
376 CloseClipboard();
377 return NULL;
378 }
379
380 memset(types[count], 0, len + 1);
381 memcpy(types[count], tmp, len);
382 }
383
384 tmptypes = realloc(types, sizeof(char *) * (count + 1));
385 if (!tmptypes) {
386 while (count > 0) {
387 free(types[count]);
388 count--;
389 }
390 free(types);
391 CloseClipboard();
392 return NULL;
393 }
394 types = tmptypes;
395 types[count] = NULL;
396 CloseClipboard();
397 return types;
398 }
399
400 int
pygame_scrap_contains(char * type)401 pygame_scrap_contains(char *type)
402 {
403 return IsClipboardFormatAvailable(_convert_format(type));
404 }
405