DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/output/output_surface_sdl.cpp
00001 #include <sys/types.h>
00002 #include <assert.h>
00003 #include <math.h>
00004 
00005 #include "dosbox.h"
00006 #include "sdlmain.h"
00007 #include "vga.h"
00008 
00009 #include <algorithm> // std::transform
00010 
00011 using namespace std;
00012 
00013 #if !defined(C_SDL2)
00014 Bitu OUTPUT_SURFACE_GetBestMode(Bitu flags)
00015 {
00016     Bitu testbpp, gotbpp;
00017 
00018     flags &= ~GFX_LOVE_8;       //Disable love for 8bpp modes
00019                                 /* Check if we can satisfy the depth it loves */
00020     if (flags & GFX_LOVE_8) testbpp = 8;
00021     else if (flags & GFX_LOVE_15) testbpp = 15;
00022     else if (flags & GFX_LOVE_16) testbpp = 16;
00023     else if (flags & GFX_LOVE_32) testbpp = 32;
00024     else testbpp = 0;
00025 
00026     if (sdl.desktop.fullscreen)
00027         gotbpp = (unsigned int)SDL_VideoModeOK(640, 480, testbpp,
00028         (unsigned int)SDL_FULLSCREEN | (unsigned int)SDL_HWSURFACE | (unsigned int)SDL_HWPALETTE);
00029     else
00030         gotbpp = sdl.desktop.bpp;
00031 
00032     /* SDL 1.x and sometimes SDL 2.x mistake 15-bit 5:5:5 RGB for 16-bit 5:6:5 RGB
00033     * which causes colors to mis-display. This seems to be common with Windows and Linux.
00034     * If SDL said 16-bit but the bit masks suggest 15-bit, then make the correction now. */
00035     if (gotbpp == 16) {
00036         if (sdl.surface->format->Gshift == 5 && sdl.surface->format->Gmask == (31U << 5U)) {
00037             LOG_MSG("NOTE: SDL returned 16-bit/pixel mode (5:6:5) but failed to recognize your screen is 15-bit/pixel mode (5:5:5)");
00038             gotbpp = 15;
00039         }
00040     }
00041 
00042     /* If we can't get our favorite mode check for another working one */
00043     switch (gotbpp) {
00044     case 8:
00045         if (flags & GFX_CAN_8) flags &= ~(GFX_CAN_15 | GFX_CAN_16 | GFX_CAN_32);
00046         break;
00047     case 15:
00048         if (flags & GFX_CAN_15) flags &= ~(GFX_CAN_8 | GFX_CAN_16 | GFX_CAN_32);
00049         break;
00050     case 16:
00051         if (flags & GFX_CAN_16) flags &= ~(GFX_CAN_8 | GFX_CAN_15 | GFX_CAN_32);
00052         break;
00053     case 24:
00054     case 32:
00055         if (flags & GFX_CAN_32) flags &= ~(GFX_CAN_8 | GFX_CAN_15 | GFX_CAN_16);
00056         break;
00057     }
00058     flags |= GFX_CAN_RANDOM;
00059     return flags;
00060 }
00061 
00062 Bitu OUTPUT_SURFACE_SetSize()
00063 {
00064     Bitu bpp = 0, retFlags = 0;
00065 
00066     // localize width and height variables because they can be locally adjusted by aspect ratio correction
00067 retry:
00068     Bitu width = sdl.draw.width;
00069     Bitu height = sdl.draw.height;
00070 
00071     if (sdl.draw.flags & GFX_CAN_32)
00072         bpp = 32;
00073     else if (sdl.draw.flags & GFX_CAN_16)
00074         bpp = 16;
00075     else if (sdl.draw.flags & GFX_CAN_15)
00076         bpp = 15;
00077     else if (sdl.draw.flags & GFX_CAN_8)
00078         bpp = 8;
00079 
00080 #if defined(WIN32) && !defined(C_SDL2)
00081     /* SDL 1.x might mis-inform us on 16bpp for 15-bit color, which is bad enough.
00082     But on Windows, we're still required to ask for 16bpp to get the 15bpp mode we want. */
00083     if (bpp == 15)
00084     {
00085         if (sdl.surface->format->Gshift == 5 && sdl.surface->format->Gmask == (31U << 5U))
00086         {
00087             LOG_MSG("SDL hack: Asking for 16-bit color (5:6:5) to get SDL to give us 15-bit color (5:5:5) to match your screen.");
00088             bpp = 16;
00089         }
00090     }
00091 #endif
00092 
00093 #if C_XBRZ || C_SURFACE_POSTRENDER_ASPECT
00094     // there is a small problem we need to solve here: aspect corrected windows can be smaller than needed due to source with non-4:3 pixel ratio
00095     // if we detect non-4:3 pixel ratio here with aspect correction on, we correct it so original fits into resized window properly
00096     if (render.aspect) aspectCorrectExtend(width, height);
00097 #endif
00098 
00099     sdl.clip.w = width; sdl.clip.h = height;
00100     if (sdl.desktop.fullscreen)
00101     {
00102         Uint32 wflags = SDL_FULLSCREEN | SDL_HWPALETTE |
00103             ((sdl.draw.flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) |
00104             (sdl.desktop.doublebuf ? SDL_DOUBLEBUF | SDL_ASYNCBLIT : 0);
00105 
00106         if (sdl.desktop.full.fixed)
00107         {
00108             sdl.clip.x = (Sint16)((sdl.desktop.full.width - width) / 2);
00109             sdl.clip.y = (Sint16)((sdl.desktop.full.height - height) / 2);
00110             sdl.surface = SDL_SetVideoMode(sdl.desktop.full.width, sdl.desktop.full.height, bpp, wflags);
00111             sdl.deferred_resize = false;
00112             sdl.must_redraw_all = true;
00113 
00114 #if C_XBRZ
00115             /* scale to fit the window.
00116              * fit by aspect ratio if asked to do so. */
00117             if (sdl_xbrz.enable)
00118             {
00119                 sdl.clip.x = sdl.clip.y = 0;
00120                 sdl.clip.w = sdl.desktop.full.width;
00121                 sdl.clip.h = sdl.desktop.full.height;
00122                 if (render.aspect) aspectCorrectFitClip(sdl.clip.w, sdl.clip.h, sdl.clip.x, sdl.clip.y, sdl.desktop.full.width, sdl.desktop.full.height);
00123             }
00124 #endif 
00125         }
00126         else
00127         {
00128             sdl.clip.x = 0; sdl.clip.y = 0;
00129             sdl.surface = SDL_SetVideoMode(width, height, bpp, wflags);
00130             sdl.deferred_resize = false;
00131             sdl.must_redraw_all = true;
00132         }
00133 
00134         if (sdl.surface == NULL) {
00135             LOG_MSG("Fullscreen not supported: %s", SDL_GetError());
00136             sdl.desktop.fullscreen = false;
00137             GFX_CaptureMouse();
00138             goto retry;
00139         }
00140     }
00141     else
00142     {
00143         int menuheight = 0;
00144 
00145         sdl.clip.x = 0; sdl.clip.y = 0;
00146 
00147 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
00148         /* scale the menu bar if the window is large enough */
00149         {
00150             Bitu consider_height = menu.maxwindow ? currentWindowHeight : height;
00151             Bitu consider_width = menu.maxwindow ? currentWindowWidth : width;
00152             Bitu final_height = max(max(consider_height, userResizeWindowHeight), (Bitu)(sdl.clip.y + sdl.clip.h));
00153             Bitu final_width = max(max(consider_width, userResizeWindowWidth), (Bitu)(sdl.clip.x + sdl.clip.w));
00154             Bitu scale = 1;
00155 
00156             while ((final_width / scale) >= (640 * 2) && (final_height / scale) >= (400 * 2))
00157                 scale++;
00158 
00159             LOG_MSG("menuScale=%lu", (unsigned long)scale);
00160             mainMenu.setScale(scale);
00161         }
00162 
00163         if (mainMenu.isVisible()) menuheight = mainMenu.menuBox.h;
00164 #endif
00165 
00166         /* menu size and consideration of width and height */
00167         Bitu consider_height = menu.maxwindow ? currentWindowHeight : (height + (unsigned int)menuheight + (sdl.overscan_width * 2));
00168         Bitu consider_width = menu.maxwindow ? currentWindowWidth : (width + (sdl.overscan_width * 2));
00169 
00170 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
00171         if (mainMenu.isVisible())
00172         {
00173             /* enforce a minimum 640x400 surface size.
00174              * the menus are useless below 640x400 */
00175             if (consider_width < (640 + (sdl.overscan_width * 2)))
00176                 consider_width = (640 + (sdl.overscan_width * 2));
00177             if (consider_height < (400 + (sdl.overscan_width * 2) + (unsigned int)menuheight))
00178                 consider_height = (400 + (sdl.overscan_width * 2) + (unsigned int)menuheight);
00179         }
00180 #endif
00181 
00182         /* decide where the rectangle on the screen goes */
00183         int final_width,final_height,ax,ay;
00184 
00185 #if C_XBRZ
00186         /* scale to fit the window.
00187          * fit by aspect ratio if asked to do so. */
00188         if (sdl_xbrz.enable)
00189         {
00190             final_height = (int)max(consider_height, userResizeWindowHeight) - (int)menuheight - ((int)sdl.overscan_width * 2);
00191             final_width = (int)max(consider_width, userResizeWindowWidth) - ((int)sdl.overscan_width * 2);
00192 
00193             sdl.clip.x = sdl.clip.y = 0;
00194             sdl.clip.w = final_width;
00195             sdl.clip.h = final_height;
00196             if (render.aspect) aspectCorrectFitClip(sdl.clip.w, sdl.clip.h, sdl.clip.x, sdl.clip.y, final_width, final_height);
00197         }
00198         else
00199 #endif 
00200         /* center the screen in the window */
00201         {
00202 
00203             final_height = (int)max(max(consider_height, userResizeWindowHeight), (Bitu)(sdl.clip.y + sdl.clip.h)) - (int)menuheight - ((int)sdl.overscan_width * 2);
00204             final_width = (int)max(max(consider_width, userResizeWindowWidth), (Bitu)(sdl.clip.x + sdl.clip.w)) - ((int)sdl.overscan_width * 2);
00205             ax = (final_width - (sdl.clip.x + sdl.clip.w)) / 2;
00206             ay = (final_height - (sdl.clip.y + sdl.clip.h)) / 2;
00207             if (ax < 0) ax = 0;
00208             if (ay < 0) ay = 0;
00209             sdl.clip.x += ax + (int)sdl.overscan_width;
00210             sdl.clip.y += ay + (int)sdl.overscan_width;
00211             // sdl.clip.w = currentWindowWidth - sdl.clip.x;
00212             // sdl.clip.h = currentWindowHeight - sdl.clip.y;
00213         }
00214 
00215         {
00216             final_width += (int)sdl.overscan_width * 2;
00217             final_height += (int)menuheight + (int)sdl.overscan_width * 2;
00218             sdl.clip.y += (int)menuheight;
00219 
00220             LOG_MSG("surface consider=%ux%u final=%ux%u",
00221                 (unsigned int)consider_width,
00222                 (unsigned int)consider_height,
00223                 (unsigned int)final_width,
00224                 (unsigned int)final_height);
00225 
00226             sdl.surface = SDL_SetVideoMode(final_width, final_height, bpp,
00227                 (unsigned int)((sdl.draw.flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) |
00228                 (unsigned int)SDL_HAX_NOREFRESH |
00229                 (unsigned int)SDL_RESIZABLE);
00230 
00231             sdl.deferred_resize = false;
00232             sdl.must_redraw_all = true;
00233 
00234             if (SDL_MUSTLOCK(sdl.surface))
00235                 SDL_LockSurface(sdl.surface);
00236 
00237             memset(sdl.surface->pixels, 0, (unsigned int)sdl.surface->pitch * (unsigned int)sdl.surface->h);
00238 
00239             if (SDL_MUSTLOCK(sdl.surface))
00240                 SDL_UnlockSurface(sdl.surface);
00241         }
00242 
00243 #ifdef WIN32
00244         if (sdl.surface == NULL)
00245         {
00246             SDL_QuitSubSystem(SDL_INIT_VIDEO);
00247 
00248             if (!sdl.using_windib)
00249             {
00250                 LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with windib enabled.");
00251                 putenv("SDL_VIDEODRIVER=windib");
00252                 sdl.using_windib = true;
00253             }
00254             else
00255             {
00256                 LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with directx enabled.");
00257                 putenv("SDL_VIDEODRIVER=directx");
00258                 sdl.using_windib = false;
00259             }
00260 
00261             SDL_InitSubSystem(SDL_INIT_VIDEO);
00262             GFX_SetIcon(); // set icon again
00263 
00264             sdl.surface = SDL_SetVideoMode(width, height, bpp, SDL_HWSURFACE);
00265             sdl.deferred_resize = false;
00266             sdl.must_redraw_all = true;
00267 
00268             if (sdl.surface) GFX_SetTitle(-1, -1, -1, false); //refresh title
00269         }
00270 #endif
00271         if (sdl.surface == NULL)
00272             E_Exit("Could not set windowed video mode %ix%i-%i: %s", (int)width, (int)height, (int)bpp, SDL_GetError());
00273     }
00274 
00275     if (sdl.surface)
00276     {
00277         switch (sdl.surface->format->BitsPerPixel)
00278         {
00279             case 8:
00280                 retFlags = GFX_CAN_8;
00281                 break;
00282             case 15:
00283                 retFlags = GFX_CAN_15;
00284                 break;
00285             case 16:
00286                 if (sdl.surface->format->Gshift == 5 && sdl.surface->format->Gmask == (31U << 5U))
00287                     retFlags = GFX_CAN_15;
00288                 else
00289                     retFlags = GFX_CAN_16;
00290                 break;
00291             case 32:
00292                 retFlags = GFX_CAN_32;
00293                 break;
00294         }
00295 
00296         if (retFlags && (sdl.surface->flags & SDL_HWSURFACE))
00297             retFlags |= GFX_HARDWARE;
00298 
00299         if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF))
00300         {
00301             sdl.blit.surface = SDL_CreateRGBSurface((Uint32)SDL_HWSURFACE,
00302                 (int)sdl.draw.width, (int)sdl.draw.height,
00303                 (int)sdl.surface->format->BitsPerPixel,
00304                 (Uint32)sdl.surface->format->Rmask,
00305                 (Uint32)sdl.surface->format->Gmask,
00306                 (Uint32)sdl.surface->format->Bmask,
00307                 (Uint32)0u);
00308             /* If this one fails be ready for some flickering... */
00309         }
00310 
00311 #if C_XBRZ
00312         if (sdl_xbrz.enable)
00313         {
00314             bool old_scale_on = sdl_xbrz.scale_on;
00315             xBRZ_SetScaleParameters(sdl.draw.width, sdl.draw.height, sdl.clip.w, sdl.clip.h);
00316             if (sdl_xbrz.scale_on != old_scale_on) {
00317                 // when we are scaling, we ask render code not to do any aspect correction
00318                 // when we are not scaling, render code is allowed to do aspect correction at will
00319                 // due to this, at each scale mode change we need to schedule resize again because window size could change
00320                 PIC_AddEvent(VGA_SetupDrawing, 50); // schedule another resize here, render has already been initialized at this point and we have just changed its option
00321             }
00322         }
00323 #endif
00324     }
00325 
00326 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
00327     mainMenu.screenWidth = (size_t)sdl.surface->w;
00328     mainMenu.updateRect();
00329     mainMenu.setRedraw();
00330     GFX_DrawSDLMenu(mainMenu, mainMenu.display_list);
00331 #endif
00332 
00333     return retFlags;
00334 }
00335 #endif