DOSBox-X
|
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