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 | |
39 | typedef 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 | |
70 | int initialized_video = 0; |
71 | |
72 | static SDL_Window *SDL_VideoWindow = NULL; |
73 | static SDL_Surface *SDL_VideoSurface = NULL; |
74 | static SDL_Surface *SDL_PublicSurface = NULL; |
75 | static SDL_Rect SDL_VideoViewport; |
76 | static char *wm_title = NULL; |
77 | static Uint32 SDL_VideoFlags = 0; |
78 | static SDL_GLContext *SDL_VideoContext = NULL; |
79 | static SDL_Surface *SDL_VideoIcon; |
80 | |
81 | static void |
82 | SDL_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 | |
95 | static int |
96 | GetVideoDisplay() |
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 | |
109 | static const SDL_VideoInfo * |
110 | SDL_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 | |
124 | static SDL_Rect ** |
125 | SDL_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 | |
189 | static void |
190 | SDL_GL_SwapBuffers(void) |
191 | { |
192 | SDL_GL_SwapWindow(SDL_VideoWindow); |
193 | } |
194 | |
195 | static int |
196 | SDL_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 | |
230 | static int |
231 | SDL_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 | |
265 | static int |
266 | SDL_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 | |
333 | static void |
334 | GetEnvironmentWindowPosition(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 | |
353 | static 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 | |
373 | static SDL_Surface * |
374 | SDL_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 | |