1 /*
2   pygame - Python Game Library
3   Copyright (C) 2000-2001  Pete Shinners
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Library General Public
7   License as published by the Free Software Foundation; either
8   version 2 of the License, or (at your option) any later version.
9 
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Library General Public License for more details.
14 
15   You should have received a copy of the GNU Library General Public
16   License along with this library; if not, write to the Free
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19   Pete Shinners
20   pete@shinners.org
21 */
22 
23 /*
24  *  pygame display module
25  */
26 #define PYGAMEAPI_DISPLAY_INTERNAL
27 #include "pygame.h"
28 
29 #include "pgcompat.h"
30 #include "pgopengl.h"
31 
32 #include "doc/display_doc.h"
33 
34 #include <SDL_syswm.h>
35 
36 static PyTypeObject pgVidInfo_Type;
37 
38 
39 static PyObject *
40 pgVidInfo_New(const pg_VideoInfo *info);
41 
42 static SDL_Renderer *pg_renderer = NULL;
43 static SDL_Texture *pg_texture = NULL;
44 
45 typedef struct _display_state_s {
46     char *title;
47     PyObject *icon;
48     Uint16 *gamma_ramp;
49     SDL_GLContext gl_context;
50     int toggle_windowed_w;
51     int toggle_windowed_h;
52     Uint8 using_gl; /* using an OPENGL display without renderer */
53     Uint8 scaled_gl;
54     int scaled_gl_w;
55     int scaled_gl_h;
56     int fullscreen_backup_x;
57     int fullscreen_backup_y;
58     SDL_bool auto_resize;
59 } _DisplayState;
60 
61 static int
62 pg_flip_internal(_DisplayState *state);
63 
64 #ifndef PYPY_VERSION
65 static struct PyModuleDef _module;
66 #define DISPLAY_MOD_STATE(mod) ((_DisplayState *)PyModule_GetState(mod))
67 #define DISPLAY_STATE DISPLAY_MOD_STATE(PyState_FindModule(&_module))
68 #else /* PYPY_VERSION */
69 static struct PyModuleDef _module;
70 static _DisplayState _modstate = {0};
71 #define DISPLAY_MOD_STATE(mod) (&_modstate)
72 #define DISPLAY_STATE DISPLAY_MOD_STATE(0)
73 #endif /* PYPY_VERSION */
74 
75 static void
_display_state_cleanup(_DisplayState * state)76 _display_state_cleanup(_DisplayState *state)
77 {
78     if (state->title) {
79         free(state->title);
80         state->title = NULL;
81     }
82     if (state->icon) {
83         Py_XDECREF(state->icon);
84         state->icon = NULL;
85     }
86     if (state->gl_context) {
87         SDL_GL_DeleteContext(state->gl_context);
88         state->gl_context = NULL;
89     }
90     if (state->gamma_ramp) {
91         free(state->gamma_ramp);
92         state->gamma_ramp = NULL;
93     }
94 }
95 
96 
97 static char *icon_defaultname = "pygame_icon.bmp";
98 static char *pkgdatamodule_name = "pygame.pkgdata";
99 static char *imagemodule_name = "pygame.image";
100 static char *resourcefunc_name = "getResource";
101 static char *load_basicfunc_name = "load_basic";
102 
103 static void
pg_close_file(PyObject * fileobj)104 pg_close_file(PyObject *fileobj)
105 {
106     PyObject *result = PyObject_CallMethod(fileobj, "close", NULL);
107     if (result) {
108         Py_DECREF(result);
109     }
110     else {
111         PyErr_Clear();
112     }
113 }
114 
115 static PyObject *
pg_display_resource(char * filename)116 pg_display_resource(char *filename)
117 {
118     PyObject *imagemodule = NULL;
119     PyObject *load_basicfunc = NULL;
120     PyObject *pkgdatamodule = NULL;
121     PyObject *resourcefunc = NULL;
122     PyObject *fresult = NULL;
123     PyObject *result = NULL;
124     PyObject *name = NULL;
125 
126     pkgdatamodule = PyImport_ImportModule(pkgdatamodule_name);
127     if (!pkgdatamodule)
128         goto display_resource_end;
129 
130     resourcefunc = PyObject_GetAttrString(pkgdatamodule, resourcefunc_name);
131     if (!resourcefunc)
132         goto display_resource_end;
133 
134     imagemodule = PyImport_ImportModule(imagemodule_name);
135     if (!imagemodule)
136         goto display_resource_end;
137 
138     load_basicfunc = PyObject_GetAttrString(imagemodule, load_basicfunc_name);
139     if (!load_basicfunc)
140         goto display_resource_end;
141 
142     fresult = PyObject_CallFunction(resourcefunc, "s", filename);
143     if (!fresult)
144         goto display_resource_end;
145 
146     name = PyObject_GetAttrString(fresult, "name");
147     if (name != NULL) {
148         if (Text_Check(name)) {
149             pg_close_file(fresult);
150             Py_DECREF(fresult);
151             fresult = name;
152             name = NULL;
153         }
154     }
155     else {
156         PyErr_Clear();
157     }
158 
159     result = PyObject_CallFunction(load_basicfunc, "O", fresult);
160     if (!result)
161         goto display_resource_end;
162 
163 display_resource_end:
164     Py_XDECREF(pkgdatamodule);
165     Py_XDECREF(resourcefunc);
166     Py_XDECREF(imagemodule);
167     Py_XDECREF(load_basicfunc);
168     Py_XDECREF(fresult);
169     Py_XDECREF(name);
170     return result;
171 }
172 
173 /* init routines */
174 static PyObject *
pg_display_quit(PyObject * self)175 pg_display_quit(PyObject *self)
176 {
177     _DisplayState *state = DISPLAY_STATE;
178     _display_state_cleanup(state);
179     if (pg_GetDefaultWindowSurface()) {
180         pgSurface_AsSurface(pg_GetDefaultWindowSurface()) = NULL;
181         pg_SetDefaultWindowSurface(NULL);
182         pg_SetDefaultWindow(NULL);
183     }
184 
185     pg_mod_autoquit(IMPPREFIX "event");
186     pg_mod_autoquit(IMPPREFIX "time");
187 
188     if (SDL_WasInit(SDL_INIT_VIDEO)) {
189         SDL_QuitSubSystem(SDL_INIT_VIDEO);
190     }
191     Py_RETURN_NONE;
192 }
193 
194 static int
_pg_mac_display_init(void)195 _pg_mac_display_init(void)
196 {
197 #if defined(__APPLE__) && defined(darwin)
198     PyObject *module, *rval;
199     int status;
200 
201     module = PyImport_ImportModule("pygame.macosx");
202     if (!module)
203         return 0;
204 
205     rval = PyObject_CallMethod(module, "Video_AutoInit", "");
206     Py_DECREF(module);
207     if (!rval)
208         return 0;
209 
210     status = PyObject_IsTrue(rval);
211     Py_DECREF(rval);
212     if (status != 1)
213         return 0;
214 #endif /* Mac */
215     return 1;
216 }
217 
218 static PyObject *
pg_display_init(PyObject * self)219 pg_display_init(PyObject *self)
220 {
221 
222     const char *drivername;
223     /* Compatibility:
224      * windib video driver was renamed in SDL2, and we don't want it to fail.
225      */
226     drivername = SDL_getenv("SDL_VIDEODRIVER");
227     if (drivername && !SDL_strncasecmp("windib", drivername, SDL_strlen(drivername))) {
228         SDL_setenv("SDL_VIDEODRIVER", "windows", 1);
229     }
230     if (!SDL_WasInit(SDL_INIT_VIDEO)) {
231         if (!_pg_mac_display_init())
232             return NULL;
233 
234         if (SDL_InitSubSystem(SDL_INIT_VIDEO))
235             return RAISE(pgExc_SDLError, SDL_GetError());
236 
237     }
238 
239     if (!pg_mod_autoinit(IMPPREFIX "time"))
240         return NULL;
241     if (!pg_mod_autoinit(IMPPREFIX "event"))
242         return NULL;
243 
244     Py_RETURN_NONE;
245 }
246 
247 static PyObject *
pg_get_init(PyObject * self)248 pg_get_init(PyObject *self)
249 {
250     return PyBool_FromLong(SDL_WasInit(SDL_INIT_VIDEO) != 0);
251 }
252 
253 static PyObject *
pg_get_active(PyObject * self)254 pg_get_active(PyObject *self)
255 {
256     Uint32 flags = SDL_GetWindowFlags(pg_GetDefaultWindow());
257     return PyBool_FromLong((flags & SDL_WINDOW_SHOWN) &&
258                            !(flags & SDL_WINDOW_MINIMIZED));
259 }
260 
261 /* vidinfo object */
262 static void
pg_vidinfo_dealloc(PyObject * self)263 pg_vidinfo_dealloc(PyObject *self)
264 {
265     PyObject_DEL(self);
266 }
267 
268 static PyObject *
pg_vidinfo_getattr(PyObject * self,char * name)269 pg_vidinfo_getattr(PyObject *self, char *name)
270 {
271     pg_VideoInfo *info = &((pgVidInfoObject *)self)->info;
272 
273     int current_w = -1;
274     int current_h = -1;
275 
276     SDL_version versioninfo;
277     SDL_VERSION(&versioninfo);
278 
279     if (versioninfo.major > 1 ||
280         (versioninfo.minor >= 2 && versioninfo.patch >= 10)) {
281         current_w = info->current_w;
282         current_h = info->current_h;
283     }
284 
285     if (!strcmp(name, "hw"))
286         return PyInt_FromLong(info->hw_available);
287     else if (!strcmp(name, "wm"))
288         return PyInt_FromLong(info->wm_available);
289     else if (!strcmp(name, "blit_hw"))
290         return PyInt_FromLong(info->blit_hw);
291     else if (!strcmp(name, "blit_hw_CC"))
292         return PyInt_FromLong(info->blit_hw_CC);
293     else if (!strcmp(name, "blit_hw_A"))
294         return PyInt_FromLong(info->blit_hw_A);
295     else if (!strcmp(name, "blit_sw"))
296         return PyInt_FromLong(info->blit_hw);
297     else if (!strcmp(name, "blit_sw_CC"))
298         return PyInt_FromLong(info->blit_hw_CC);
299     else if (!strcmp(name, "blit_sw_A"))
300         return PyInt_FromLong(info->blit_hw_A);
301     else if (!strcmp(name, "blit_fill"))
302         return PyInt_FromLong(info->blit_fill);
303     else if (!strcmp(name, "video_mem"))
304         return PyInt_FromLong(info->video_mem);
305     else if (!strcmp(name, "bitsize"))
306         return PyInt_FromLong(info->vfmt->BitsPerPixel);
307     else if (!strcmp(name, "bytesize"))
308         return PyInt_FromLong(info->vfmt->BytesPerPixel);
309     else if (!strcmp(name, "masks"))
310         return Py_BuildValue("(iiii)", info->vfmt->Rmask, info->vfmt->Gmask,
311                              info->vfmt->Bmask, info->vfmt->Amask);
312     else if (!strcmp(name, "shifts"))
313         return Py_BuildValue("(iiii)", info->vfmt->Rshift, info->vfmt->Gshift,
314                              info->vfmt->Bshift, info->vfmt->Ashift);
315     else if (!strcmp(name, "losses"))
316         return Py_BuildValue("(iiii)", info->vfmt->Rloss, info->vfmt->Gloss,
317                              info->vfmt->Bloss, info->vfmt->Aloss);
318     else if (!strcmp(name, "current_h"))
319         return PyInt_FromLong(current_h);
320     else if (!strcmp(name, "current_w"))
321         return PyInt_FromLong(current_w);
322 
323     return RAISE(PyExc_AttributeError, "does not exist in vidinfo");
324 }
325 
326 PyObject *
pg_vidinfo_str(PyObject * self)327 pg_vidinfo_str(PyObject *self)
328 {
329     char str[1024];
330     int current_w = -1;
331     int current_h = -1;
332     pg_VideoInfo *info = &((pgVidInfoObject *)self)->info;
333 
334     SDL_version versioninfo;
335     SDL_VERSION(&versioninfo);
336 
337     if (versioninfo.major > 1 ||
338         (versioninfo.minor >= 2 && versioninfo.patch >= 10)) {
339         current_w = info->current_w;
340         current_h = info->current_h;
341     }
342 
343     sprintf(str,
344             "<VideoInfo(hw = %d, wm = %d,video_mem = %d\n"
345             "         blit_hw = %d, blit_hw_CC = %d, blit_hw_A = %d,\n"
346             "         blit_sw = %d, blit_sw_CC = %d, blit_sw_A = %d,\n"
347             "         bitsize  = %d, bytesize = %d,\n"
348             "         masks =  (%d, %d, %d, %d),\n"
349             "         shifts = (%d, %d, %d, %d),\n"
350             "         losses =  (%d, %d, %d, %d),\n"
351             "         current_w = %d, current_h = %d\n"
352             ">\n",
353             info->hw_available, info->wm_available, info->video_mem,
354             info->blit_hw, info->blit_hw_CC, info->blit_hw_A, info->blit_sw,
355             info->blit_sw_CC, info->blit_sw_A, info->vfmt->BitsPerPixel,
356             info->vfmt->BytesPerPixel, info->vfmt->Rmask, info->vfmt->Gmask,
357             info->vfmt->Bmask, info->vfmt->Amask, info->vfmt->Rshift,
358             info->vfmt->Gshift, info->vfmt->Bshift, info->vfmt->Ashift,
359             info->vfmt->Rloss, info->vfmt->Gloss, info->vfmt->Bloss,
360             info->vfmt->Aloss, current_w, current_h);
361     return Text_FromUTF8(str);
362 }
363 
364 static PyTypeObject pgVidInfo_Type = {
365     PyVarObject_HEAD_INIT(NULL,0)
366     "VidInfo",                    /*name*/
367     sizeof(pgVidInfoObject),      /*basic size*/
368     0,                            /*itemsize*/
369     pg_vidinfo_dealloc,           /*dealloc*/
370     0,                            /*print*/
371     pg_vidinfo_getattr,           /*getattr*/
372     NULL,                         /*setattr*/
373     NULL,                         /*compare*/
374     pg_vidinfo_str,               /*repr*/
375     NULL,                         /*as_number*/
376     NULL,                         /*as_sequence*/
377     NULL,                         /*as_mapping*/
378     (hashfunc)NULL,               /*hash*/
379     (ternaryfunc)NULL,            /*call*/
380     (reprfunc)NULL,               /*str*/
381 };
382 
383 static PyObject *
pgVidInfo_New(const pg_VideoInfo * i)384 pgVidInfo_New(const pg_VideoInfo *i)
385 {
386     pgVidInfoObject *info;
387     if (!i)
388         return RAISE(pgExc_SDLError, SDL_GetError());
389     info = PyObject_NEW(pgVidInfoObject, &pgVidInfo_Type);
390     if (!info)
391         return NULL;
392     info->info = *i;
393     info->info.vfmt = &info->info.vfmt_data;
394     return (PyObject *)info;
395 }
396 
397 static pg_VideoInfo *
pg_GetVideoInfo(pg_VideoInfo * info)398 pg_GetVideoInfo(pg_VideoInfo *info)
399 {
400     SDL_DisplayMode mode;
401     SDL_PixelFormat *tempformat;
402     Uint32 formatenum;
403     pgSurfaceObject *winsurfobj;
404     SDL_Surface *winsurf;
405 
406 #pragma PG_WARN(hardcoding wm_available to 1)
407 #pragma PG_WARN(setting available video RAM to 0 KB)
408 
409     memset(info, 0, sizeof(pg_VideoInfo));
410     info->wm_available = 1;
411 
412     winsurfobj = pg_GetDefaultWindowSurface();
413     if (winsurfobj) {
414         winsurf = pgSurface_AsSurface(winsurfobj);
415         info->current_w = winsurf->w;
416         info->current_h = winsurf->h;
417         info->vfmt_data = *(winsurf->format);
418         info->vfmt = &info->vfmt_data;
419     }
420     else {
421         if (SDL_GetCurrentDisplayMode(0, &mode) == 0) {
422             info->current_w = mode.w;
423             info->current_h = mode.h;
424             formatenum = mode.format;
425         }
426         else {
427             info->current_w = -1;
428             info->current_h = -1;
429             formatenum = SDL_PIXELFORMAT_UNKNOWN;
430         }
431 
432         if ((tempformat = SDL_AllocFormat(formatenum))) {
433             info->vfmt_data = *tempformat;
434             info->vfmt = &info->vfmt_data;
435             SDL_FreeFormat(tempformat);
436         }
437         else {
438             PyErr_SetString(pgExc_SDLError, SDL_GetError());
439             return (pg_VideoInfo *)NULL;
440         }
441     }
442 
443     return info;
444 }
445 
446 static PyObject *
pgInfo(PyObject * self)447 pgInfo(PyObject *self)
448 {
449     pg_VideoInfo info;
450     VIDEO_INIT_CHECK();
451     return pgVidInfo_New(pg_GetVideoInfo(&info));
452 }
453 
454 static PyObject *
pg_get_wm_info(PyObject * self)455 pg_get_wm_info(PyObject *self)
456 {
457     PyObject *dict;
458     PyObject *tmp;
459     SDL_SysWMinfo info;
460     SDL_Window *win;
461 
462     VIDEO_INIT_CHECK();
463 
464     SDL_VERSION(&(info.version))
465     dict = PyDict_New();
466     if (!dict)
467         return NULL;
468 
469 
470     win = pg_GetDefaultWindow();
471     if (!win)
472         return dict;
473     if (!SDL_GetWindowWMInfo(win, &info))
474         return dict;
475 
476 #if defined(SDL_VIDEO_DRIVER_WINDOWS)
477     tmp = PyLong_FromLong((long)info.info.win.window);
478     PyDict_SetItemString(dict, "window", tmp);
479     Py_DECREF(tmp);
480 
481     tmp = PyLong_FromLong((long)info.info.win.hdc);
482     PyDict_SetItemString(dict, "hdc", tmp);
483     Py_DECREF(tmp);
484 
485     tmp = PyLong_FromLong((long)info.info.win.hinstance);
486     PyDict_SetItemString(dict, "hinstance", tmp);
487     Py_DECREF(tmp);
488 #endif
489 #if defined(SDL_VIDEO_DRIVER_WINRT)
490     tmp = PyCapsule_New(info.info.winrt.window, "window", NULL);
491     PyDict_SetItemString(dict, "window", tmp);
492     Py_DECREF(tmp);
493 #endif
494 #if defined(SDL_VIDEO_DRIVER_X11)
495     tmp = PyInt_FromLong(info.info.x11.window);
496     PyDict_SetItemString(dict, "window", tmp);
497     Py_DECREF(tmp);
498 
499     tmp = PyCapsule_New(info.info.x11.display, "display", NULL);
500     PyDict_SetItemString(dict, "display", tmp);
501     Py_DECREF(tmp);
502 #endif
503 #if defined(SDL_VIDEO_DRIVER_DIRECTFB)
504     tmp = PyCapsule_New(info.info.dfb.dfb, "dfb", NULL);
505     PyDict_SetItemString(dict, "dfb", tmp);
506     Py_DECREF(tmp);
507 
508     tmp = PyCapsule_New(info.info.dfb.window, "window", NULL);
509     PyDict_SetItemString(dict, "window", tmp);
510     Py_DECREF(tmp);
511 
512     tmp = PyCapsule_New(info.info.dfb.surface, "surface", NULL);
513     PyDict_SetItemString(dict, "surface", tmp);
514     Py_DECREF(tmp);
515 #endif
516 #if defined(SDL_VIDEO_DRIVER_COCOA)
517     tmp = PyCapsule_New(info.info.cocoa.window, "window", NULL);
518     PyDict_SetItemString(dict, "window", tmp);
519     Py_DECREF(tmp);
520 #endif
521 #if defined(SDL_VIDEO_DRIVER_UIKIT)
522     tmp = PyCapsule_New(info.info.uikit.window, "window", NULL);
523     PyDict_SetItemString(dict, "window", tmp);
524     Py_DECREF(tmp);
525 
526     tmp = PyLong_FromLong(info.info.uikit.framebuffer);
527     PyDict_SetItemString(dict, "framebuffer", tmp);
528     Py_DECREF(tmp);
529 
530     tmp = PyLong_FromLong(info.info.uikit.colorbuffer);
531     PyDict_SetItemString(dict, "colorbuffer", tmp);
532     Py_DECREF(tmp);
533 
534     tmp = PyLong_FromLong(info.info.uikit.resolveFramebuffer);
535     PyDict_SetItemString(dict, "resolveFramebuffer", tmp);
536     Py_DECREF(tmp);
537 #endif
538 #if defined(SDL_VIDEO_DRIVER_WAYLAND)
539     tmp = PyCapsule_New(info.info.wl.display, "display", NULL);
540     PyDict_SetItemString(dict, "display", tmp);
541     Py_DECREF(tmp);
542 
543     tmp = PyCapsule_New(info.info.wl.surface, "surface", NULL);
544     PyDict_SetItemString(dict, "surface", tmp);
545     Py_DECREF(tmp);
546 
547     tmp = PyCapsule_New(info.info.wl.shell_surface, "shell_surface", NULL);
548     PyDict_SetItemString(dict, "shell_surface", tmp);
549     Py_DECREF(tmp);
550 #endif
551 #if defined(SDL_VIDEO_DRIVER_MIR) /* no longer available, left for API/ABI \
552                                      compatibility. Remove in 2.1! */
553     tmp = PyCapsule_New(info.info.mir.connection, "connection", NULL);
554     PyDict_SetItemString(dict, "connection", tmp);
555     Py_DECREF(tmp);
556 
557     tmp = PyCapsule_New(info.info.mir.surface, "surface", NULL);
558     PyDict_SetItemString(dict, "surface", tmp);
559     Py_DECREF(tmp);
560 #endif
561 #if defined(SDL_VIDEO_DRIVER_ANDROID)
562     tmp = PyCapsule_New(info.info.android.window, "window", NULL);
563     PyDict_SetItemString(dict, "window", tmp);
564     Py_DECREF(tmp);
565 
566     tmp = PyLong_FromLong((long)info.info.android.surface);
567     PyDict_SetItemString(dict, "surface", tmp);
568     Py_DECREF(tmp);
569 #endif
570 #if defined(SDL_VIDEO_DRIVER_VIVANTE)
571     tmp = PyLong_FromLong((long)info.info.vivante.display);
572     PyDict_SetItemString(dict, "display", tmp);
573     Py_DECREF(tmp);
574 
575     tmp = PyLong_FromLong((long)info.info.vivante.window);
576     PyDict_SetItemString(dict, "window", tmp);
577     Py_DECREF(tmp);
578 #endif
579 
580 
581     return dict;
582 }
583 
584 /* display functions */
585 static PyObject *
pg_get_driver(PyObject * self)586 pg_get_driver(PyObject *self)
587 {
588     const char *name = NULL;
589     VIDEO_INIT_CHECK();
590     name = SDL_GetCurrentVideoDriver();
591     if (!name)
592         Py_RETURN_NONE;
593     return Text_FromUTF8(name);
594 }
595 
596 static PyObject *
pg_get_surface(PyObject * self)597 pg_get_surface(PyObject *self)
598 {
599     _DisplayState *state = DISPLAY_MOD_STATE(self);
600     SDL_Window *win = pg_GetDefaultWindow();
601 
602     if (pg_renderer!=NULL || state->using_gl) {
603         pgSurfaceObject *surface = pg_GetDefaultWindowSurface();
604         if (!surface)
605             Py_RETURN_NONE;
606         Py_INCREF(surface);
607         return (PyObject *)surface;
608     }
609     else if (win==NULL) {
610         Py_RETURN_NONE;
611     }
612     else {
613         SDL_Surface *sdl_surface = SDL_GetWindowSurface(win);
614         pgSurfaceObject *old_surface = pg_GetDefaultWindowSurface();
615         if (sdl_surface != old_surface->surf) {
616             pgSurfaceObject *new_surface = pgSurface_New2(sdl_surface, SDL_FALSE);
617             if (!new_surface)
618                 return NULL;
619             pg_SetDefaultWindowSurface(new_surface);
620             Py_INCREF((PyObject *)new_surface);
621             return (PyObject *)new_surface;
622         }
623         Py_INCREF(old_surface);
624         return (PyObject *)old_surface;
625     }
626     return NULL;
627 }
628 
629 static PyObject *
pg_gl_set_attribute(PyObject * self,PyObject * arg)630 pg_gl_set_attribute(PyObject *self, PyObject *arg)
631 {
632     int flag, value, result;
633     VIDEO_INIT_CHECK();
634     if (!PyArg_ParseTuple(arg, "ii", &flag, &value))
635         return NULL;
636     if (flag == -1) /*an undefined/unsupported val, ignore*/
637         Py_RETURN_NONE;
638     result = SDL_GL_SetAttribute(flag, value);
639     if (result == -1)
640         return RAISE(pgExc_SDLError, SDL_GetError());
641     Py_RETURN_NONE;
642 }
643 
644 static PyObject *
pg_gl_get_attribute(PyObject * self,PyObject * arg)645 pg_gl_get_attribute(PyObject *self, PyObject *arg)
646 {
647     int flag, value, result;
648     VIDEO_INIT_CHECK();
649     if (!PyArg_ParseTuple(arg, "i", &flag))
650         return NULL;
651     result = SDL_GL_GetAttribute(flag, &value);
652     if (result == -1)
653         return RAISE(pgExc_SDLError, SDL_GetError());
654     return PyInt_FromLong(value);
655 }
656 
657 
658 /*
659 ** Looks at the SDL1 environment variables:
660 **    - SDL_VIDEO_WINDOW_POS
661 *         "x,y"
662 *         "center"
663 **    - SDL_VIDEO_CENTERED
664 *         if set the window should be centered.
665 *
666 *  Returns:
667 *      0 if we do not want to position the window.
668 *      1 if we set the x and y.
669 *          x, and y are set to the x and y.
670 *          center_window is set to 0.
671 *      2 if we want the window centered.
672 *          center_window is set to 1.
673 */
674 int
_get_video_window_pos(int * x,int * y,int * center_window)675 _get_video_window_pos(int *x, int *y, int *center_window)
676 {
677     const char *sdl_video_window_pos = SDL_getenv("SDL_VIDEO_WINDOW_POS");
678     const char *sdl_video_centered = SDL_getenv("SDL_VIDEO_CENTERED");
679     int xx, yy;
680     if (sdl_video_window_pos) {
681         if (SDL_sscanf(sdl_video_window_pos, "%d,%d", &xx, &yy) == 2) {
682             *x = xx;
683             *y = yy;
684             *center_window = 0;
685             return 1;
686         }
687         if (SDL_strcmp(sdl_video_window_pos, "center") == 0) {
688             sdl_video_centered = sdl_video_window_pos;
689         }
690     }
691     if (sdl_video_centered) {
692         *center_window = 1;
693         return 2;
694     }
695     return 0;
696 }
697 
698 static int SDLCALL
pg_ResizeEventWatch(void * userdata,SDL_Event * event)699 pg_ResizeEventWatch(void *userdata, SDL_Event *event) {
700     SDL_Window *pygame_window;
701     PyObject *self;
702     _DisplayState *state;
703     SDL_Window *window;
704 
705     if (event->type != SDL_WINDOWEVENT)
706         return 0;
707 
708     self= (PyObject *) userdata;
709     pygame_window = pg_GetDefaultWindow();
710     state = DISPLAY_MOD_STATE(self);
711 
712     window = SDL_GetWindowFromID(event->window.windowID);
713     if (window != pygame_window)
714         return 0;
715 
716     if (pg_renderer!=NULL) {
717 #if (SDL_VERSION_ATLEAST(2, 0, 5))
718 
719         if (event->window.event == SDL_WINDOWEVENT_MAXIMIZED) {
720             SDL_RenderSetIntegerScale(pg_renderer,
721                                       SDL_FALSE);
722         }
723         if (event->window.event == SDL_WINDOWEVENT_RESTORED) {
724             SDL_RenderSetIntegerScale(pg_renderer,
725                                       !(SDL_GetHintBoolean("SDL_HINT_RENDER_SCALE_QUALITY",SDL_FALSE)));
726         }
727 #endif
728         return 0;
729     }
730 
731     if (state->using_gl) {
732         if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
733 
734             GL_glViewport_Func p_glViewport = (GL_glViewport_Func)SDL_GL_GetProcAddress("glViewport");
735             int wnew=event->window.data1;
736             int hnew=event->window.data2;
737             SDL_GL_MakeCurrent(pygame_window, state->gl_context);
738             if (state->scaled_gl) {
739                 float saved_aspect_ratio =
740                     ((float)state->scaled_gl_w) / (float)state->scaled_gl_h;
741                 float window_aspect_ratio = ((float)wnew) / (float)hnew;
742 
743                 if (window_aspect_ratio > saved_aspect_ratio) {
744                     int width = (int)(hnew * saved_aspect_ratio);
745                     p_glViewport((wnew - width) / 2, 0, width, hnew);
746                 }
747                 else {
748                     p_glViewport(0, 0, wnew, (int)(wnew / saved_aspect_ratio));
749                 }
750             }
751             else {
752                 p_glViewport(0, 0, wnew, hnew);
753             }
754         }
755         return 0;
756     }
757 
758     if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
759         if (window == pygame_window) {
760             SDL_Surface *sdl_surface = SDL_GetWindowSurface(window);
761             pgSurfaceObject *old_surface = pg_GetDefaultWindowSurface();
762             if (sdl_surface != old_surface->surf) {
763                 old_surface->surf=sdl_surface;
764             }
765         }
766     }
767     return 0;
768 }
769 
770 static PyObject *
pg_display_set_autoresize(PyObject * self,PyObject * args)771 pg_display_set_autoresize(PyObject *self, PyObject *args)
772 {
773     SDL_bool do_resize;
774     _DisplayState *state = DISPLAY_MOD_STATE(self);
775 
776     if (!PyArg_ParseTuple(args, "p",  &do_resize))
777         return NULL;
778 
779     state->auto_resize=do_resize;
780     SDL_DelEventWatch(pg_ResizeEventWatch, self);
781 
782     if(do_resize) {
783         SDL_AddEventWatch(pg_ResizeEventWatch, self);
784         Py_RETURN_TRUE;
785     }
786     else {
787         Py_RETURN_FALSE;
788     }
789 }
790 
791 
792 int
_get_display(SDL_Window * win)793 _get_display(SDL_Window *win)
794 {
795     char *display_env = SDL_getenv("PYGAME_DISPLAY");
796     int display = 0; /* default display 0 */
797 
798     if (win != NULL) {
799         display = SDL_GetWindowDisplayIndex(win);
800         return display;
801     }
802     else if (display_env != NULL) {
803         display = SDL_atoi(display_env);
804         return display;
805     }
806     /* On e.g. Linux X11, checking the mouse pointer requires that the
807      * video subsystem is initialized to avoid crashes.
808      *
809      * Note that we do not bother raising an error here; the condition will
810      * be rechecked after parsing the arguments and the function will throw
811      * the relevant error there.
812      */
813     else if (SDL_WasInit(SDL_INIT_VIDEO)) {
814         /* get currently "active" desktop, containing mouse ptr */
815         int num_displays, i;
816         SDL_Rect display_bounds;
817         SDL_Point mouse_position;
818         SDL_GetGlobalMouseState(&mouse_position.x, &mouse_position.y);
819         num_displays = SDL_GetNumVideoDisplays();
820 
821         for (i = 0; i < num_displays; i++) {
822             if (SDL_GetDisplayBounds(i, &display_bounds) == 0) {
823                 if (SDL_PointInRect(&mouse_position, &display_bounds)) {
824                     display = i;
825                     break;
826                 }
827             }
828         }
829     }
830     return display;
831 }
832 
833 static PyObject *
pg_set_mode(PyObject * self,PyObject * arg,PyObject * kwds)834 pg_set_mode(PyObject *self, PyObject *arg, PyObject *kwds)
835 {
836     static const char *const DefaultTitle = "pygame window";
837 
838     _DisplayState *state = DISPLAY_MOD_STATE(self);
839     SDL_Window *win = pg_GetDefaultWindow();
840     pgSurfaceObject *surface = pg_GetDefaultWindowSurface();
841     SDL_Surface *surf = NULL;
842     SDL_Surface *newownedsurf = NULL;
843     int depth = 0;
844     int flags = 0;
845     int w, h;
846     PyObject *size = NULL;
847     int vsync = SDL_FALSE;
848     /* display will get overwritten by ParseTupleAndKeywords only if display
849        parameter is given. By default, put the new window on the same
850        screen as the old one */
851     int display = _get_display(win);
852     char *title = state->title;
853     int init_flip = 0;
854     char *scale_env;
855 
856     char *keywords[] = {"size", "flags", "depth", "display", "vsync", NULL};
857 
858     scale_env = SDL_getenv("PYGAME_FORCE_SCALE");
859 
860     if (!PyArg_ParseTupleAndKeywords(arg, kwds, "|Oiiii", keywords, &size,
861                                      &flags, &depth, &display, &vsync))
862         return NULL;
863 
864     if (scale_env != NULL) {
865         flags |= PGS_SCALED;
866         if (strcmp(scale_env, "photo") == 0) {
867             SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, "best",
868                                     SDL_HINT_NORMAL);
869         }
870     }
871 
872     if (size != NULL) {
873         if (!pg_TwoIntsFromObj(size, &w, &h))
874             return RAISE(PyExc_TypeError, "size must be two numbers");
875         if (w < 0 || h < 0)
876             return RAISE(pgExc_SDLError, "Cannot set negative sized display mode");
877     }
878     else {
879         w = 0;
880         h = 0;
881     }
882 
883     if (!SDL_WasInit(SDL_INIT_VIDEO)) {
884         /* note SDL works special like this too */
885         if (!pg_display_init(NULL))
886             return NULL;
887     }
888 
889     state->using_gl = (flags & PGS_OPENGL) != 0;
890     state->scaled_gl = state->using_gl && (flags & PGS_SCALED) != 0;
891 
892     if (state->scaled_gl) {
893         if (PyErr_WarnEx(PyExc_FutureWarning,
894                          "SCALED|OPENGL is experimental and subject to change",
895                          1) != 0)
896             return NULL;
897     }
898 
899     if (!state->title) {
900         state->title = malloc((strlen(DefaultTitle) + 1) * sizeof(char));
901         if (!state->title)
902             return PyErr_NoMemory();
903         strcpy(state->title, DefaultTitle);
904         title = state->title;
905     }
906 
907     // if (vsync && !(flags & (PGS_SCALED | PGS_OPENGL))) {
908     //     return RAISE(pgExc_SDLError,
909     //                  "vsync needs either SCALED or OPENGL flag");
910     // }
911 
912     /* set these only in toggle_fullscreen, clear on set_mode */
913     state->toggle_windowed_w = 0;
914     state->toggle_windowed_h = 0;
915 
916     if (pg_texture) {
917         SDL_DestroyTexture(pg_texture);
918         pg_texture = NULL;
919     }
920 
921     if (pg_renderer) {
922         SDL_DestroyRenderer(pg_renderer);
923         pg_renderer = NULL;
924     }
925 
926     SDL_DelEventWatch(pg_ResizeEventWatch, self);
927 
928     {
929         Uint32 sdl_flags = 0;
930         SDL_DisplayMode display_mode;
931 
932         if (SDL_GetDesktopDisplayMode(display, &display_mode) != 0) {
933             return RAISE(pgExc_SDLError, SDL_GetError());
934         }
935 
936         if (w == 0 && h == 0 && !(flags & PGS_SCALED)) {
937             /* We are free to choose a resolution in this case, so we can
938            avoid changing the physical resolution. This used to default
939            to the max supported by the monitor, but we can use current
940            desktop resolution without breaking compatibility. */
941             w = display_mode.w;
942             h = display_mode.h;
943         }
944 
945         if (flags & PGS_FULLSCREEN) {
946             if (flags & PGS_SCALED) {
947                 sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
948             }
949             else if (w == display_mode.w && h == display_mode.h) {
950                 /* No need to change physical resolution.
951                Borderless fullscreen is preferred when possible */
952                 sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
953             }
954             else {
955                 sdl_flags |= SDL_WINDOW_FULLSCREEN;
956             }
957         }
958 
959         if (flags & PGS_SCALED) {
960             if (w == 0 || h == 0)
961                 return RAISE(pgExc_SDLError,
962                              "Cannot set 0 sized SCALED display mode");
963         }
964 
965         if (flags & PGS_OPENGL)
966             sdl_flags |= SDL_WINDOW_OPENGL;
967         if (flags & PGS_NOFRAME)
968             sdl_flags |= SDL_WINDOW_BORDERLESS;
969         if (flags & PGS_RESIZABLE) {
970             sdl_flags |= SDL_WINDOW_RESIZABLE;
971             if(state->auto_resize)
972                 SDL_AddEventWatch(pg_ResizeEventWatch, self);
973         }
974         if (flags & PGS_SHOWN)
975             sdl_flags |= SDL_WINDOW_SHOWN;
976         if (flags & PGS_HIDDEN)
977             sdl_flags |= SDL_WINDOW_HIDDEN;
978         if (!(sdl_flags & SDL_WINDOW_HIDDEN))
979             sdl_flags |= SDL_WINDOW_SHOWN;
980         if (flags & PGS_OPENGL) {
981             /* Must be called before creating context */
982             if (flags & PGS_DOUBLEBUF) {
983                 flags &= ~PGS_DOUBLEBUF;
984                 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
985             }
986             else
987                 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
988         }
989 
990 #pragma PG_WARN(Not setting bpp ?)
991 #pragma PG_WARN(Add mode stuff.)
992         {
993             int w_1 = w, h_1 = h;
994             int scale = 1;
995             int center_window = 0;
996             int x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
997             int y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
998 
999             _get_video_window_pos(&x, &y, &center_window);
1000             if (center_window) {
1001                 x = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
1002                 y = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
1003             }
1004 
1005             if (win) {
1006                 if (SDL_GetWindowDisplayIndex(win) == display) {
1007                     // fullscreen windows don't hold window x and y as needed
1008                     if (SDL_GetWindowFlags(win) & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) {
1009                         x = state->fullscreen_backup_x;
1010                         y = state->fullscreen_backup_y;
1011 
1012                         // if the program goes into fullscreen first the "saved x and y" are "undefined position"
1013                         // that should be interpreted as a cue to center the window
1014                         if (x == SDL_WINDOWPOS_UNDEFINED_DISPLAY(display))
1015                             x = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
1016                         if (y == SDL_WINDOWPOS_UNDEFINED_DISPLAY(display))
1017                             y = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
1018                     }
1019                     else {
1020                         SDL_GetWindowPosition(win, &x, &y);
1021                     }
1022 
1023                 }
1024                 if (!(flags & PGS_OPENGL) !=
1025                     !(SDL_GetWindowFlags(win) & SDL_WINDOW_OPENGL)) {
1026                     pg_SetDefaultWindow(NULL);
1027                     win = NULL;
1028                 }
1029             }
1030 
1031             if (flags & PGS_SCALED && !(flags & PGS_FULLSCREEN)) {
1032                 SDL_Rect display_bounds;
1033                 int fractional_scaling = SDL_FALSE;
1034 
1035 #if (SDL_VERSION_ATLEAST(2, 0, 5))
1036                 if (0 !=
1037                     SDL_GetDisplayUsableBounds(display, &display_bounds)) {
1038                     return RAISE(pgExc_SDLError, SDL_GetError());
1039                 }
1040 #else
1041                 display_bounds.w = display_mode.w - 80;
1042                 display_bounds.h = display_mode.h - 30;
1043 #endif
1044 
1045 #if (SDL_VERSION_ATLEAST(2, 0, 5))
1046                 if (SDL_GetHintBoolean("SDL_HINT_RENDER_SCALE_QUALITY",
1047                                        SDL_FALSE))
1048                     fractional_scaling = SDL_TRUE;
1049 #endif
1050                 if (state->scaled_gl)
1051                     fractional_scaling = SDL_TRUE;
1052 
1053                 if (fractional_scaling) {
1054                     float aspect_ratio = ((float)w) / (float)h;
1055 
1056                     w_1 = display_bounds.w;
1057                     h_1 = display_bounds.h;
1058 
1059                     if (((float)w_1) / (float)h_1 > aspect_ratio) {
1060                         w_1 = h_1 * aspect_ratio;
1061                     }
1062                     else {
1063                         h_1 = w_1 / aspect_ratio;
1064                     }
1065                 }
1066                 else {
1067                     int xscale, yscale;
1068 
1069                     xscale = display_bounds.w / w;
1070                     yscale = display_bounds.h / h;
1071 
1072                     scale = xscale < yscale ? xscale : yscale;
1073 
1074                     if (scale < 1)
1075                         scale = 1;
1076 
1077                     w_1 = w * scale;
1078                     h_1 = h * scale;
1079                 }
1080             }
1081 
1082             // SDL doesn't preserve window position in fullscreen mode
1083             // However, windows coming out of fullscreen need these to go back into the correct position
1084             if (sdl_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) {
1085                 state->fullscreen_backup_x = x;
1086                 state->fullscreen_backup_y = y;
1087             }
1088 
1089             if (!win) {
1090                 /*open window*/
1091                 win = SDL_CreateWindow(title, x, y, w_1, h_1, sdl_flags);
1092                 if (!win)
1093                     return RAISE(pgExc_SDLError, SDL_GetError());
1094                 init_flip = 1;
1095             }
1096             else {
1097                 /* set min size to (1,1) to erase any previously set min size
1098                  * relevant for windows leaving SCALED, which sets a min size
1099                  * only relevant on Windows, I believe.
1100                  * See https://github.com/pygame/pygame/issues/2327 */
1101                 SDL_SetWindowMinimumSize(win, 1, 1);
1102 
1103                 /* change existing window.
1104                  this invalidates the display surface*/
1105                 SDL_SetWindowTitle(win, title);
1106                 SDL_SetWindowSize(win, w_1, h_1);
1107 
1108 #if (SDL_VERSION_ATLEAST(2, 0, 5))
1109                 SDL_SetWindowResizable(win, flags & PGS_RESIZABLE);
1110 #endif
1111                 SDL_SetWindowBordered(win, (flags & PGS_NOFRAME) == 0);
1112 
1113                 if ((flags & PGS_SHOWN) || !(flags & PGS_HIDDEN))
1114                     SDL_ShowWindow(win);
1115                 else if (flags & PGS_HIDDEN)
1116                     SDL_HideWindow(win);
1117 
1118                 if (0 !=
1119                     SDL_SetWindowFullscreen(
1120                         win, sdl_flags & (SDL_WINDOW_FULLSCREEN |
1121                                           SDL_WINDOW_FULLSCREEN_DESKTOP))) {
1122                     return RAISE(pgExc_SDLError, SDL_GetError());
1123                 }
1124 
1125                 SDL_SetWindowPosition(win, x, y);
1126 
1127                 assert(surface);
1128             }
1129         }
1130 
1131         if (state->using_gl) {
1132             if (!state->gl_context) {
1133                 state->gl_context = SDL_GL_CreateContext(win);
1134                 if (!state->gl_context) {
1135                     _display_state_cleanup(state);
1136                     PyErr_SetString(pgExc_SDLError, SDL_GetError());
1137                     goto DESTROY_WINDOW;
1138                 }
1139                 /* SDL_GetWindowSurface can not be used when using GL.
1140                 According to https://wiki.libsdl.org/SDL_GetWindowSurface
1141 
1142                 So we make a fake surface.
1143                 */
1144                 surf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
1145                                             0xff << 16, 0xff << 8, 0xff, 0);
1146                 newownedsurf = surf;
1147             }
1148             else {
1149                 surf = pgSurface_AsSurface(surface);
1150             }
1151             if (flags & PGS_SCALED) {
1152                 state->scaled_gl_w = w;
1153                 state->scaled_gl_h = h;
1154             }
1155 
1156             /* Even if this succeeds, we can never *really* know if vsync
1157                actually works. There may be screen tearing, blocking double
1158                buffering, triple buffering, render-offloading where the driver
1159                for the on-board graphics *doesn't* have vsync enabled, or cases
1160                where the driver lies to us because the user has configured
1161                vsync to be aways on or always off, or vsync is on by default
1162                for the whole desktop because of wayland GL compositing. */
1163             if (vsync) {
1164                 if (SDL_GL_SetSwapInterval(-1) != 0) {
1165                     if (PyErr_WarnEx(PyExc_Warning,
1166                                      "adaptive vsync for OpenGL not "
1167                                      "available, trying regular",
1168                                      1) != 0) {
1169                         _display_state_cleanup(state);
1170                         goto DESTROY_WINDOW;
1171                     }
1172                     if (SDL_GL_SetSwapInterval(1) != 0) {
1173                         if (PyErr_WarnEx(PyExc_Warning,
1174                                          "regular vsync for OpenGL *also* not "
1175                                          "available",
1176                                          1) != 0) {
1177                             _display_state_cleanup(state);
1178                             goto DESTROY_WINDOW;
1179                         }
1180                     }
1181                 }
1182             }
1183             else {
1184                 SDL_GL_SetSwapInterval(0);
1185             }
1186         }
1187         else {
1188             if (state->gl_context) {
1189                 SDL_GL_DeleteContext(state->gl_context);
1190                 state->gl_context = NULL;
1191             }
1192 
1193             if (flags & PGS_SCALED) {
1194                 if (pg_renderer == NULL) {
1195                     SDL_RendererInfo info;
1196 
1197                     SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY,
1198                                             "nearest", SDL_HINT_DEFAULT);
1199 
1200                     if (vsync) {
1201                         pg_renderer = SDL_CreateRenderer(
1202                             win, -1, SDL_RENDERER_PRESENTVSYNC);
1203                     }
1204                     else {
1205                         pg_renderer = SDL_CreateRenderer(win, -1, 0);
1206                     }
1207 
1208                     if (pg_renderer == NULL) {
1209                         return RAISE(pgExc_SDLError,
1210                                      "failed to create renderer");
1211                     }
1212 
1213 #if (SDL_VERSION_ATLEAST(2, 0, 5))
1214 
1215                     /* use whole screen with uneven pixels on fullscreen,
1216                        exact scale otherwise.
1217                        we chose the window size for this to work */
1218                     SDL_RenderSetIntegerScale(
1219                         pg_renderer,
1220                         !(flags & PGS_FULLSCREEN ||
1221                           SDL_GetHintBoolean("SDL_HINT_RENDER_SCALE_QUALITY",
1222                                              SDL_FALSE)));
1223 #endif
1224                     SDL_RenderSetLogicalSize(pg_renderer, w, h);
1225                     /* this must be called after creating the renderer!*/
1226                     SDL_SetWindowMinimumSize(win, w, h);
1227 
1228                     SDL_GetRendererInfo(pg_renderer, &info);
1229                     if (vsync && !(info.flags & SDL_RENDERER_PRESENTVSYNC)) {
1230                         if (PyErr_WarnEx(PyExc_Warning,
1231                                          "could not enable vsync", 1) != 0) {
1232                             _display_state_cleanup(state);
1233                             goto DESTROY_WINDOW;
1234                         }
1235                     }
1236                     if (!(info.flags & SDL_RENDERER_ACCELERATED)) {
1237                         if (PyErr_WarnEx(PyExc_Warning,
1238                                          "no fast renderer available",
1239                                          1) != 0) {
1240                             _display_state_cleanup(state);
1241                             goto DESTROY_WINDOW;
1242                         }
1243                     }
1244 
1245                     pg_texture = SDL_CreateTexture(
1246                         pg_renderer, SDL_PIXELFORMAT_ARGB8888,
1247                         SDL_TEXTUREACCESS_STREAMING, w, h);
1248                 }
1249                 surf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
1250                                             0xff << 16, 0xff << 8, 0xff, 0);
1251                 newownedsurf = surf;
1252             }
1253             else {
1254                 surf = SDL_GetWindowSurface(win);
1255             }
1256         }
1257         if (state->gamma_ramp) {
1258             int result = SDL_SetWindowGammaRamp(win, state->gamma_ramp,
1259                                                 state->gamma_ramp + 256,
1260                                                 state->gamma_ramp + 512);
1261             if (result) /* SDL Error? */
1262             {
1263                 /* Discard a possibly faulty gamma ramp. */
1264                 _display_state_cleanup(state);
1265 
1266                 /* Recover error, then destroy the window */
1267                 PyErr_SetString(pgExc_SDLError, SDL_GetError());
1268                 goto DESTROY_WINDOW;
1269             }
1270         }
1271 
1272         if (state->using_gl && pg_renderer != NULL) {
1273             _display_state_cleanup(state);
1274             PyErr_SetString(
1275                 pgExc_SDLError,
1276                 "GL context and SDL_Renderer created at the same time");
1277             goto DESTROY_WINDOW;
1278         }
1279 
1280         if (!surf) {
1281             _display_state_cleanup(state);
1282             PyErr_SetString(pgExc_SDLError, SDL_GetError());
1283             goto DESTROY_WINDOW;
1284         }
1285         if (!surface) {
1286             surface = pgSurface_New2(surf, newownedsurf != NULL);
1287         }
1288         else {
1289             pgSurface_SetSurface(surface, surf, newownedsurf != NULL);
1290             Py_INCREF(surface);
1291         }
1292         if (!surface) {
1293             if (newownedsurf)
1294                 SDL_FreeSurface(newownedsurf);
1295             _display_state_cleanup(state);
1296             goto DESTROY_WINDOW;
1297         }
1298 
1299         /*no errors; make the window available*/
1300         pg_SetDefaultWindow(win);
1301         pg_SetDefaultWindowSurface(surface);
1302         Py_DECREF(surface);
1303 
1304         /* ensure window is initially black */
1305         if (init_flip)
1306             pg_flip_internal(state);
1307     }
1308 
1309     /*set the window icon*/
1310     if (!state->icon) {
1311         state->icon = pg_display_resource(icon_defaultname);
1312         if (!state->icon)
1313             PyErr_Clear();
1314         else {
1315             SDL_SetColorKey(pgSurface_AsSurface(state->icon), SDL_TRUE, 0);
1316         }
1317     }
1318     if (state->icon)
1319         SDL_SetWindowIcon(win, pgSurface_AsSurface(state->icon));
1320 
1321     /*probably won't do much, but can't hurt, and might help*/
1322     SDL_PumpEvents();
1323 
1324     /*return the window's surface (screen)*/
1325     Py_INCREF(surface);
1326     return (PyObject *)surface;
1327 
1328 DESTROY_WINDOW:
1329 
1330     if (win == pg_GetDefaultWindow())
1331         pg_SetDefaultWindow(NULL);
1332     else if (win)
1333         SDL_DestroyWindow(win);
1334     return NULL;
1335 }
1336 
1337 static int
_pg_get_default_display_masks(int bpp,Uint32 * Rmask,Uint32 * Gmask,Uint32 * Bmask)1338 _pg_get_default_display_masks(int bpp, Uint32 *Rmask, Uint32 *Gmask,
1339                               Uint32 *Bmask)
1340 {
1341     switch (bpp) {
1342         case 8:
1343             *Rmask = 0;
1344             *Gmask = 0;
1345             *Bmask = 0;
1346             break;
1347         case 12:
1348             *Rmask = 0xFF >> 4 << 8;
1349             *Gmask = 0xFF >> 4 << 4;
1350             *Bmask = 0xFF >> 4;
1351             break;
1352         case 15:
1353             *Rmask = 0xFF >> 3 << 10;
1354             *Gmask = 0xFF >> 3 << 5;
1355             *Bmask = 0xFF >> 3;
1356             break;
1357         case 16:
1358             *Rmask = 0xFF >> 3 << 11;
1359             *Gmask = 0xFF >> 2 << 5;
1360             *Bmask = 0xFF >> 3;
1361             break;
1362         case 24:
1363         case 32:
1364             *Rmask = 0xFF << 16;
1365             *Gmask = 0xFF << 8;
1366             *Bmask = 0xFF;
1367             break;
1368         default:
1369             PyErr_SetString(PyExc_ValueError, "nonstandard bit depth given");
1370             return -1;
1371     }
1372     return 0;
1373 }
1374 
1375 static PyObject *
pg_window_size(PyObject * self)1376 pg_window_size(PyObject *self)
1377 {
1378     SDL_Window *win = pg_GetDefaultWindow();
1379     int w, h;
1380     if (!win)
1381         return RAISE(pgExc_SDLError, "No open window");
1382     SDL_GetWindowSize(win, &w, &h);
1383     return Py_BuildValue("(ii)", w, h);
1384 }
1385 
1386 static PyObject *
pg_mode_ok(PyObject * self,PyObject * args,PyObject * kwds)1387 pg_mode_ok(PyObject *self, PyObject *args, PyObject *kwds)
1388 {
1389     SDL_DisplayMode desired, closest;
1390     int bpp = 0;
1391     int flags = SDL_SWSURFACE;
1392     int display_index = 0;
1393 
1394     char *keywords[] = {"size", "flags", "depth", "display", NULL};
1395 
1396     VIDEO_INIT_CHECK();
1397 
1398     if (!PyArg_ParseTupleAndKeywords(args, kwds, "(ii)|iii", keywords,
1399                                      &desired.w, &desired.h, &flags, &bpp,
1400                                      &display_index)) {
1401         return NULL;
1402     }
1403     if (display_index < 0 || display_index >= SDL_GetNumVideoDisplays()) {
1404         return RAISE(PyExc_ValueError,
1405                      "The display index must be between 0"
1406                      " and the number of displays.");
1407     }
1408 #pragma PG_WARN(Ignoring most flags)
1409 
1410     desired.driverdata = 0;
1411     desired.refresh_rate = 0;
1412 
1413     if (bpp == 0) {
1414         desired.format = 0;
1415     }
1416     else {
1417         Uint32 Rmask, Gmask, Bmask;
1418         if (_pg_get_default_display_masks(bpp, &Rmask, &Gmask, &Bmask)) {
1419             PyErr_Clear();
1420             return PyInt_FromLong((long)0);
1421         }
1422         desired.format =
1423             SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, 0);
1424     }
1425     if (!SDL_GetClosestDisplayMode(display_index, &desired, &closest)) {
1426         if (flags & PGS_FULLSCREEN)
1427             return PyInt_FromLong((long)0);
1428         closest.format = desired.format;
1429     }
1430     if ((flags & PGS_FULLSCREEN) &&
1431         (closest.w != desired.w || closest.h != desired.h))
1432         return PyInt_FromLong((long)0);
1433     return PyInt_FromLong(SDL_BITSPERPIXEL(closest.format));
1434 }
1435 
1436 static PyObject *
pg_list_modes(PyObject * self,PyObject * args,PyObject * kwds)1437 pg_list_modes(PyObject *self, PyObject *args, PyObject *kwds)
1438 {
1439     SDL_DisplayMode mode;
1440     int nummodes;
1441     int bpp = 0;
1442     int flags = PGS_FULLSCREEN;
1443     int display_index = 0;
1444     int last_width = -1, last_height = -1;
1445     PyObject *list, *size;
1446     int i;
1447 
1448     char *keywords[] = {"depth", "flags", "display", NULL};
1449 
1450     VIDEO_INIT_CHECK();
1451 
1452     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|bii", keywords, &bpp,
1453                                      &flags, &display_index)) {
1454         return NULL;
1455     }
1456 
1457     if (display_index < 0 || display_index >= SDL_GetNumVideoDisplays()) {
1458         return RAISE(PyExc_ValueError,
1459                      "The display index must be between 0"
1460                      " and the number of displays.");
1461     }
1462 #pragma PG_WARN(Ignoring flags)
1463 
1464     if (bpp == 0) {
1465         SDL_DisplayMode curmode;
1466         if (SDL_GetCurrentDisplayMode(display_index, &curmode))
1467             return RAISE(pgExc_SDLError, SDL_GetError());
1468         bpp = SDL_BITSPERPIXEL(curmode.format);
1469     }
1470 
1471     nummodes = SDL_GetNumDisplayModes(display_index);
1472     if (nummodes < 0)
1473         return RAISE(pgExc_SDLError, SDL_GetError());
1474 
1475     if (!(list = PyList_New(0)))
1476         return NULL;
1477 
1478     for (i = 0; i < nummodes; i++) {
1479         if (SDL_GetDisplayMode(display_index, i, &mode) < 0) {
1480             Py_DECREF(list);
1481             return RAISE(pgExc_SDLError, SDL_GetError());
1482         }
1483         /* use reasonable defaults (cf. SDL_video.c) */
1484         if (!mode.format)
1485             mode.format = SDL_PIXELFORMAT_RGB888;
1486         if (!mode.w)
1487             mode.w = 640;
1488         if (!mode.h)
1489             mode.h = 480;
1490         if (SDL_BITSPERPIXEL(mode.format) != bpp)
1491             continue;
1492         if (last_width == mode.w && last_height == mode.h && last_width != -1) {
1493             continue;
1494         }
1495         if (!(size = Py_BuildValue("(ii)", mode.w, mode.h))) {
1496             Py_DECREF(list);
1497             return NULL;
1498         }
1499         last_width = mode.w;
1500         last_height = mode.h;
1501         if (0 != PyList_Append(list, size)) {
1502             Py_DECREF(list);
1503             Py_DECREF(size);
1504             return NULL; /* Exception already set. */
1505         }
1506         Py_DECREF(size);
1507     }
1508     return list;
1509 }
1510 
1511 static int
pg_flip_internal(_DisplayState * state)1512 pg_flip_internal(_DisplayState *state)
1513 {
1514     SDL_Window *win = pg_GetDefaultWindow();
1515     int status = 0;
1516 
1517     /* Same check as VIDEO_INIT_CHECK() but returns -1 instead of NULL on
1518      * fail. */
1519     if (!SDL_WasInit(SDL_INIT_VIDEO)) {
1520         PyErr_SetString(pgExc_SDLError, "video system not initialized");
1521         return -1;
1522     }
1523 
1524     if (!win) {
1525         PyErr_SetString(pgExc_SDLError, "Display mode not set");
1526         return -1;
1527     }
1528 
1529     Py_BEGIN_ALLOW_THREADS;
1530     if (state->using_gl) {
1531         SDL_GL_SwapWindow(win);
1532     }
1533     else {
1534         if (pg_renderer != NULL) {
1535             SDL_Surface *screen =
1536                 pgSurface_AsSurface(pg_GetDefaultWindowSurface());
1537             SDL_UpdateTexture(pg_texture, NULL, screen->pixels, screen->pitch);
1538             SDL_RenderClear(pg_renderer);
1539             SDL_RenderCopy(pg_renderer, pg_texture, NULL, NULL);
1540             SDL_RenderPresent(pg_renderer);
1541         }
1542         else {
1543             /* Force a re-initialization of the surface in case it
1544              * has been resized to avoid "please call SDL_GetWindowSurface"
1545              * errors that the programmer cannot fix
1546              */
1547             pgSurfaceObject *screen = pg_GetDefaultWindowSurface();
1548             SDL_Surface *new_surface = SDL_GetWindowSurface(win);
1549 
1550             if (new_surface != ((pgSurfaceObject *)screen)->surf) {
1551                 screen->surf = new_surface;
1552             }
1553             status = SDL_UpdateWindowSurface(win);
1554         }
1555     }
1556     Py_END_ALLOW_THREADS;
1557 
1558     if (status < 0) {
1559         PyErr_SetString(pgExc_SDLError, SDL_GetError());
1560         return -1;
1561     }
1562 
1563     return 0;
1564 }
1565 
1566 static PyObject *
pg_flip(PyObject * self)1567 pg_flip(PyObject *self)
1568 {
1569     if (pg_flip_internal(DISPLAY_MOD_STATE(self)) < 0) {
1570         return NULL;
1571     }
1572     Py_RETURN_NONE;
1573 }
1574 
1575 static PyObject *
pg_num_displays(PyObject * self)1576 pg_num_displays(PyObject *self)
1577 {
1578     int ret = SDL_GetNumVideoDisplays();
1579     if (ret < 0)
1580         return RAISE(pgExc_SDLError, SDL_GetError());
1581     return PyInt_FromLong(ret);
1582 }
1583 
1584 
1585 /*BAD things happen when out-of-bound rects go to updaterect*/
1586 static SDL_Rect *
pg_screencroprect(GAME_Rect * r,int w,int h,SDL_Rect * cur)1587 pg_screencroprect(GAME_Rect *r, int w, int h, SDL_Rect *cur)
1588 {
1589     if (r->x > w || r->y > h || (r->x + r->w) <= 0 || (r->y + r->h) <= 0)
1590         return 0;
1591     else {
1592         int right = MIN(r->x + r->w, w);
1593         int bottom = MIN(r->y + r->h, h);
1594         cur->x = (short)MAX(r->x, 0);
1595         cur->y = (short)MAX(r->y, 0);
1596         cur->w = (unsigned short)right - cur->x;
1597         cur->h = (unsigned short)bottom - cur->y;
1598     }
1599     return cur;
1600 }
1601 
1602 static PyObject *
pg_update(PyObject * self,PyObject * arg)1603 pg_update(PyObject *self, PyObject *arg)
1604 {
1605     SDL_Window *win = pg_GetDefaultWindow();
1606     _DisplayState *state = DISPLAY_MOD_STATE(self);
1607     GAME_Rect *gr, temp = {0};
1608     int wide, high;
1609     PyObject *obj;
1610 
1611     VIDEO_INIT_CHECK();
1612 
1613     if (!win)
1614         return RAISE(pgExc_SDLError, "Display mode not set");
1615     if (pg_renderer != NULL) {
1616         return pg_flip(self);
1617     }
1618     SDL_GetWindowSize(win, &wide, &high);
1619 
1620     if (state->using_gl)
1621         return RAISE(pgExc_SDLError, "Cannot update an OPENGL display");
1622 
1623     /*determine type of argument we got*/
1624     if (PyTuple_Size(arg) == 0) {
1625         return pg_flip(self);
1626     }
1627     else {
1628         obj = PyTuple_GET_ITEM(arg, 0);
1629         if (obj == Py_None)
1630             gr = &temp;
1631         else {
1632             gr = pgRect_FromObject(arg, &temp);
1633             if (!gr)
1634                 PyErr_Clear();
1635             else if (gr != &temp) {
1636                 memcpy(&temp, gr, sizeof(temp));
1637                 gr = &temp;
1638             }
1639         }
1640     }
1641 
1642     if (gr) {
1643         SDL_Rect sdlr;
1644 
1645         if (pg_screencroprect(gr, wide, high, &sdlr))
1646             SDL_UpdateWindowSurfaceRects(win, &sdlr, 1);
1647     }
1648     else {
1649         PyObject *seq;
1650         PyObject *r;
1651         Py_ssize_t loop, num;
1652         int count;
1653         SDL_Rect *rects;
1654         if (PyTuple_Size(arg) != 1)
1655             return RAISE(
1656                 PyExc_ValueError,
1657                 "update requires a rectstyle or sequence of recstyles");
1658         seq = PyTuple_GET_ITEM(arg, 0);
1659         if (!seq || !PySequence_Check(seq))
1660             return RAISE(
1661                 PyExc_ValueError,
1662                 "update requires a rectstyle or sequence of recstyles");
1663 
1664         num = PySequence_Length(seq);
1665         rects = PyMem_New(SDL_Rect, num);
1666         if (!rects)
1667             return NULL;
1668         count = 0;
1669         for (loop = 0; loop < num; ++loop) {
1670             SDL_Rect *cur_rect = (rects + count);
1671 
1672             /*get rect from the sequence*/
1673             r = PySequence_GetItem(seq, loop);
1674             if (r == Py_None) {
1675                 Py_DECREF(r);
1676                 continue;
1677             }
1678             gr = pgRect_FromObject(r, &temp);
1679             Py_XDECREF(r);
1680             if (!gr) {
1681                 PyMem_Free((char *)rects);
1682                 return RAISE(PyExc_ValueError,
1683                              "update_rects requires a single list of rects");
1684             }
1685 
1686             if (gr->w < 1 && gr->h < 1)
1687                 continue;
1688 
1689             /*bail out if rect not onscreen*/
1690             if (!pg_screencroprect(gr, wide, high, cur_rect))
1691                 continue;
1692 
1693             ++count;
1694         }
1695 
1696         if (count) {
1697             Py_BEGIN_ALLOW_THREADS;
1698             SDL_UpdateWindowSurfaceRects(win, rects, count);
1699             Py_END_ALLOW_THREADS;
1700         }
1701 
1702         PyMem_Free((char *)rects);
1703     }
1704     Py_RETURN_NONE;
1705 }
1706 
1707 static PyObject *
pg_set_palette(PyObject * self,PyObject * args)1708 pg_set_palette(PyObject *self, PyObject *args)
1709 {
1710     pgSurfaceObject *surface = pg_GetDefaultWindowSurface();
1711     SDL_Surface *surf;
1712     SDL_Palette *pal;
1713     SDL_Color *colors;
1714     PyObject *list, *item = NULL;
1715     int i, len;
1716     int r, g, b;
1717 
1718     VIDEO_INIT_CHECK();
1719     if (!PyArg_ParseTuple(args, "|O", &list))
1720         return NULL;
1721     if (!surface)
1722         return RAISE(pgExc_SDLError, "No display mode is set");
1723     Py_INCREF(surface);
1724     surf = pgSurface_AsSurface(surface);
1725     pal = surf->format->palette;
1726     if (surf->format->BytesPerPixel != 1 || !pal) {
1727         Py_DECREF(surface);
1728         return RAISE(pgExc_SDLError, "Display mode is not colormapped");
1729     }
1730 
1731     if (!list) {
1732         Py_DECREF(surface);
1733         Py_RETURN_NONE;
1734     }
1735 
1736     if (!PySequence_Check(list)) {
1737         Py_DECREF(surface);
1738         return RAISE(PyExc_ValueError, "Argument must be a sequence type");
1739     }
1740 
1741     len = MIN(pal->ncolors, PySequence_Length(list));
1742 
1743     colors = (SDL_Color *)malloc(len * sizeof(SDL_Color));
1744     if (!colors) {
1745         Py_DECREF(surface);
1746         return PyErr_NoMemory();
1747     }
1748 
1749     for (i = 0; i < len; i++) {
1750         item = PySequence_GetItem(list, i);
1751         if (!PySequence_Check(item) || PySequence_Length(item) != 3) {
1752             Py_DECREF(item);
1753             free((char *)colors);
1754             Py_DECREF(surface);
1755             return RAISE(PyExc_TypeError,
1756                          "takes a sequence of sequence of RGB");
1757         }
1758         if (!pg_IntFromObjIndex(item, 0, &r) ||
1759             !pg_IntFromObjIndex(item, 1, &g) ||
1760             !pg_IntFromObjIndex(item, 2, &b)) {
1761             Py_DECREF(item);
1762             free((char *)colors);
1763             Py_DECREF(surface);
1764             return RAISE(PyExc_TypeError,
1765                          "RGB sequence must contain numeric values");
1766         }
1767 
1768         colors[i].r = (unsigned char)r;
1769         colors[i].g = (unsigned char)g;
1770         colors[i].b = (unsigned char)b;
1771         colors[i].a = SDL_ALPHA_OPAQUE;
1772 
1773         Py_DECREF(item);
1774     }
1775 
1776     pal = SDL_AllocPalette(len);
1777     if (!pal) {
1778         free((char *)colors);
1779         Py_DECREF(surface);
1780         return RAISE(pgExc_SDLError, SDL_GetError());
1781     }
1782     if (!SDL_SetPaletteColors(pal, colors, 0, len)) {
1783         SDL_FreePalette(pal);
1784         free((char *)colors);
1785         Py_DECREF(surface);
1786         return RAISE(pgExc_SDLError, SDL_GetError());
1787     }
1788 
1789     SDL_SetSurfacePalette(surf, pal);
1790     SDL_FreePalette(pal);
1791     free((char *)colors);
1792     Py_DECREF(surface);
1793     Py_RETURN_NONE;
1794 }
1795 
1796 static PyObject *
pg_set_gamma(PyObject * self,PyObject * arg)1797 pg_set_gamma(PyObject *self, PyObject *arg)
1798 {
1799     float r, g, b;
1800     int result = 0;
1801     _DisplayState *state = DISPLAY_MOD_STATE(self);
1802     SDL_Window *win = pg_GetDefaultWindow();
1803     Uint16 *gamma_ramp;
1804 
1805     if (!PyArg_ParseTuple(arg, "f|ff", &r, &g, &b))
1806         return NULL;
1807     if (PyTuple_Size(arg) == 1)
1808         g = b = r;
1809     VIDEO_INIT_CHECK();
1810 
1811     gamma_ramp = (Uint16 *)malloc((3 * 256) * sizeof(Uint16));
1812     if (!gamma_ramp)
1813         return PyErr_NoMemory();
1814     SDL_CalculateGammaRamp(r, gamma_ramp);
1815     SDL_CalculateGammaRamp(g, gamma_ramp + 256);
1816     SDL_CalculateGammaRamp(b, gamma_ramp + 512);
1817     if (win) {
1818         result = SDL_SetWindowGammaRamp(win, gamma_ramp, gamma_ramp + 256,
1819                                         gamma_ramp + 512);
1820         if (result) {
1821             /* Discard a possibly faulty gamma ramp */
1822             free(gamma_ramp);
1823             gamma_ramp = NULL;
1824         }
1825     }
1826     if (gamma_ramp) {
1827         if (state->gamma_ramp)
1828             free(state->gamma_ramp);
1829         state->gamma_ramp = gamma_ramp;
1830     }
1831     return PyBool_FromLong(result == 0);
1832 }
1833 
1834 
1835 static int
pg_convert_to_uint16(PyObject * python_array,Uint16 * c_uint16_array)1836 pg_convert_to_uint16(PyObject *python_array, Uint16 *c_uint16_array)
1837 {
1838     int i;
1839     PyObject *item;
1840 
1841     if (!c_uint16_array) {
1842         PyErr_SetString(PyExc_RuntimeError,
1843                         "Memory not allocated for c_uint16_array.");
1844         return 0;
1845     }
1846     if (!PySequence_Check(python_array)) {
1847         PyErr_SetString(PyExc_TypeError, "Array must be sequence type");
1848         return 0;
1849     }
1850     if (PySequence_Size(python_array) != 256) {
1851         PyErr_SetString(PyExc_ValueError,
1852                         "gamma ramp must be 256 elements long");
1853         return 0;
1854     }
1855     for (i = 0; i < 256; i++) {
1856         item = PySequence_GetItem(python_array, i);
1857         if (!PyInt_Check(item)) {
1858             PyErr_SetString(PyExc_ValueError,
1859                             "gamma ramp must contain integer elements");
1860             return 0;
1861         }
1862         c_uint16_array[i] = (Uint16)PyInt_AsLong(item);
1863         Py_XDECREF(item);
1864     }
1865     return 1;
1866 }
1867 
1868 static PyObject *
pg_set_gamma_ramp(PyObject * self,PyObject * arg)1869 pg_set_gamma_ramp(PyObject *self, PyObject *arg)
1870 {
1871     _DisplayState *state = DISPLAY_MOD_STATE(self);
1872     SDL_Window *win = pg_GetDefaultWindow();
1873     Uint16 *gamma_ramp = (Uint16 *)malloc((3 * 256) * sizeof(Uint16));
1874     Uint16 *r, *g, *b;
1875     int result = 0;
1876     if (!gamma_ramp)
1877         return PyErr_NoMemory();
1878     r = gamma_ramp;
1879     g = gamma_ramp + 256;
1880     b = gamma_ramp + 512;
1881     if (!PyArg_ParseTuple(arg, "O&O&O&", pg_convert_to_uint16, r,
1882                           pg_convert_to_uint16, g, pg_convert_to_uint16, b)) {
1883         free(gamma_ramp);
1884         return NULL;
1885     }
1886     VIDEO_INIT_CHECK();
1887     if (win) {
1888         result = SDL_SetWindowGammaRamp(win, gamma_ramp, gamma_ramp + 256,
1889                                         gamma_ramp + 512);
1890         if (result) {
1891             /* Discard a possibly faulty gamma ramp */
1892             free(gamma_ramp);
1893             gamma_ramp = NULL;
1894         }
1895     }
1896     if (gamma_ramp) {
1897         if (state->gamma_ramp)
1898             free(state->gamma_ramp);
1899         state->gamma_ramp = gamma_ramp;
1900     }
1901     return PyBool_FromLong(result == 0);
1902 }
1903 
1904 static PyObject *
pg_set_caption(PyObject * self,PyObject * arg)1905 pg_set_caption(PyObject *self, PyObject *arg)
1906 {
1907     _DisplayState *state = DISPLAY_MOD_STATE(self);
1908     SDL_Window *win = pg_GetDefaultWindow();
1909     char *title, *icontitle = NULL;
1910     if (!PyArg_ParseTuple(arg, "es|es", "UTF-8", &title, "UTF-8", &icontitle))
1911         return NULL;
1912 
1913     if (state->title)
1914         free(state->title);
1915     state->title = (char *)malloc((strlen(title) + 1) * sizeof(char));
1916     if (!state->title) {
1917         PyErr_NoMemory();
1918         goto error;
1919     }
1920     strcpy(state->title, title);
1921     if (win)
1922         SDL_SetWindowTitle(win, title);
1923     /* TODO: icon title? */
1924 
1925     PyMem_Free(title);
1926     PyMem_Free(icontitle);
1927     Py_RETURN_NONE;
1928 
1929 error:
1930     PyMem_Free(title);
1931     PyMem_Free(icontitle);
1932     return NULL;
1933 }
1934 
1935 static PyObject *
pg_get_caption(PyObject * self)1936 pg_get_caption(PyObject *self)
1937 {
1938     _DisplayState *state = DISPLAY_MOD_STATE(self);
1939     SDL_Window *win = pg_GetDefaultWindow();
1940     const char *title = win ? SDL_GetWindowTitle(win) : state->title;
1941 
1942     if (title && *title) {
1943         PyObject *titleObj = Text_FromUTF8(title);
1944         PyObject *ret = PyTuple_Pack(2, titleObj, titleObj);
1945         Py_DECREF(titleObj);
1946         /* TODO: icon title? */
1947         return ret;
1948     }
1949     return PyTuple_New(0);
1950 }
1951 
1952 static PyObject *
pg_set_icon(PyObject * self,PyObject * arg)1953 pg_set_icon(PyObject *self, PyObject *arg)
1954 {
1955     _DisplayState *state = DISPLAY_MOD_STATE(self);
1956     SDL_Window *win = pg_GetDefaultWindow();
1957     PyObject *surface;
1958     if (!PyArg_ParseTuple(arg, "O!", &pgSurface_Type, &surface))
1959         return NULL;
1960 
1961     if (!SDL_WasInit(SDL_INIT_VIDEO)) {
1962         if (!pg_display_init(NULL))
1963             return NULL;
1964     }
1965     Py_INCREF(surface);
1966     Py_XDECREF(state->icon);
1967     state->icon = surface;
1968     if (win)
1969         SDL_SetWindowIcon(win, pgSurface_AsSurface(surface));
1970     Py_RETURN_NONE;
1971 }
1972 
1973 static PyObject *
pg_iconify(PyObject * self)1974 pg_iconify(PyObject *self)
1975 {
1976     SDL_Window *win = pg_GetDefaultWindow();
1977     VIDEO_INIT_CHECK();
1978     if (!win)
1979         return RAISE(pgExc_SDLError, "No open window");
1980     SDL_MinimizeWindow(win);
1981     return PyBool_FromLong(1);
1982 }
1983 
1984 /* This is only here for debugging purposes. Games should not rely on the
1985  * implementation details of specific renderers, only on the documented
1986  * behaviour of SDL_Renderer. It's fine to debug-print which renderer a game is
1987  * running on, or to inform the user when the game is not running with HW
1988  * acceleration, but openGL can still be available without HW acceleration. */
1989 static PyObject *
pg_get_scaled_renderer_info(PyObject * self)1990 pg_get_scaled_renderer_info(PyObject *self)
1991 {
1992     SDL_Window *win = pg_GetDefaultWindow();
1993     SDL_RendererInfo r_info;
1994 
1995     VIDEO_INIT_CHECK();
1996     if (!win)
1997         return RAISE(pgExc_SDLError, "No open window");
1998 
1999     if (pg_renderer != NULL) {
2000         if (SDL_GetRendererInfo(pg_renderer, &r_info) == 0) {
2001             return PyTuple_Pack(2, PyUnicode_FromString(r_info.name),
2002                                 PyLong_FromLong(r_info.flags));
2003         }
2004         else {
2005             Py_RETURN_NONE;
2006         }
2007     }
2008     else {
2009         Py_RETURN_NONE;
2010     }
2011 }
2012 
2013 static PyObject *
pg_get_desktop_screen_sizes(PyObject * self)2014 pg_get_desktop_screen_sizes(PyObject *self)
2015 {
2016     int display_count, i;
2017     SDL_DisplayMode dm;
2018     PyObject *result;
2019 
2020     VIDEO_INIT_CHECK();
2021 
2022     display_count = SDL_GetNumVideoDisplays();
2023 
2024     result = PyList_New(display_count);
2025     if (result == NULL) {
2026         Py_RETURN_NONE;
2027     }
2028     for (i = 0; i < display_count; i++) {
2029         if (SDL_GetDesktopDisplayMode(i, &dm) != 0) {
2030             Py_RETURN_NONE;
2031         }
2032         if (PyList_SetItem(result, i,
2033                            PyTuple_Pack(2, PyLong_FromLong(dm.w),
2034                                         PyLong_FromLong(dm.h))) != 0) {
2035             Py_RETURN_NONE;
2036         }
2037     }
2038     return result;
2039 }
2040 
2041 static PyObject *
pg_is_fullscreen(PyObject * self)2042 pg_is_fullscreen(PyObject *self)
2043 {
2044     SDL_Window *win = pg_GetDefaultWindow();
2045     int flags;
2046 
2047     VIDEO_INIT_CHECK();
2048     if (!win)
2049         return RAISE(pgExc_SDLError, "No open window");
2050 
2051     flags = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN_DESKTOP;
2052 
2053     if (flags & SDL_WINDOW_FULLSCREEN)
2054         Py_RETURN_TRUE;
2055     else
2056         Py_RETURN_FALSE;
2057 }
2058 
2059 static PyObject *
pg_toggle_fullscreen(PyObject * self,PyObject * args)2060 pg_toggle_fullscreen(PyObject *self, PyObject *args)
2061 {
2062     SDL_Window *win = pg_GetDefaultWindow();
2063     int result, flags;
2064     int window_w, window_h, w, h, window_display;
2065     SDL_DisplayMode display_mode;
2066     pgSurfaceObject *display_surface;
2067     _DisplayState *state = DISPLAY_MOD_STATE(self);
2068     GL_glViewport_Func p_glViewport = NULL;
2069     SDL_SysWMinfo wm_info;
2070     SDL_RendererInfo r_info;
2071 
2072     VIDEO_INIT_CHECK();
2073     if (!win)
2074         return RAISE(pgExc_SDLError, "No open window");
2075 
2076     flags = SDL_GetWindowFlags(win) & SDL_WINDOW_FULLSCREEN_DESKTOP;
2077     /* SDL_WINDOW_FULLSCREEN_DESKTOP includes SDL_WINDOW_FULLSCREEN */
2078 
2079     SDL_VERSION(&wm_info.version);
2080     if (!SDL_GetWindowWMInfo(win, &wm_info)) {
2081         return RAISE(pgExc_SDLError, SDL_GetError());
2082     }
2083 
2084     if (state->using_gl && pg_renderer != NULL) {
2085         return RAISE(pgExc_SDLError,
2086                      "OPENGL and SDL_Renderer active at the same time");
2087     }
2088 
2089     if (pg_renderer != NULL) {
2090         if (SDL_GetRendererInfo(pg_renderer, &r_info) != 0) {
2091             return RAISE(pgExc_SDLError, SDL_GetError());
2092         }
2093     }
2094 
2095     switch (wm_info.subsystem) {
2096         // if we get this to work correctly with more systems, move them here
2097         case SDL_SYSWM_WINDOWS:
2098         case SDL_SYSWM_X11:
2099         case SDL_SYSWM_COCOA:
2100 #if SDL_VERSION_ATLEAST(2, 0, 2)
2101         case SDL_SYSWM_WAYLAND:
2102 #endif
2103             break;
2104 
2105             // These probably have fullscreen/windowed, but not tested yet.
2106             // before merge, this section should be handled by moving items
2107             // into the "supported" category, or returning early.
2108 
2109 #if SDL_VERSION_ATLEAST(2, 0, 3)
2110         case SDL_SYSWM_WINRT:  // currently not supported by pygame?
2111 #endif
2112             return PyInt_FromLong(-1);
2113 
2114         // On these platforms, everything is fullscreen at all times anyway
2115         // So we silently fail
2116         // In the future, add consoles like xbone/switch here
2117         case SDL_SYSWM_DIRECTFB:
2118         case SDL_SYSWM_UIKIT:  // iOS currently not supported by pygame
2119 #if SDL_VERSION_ATLEAST(2, 0, 4)
2120         case SDL_SYSWM_ANDROID:  // currently not supported by pygame
2121 #endif
2122             if (PyErr_WarnEx(PyExc_Warning,
2123                              "cannot leave FULLSCREEN on this platform",
2124                              1) != 0) {
2125                 return NULL;
2126             }
2127             return PyInt_FromLong(-1);
2128 
2129             // Untested and unsupported platforms
2130 #if SDL_VERSION_ATLEAST(2, 0, 2)
2131         case SDL_SYSWM_MIR:  // nobody uses mir any more, wayland has won
2132 #endif
2133 #if SDL_VERSION_ATLEAST(2, 0, 5)
2134         case SDL_SYSWM_VIVANTE:
2135 #endif
2136         case SDL_SYSWM_UNKNOWN:
2137         default:
2138             return RAISE(pgExc_SDLError, "Unsupported platform");
2139     }
2140 
2141     display_surface = pg_GetDefaultWindowSurface();
2142 
2143     // could also take the size of the old display surface
2144     SDL_GetWindowSize(win, &window_w, &window_h);
2145     window_display = SDL_GetWindowDisplayIndex(win);
2146     if (SDL_GetDesktopDisplayMode(window_display, &display_mode) != 0) {
2147         return RAISE(pgExc_SDLError, SDL_GetError());
2148     }
2149 
2150     /*
2151       if (pg_renderer != NULL) {
2152         SDL_RenderGetLogicalSize(pg_renderer, &w, &h);
2153     } else
2154     */
2155     if (state->using_gl) {
2156         p_glViewport = (GL_glViewport_Func)SDL_GL_GetProcAddress("glViewport");
2157         SDL_GL_GetDrawableSize(win, &w, &h);
2158     }
2159     else {
2160         w = display_surface->surf->w;
2161         h = display_surface->surf->h;
2162     }
2163 
2164     if (flags & SDL_WINDOW_FULLSCREEN) {
2165         /* TOGGLE FULLSCREEN OFF */
2166 
2167         if (pg_renderer != NULL) {
2168             int scale = 1;
2169             int xscale, yscale;
2170 
2171             xscale = window_w / w;
2172             yscale = window_h / h;
2173             scale = xscale < yscale ? xscale : yscale;
2174             if (scale < 1) {
2175                 scale = 1;
2176             }
2177             result = SDL_SetWindowFullscreen(win, 0);
2178             if (result != 0) {
2179                 return RAISE(pgExc_SDLError, SDL_GetError());
2180             }
2181             SDL_SetWindowSize(win, w * scale, h * scale);
2182 
2183             if (r_info.flags & SDL_RENDERER_SOFTWARE &&
2184                 wm_info.subsystem == SDL_SYSWM_X11) {
2185                 /* display surface lost? */
2186                 SDL_DestroyTexture(pg_texture);
2187                 SDL_DestroyRenderer(pg_renderer);
2188                 pg_renderer =
2189                     SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE);
2190                 pg_texture =
2191                     SDL_CreateTexture(pg_renderer, SDL_PIXELFORMAT_ARGB8888,
2192                                       SDL_TEXTUREACCESS_STREAMING, w, h);
2193             }
2194             SDL_RenderSetLogicalSize(pg_renderer, w, h);
2195 
2196 #if (SDL_VERSION_ATLEAST(2, 0, 5))
2197             /* use exact integer scale in windowed mode */
2198             SDL_RenderSetIntegerScale(
2199                 pg_renderer, !SDL_GetHintBoolean(
2200                                  "SDL_HINT_RENDER_SCALE_QUALITY", SDL_FALSE));
2201 #endif
2202             SDL_SetWindowMinimumSize(win, w, h);
2203         }
2204         else if (state->using_gl) {
2205             /* this is literally the only place where state->toggle_windowed_w
2206              * should ever be read. We only use it because with GL, there is no
2207              * display surface we can query for dimensions. */
2208             result = SDL_SetWindowFullscreen(win, 0);
2209             if (result != 0) {
2210                 return RAISE(pgExc_SDLError, SDL_GetError());
2211             }
2212             SDL_GL_MakeCurrent(win, state->gl_context);
2213             if (state->toggle_windowed_w > 0 && state->toggle_windowed_h > 0) {
2214                 if (state->scaled_gl) {
2215                     float saved_aspect_ratio =
2216                         ((float)state->toggle_windowed_w) /
2217                         (float)state->toggle_windowed_h;
2218                     float window_aspect_ratio =
2219                         ((float)display_mode.w) / (float)display_mode.h;
2220 
2221                     if (window_aspect_ratio > saved_aspect_ratio) {
2222                         int width = (int)(state->toggle_windowed_h *
2223                                           saved_aspect_ratio);
2224                         p_glViewport((state->toggle_windowed_w - width) / 2, 0,
2225                                      width, state->toggle_windowed_h);
2226                     }
2227                     else {
2228                         p_glViewport(0, 0, state->toggle_windowed_w,
2229                                      (int)(state->toggle_windowed_w /
2230                                            saved_aspect_ratio));
2231                     }
2232                 }
2233                 else {
2234                     p_glViewport(0, 0, state->toggle_windowed_w,
2235                                  state->toggle_windowed_h);
2236                 }
2237             }
2238         }
2239         else if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) ==
2240                  SDL_WINDOW_FULLSCREEN_DESKTOP) {
2241             result = SDL_SetWindowFullscreen(win, 0);
2242             if (result != 0) {
2243                 return RAISE(pgExc_SDLError, SDL_GetError());
2244             }
2245             display_surface->surf = SDL_GetWindowSurface(win);
2246         }
2247         else if (wm_info.subsystem == SDL_SYSWM_X11) {
2248             /* This is a HACK, specifically to work around faulty behaviour of
2249              * SDL_SetWindowFullscreen on X11 when switching out of fullscreen
2250              * would change the physical resolution of the display back to the
2251              * desktop resolution in SDL 2.0.8 (unsure about other versions).
2252              * The display surface gets messed up, so we re-create the window.
2253              * This is only relevant in the non-GL case. */
2254             int wx = SDL_WINDOWPOS_UNDEFINED_DISPLAY(window_display);
2255             int wy = SDL_WINDOWPOS_UNDEFINED_DISPLAY(window_display);
2256             if (PyErr_WarnEx(PyExc_Warning,
2257                              "re-creating window in toggle_fullscreen",
2258                              1) != 0) {
2259                 return NULL;
2260             }
2261             win = SDL_CreateWindow(state->title, wx, wy, w, h, 0);
2262             if (win == NULL) {
2263                 return RAISE(pgExc_SDLError, SDL_GetError());
2264             }
2265             else {
2266                 result = 0;
2267             }
2268             display_surface->surf = SDL_GetWindowSurface(win);
2269             pg_SetDefaultWindow(win);
2270         }
2271         else {
2272             result = SDL_SetWindowFullscreen(win, 0);
2273             if (result != 0) {
2274                 return RAISE(pgExc_SDLError, SDL_GetError());
2275             }
2276             display_surface->surf = SDL_GetWindowSurface(win);
2277         }
2278         state->toggle_windowed_w = 0;
2279         state->toggle_windowed_h = 0;
2280     }
2281     else {
2282         /* TOGGLE FULLSCREEN ON */
2283 
2284         state->toggle_windowed_w = w;
2285         state->toggle_windowed_h = h;
2286         if (pg_renderer != NULL) {
2287             result =
2288                 SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP);
2289             if (result != 0) {
2290                 return RAISE(pgExc_SDLError, SDL_GetError());
2291             }
2292             if (r_info.flags & SDL_RENDERER_SOFTWARE &&
2293                 wm_info.subsystem == SDL_SYSWM_X11) {
2294                 if (PyErr_WarnEx(
2295                         PyExc_Warning,
2296                         "recreating software renderer in toggle_fullscreen",
2297                         1) != 0) {
2298                     return NULL;
2299                 }
2300                 /* display surface lost? only on x11? */
2301                 SDL_DestroyTexture(pg_texture);
2302                 SDL_DestroyRenderer(pg_renderer);
2303                 pg_renderer =
2304                     SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE);
2305                 pg_texture =
2306                     SDL_CreateTexture(pg_renderer, SDL_PIXELFORMAT_ARGB8888,
2307                                       SDL_TEXTUREACCESS_STREAMING, w, h);
2308             }
2309 
2310             SDL_RenderSetLogicalSize(pg_renderer, w, h);
2311 #if (SDL_VERSION_ATLEAST(2, 0, 5))
2312             SDL_RenderSetIntegerScale(pg_renderer, SDL_FALSE);
2313 #endif
2314         }
2315         else if (state->using_gl) {
2316             result =
2317                 SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP);
2318             if (result != 0) {
2319                 return RAISE(pgExc_SDLError, SDL_GetError());
2320             }
2321             SDL_GL_MakeCurrent(win, state->gl_context);
2322             if (state->scaled_gl) {
2323                 float saved_aspect_ratio =
2324                     ((float)state->scaled_gl_w) / (float)state->scaled_gl_h;
2325                 float window_aspect_ratio =
2326                     ((float)display_mode.w) / (float)display_mode.h;
2327 
2328                 if (window_aspect_ratio > saved_aspect_ratio) {
2329                     int width = (int)(display_mode.h * saved_aspect_ratio);
2330                     p_glViewport((display_mode.w - width) / 2, 0, width,
2331                                  display_mode.h);
2332                 }
2333                 else {
2334                     p_glViewport(0, 0, display_mode.w,
2335                                  (int)(display_mode.w / saved_aspect_ratio));
2336                 }
2337             }
2338             else {
2339                 p_glViewport(0, 0, display_mode.w, display_mode.h);
2340             }
2341         }
2342         else if (w == display_mode.w && h == display_mode.h) {
2343             result =
2344                 SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP);
2345             if (result != 0) {
2346                 return RAISE(pgExc_SDLError, SDL_GetError());
2347             }
2348             display_surface->surf = SDL_GetWindowSurface(win);
2349         }
2350         else if (wm_info.subsystem == SDL_SYSWM_WAYLAND) {
2351             if (PyErr_WarnEx(PyExc_Warning,
2352                              "skipping toggle_fullscreen on wayland",
2353                              1) != 0) {
2354                 return NULL;
2355             }
2356             return PyInt_FromLong(-1);
2357         }
2358         else {
2359             result = SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN);
2360             if (result != 0) {
2361                 return RAISE(pgExc_SDLError, SDL_GetError());
2362             }
2363             display_surface->surf = SDL_GetWindowSurface(win);
2364             if (w != display_surface->surf->w ||
2365                 h != display_surface->surf->h) {
2366                 int wx = SDL_WINDOWPOS_UNDEFINED_DISPLAY(window_display);
2367                 int wy = SDL_WINDOWPOS_UNDEFINED_DISPLAY(window_display);
2368                 win = SDL_CreateWindow(state->title, wx, wy, w, h, 0);
2369                 if (win == NULL) {
2370                     return RAISE(pgExc_SDLError, SDL_GetError());
2371                 }
2372                 display_surface->surf = SDL_GetWindowSurface(win);
2373                 pg_SetDefaultWindow(win);
2374                 if (PyErr_WarnEx(PyExc_Warning,
2375                                  "re-creating window in toggle_fullscreen",
2376                                  1) != 0) {
2377                     return NULL;
2378                 }
2379                 return PyInt_FromLong(-1);
2380             }
2381         }
2382     }
2383     return PyInt_FromLong(result != 0);
2384 }
2385 
2386 /* This API is provisional, and, not finalised, and should not be documented
2387  * in any user-facing docs until we are sure when this is safe to call and when
2388  * it should raise an exception */
2389 static PyObject *
pg_display_resize_event(PyObject * self,PyObject * event)2390 pg_display_resize_event(PyObject *self, PyObject *event)
2391 {
2392     /* Call this from your game if you want to use RESIZABLE with SCALED
2393      * TODO: Document, handle bad args, bail on FULLSCREEN
2394      */
2395     int wnew = PyLong_AsLong(PyObject_GetAttrString(event, "w"));
2396     int hnew = PyLong_AsLong(PyObject_GetAttrString(event, "h"));
2397     SDL_Window *win = pg_GetDefaultWindow();
2398     int flags;
2399     int window_w, window_h, w, h, window_display, result;
2400     SDL_DisplayMode display_mode;
2401     _DisplayState *state = DISPLAY_MOD_STATE(self);
2402     GL_glViewport_Func p_glViewport = NULL;
2403 
2404     VIDEO_INIT_CHECK();
2405     if (!win)
2406         return RAISE(pgExc_SDLError, "No open window");
2407 
2408     flags = SDL_GetWindowFlags(win) &
2409             (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP);
2410 
2411     if (flags) {
2412         return PyInt_FromLong(-1);
2413     }
2414 
2415     // could also take the size of the old display surface
2416     SDL_GetWindowSize(win, &window_w, &window_h);
2417     window_display = SDL_GetWindowDisplayIndex(win);
2418     if (SDL_GetDesktopDisplayMode(window_display, &display_mode) != 0) {
2419         return RAISE(pgExc_SDLError, SDL_GetError());
2420     }
2421 
2422     if (state->using_gl) {
2423         p_glViewport = (GL_glViewport_Func)SDL_GL_GetProcAddress("glViewport");
2424         SDL_SetWindowSize(win, wnew, hnew);
2425         SDL_GL_MakeCurrent(win, state->gl_context);
2426         if (state->scaled_gl) {
2427             float saved_aspect_ratio =
2428                 ((float)state->scaled_gl_w) / (float)state->scaled_gl_h;
2429             float window_aspect_ratio = ((float)wnew) / (float)hnew;
2430 
2431             if (window_aspect_ratio > saved_aspect_ratio) {
2432                 int width = (int)(hnew * saved_aspect_ratio);
2433                 p_glViewport((wnew - width) / 2, 0, width, hnew);
2434             }
2435             else {
2436                 p_glViewport(0, 0, wnew, (int)(wnew / saved_aspect_ratio));
2437             }
2438         }
2439         else {
2440             p_glViewport(0, 0, wnew, hnew);
2441         }
2442     }
2443     else if (pg_renderer != NULL) {
2444         SDL_RenderGetLogicalSize(pg_renderer, &w, &h);
2445         SDL_SetWindowSize(win, (w > wnew) ? w : wnew, (h > hnew) ? h : hnew);
2446         result = SDL_RenderSetLogicalSize(pg_renderer, w, h);
2447         if (result != 0) {
2448             return RAISE(pgExc_SDLError, SDL_GetError());
2449         }
2450     }
2451     else {
2452         /* do not do anything that would invalidate a display surface! */
2453         return PyInt_FromLong(-1);
2454     }
2455     Py_RETURN_FALSE;
2456 }
2457 
2458 
2459 
2460 static PyObject *
pg_get_allow_screensaver(PyObject * self)2461 pg_get_allow_screensaver(PyObject *self) {
2462     /* SDL_IsScreenSaverEnabled() unconditionally returns SDL_True if
2463      * the video system is not initialized.  Therefore we insist on
2464      * the video being initialized before calling it.
2465      */
2466    VIDEO_INIT_CHECK();
2467     return PyBool_FromLong(SDL_IsScreenSaverEnabled() == SDL_TRUE);
2468 }
2469 
2470 static PyObject *
pg_set_allow_screensaver(PyObject * self,PyObject * arg,PyObject * kwargs)2471 pg_set_allow_screensaver(PyObject *self, PyObject *arg, PyObject *kwargs) {
2472     int val = 1;
2473     static char *keywords[] = {"value", NULL};
2474 
2475     if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "|i", keywords, &val)) {
2476         return NULL;
2477     }
2478 
2479     VIDEO_INIT_CHECK();
2480     if (val) {
2481         SDL_EnableScreenSaver();
2482     } else {
2483         SDL_DisableScreenSaver();
2484     }
2485 
2486     Py_RETURN_NONE;
2487 }
2488 
2489 static PyMethodDef _pg_display_methods[] = {
2490     {"init", (PyCFunction)pg_display_init, METH_NOARGS, DOC_PYGAMEDISPLAYINIT},
2491     {"quit", (PyCFunction)pg_display_quit, METH_NOARGS, DOC_PYGAMEDISPLAYQUIT},
2492     {"get_init", (PyCFunction)pg_get_init, METH_NOARGS, DOC_PYGAMEDISPLAYGETINIT},
2493     {"get_active", (PyCFunction)pg_get_active, METH_NOARGS, DOC_PYGAMEDISPLAYGETACTIVE},
2494 
2495     /* { "set_driver", set_driver, 1, doc_set_driver }, */
2496     {"get_driver", (PyCFunction)pg_get_driver, METH_NOARGS,
2497         DOC_PYGAMEDISPLAYGETDRIVER},
2498     {"get_wm_info", (PyCFunction)pg_get_wm_info, METH_NOARGS,
2499         DOC_PYGAMEDISPLAYGETWMINFO},
2500     {"Info", (PyCFunction)pgInfo, METH_NOARGS, DOC_PYGAMEDISPLAYINFO},
2501     {"get_surface", (PyCFunction)pg_get_surface, METH_NOARGS, DOC_PYGAMEDISPLAYGETSURFACE},
2502     {"get_window_size", (PyCFunction)pg_window_size, METH_NOARGS,
2503      DOC_PYGAMEDISPLAYGETWINDOWSIZE},
2504 
2505     {"set_mode", (PyCFunction)pg_set_mode, METH_VARARGS | METH_KEYWORDS,
2506      DOC_PYGAMEDISPLAYSETMODE},
2507     {"mode_ok", (PyCFunction)pg_mode_ok, METH_VARARGS | METH_KEYWORDS,
2508      DOC_PYGAMEDISPLAYMODEOK},
2509     {"list_modes", (PyCFunction)pg_list_modes, METH_VARARGS | METH_KEYWORDS,
2510      DOC_PYGAMEDISPLAYLISTMODES},
2511     {"get_num_displays", (PyCFunction)pg_num_displays, METH_NOARGS,
2512      DOC_PYGAMEDISPLAYGETNUMDISPLAYS},
2513 
2514     {"flip", (PyCFunction)pg_flip, METH_NOARGS, DOC_PYGAMEDISPLAYFLIP},
2515     {"update", (PyCFunction)pg_update, METH_VARARGS, DOC_PYGAMEDISPLAYUPDATE},
2516 
2517     {"set_palette", pg_set_palette, METH_VARARGS, DOC_PYGAMEDISPLAYSETPALETTE},
2518     {"set_gamma", pg_set_gamma, METH_VARARGS, DOC_PYGAMEDISPLAYSETGAMMA},
2519     {"set_gamma_ramp", pg_set_gamma_ramp, METH_VARARGS,
2520      DOC_PYGAMEDISPLAYSETGAMMARAMP},
2521 
2522     {"set_caption", pg_set_caption, METH_VARARGS, DOC_PYGAMEDISPLAYSETCAPTION},
2523     {"get_caption", (PyCFunction)pg_get_caption, METH_NOARGS, DOC_PYGAMEDISPLAYGETCAPTION},
2524     {"set_icon", pg_set_icon, METH_VARARGS, DOC_PYGAMEDISPLAYSETICON},
2525 
2526     {"iconify", (PyCFunction)pg_iconify, METH_NOARGS, DOC_PYGAMEDISPLAYICONIFY},
2527     {"toggle_fullscreen", (PyCFunction)pg_toggle_fullscreen, METH_NOARGS,
2528      DOC_PYGAMEDISPLAYTOGGLEFULLSCREEN},
2529 
2530     {"_set_autoresize", (PyCFunction)pg_display_set_autoresize
2531      , METH_VARARGS, "provisional API, subject to change"},
2532     {"_resize_event", (PyCFunction)pg_display_resize_event, METH_O,
2533      "DEPRECATED, never officially supported, kept only for compatibility with release candidate"},
2534     {"_get_renderer_info", (PyCFunction)pg_get_scaled_renderer_info,
2535      METH_NOARGS, "provisional API, subject to change"},
2536     {"get_desktop_sizes", (PyCFunction)pg_get_desktop_screen_sizes,
2537      METH_NOARGS, DOC_PYGAMEDISPLAYGETDESKTOPSIZES},
2538     {"is_fullscreen", (PyCFunction)pg_is_fullscreen, METH_NOARGS,
2539      "provisional API, subject to change"},
2540 
2541     {"gl_set_attribute", pg_gl_set_attribute, METH_VARARGS,
2542      DOC_PYGAMEDISPLAYGLSETATTRIBUTE},
2543     {"gl_get_attribute", pg_gl_get_attribute, METH_VARARGS,
2544      DOC_PYGAMEDISPLAYGLGETATTRIBUTE},
2545 
2546     {"get_allow_screensaver", (PyCFunction)pg_get_allow_screensaver, METH_NOARGS,
2547      DOC_PYGAMEDISPLAYGETALLOWSCREENSAVER},
2548     {"set_allow_screensaver", (PyCFunction)pg_set_allow_screensaver, METH_VARARGS | METH_KEYWORDS,
2549      DOC_PYGAMEDISPLAYSETALLOWSCREENSAVER},
2550 
2551     {NULL, NULL, 0, NULL}};
2552 
2553 #ifndef PYPY_VERSION
2554 static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT,
2555                                      "display",
2556                                      DOC_PYGAMEDISPLAY,
2557                                      sizeof(_DisplayState),
2558                                      _pg_display_methods,
2559 #pragma PG_WARN(At some point should add GC slot functions.)
2560                                      NULL,
2561                                      NULL,
2562                                      NULL,
2563                                      NULL};
2564 #else  /* PYPY_VERSION */
2565 static struct PyModuleDef _module = {
2566     PyModuleDef_HEAD_INIT,
2567     "display",
2568     DOC_PYGAMEDISPLAY,
2569     -1, /* PyModule_GetState() not implemented */
2570     _pg_display_methods,
2571     NULL,
2572     NULL,
2573     NULL,
2574     NULL};
2575 #endif /* PYPY_VERSION */
2576 
MODINIT_DEFINE(display)2577 MODINIT_DEFINE(display)
2578 {
2579     PyObject *module;
2580     _DisplayState *state;
2581 
2582     /* imported needed apis; Do this first so if there is an error
2583        the module is not loaded.
2584     */
2585     import_pygame_base();
2586     if (PyErr_Occurred()) {
2587         MODINIT_ERROR;
2588     }
2589     import_pygame_rect();
2590     if (PyErr_Occurred()) {
2591         MODINIT_ERROR;
2592     }
2593     import_pygame_surface();
2594     if (PyErr_Occurred()) {
2595         MODINIT_ERROR;
2596     }
2597 
2598     /* type preparation */
2599     if (PyType_Ready(&pgVidInfo_Type) < 0) {
2600         MODINIT_ERROR;
2601     }
2602 
2603     /* create the module */
2604     module = PyModule_Create(&_module);
2605     if (module == NULL) {
2606         MODINIT_ERROR;
2607     }
2608     state = DISPLAY_MOD_STATE(module);
2609     state->title = NULL;
2610     state->icon = NULL;
2611     state->gamma_ramp = NULL;
2612     state->using_gl = 0;
2613     state->auto_resize = SDL_TRUE;
2614 
2615     MODINIT_RETURN(module);
2616 }
2617