2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
22 #include <SDL_surface.h>
34 SDL_PixelFormat *src_fmt;
35 SDL_PixelFormat *dst_fmt;
42 /* Blit mapping definition */
43 typedef struct SDL_BlitMap
51 /* the version count matches the destination; mismatch indicates
53 Uint32 dst_palette_version;
54 Uint32 src_palette_version;
57 typedef struct SDL_VideoInfo
59 Uint32 hw_available:1;
60 Uint32 wm_available:1;
70 Uint32 UnusedBits3:16;
73 SDL_PixelFormat *vfmt;
79 #define SDL_ANYFORMAT 0x00100000
80 #define SDL_HWPALETTE 0x00200000
81 #define SDL_FULLSCREEN 0x00800000
82 #define SDL_RESIZABLE 0x01000000
83 #define SDL_NOFRAME 0x02000000
84 #define SDL_OPENGL 0x04000000
85 #define SDL_HWSURFACE 0x08000001 /**< \note Not used */
87 #define SDL_BUTTON_WHEELUP 4
88 #define SDL_BUTTON_WHEELDOWN 5
90 int initialized_video = 0;
92 static SDL_Window *SDL_VideoWindow = NULL;
93 static SDL_Surface *SDL_WindowSurface = NULL;
94 static SDL_Surface *SDL_VideoSurface = NULL;
95 static SDL_Surface *SDL_ShadowSurface = NULL;
96 static SDL_Surface *SDL_PublicSurface = NULL;
97 static SDL_Rect SDL_VideoViewport;
98 static char *wm_title = NULL;
99 static Uint32 SDL_VideoFlags = 0;
100 static SDL_GLContext *SDL_VideoContext = NULL;
101 static SDL_Surface *SDL_VideoIcon;
104 SDL_WM_SetCaption(const char *title, const char *icon)
110 wm_title = SDL_strdup(title);
114 SDL_SetWindowTitle(SDL_VideoWindow, wm_title);
120 const char *variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
122 variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_HEAD");
125 return SDL_atoi(variable);
131 static const SDL_VideoInfo *
132 SDL_GetVideoInfo(void)
134 static SDL_VideoInfo info;
135 SDL_DisplayMode mode;
137 /* Memory leak, compatibility code, who cares? */
138 if (!info.vfmt && SDL_GetDesktopDisplayMode(GetVideoDisplay(), &mode) == 0) {
139 info.vfmt = SDL_AllocFormat(mode.format);
140 info.current_w = mode.w;
141 info.current_h = mode.h;
147 SDL_ListModes(const SDL_PixelFormat * format, Uint32 flags)
152 if (!initialized_video) {
156 if (!(flags & SDL_FULLSCREEN)) {
157 return (SDL_Rect **) (-1);
161 format = SDL_GetVideoInfo()->vfmt;
164 /* Memory leak, but this is a compatibility function, who cares? */
167 for (i = 0; i < SDL_GetNumDisplayModes(GetVideoDisplay()); ++i) {
168 SDL_DisplayMode mode;
171 SDL_GetDisplayMode(GetVideoDisplay(), i, &mode);
172 if (!mode.w || !mode.h) {
173 return (SDL_Rect **) (-1);
176 /* Copied from src/video/SDL_pixels.c:SDL_PixelFormatEnumToMasks */
177 if (SDL_BYTESPERPIXEL(mode.format) <= 2) {
178 bpp = SDL_BITSPERPIXEL(mode.format);
180 bpp = SDL_BYTESPERPIXEL(mode.format) * 8;
183 if (bpp != format->BitsPerPixel) {
186 if (nmodes > 0 && modes[nmodes - 1]->w == mode.w
187 && modes[nmodes - 1]->h == mode.h) {
191 modes = SDL_realloc(modes, (nmodes + 2) * sizeof(*modes));
195 modes[nmodes] = (SDL_Rect *) SDL_malloc(sizeof(SDL_Rect));
196 if (!modes[nmodes]) {
199 modes[nmodes]->x = 0;
200 modes[nmodes]->y = 0;
201 modes[nmodes]->w = mode.w;
202 modes[nmodes]->h = mode.h;
206 modes[nmodes] = NULL;
212 SDL_UpdateRects(SDL_Surface * screen, int numrects, SDL_Rect * rects)
216 if (screen == SDL_ShadowSurface) {
217 for (i = 0; i < numrects; ++i) {
218 SDL_BlitSurface(SDL_ShadowSurface, &rects[i], SDL_VideoSurface,
222 /* Fall through to video surface update */
223 screen = SDL_VideoSurface;
225 if (screen == SDL_VideoSurface) {
226 if (SDL_VideoViewport.x || SDL_VideoViewport.y) {
227 SDL_Rect *stackrects = SDL_stack_alloc(SDL_Rect, numrects);
229 const SDL_Rect *rect;
231 /* Offset all the rectangles before updating */
232 for (i = 0; i < numrects; ++i) {
234 stackrect = &stackrects[i];
235 stackrect->x = SDL_VideoViewport.x + rect->x;
236 stackrect->y = SDL_VideoViewport.y + rect->y;
237 stackrect->w = rect->w;
238 stackrect->h = rect->h;
240 SDL_UpdateWindowSurfaceRects(SDL_VideoWindow, stackrects, numrects);
241 SDL_stack_free(stackrects);
243 SDL_UpdateWindowSurfaceRects(SDL_VideoWindow, rects, numrects);
249 SDL_UpdateRect(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h)
254 /* Fill the rectangle */
257 rect.w = (int) (w ? w : screen->w);
258 rect.h = (int) (h ? h : screen->h);
259 SDL_UpdateRects(screen, 1, &rect);
264 SDL_Flip(SDL_Surface * screen)
266 SDL_UpdateRect(screen, 0, 0, 0, 0);
271 * Calculate the pad-aligned scanline width of a surface
274 SDL_CalculatePitch(SDL_Surface * surface)
278 /* Surface should be 4-byte aligned for speed */
279 pitch = surface->w * surface->format->BytesPerPixel;
280 switch (surface->format->BitsPerPixel) {
282 pitch = (pitch + 7) / 8;
285 pitch = (pitch + 1) / 2;
290 pitch = (pitch + 3) & ~3; /* 4-byte aligning */
295 SDL_InvalidateMap(SDL_BlitMap * map)
301 /* Release our reference to the surface - see the note below */
302 if (--map->dst->refcount <= 0) {
303 SDL_FreeSurface(map->dst);
307 map->src_palette_version = 0;
308 map->dst_palette_version = 0;
309 if (map->info.table) {
310 SDL_free(map->info.table);
311 map->info.table = NULL;
316 SDL_GL_SwapBuffers(void)
318 SDL_GL_SwapWindow(SDL_VideoWindow);
322 SDL_WM_ToggleFullScreen(SDL_Surface * surface)
331 if (!SDL_PublicSurface) {
332 SDL_SetError("SDL_SetVideoMode() hasn't been called");
336 /* Copy the old bits out */
337 length = SDL_PublicSurface->w * SDL_PublicSurface->format->BytesPerPixel;
338 pixels = SDL_malloc(SDL_PublicSurface->h * length);
339 if (pixels && SDL_PublicSurface->pixels) {
340 src = (Uint8*)SDL_PublicSurface->pixels;
341 dst = (Uint8*)pixels;
342 for (row = 0; row < SDL_PublicSurface->h; ++row) {
343 SDL_memcpy(dst, src, length);
344 src += SDL_PublicSurface->pitch;
349 /* Do the physical mode switch */
350 if (SDL_GetWindowFlags(SDL_VideoWindow) & SDL_WINDOW_FULLSCREEN) {
351 if (SDL_SetWindowFullscreen(SDL_VideoWindow, 0) < 0) {
354 SDL_PublicSurface->flags &= ~SDL_FULLSCREEN;
356 if (SDL_SetWindowFullscreen(SDL_VideoWindow, 1) < 0) {
359 SDL_PublicSurface->flags |= SDL_FULLSCREEN;
362 /* Recreate the screen surface */
363 SDL_WindowSurface = SDL_GetWindowSurface(SDL_VideoWindow);
364 if (!SDL_WindowSurface) {
365 /* We're totally hosed... */
369 /* Center the public surface in the window surface */
370 SDL_GetWindowSize(SDL_VideoWindow, &window_w, &window_h);
371 SDL_VideoViewport.x = (window_w - SDL_VideoSurface->w)/2;
372 SDL_VideoViewport.y = (window_h - SDL_VideoSurface->h)/2;
373 SDL_VideoViewport.w = SDL_VideoSurface->w;
374 SDL_VideoViewport.h = SDL_VideoSurface->h;
376 /* Do some shuffling behind the application's back if format changes */
377 if (SDL_VideoSurface->format->format != SDL_WindowSurface->format->format) {
378 if (SDL_ShadowSurface) {
379 if (SDL_ShadowSurface->format->format == SDL_WindowSurface->format->format) {
380 /* Whee! We don't need a shadow surface anymore! */
381 SDL_VideoSurface->flags &= ~SDL_DONTFREE;
382 SDL_FreeSurface(SDL_VideoSurface);
383 SDL_free(SDL_ShadowSurface->pixels);
384 SDL_VideoSurface = SDL_ShadowSurface;
385 SDL_VideoSurface->flags |= SDL_PREALLOC;
386 SDL_ShadowSurface = NULL;
388 /* No problem, just change the video surface format */
389 SDL_FreeFormat(SDL_VideoSurface->format);
390 SDL_VideoSurface->format = SDL_WindowSurface->format;
391 SDL_VideoSurface->format->refcount++;
392 SDL_InvalidateMap(SDL_ShadowSurface->map);
395 /* We can make the video surface the shadow surface */
396 SDL_ShadowSurface = SDL_VideoSurface;
397 SDL_ShadowSurface->pitch = SDL_CalculatePitch(SDL_ShadowSurface);
398 SDL_ShadowSurface->pixels = SDL_malloc(SDL_ShadowSurface->h * SDL_ShadowSurface->pitch);
399 if (!SDL_ShadowSurface->pixels) {
400 /* Uh oh, we're hosed */
401 SDL_ShadowSurface = NULL;
404 SDL_ShadowSurface->flags &= ~SDL_PREALLOC;
406 SDL_VideoSurface = SDL_CreateRGBSurfaceFrom(NULL, 0, 0, 32, 0, 0, 0, 0, 0);
407 SDL_VideoSurface->flags = SDL_ShadowSurface->flags;
408 SDL_VideoSurface->flags |= SDL_PREALLOC;
409 SDL_FreeFormat(SDL_VideoSurface->format);
410 SDL_VideoSurface->format = SDL_WindowSurface->format;
411 SDL_VideoSurface->format->refcount++;
412 SDL_VideoSurface->w = SDL_ShadowSurface->w;
413 SDL_VideoSurface->h = SDL_ShadowSurface->h;
417 /* Update the video surface */
418 SDL_VideoSurface->pitch = SDL_WindowSurface->pitch;
419 SDL_VideoSurface->pixels = (void *)((Uint8 *)SDL_WindowSurface->pixels +
420 SDL_VideoViewport.y * SDL_VideoSurface->pitch +
421 SDL_VideoViewport.x * SDL_VideoSurface->format->BytesPerPixel);
422 SDL_SetClipRect(SDL_VideoSurface, NULL);
424 /* Copy the old bits back */
426 src = (Uint8*)pixels;
427 dst = (Uint8*)SDL_PublicSurface->pixels;
428 for (row = 0; row < SDL_PublicSurface->h; ++row) {
429 SDL_memcpy(dst, src, length);
431 dst += SDL_PublicSurface->pitch;
433 SDL_Flip(SDL_PublicSurface);
444 if (SDL_ShadowSurface) {
445 SDL_FillRect(SDL_ShadowSurface, NULL,
446 SDL_MapRGB(SDL_ShadowSurface->format, 0, 0, 0));
448 SDL_FillRect(SDL_WindowSurface, NULL, 0);
449 SDL_UpdateWindowSurface(SDL_VideoWindow);
453 SDL_ResizeVideoMode(int width, int height, int bpp, Uint32 flags)
457 /* We can't resize something we don't have... */
458 if (!SDL_VideoSurface) {
462 /* We probably have to recreate the window in fullscreen mode */
463 if (flags & SDL_FULLSCREEN) {
467 /* I don't think there's any change we can gracefully make in flags */
468 if (flags != SDL_VideoFlags) {
471 if (bpp != SDL_VideoSurface->format->BitsPerPixel) {
475 /* Resize the window */
476 SDL_GetWindowSize(SDL_VideoWindow, &w, &h);
477 if (w != width || h != height) {
478 SDL_SetWindowSize(SDL_VideoWindow, width, height);
481 /* If we're in OpenGL mode, just resize the stub surface and we're done! */
482 if (flags & SDL_OPENGL) {
483 SDL_VideoSurface->w = width;
484 SDL_VideoSurface->h = height;
488 SDL_WindowSurface = SDL_GetWindowSurface(SDL_VideoWindow);
489 if (!SDL_WindowSurface) {
492 if (SDL_VideoSurface->format != SDL_WindowSurface->format) {
495 SDL_VideoSurface->w = width;
496 SDL_VideoSurface->h = height;
497 SDL_VideoSurface->pixels = SDL_WindowSurface->pixels;
498 SDL_VideoSurface->pitch = SDL_WindowSurface->pitch;
499 SDL_SetClipRect(SDL_VideoSurface, NULL);
501 if (SDL_ShadowSurface) {
502 SDL_ShadowSurface->w = width;
503 SDL_ShadowSurface->h = height;
504 SDL_ShadowSurface->pitch = SDL_CalculatePitch(SDL_ShadowSurface);
505 SDL_ShadowSurface->pixels =
506 SDL_realloc(SDL_ShadowSurface->pixels,
507 SDL_ShadowSurface->h * SDL_ShadowSurface->pitch);
508 SDL_SetClipRect(SDL_ShadowSurface, NULL);
509 SDL_InvalidateMap(SDL_ShadowSurface->map);
511 SDL_PublicSurface = SDL_VideoSurface;
520 SDL_CompatEventFilter(void *userdata, SDL_Event * event)
524 switch (event->type) {
525 case SDL_WINDOWEVENT:
526 switch (event->window.event) {
527 case SDL_WINDOWEVENT_CLOSE:
528 fake.type = SDL_QUIT;
529 SDL_PushEvent(&fake);
536 if (event->key.type == SDL_KEYDOWN && event->key.keysym.sym < 256) {
537 unicode = event->key.keysym.sym;
538 if (unicode >= 'a' && unicode <= 'z') {
539 int shifted = !!(event->key.keysym.mod & KMOD_SHIFT);
540 int capslock = !!(event->key.keysym.mod & KMOD_CAPS);
541 if ((shifted ^ capslock) != 0) {
542 unicode = SDL_toupper(unicode);
547 event->key.keysym.unicode = unicode;
553 /* FIXME: Generate an old style key repeat event if needed */
554 //printf("TEXTINPUT: '%s'\n", event->text.text);
557 case SDL_MOUSEMOTION:
559 event->motion.x -= SDL_VideoViewport.x;
560 event->motion.y -= SDL_VideoViewport.y;
563 case SDL_MOUSEBUTTONDOWN:
564 case SDL_MOUSEBUTTONUP:
566 event->button.x -= SDL_VideoViewport.x;
567 event->button.y -= SDL_VideoViewport.y;
575 if (event->wheel.y == 0) {
579 SDL_GetMouseState(&x, &y);
581 if (event->wheel.y > 0) {
582 button = SDL_BUTTON_WHEELUP;
584 button = SDL_BUTTON_WHEELDOWN;
587 fake.button.button = button;
590 fake.button.windowID = event->wheel.windowID;
592 fake.type = SDL_MOUSEBUTTONDOWN;
593 fake.button.state = SDL_PRESSED;
594 SDL_PushEvent(&fake);
596 fake.type = SDL_MOUSEBUTTONUP;
597 fake.button.state = SDL_RELEASED;
598 SDL_PushEvent(&fake);
607 GetEnvironmentWindowPosition(int w, int h, int *x, int *y)
609 int display = GetVideoDisplay();
610 const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
611 const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
613 if (SDL_sscanf(window, "%d,%d", x, y) == 2) {
616 if (SDL_strcmp(window, "center") == 0) {
621 *x = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
622 *y = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
627 SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
629 SDL_DisplayMode desktop_mode;
630 int display = GetVideoDisplay();
631 int window_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
632 int window_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
636 Uint32 surface_flags;
638 if (!initialized_video) {
639 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0) {
642 initialized_video = 1;
645 SDL_GetDesktopDisplayMode(display, &desktop_mode);
648 width = desktop_mode.w;
651 height = desktop_mode.h;
654 bpp = SDL_BITSPERPIXEL(desktop_mode.format);
657 /* See if we can simply resize the existing window and surface */
658 if (SDL_ResizeVideoMode(width, height, bpp, flags) == 0) {
659 return SDL_PublicSurface;
662 /* Destroy existing window */
663 SDL_PublicSurface = NULL;
664 if (SDL_ShadowSurface) {
665 SDL_ShadowSurface->flags &= ~SDL_DONTFREE;
666 SDL_FreeSurface(SDL_ShadowSurface);
667 SDL_ShadowSurface = NULL;
669 if (SDL_VideoSurface) {
670 SDL_VideoSurface->flags &= ~SDL_DONTFREE;
671 SDL_FreeSurface(SDL_VideoSurface);
672 SDL_VideoSurface = NULL;
674 if (SDL_VideoContext) {
675 /* SDL_GL_MakeCurrent(0, NULL); *//* Doesn't do anything */
676 SDL_GL_DeleteContext(SDL_VideoContext);
677 SDL_VideoContext = NULL;
679 if (SDL_VideoWindow) {
680 SDL_GetWindowPosition(SDL_VideoWindow, &window_x, &window_y);
681 SDL_DestroyWindow(SDL_VideoWindow);
684 /* Set up the event filter */
685 if (!SDL_GetEventFilter(NULL, NULL)) {
686 SDL_SetEventFilter(SDL_CompatEventFilter, NULL);
689 /* Create a new window */
690 window_flags = SDL_WINDOW_SHOWN;
691 if (flags & SDL_FULLSCREEN) {
692 window_flags |= SDL_WINDOW_FULLSCREEN;
694 if (flags & SDL_OPENGL) {
695 window_flags |= SDL_WINDOW_OPENGL;
697 if (flags & SDL_RESIZABLE) {
698 window_flags |= SDL_WINDOW_RESIZABLE;
700 if (flags & SDL_NOFRAME) {
701 window_flags |= SDL_WINDOW_BORDERLESS;
703 GetEnvironmentWindowPosition(width, height, &window_x, &window_y);
705 SDL_CreateWindow(wm_title, window_x, window_y, width, height,
707 if (!SDL_VideoWindow) {
710 SDL_SetWindowIcon(SDL_VideoWindow, SDL_VideoIcon);
712 window_flags = SDL_GetWindowFlags(SDL_VideoWindow);
714 if (window_flags & SDL_WINDOW_FULLSCREEN) {
715 surface_flags |= SDL_FULLSCREEN;
717 if ((window_flags & SDL_WINDOW_OPENGL) && (flags & SDL_OPENGL)) {
718 surface_flags |= SDL_OPENGL;
720 if (window_flags & SDL_WINDOW_RESIZABLE) {
721 surface_flags |= SDL_RESIZABLE;
723 if (window_flags & SDL_WINDOW_BORDERLESS) {
724 surface_flags |= SDL_NOFRAME;
727 SDL_VideoFlags = flags;
729 /* If we're in OpenGL mode, just create a stub surface and we're done! */
730 if (flags & SDL_OPENGL) {
731 SDL_VideoContext = SDL_GL_CreateContext(SDL_VideoWindow);
732 if (!SDL_VideoContext) {
735 if (SDL_GL_MakeCurrent(SDL_VideoWindow, SDL_VideoContext) < 0) {
739 SDL_CreateRGBSurfaceFrom(NULL, width, height, bpp, 0, 0, 0, 0, 0);
740 if (!SDL_VideoSurface) {
743 SDL_VideoSurface->flags |= surface_flags;
744 SDL_PublicSurface = SDL_VideoSurface;
745 return SDL_PublicSurface;
748 /* Create the screen surface */
749 SDL_WindowSurface = SDL_GetWindowSurface(SDL_VideoWindow);
750 if (!SDL_WindowSurface) {
754 /* Center the public surface in the window surface */
755 SDL_GetWindowSize(SDL_VideoWindow, &window_w, &window_h);
756 SDL_VideoViewport.x = (window_w - width)/2;
757 SDL_VideoViewport.y = (window_h - height)/2;
758 SDL_VideoViewport.w = width;
759 SDL_VideoViewport.h = height;
761 SDL_VideoSurface = SDL_CreateRGBSurfaceFrom(NULL, 0, 0, 32, 0, 0, 0, 0, 0);
762 SDL_VideoSurface->flags |= surface_flags;
763 SDL_VideoSurface->flags |= SDL_DONTFREE;
764 SDL_FreeFormat(SDL_VideoSurface->format);
765 SDL_VideoSurface->format = SDL_WindowSurface->format;
766 SDL_VideoSurface->format->refcount++;
767 SDL_VideoSurface->w = width;
768 SDL_VideoSurface->h = height;
769 SDL_VideoSurface->pitch = SDL_WindowSurface->pitch;
770 SDL_VideoSurface->pixels = (void *)((Uint8 *)SDL_WindowSurface->pixels +
771 SDL_VideoViewport.y * SDL_VideoSurface->pitch +
772 SDL_VideoViewport.x * SDL_VideoSurface->format->BytesPerPixel);
773 SDL_SetClipRect(SDL_VideoSurface, NULL);
775 /* Create a shadow surface if necessary */
776 if ((bpp != SDL_VideoSurface->format->BitsPerPixel)
777 && !(flags & SDL_ANYFORMAT)) {
779 SDL_CreateRGBSurface(0, width, height, bpp, 0, 0, 0, 0);
780 if (!SDL_ShadowSurface) {
783 SDL_ShadowSurface->flags |= surface_flags;
784 SDL_ShadowSurface->flags |= SDL_DONTFREE;
786 /* 8-bit SDL_ShadowSurface surfaces report that they have exclusive palette */
787 if (SDL_ShadowSurface->format->palette) {
788 SDL_ShadowSurface->flags |= SDL_HWPALETTE;
789 //TODO SDL_DitherColors(SDL_ShadowSurface->format->palette->colors,
790 // SDL_ShadowSurface->format->BitsPerPixel);
792 SDL_FillRect(SDL_ShadowSurface, NULL,
793 SDL_MapRGB(SDL_ShadowSurface->format, 0, 0, 0));
796 (SDL_ShadowSurface ? SDL_ShadowSurface : SDL_VideoSurface);
800 /* We're finally done! */
801 return SDL_PublicSurface;