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_Rect**)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);
534 /* FIXME: Generate an old style key repeat event if needed */
535 //printf("TEXTINPUT: '%s'\n", event->text.text);
538 case SDL_MOUSEMOTION:
540 event->motion.x -= SDL_VideoViewport.x;
541 event->motion.y -= SDL_VideoViewport.y;
544 case SDL_MOUSEBUTTONDOWN:
545 case SDL_MOUSEBUTTONUP:
547 event->button.x -= SDL_VideoViewport.x;
548 event->button.y -= SDL_VideoViewport.y;
556 if (event->wheel.y == 0) {
560 SDL_GetMouseState(&x, &y);
562 if (event->wheel.y > 0) {
563 button = SDL_BUTTON_WHEELUP;
565 button = SDL_BUTTON_WHEELDOWN;
568 fake.button.button = button;
571 fake.button.windowID = event->wheel.windowID;
573 fake.type = SDL_MOUSEBUTTONDOWN;
574 fake.button.state = SDL_PRESSED;
575 SDL_PushEvent(&fake);
577 fake.type = SDL_MOUSEBUTTONUP;
578 fake.button.state = SDL_RELEASED;
579 SDL_PushEvent(&fake);
588 GetEnvironmentWindowPosition(int w, int h, int *x, int *y)
590 int display = GetVideoDisplay();
591 const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
592 const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
594 if (SDL_sscanf(window, "%d,%d", x, y) == 2) {
597 if (SDL_strcmp(window, "center") == 0) {
602 *x = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
603 *y = SDL_WINDOWPOS_CENTERED_DISPLAY(display);
608 SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
610 SDL_DisplayMode desktop_mode;
611 int display = GetVideoDisplay();
612 int window_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
613 int window_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display);
617 Uint32 surface_flags;
619 if (!initialized_video) {
620 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0) {
623 initialized_video = 1;
626 SDL_GetDesktopDisplayMode(display, &desktop_mode);
629 width = desktop_mode.w;
632 height = desktop_mode.h;
635 bpp = SDL_BITSPERPIXEL(desktop_mode.format);
638 /* See if we can simply resize the existing window and surface */
639 if (SDL_ResizeVideoMode(width, height, bpp, flags) == 0) {
640 return SDL_PublicSurface;
643 /* Destroy existing window */
644 SDL_PublicSurface = NULL;
645 if (SDL_ShadowSurface) {
646 SDL_ShadowSurface->flags &= ~SDL_DONTFREE;
647 SDL_FreeSurface(SDL_ShadowSurface);
648 SDL_ShadowSurface = NULL;
650 if (SDL_VideoSurface) {
651 SDL_VideoSurface->flags &= ~SDL_DONTFREE;
652 SDL_FreeSurface(SDL_VideoSurface);
653 SDL_VideoSurface = NULL;
655 if (SDL_VideoContext) {
656 /* SDL_GL_MakeCurrent(0, NULL); *//* Doesn't do anything */
657 SDL_GL_DeleteContext(SDL_VideoContext);
658 SDL_VideoContext = NULL;
660 if (SDL_VideoWindow) {
661 SDL_GetWindowPosition(SDL_VideoWindow, &window_x, &window_y);
662 SDL_DestroyWindow(SDL_VideoWindow);
665 /* Set up the event filter */
666 if (!SDL_GetEventFilter(NULL, NULL)) {
667 SDL_SetEventFilter(SDL_CompatEventFilter, NULL);
670 /* Create a new window */
671 window_flags = SDL_WINDOW_SHOWN;
672 if (flags & SDL_FULLSCREEN) {
673 window_flags |= SDL_WINDOW_FULLSCREEN;
675 if (flags & SDL_OPENGL) {
676 window_flags |= SDL_WINDOW_OPENGL;
678 if (flags & SDL_RESIZABLE) {
679 window_flags |= SDL_WINDOW_RESIZABLE;
681 if (flags & SDL_NOFRAME) {
682 window_flags |= SDL_WINDOW_BORDERLESS;
684 GetEnvironmentWindowPosition(width, height, &window_x, &window_y);
686 SDL_CreateWindow(wm_title, window_x, window_y, width, height,
688 if (!SDL_VideoWindow) {
691 SDL_SetWindowIcon(SDL_VideoWindow, SDL_VideoIcon);
693 window_flags = SDL_GetWindowFlags(SDL_VideoWindow);
695 if (window_flags & SDL_WINDOW_FULLSCREEN) {
696 surface_flags |= SDL_FULLSCREEN;
698 if ((window_flags & SDL_WINDOW_OPENGL) && (flags & SDL_OPENGL)) {
699 surface_flags |= SDL_OPENGL;
701 if (window_flags & SDL_WINDOW_RESIZABLE) {
702 surface_flags |= SDL_RESIZABLE;
704 if (window_flags & SDL_WINDOW_BORDERLESS) {
705 surface_flags |= SDL_NOFRAME;
708 SDL_VideoFlags = flags;
710 /* If we're in OpenGL mode, just create a stub surface and we're done! */
711 if (flags & SDL_OPENGL) {
712 SDL_VideoContext = (SDL_GLContext *)SDL_GL_CreateContext(SDL_VideoWindow);
713 if (!SDL_VideoContext) {
716 if (SDL_GL_MakeCurrent(SDL_VideoWindow, SDL_VideoContext) < 0) {
720 SDL_CreateRGBSurfaceFrom(NULL, width, height, bpp, 0, 0, 0, 0, 0);
721 if (!SDL_VideoSurface) {
724 SDL_VideoSurface->flags |= surface_flags;
725 SDL_PublicSurface = SDL_VideoSurface;
726 return SDL_PublicSurface;
729 /* Create the screen surface */
730 SDL_WindowSurface = SDL_GetWindowSurface(SDL_VideoWindow);
731 if (!SDL_WindowSurface) {
735 /* Center the public surface in the window surface */
736 SDL_GetWindowSize(SDL_VideoWindow, &window_w, &window_h);
737 SDL_VideoViewport.x = (window_w - width)/2;
738 SDL_VideoViewport.y = (window_h - height)/2;
739 SDL_VideoViewport.w = width;
740 SDL_VideoViewport.h = height;
742 SDL_VideoSurface = SDL_CreateRGBSurfaceFrom(NULL, 0, 0, 32, 0, 0, 0, 0, 0);
743 SDL_VideoSurface->flags |= surface_flags;
744 SDL_VideoSurface->flags |= SDL_DONTFREE;
745 SDL_FreeFormat(SDL_VideoSurface->format);
746 SDL_VideoSurface->format = SDL_WindowSurface->format;
747 SDL_VideoSurface->format->refcount++;
748 SDL_VideoSurface->w = width;
749 SDL_VideoSurface->h = height;
750 SDL_VideoSurface->pitch = SDL_WindowSurface->pitch;
751 SDL_VideoSurface->pixels = (void *)((Uint8 *)SDL_WindowSurface->pixels +
752 SDL_VideoViewport.y * SDL_VideoSurface->pitch +
753 SDL_VideoViewport.x * SDL_VideoSurface->format->BytesPerPixel);
754 SDL_SetClipRect(SDL_VideoSurface, NULL);
756 /* Create a shadow surface if necessary */
757 if ((bpp != SDL_VideoSurface->format->BitsPerPixel)
758 && !(flags & SDL_ANYFORMAT)) {
760 SDL_CreateRGBSurface(0, width, height, bpp, 0, 0, 0, 0);
761 if (!SDL_ShadowSurface) {
764 SDL_ShadowSurface->flags |= surface_flags;
765 SDL_ShadowSurface->flags |= SDL_DONTFREE;
767 /* 8-bit SDL_ShadowSurface surfaces report that they have exclusive palette */
768 if (SDL_ShadowSurface->format->palette) {
769 SDL_ShadowSurface->flags |= SDL_HWPALETTE;
770 //TODO SDL_DitherColors(SDL_ShadowSurface->format->palette->colors,
771 // SDL_ShadowSurface->format->BitsPerPixel);
773 SDL_FillRect(SDL_ShadowSurface, NULL,
774 SDL_MapRGB(SDL_ShadowSurface->format, 0, 0, 0));
777 (SDL_ShadowSurface ? SDL_ShadowSurface : SDL_VideoSurface);
781 /* We're finally done! */
782 return SDL_PublicSurface;