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