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 = height + (unsigned int)menuheight + (sdl.overscan_width * 2);
00168         Bitu consider_width = width + (sdl.overscan_width * 2);
00169 
00170         if (menu.maxwindow) {
00171             if (consider_height < currentWindowHeight)
00172                 consider_height = currentWindowHeight;
00173             if (consider_width < currentWindowWidth)
00174                 consider_width = currentWindowWidth;
00175         }
00176 
00177 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
00178         if (mainMenu.isVisible())
00179         {
00180             /* enforce a minimum 640x400 surface size.
00181              * the menus are useless below 640x400 */
00182             if (consider_width < (640 + (sdl.overscan_width * 2)))
00183                 consider_width = (640 + (sdl.overscan_width * 2));
00184             if (consider_height < (400 + (sdl.overscan_width * 2) + (unsigned int)menuheight))
00185                 consider_height = (400 + (sdl.overscan_width * 2) + (unsigned int)menuheight);
00186         }
00187 #endif
00188 
00189         /* decide where the rectangle on the screen goes */
00190         int final_width,final_height,ax,ay;
00191 
00192 #if C_XBRZ
00193         /* scale to fit the window.
00194          * fit by aspect ratio if asked to do so. */
00195         if (sdl_xbrz.enable)
00196         {
00197             final_height = (int)max(consider_height, userResizeWindowHeight) - (int)menuheight - ((int)sdl.overscan_width * 2);
00198             final_width = (int)max(consider_width, userResizeWindowWidth) - ((int)sdl.overscan_width * 2);
00199 
00200             sdl.clip.x = sdl.clip.y = 0;
00201             sdl.clip.w = final_width;
00202             sdl.clip.h = final_height;
00203             if (render.aspect) aspectCorrectFitClip(sdl.clip.w, sdl.clip.h, sdl.clip.x, sdl.clip.y, final_width, final_height);
00204         }
00205         else
00206 #endif 
00207         /* center the screen in the window */
00208         {
00209 
00210             final_height = (int)max(max(consider_height, userResizeWindowHeight), (Bitu)(sdl.clip.y + sdl.clip.h)) - (int)menuheight - ((int)sdl.overscan_width * 2);
00211             final_width = (int)max(max(consider_width, userResizeWindowWidth), (Bitu)(sdl.clip.x + sdl.clip.w)) - ((int)sdl.overscan_width * 2);
00212             ax = (final_width - (sdl.clip.x + sdl.clip.w)) / 2;
00213             ay = (final_height - (sdl.clip.y + sdl.clip.h)) / 2;
00214             if (ax < 0) ax = 0;
00215             if (ay < 0) ay = 0;
00216             sdl.clip.x += ax + (int)sdl.overscan_width;
00217             sdl.clip.y += ay + (int)sdl.overscan_width;
00218             // sdl.clip.w = currentWindowWidth - sdl.clip.x;
00219             // sdl.clip.h = currentWindowHeight - sdl.clip.y;
00220         }
00221 
00222         {
00223             final_width += (int)sdl.overscan_width * 2;
00224             final_height += (int)menuheight + (int)sdl.overscan_width * 2;
00225             sdl.clip.y += (int)menuheight;
00226 
00227             LOG_MSG("surface consider=%ux%u final=%ux%u",
00228                 (unsigned int)consider_width,
00229                 (unsigned int)consider_height,
00230                 (unsigned int)final_width,
00231                 (unsigned int)final_height);
00232 
00233             sdl.surface = SDL_SetVideoMode(final_width, final_height, bpp,
00234                 (unsigned int)((sdl.draw.flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) |
00235                 (unsigned int)SDL_HAX_NOREFRESH |
00236                 (unsigned int)SDL_RESIZABLE);
00237 
00238             sdl.deferred_resize = false;
00239             sdl.must_redraw_all = true;
00240 
00241             if (SDL_MUSTLOCK(sdl.surface))
00242                 SDL_LockSurface(sdl.surface);
00243 
00244             memset(sdl.surface->pixels, 0, (unsigned int)sdl.surface->pitch * (unsigned int)sdl.surface->h);
00245 
00246             if (SDL_MUSTLOCK(sdl.surface))
00247                 SDL_UnlockSurface(sdl.surface);
00248         }
00249 
00250 #ifdef WIN32
00251         if (sdl.surface == NULL)
00252         {
00253             SDL_QuitSubSystem(SDL_INIT_VIDEO);
00254 
00255             if (!sdl.using_windib)
00256             {
00257                 LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with windib enabled.");
00258                 putenv("SDL_VIDEODRIVER=windib");
00259                 sdl.using_windib = true;
00260             }
00261             else
00262             {
00263                 LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with directx enabled.");
00264                 putenv("SDL_VIDEODRIVER=directx");
00265                 sdl.using_windib = false;
00266             }
00267 
00268             SDL_InitSubSystem(SDL_INIT_VIDEO);
00269             GFX_SetIcon(); // set icon again
00270 
00271             sdl.surface = SDL_SetVideoMode(width, height, bpp, SDL_HWSURFACE);
00272             sdl.deferred_resize = false;
00273             sdl.must_redraw_all = true;
00274 
00275             if (sdl.surface) GFX_SetTitle(-1, -1, -1, false); //refresh title
00276         }
00277 #endif
00278         if (sdl.surface == NULL)
00279             E_Exit("Could not set windowed video mode %ix%i-%i: %s", (int)width, (int)height, (int)bpp, SDL_GetError());
00280     }
00281 
00282     if (sdl.surface)
00283     {
00284         switch (sdl.surface->format->BitsPerPixel)
00285         {
00286             case 8:
00287                 retFlags = GFX_CAN_8;
00288                 break;
00289             case 15:
00290                 retFlags = GFX_CAN_15;
00291                 break;
00292             case 16:
00293                 if (sdl.surface->format->Gshift == 5 && sdl.surface->format->Gmask == (31U << 5U))
00294                     retFlags = GFX_CAN_15;
00295                 else
00296                     retFlags = GFX_CAN_16;
00297                 break;
00298             case 32:
00299                 retFlags = GFX_CAN_32;
00300                 break;
00301         }
00302 
00303         if (retFlags && (sdl.surface->flags & SDL_HWSURFACE))
00304             retFlags |= GFX_HARDWARE;
00305 
00306         if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF))
00307         {
00308             sdl.blit.surface = SDL_CreateRGBSurface((Uint32)SDL_HWSURFACE,
00309                 (int)sdl.draw.width, (int)sdl.draw.height,
00310                 (int)sdl.surface->format->BitsPerPixel,
00311                 (Uint32)sdl.surface->format->Rmask,
00312                 (Uint32)sdl.surface->format->Gmask,
00313                 (Uint32)sdl.surface->format->Bmask,
00314                 (Uint32)0u);
00315             /* If this one fails be ready for some flickering... */
00316         }
00317 
00318 #if C_XBRZ
00319         if (sdl_xbrz.enable)
00320         {
00321             bool old_scale_on = sdl_xbrz.scale_on;
00322             xBRZ_SetScaleParameters(sdl.draw.width, sdl.draw.height, sdl.clip.w, sdl.clip.h);
00323             if (sdl_xbrz.scale_on != old_scale_on) {
00324                 // when we are scaling, we ask render code not to do any aspect correction
00325                 // when we are not scaling, render code is allowed to do aspect correction at will
00326                 // due to this, at each scale mode change we need to schedule resize again because window size could change
00327                 PIC_AddEvent(VGA_SetupDrawing, 50); // schedule another resize here, render has already been initialized at this point and we have just changed its option
00328             }
00329         }
00330 #endif
00331     }
00332 
00333 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
00334     mainMenu.screenWidth = (size_t)sdl.surface->w;
00335     mainMenu.updateRect();
00336     mainMenu.setRedraw();
00337     GFX_DrawSDLMenu(mainMenu, mainMenu.display_list);
00338 #endif
00339 
00340     return retFlags;
00341 }
00342 #endif