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