1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include <SDL_config.h>
23#include <SDL_surface.h>
24
25#ifndef USE_GLES
26
27#ifndef SDL_VIDEO_OPENGL
28#error SDL is not build with OpenGL support. Try USE_GLES=1
29#endif
30
31#else // !USE_GLES
32
33#ifndef SDL_VIDEO_OPENGL_ES2
34#error SDL is not build with OpenGL ES2 support. Try USE_GLES=0
35#endif
36
37#endif // !USE_GLES
38
39typedef struct SDL_VideoInfo
40{
41 Uint32 hw_available:1;
42 Uint32 wm_available:1;
43 Uint32 UnusedBits1:6;
44 Uint32 UnusedBits2:1;
45 Uint32 blit_hw:1;
46 Uint32 blit_hw_CC:1;
47 Uint32 blit_hw_A:1;
48 Uint32 blit_sw:1;
49 Uint32 blit_sw_CC:1;
50 Uint32 blit_sw_A:1;
51 Uint32 blit_fill:1;
52 Uint32 UnusedBits3:16;
53 Uint32 video_mem;
54
55 SDL_PixelFormat *vfmt;
56
57 int current_w;
58 int current_h;
59} SDL_VideoInfo;
60
61#define SDL_FULLSCREEN 0x00800000
62#define SDL_RESIZABLE 0x01000000
63#define SDL_NOFRAME 0x02000000
64#define SDL_OPENGL 0x04000000
65#define SDL_HWSURFACE 0x08000001 /**< \note Not used */
66
67#define SDL_BUTTON_WHEELUP 4
68#define SDL_BUTTON_WHEELDOWN 5
69
70int initialized_video = 0;
71
72static SDL_Window *SDL_VideoWindow = NULL;
73static SDL_Surface *SDL_VideoSurface = NULL;
74static SDL_Surface *SDL_PublicSurface = NULL;
75static SDL_Rect SDL_VideoViewport;
76static char *wm_title = NULL;
77static Uint32 SDL_VideoFlags = 0;
78static SDL_GLContext *SDL_VideoContext = NULL;
79static SDL_Surface *SDL_VideoIcon;
80
81static void
82SDL_WM_SetCaption(const char *title, const char *icon)
83{
84 if (wm_title) {
85 SDL_free(wm_title);
86 }
87 if (title) {
88 wm_title = SDL_strdup(title);
89 } else {
90 wm_title = NULL;
91 }
92 SDL_SetWindowTitle(SDL_VideoWindow, wm_title);
93}
94
95static int
96GetVideoDisplay()
97{
98 const char *variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
99 if ( !variable ) {
100 variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_HEAD");
101 }
102 if ( variable ) {
103 return SDL_atoi(variable);
104 } else {
105 return 0;
106 }
107}
108
109static const SDL_VideoInfo *
110SDL_GetVideoInfo(void)
111{
112 static SDL_VideoInfo info;
113 SDL_DisplayMode mode;
114
115 /* Memory leak, compatibility code, who cares? */
116 if (!info.vfmt && SDL_GetDesktopDisplayMode(GetVideoDisplay(), &mode) == 0) {
117 info.vfmt = SDL_AllocFormat(mode.format);
118 info.current_w = mode.w;
119 info.current_h = mode.h;
120 }
121 return &info;
122}
123
124static SDL_Rect **
125SDL_ListModes(const SDL_PixelFormat * format, Uint32 flags)
126{
127 int i, nmodes;
128 SDL_Rect **modes;
129
130 if (!initialized_video) {
131 return NULL;
132 }
133
134 if (!(flags & SDL_FULLSCREEN)) {
135 return (SDL_Rect **) (-1);
136 }
137
138 if (!format) {
139 format = SDL_GetVideoInfo()->vfmt;
140 }
141
142 /* Memory leak, but this is a compatibility function, who cares? */
143 nmodes = 0;
144 modes = NULL;
145 for (i = 0; i < SDL_GetNumDisplayModes(GetVideoDisplay()); ++i) {
146 SDL_DisplayMode mode;
147 int bpp;
148
149 SDL_GetDisplayMode(GetVideoDisplay(), i, &mode);
150 if (!mode.w || !mode.h) {
151 return (SDL_Rect **) (-1);
152 }
153
154 /* Copied from src/video/SDL_pixels.c:SDL_PixelFormatEnumToMasks */
155 if (SDL_BYTESPERPIXEL(mode.format) <= 2) {
156 bpp = SDL_BITSPERPIXEL(mode.format);
157 } else {
158 bpp = SDL_BYTESPERPIXEL(mode.format) * 8;
159 }
160
161 if (bpp != format->BitsPerPixel) {
162 continue;
163 }
164 if (nmodes > 0 && modes[nmodes - 1]->w == mode.w
165 && modes[nmodes - 1]->h == mode.h) {
166 continue;
167 }
168
169 modes = SDL_realloc(modes, (nmodes + 2) * sizeof(*modes));
170 if (!modes) {
171 return NULL;
172 }
173 modes[nmodes] = (SDL_Rect *) SDL_malloc(sizeof(SDL_Rect));
174 if (!modes[nmodes]) {
175 return NULL;
176 }
177 modes[nmodes]->x = 0;
178 modes[nmodes]->y = 0;
179 modes[nmodes]->w = mode.w;
180 modes[nmodes]->h = mode.h;
181 ++nmodes;
182 }
183 if (modes) {
184 modes[nmodes] = NULL;
185 }
186 return modes;
187}
188
189static void
190SDL_GL_SwapBuffers(void)
191{
192 SDL_GL_SwapWindow(SDL_VideoWindow);
193}
194
195static int
196SDL_WM_ToggleFullScreen(SDL_Surface * surface)
197{
198 int window_w;
199 int window_h;
200
201 if (!SDL_PublicSurface) {
202 SDL_SetError("SDL_SetVideoMode() hasn't been called");
203 return 0;
204 }
205
206 /* Do the physical mode switch */
207 if (SDL_GetWindowFlags(SDL_VideoWindow) & SDL_WINDOW_FULLSCREEN) {
208 if (SDL_SetWindowFullscreen(SDL_VideoWindow, 0) < 0) {
209 return 0;
210 }
211 SDL_PublicSurface->flags &= ~SDL_FULLSCREEN;
212 } else {
213 if (SDL_SetWindowFullscreen(SDL_VideoWindow, 1) < 0) {
214 return 0;
215 }
216 SDL_PublicSurface->flags |= SDL_FULLSCREEN;
217 }
218
219 /* Center the public surface in the window surface */
220 SDL_GetWindowSize(SDL_VideoWindow, &window_w, &window_h);
221 SDL_VideoViewport.x = 0;
222 SDL_VideoViewport.y = 0;
223 SDL_VideoViewport.w = window_w;
224 SDL_VideoViewport.h = window_h;
225
226 /* We're done! */
227 return 1;
228}
229
230static int
231SDL_ResizeVideoMode(int width, int height, int bpp, Uint32 flags)
232{
233 int w, h;
234
235 /* We can't resize something we don't have... */
236 if (!SDL_PublicSurface) {
237 return -1;
238 }
239
240 /* We probably have to recreate the window in fullscreen mode */
241 if (flags & SDL_FULLSCREEN) {
242 return -1;
243 }
244
245 /* I don't think there's any change we can gracefully make in flags */
246 if (flags != SDL_VideoFlags) {
247 return -1;
248 }
249 if (bpp != SDL_VideoSurface->format->BitsPerPixel) {
250 return -1;
251 }
252
253 /* Resize the window */
254 SDL_GetWindowSize(SDL_VideoWindow, &w, &h);
255 if (w != width || h != height) {
256 SDL_SetWindowSize(SDL_VideoWindow, width, height);
257 }
258
259 SDL_VideoSurface->w = width;
260 SDL_VideoSurface->h = height;
261
262 return 0;
263}
264
265static int
266SDL_CompatEventFilter(void *userdata, SDL_Event * event)
267{
268 SDL_Event fake;
269
270 switch (event->type) {
271 case SDL_WINDOWEVENT:
272 switch (event->window.event) {
273 case SDL_WINDOWEVENT_CLOSE:
274 fake.type = SDL_QUIT;
275 SDL_PushEvent(&fake);
276 break;
277 }
278 case SDL_TEXTINPUT:
279 {
280 /* FIXME: Generate an old style key repeat event if needed */
281 //printf("TEXTINPUT: '%s'\n", event->text.text);
282 break;
283 }
284 case SDL_MOUSEMOTION:
285 {
286 event->motion.x -= SDL_VideoViewport.x;
287 event->motion.y -= SDL_VideoViewport.y;
288 break;
289 }
290 case SDL_MOUSEBUTTONDOWN:
291 case SDL_MOUSEBUTTONUP:
292 {
293 event->button.x -= SDL_VideoViewport.x;
294 event->button.y -= SDL_VideoViewport.y;
295 break;
296 }
297 case SDL_MOUSEWHEEL:
298 {
299 Uint8 button;
300 int x, y;
301
302 if (event->wheel.y == 0) {
303 break;
304 }
305
306 SDL_GetMouseState(&x, &y);
307
308 if (event->wheel.y > 0) {
309 button = SDL_BUTTON_WHEELUP;
310 } else {
311 button = SDL_BUTTON_WHEELDOWN;
312 }
313
314 fake.button.button = button;
315 fake.button.x = x;
316 fake.button.y = y;
317 fake.button.windowID = event->wheel.windowID;
318
319 fake.type = SDL_MOUSEBUTTONDOWN;
320 fake.button.state = SDL_PRESSED;
321 SDL_PushEvent(&fake);
322
323 fake.type = SDL_MOUSEBUTTONUP;
324 fake.button.state = SDL_RELEASED;
325 SDL_PushEvent(&fake);
326 break;
327 }
328
329 }
330 return 1;
331}
332
333static void
334GetEnvironmentWindowPosition(int w, int h, int *x, int *y)
335{
336 int display = GetVideoDisplay();
337 const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
338 const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
339 if (window) {
340 if (SDL_sscanf(window, "%d,%d", x, y) == 2) {
341 return;
342 }
343 if (SDL_strcmp(window, "center") == 0) {
344 center = window;
345 }
346 }
347 if (center) {
348 *x = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
349 *y = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
350 }
351}
352
353static void SDL2_DestroyWindow(void)
354{
355 /* Destroy existing window */
356 SDL_PublicSurface = NULL;
357 if (SDL_VideoSurface) {
358 SDL_VideoSurface->flags &= ~SDL_DONTFREE;
359 SDL_FreeSurface(SDL_VideoSurface);
360 SDL_VideoSurface = NULL;
361 }
362 if (SDL_VideoContext) {
363 /* SDL_GL_MakeCurrent(0, NULL); *//* Doesn't do anything */
364 SDL_GL_DeleteContext(SDL_VideoContext);
365 SDL_VideoContext = NULL;
366 }
367 if (SDL_VideoWindow) {
368 SDL_DestroyWindow(SDL_VideoWindow);
369 SDL_VideoWindow = NULL;
370 }
371}
372
373static SDL_Surface *
374SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
375{
376 SDL_DisplayMode desktop_mode;
377 int display = GetVideoDisplay();
378 int window_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
379 int window_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
380 Uint32 window_flags;
381 Uint32 surface_flags;
382
383 if (!initialized_video) {
384 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0) {
385 return NULL;
386 }
387 initialized_video = 1;
388 }
389
390 SDL_GetDesktopDisplayMode(display, &desktop_mode);
391
392 if (width == 0) {
393 width = desktop_mode.w;
394 }
395 if (height == 0) {
396 height = desktop_mode.h;
397 }
398 if (bpp == 0) {
399 bpp = SDL_BITSPERPIXEL(desktop_mode.format);
400 }
401
402 /* See if we can simply resize the existing window and surface */
403 if (SDL_ResizeVideoMode(width, height, bpp, flags) == 0) {
404 return SDL_PublicSurface;
405 }
406
407 /* Destroy existing window */
408 if (SDL_VideoWindow)
409 SDL_GetWindowPosition(SDL_VideoWindow, &window_x, &window_y);
410 SDL2_DestroyWindow();
411
412 /* Set up the event filter */
413 if (!SDL_GetEventFilter(NULL, NULL)) {
414 SDL_SetEventFilter(SDL_CompatEventFilter, NULL);
415 }
416
417#ifndef USE_GLES
418 if (l_ForceCompatibilityContext)
419 {
420 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
421 }
422#else // !USE_GLES
423 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
424 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
425 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
426#endif // !USE_GLES
427
428 /* Create a new window */
429 window_flags = SDL_WINDOW_SHOWN;
430 if (flags & SDL_FULLSCREEN) {
431 window_flags |= SDL_WINDOW_FULLSCREEN;
432 }
433 if (flags & SDL_OPENGL) {
434 window_flags |= SDL_WINDOW_OPENGL;
435 }
436 if (flags & SDL_RESIZABLE) {
437 window_flags |= SDL_WINDOW_RESIZABLE;
438 }
439 if (flags & SDL_NOFRAME) {
440 window_flags |= SDL_WINDOW_BORDERLESS;
441 }
442 GetEnvironmentWindowPosition(width, height, &window_x, &window_y);
443 SDL_VideoWindow =
444 SDL_CreateWindow(wm_title, window_x, window_y, width, height,
445 window_flags);
446 if (!SDL_VideoWindow) {
447 return NULL;
448 }
449 SDL_SetWindowIcon(SDL_VideoWindow, SDL_VideoIcon);
450
451 window_flags = SDL_GetWindowFlags(SDL_VideoWindow);
452 surface_flags = 0;
453 if (window_flags & SDL_WINDOW_FULLSCREEN) {
454 surface_flags |= SDL_FULLSCREEN;
455 }
456 if ((window_flags & SDL_WINDOW_OPENGL) && (flags & SDL_OPENGL)) {
457 surface_flags |= SDL_OPENGL;
458 }
459 if (window_flags & SDL_WINDOW_RESIZABLE) {
460 surface_flags |= SDL_RESIZABLE;
461 }
462 if (window_flags & SDL_WINDOW_BORDERLESS) {
463 surface_flags |= SDL_NOFRAME;
464 }
465
466 SDL_VideoFlags = flags;
467
468 /* If we're in OpenGL mode, just create a stub surface and we're done! */
469 if (flags & SDL_OPENGL) {
470 SDL_VideoContext = SDL_GL_CreateContext(SDL_VideoWindow);
471 if (!SDL_VideoContext) {
472 return NULL;
473 }
474 if (SDL_GL_MakeCurrent(SDL_VideoWindow, SDL_VideoContext) < 0) {
475 return NULL;
476 }
477 SDL_VideoSurface =
478 SDL_CreateRGBSurfaceFrom(NULL, width, height, bpp, 0, 0, 0, 0, 0);
479 if (!SDL_VideoSurface) {
480 return NULL;
481 }
482 SDL_VideoSurface->flags |= surface_flags;
483 SDL_PublicSurface = SDL_VideoSurface;
484 return SDL_PublicSurface;
485 }
486
487 /* We're finally done! */
488 return NULL;
489}
490