/** \mainpage DOSBox-X emulation * * \section i Introduction * * \section f Features * * \li Accurate x86 emulation * */ /* * Copyright (C) 2002-2013 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif #endif #ifdef OS2 # define INCL_DOS # define INCL_WIN #endif bool OpenGL_using(void); void GFX_OpenGLRedrawScreen(void); #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include // std::transform #ifdef WIN32 # include # include # include #endif #include "cross.h" #include "SDL.h" #include "dosbox.h" #include "video.h" #include "mouse.h" #include "pic.h" #include "timer.h" #include "setup.h" #include "bios.h" #include "support.h" #include "debug.h" #include "render.h" #include "menu.h" #include "SDL_video.h" #include "ide.h" #include "bitop.h" #include "ptrop.h" #include "mapper.h" #include "../src/libs/gui_tk/gui_tk.h" #ifdef __WIN32__ # include "callback.h" # include "dos_inc.h" # include # include "Commdlg.h" # include "windows.h" # include "Shellapi.h" # include "shell.h" # include "SDL_syswm.h" # include # include # include # if defined(__MINGW32__) && !defined(HX_DOS) # include // input method editor # endif #endif // WIN32 #include "mapper.h" #include "vga.h" #include "keyboard.h" #include "cpu.h" #include "fpu.h" #include "cross.h" #include "keymap.h" #include "control.h" #ifdef _MSC_VER # define MIN(a,b) ((a) < (b) ? (a) : (b)) # define MAX(a,b) ((a) > (b) ? (a) : (b)) #else # define MIN(a,b) std::min(a,b) # define MAX(a,b) std::max(a,b) #endif #if C_XBRZ #include #include #include #endif #if !defined(C_SDL2) # include "SDL_version.h" # ifndef SDL_DOSBOX_X_SPECIAL # error This code must be compiled using the SDL 1.x library provided in this source repository # endif #endif #if defined(WIN32) && !defined(C_SDL2) bool isVirtualBox = false; /* OpenGL never works with Windows XP inside VirtualBox */ HMENU MainMenu = NULL; #endif bool OpenGL_using(void); #if defined(WIN32) && !defined(S_ISREG) # define S_ISREG(x) ((x & S_IFREG) == S_IFREG) #endif #ifndef SDL_MAIN_NOEXCEPT #define SDL_MAIN_NOEXCEPT #endif using namespace std; const char *scaler_menu_opts[][2] = { { "none", "None" }, { "normal2x", "Normal 2X" }, { "normal3x", "Normal 3X" }, { "normal4x", "Normal 4X" }, { "normal5x", "Normal 5X" }, { "hardware_none", "Hardware None" }, { "hardware2x", "Hardware 2X" }, { "hardware3x", "Hardware 3X" }, { "hardware4x", "Hardware 4X" }, { "hardware5x", "Hardware 5X" }, { "tv2x", "TV 2X" }, { "tv3x", "TV 3X" }, { "scan2x", "Scan 2X" }, { "scan3x", "Scan 3X" }, { "rgb2x", "RGB 2X" }, { "rgb3x", "RGB 3X" }, { "advmame2x", "Advanced MAME 2X" }, { "advmame3x", "Advanced MAME 3X" }, { "hq2x", "HQ 2X" }, { "hq3x", "HQ 3X" }, { "advinterp2x", "Advanced Interpolation 2X" }, { "advinterp3x", "Advanced Interpolation 3X" }, { "2xsai", "2xSai" }, { "super2xsai", "Super2xSai" }, { "supereagle", "SuperEagle" }, #if C_XBRZ { "xbrz", "xBRZ" }, { "xbrz_bilinear", "xBRZ Bilinear" }, #endif { NULL, NULL } }; void UpdateOverscanMenu(void); const char *DKM_to_string(const unsigned int dkm) { switch (dkm) { case DKM_US: return "us"; case DKM_DEU: return "ger"; case DKM_JPN_PC98: return "jpn_pc98"; case DKM_JPN: return "jpn"; default: break; }; return ""; } const char *DKM_to_descriptive_string(const unsigned int dkm) { switch (dkm) { case DKM_US: return "US English"; case DKM_DEU: return "German"; case DKM_JPN_PC98: return "Japanese (PC-98)"; case DKM_JPN: return "Japanese"; default: break; }; return ""; } unsigned int mapper_keyboard_layout = DKM_US; unsigned int host_keyboard_layout = DKM_US; void KeyboardLayoutDetect(void) { unsigned int nlayout = DKM_US; #if defined(LINUX) unsigned int Linux_GetKeyboardLayout(void); nlayout = Linux_GetKeyboardLayout(); # if !defined(C_SDL2) /* BUGFIX: The xkbmap for 'jp' in Linux/X11 has a problem that maps both * Ro and Yen to backslash, which in SDL's default state makes * it impossible to map them properly in the mapper. */ if (nlayout == DKM_JPN) { LOG_MSG("Engaging Linux/X11 fix for jp xkbmap in order to handle Ro/Yen keys"); void Linux_JPXKBFix(void); Linux_JPXKBFix(); } # endif #elif defined(WIN32) WORD lid = LOWORD(GetKeyboardLayout(0)); LOG_MSG("Windows keyboard layout ID is 0x%04x", lid); switch (lid) { case 0x0407: nlayout = DKM_DEU; break; case 0x0409: nlayout = DKM_US; break; case 0x0411: nlayout = DKM_JPN; break; default: break; }; #endif host_keyboard_layout = nlayout; LOG_MSG("Host keyboard layout is now %s (%s)", DKM_to_string(host_keyboard_layout), DKM_to_descriptive_string(host_keyboard_layout)); } void SetMapperKeyboardLayout(const unsigned int dkm) { /* TODO: Make mapper re-initialize layout. If the mapper interface is visible, redraw it. */ mapper_keyboard_layout = dkm; LOG_MSG("Mapper keyboard layout is now %s (%s)", DKM_to_string(mapper_keyboard_layout), DKM_to_descriptive_string(mapper_keyboard_layout)); } #if defined(WIN32) && !defined(C_SDL2) extern "C" unsigned char SDL1_hax_hasLayoutChanged(void); extern "C" void SDL1_hax_ackLayoutChanged(void); #endif void CheckMapperKeyboardLayout(void) { #if defined(WIN32) && !defined(C_SDL2) if (SDL1_hax_hasLayoutChanged()) { SDL1_hax_ackLayoutChanged(); LOG_MSG("Keyboard layout changed"); KeyboardLayoutDetect(); if (host_keyboard_layout == DKM_JPN && IS_PC98_ARCH) SetMapperKeyboardLayout(DKM_JPN_PC98); else SetMapperKeyboardLayout(host_keyboard_layout); } #endif } /* yksoft1 says that older MinGW headers lack this value --Jonathan C. */ #ifndef MAPVK_VK_TO_VSC #define MAPVK_VK_TO_VSC 0 #endif bool boot_debug_break = false; bool window_was_maximized = false; /* this flag is needed in order to know if we're AT the shell, or if we're in a program running under the shell. */ bool dos_shell_running_program = false; Bitu userResizeWindowWidth = 0, userResizeWindowHeight = 0; Bitu currentWindowWidth = 640, currentWindowHeight = 480; int NonUserResizeCounter = 0; int gl_menudraw_countdown = 0; int gl_clear_countdown = 0; Bitu time_limit_ms = 0; extern bool keep_umb_on_boot; extern bool keep_private_area_on_boot; extern bool dos_kernel_disabled; bool guest_machine_power_on = false; std::string custom_savedir; void SHELL_Run(); void DisableINT33(); void EMS_DoShutDown(); void XMS_DoShutDown(); void DOS_DoShutDown(); void GUS_DOS_Shutdown(); void SBLASTER_DOS_Shutdown(); void DOS_ShutdownDevices(void); void RemoveEMSPageFrame(void); void RemoveUMBBlock(); void DOS_GetMemory_unmap(); void VFILE_Shutdown(void); void PROGRAMS_Shutdown(void); void DOS_UninstallMisc(void); void CALLBACK_Shutdown(void); void DOS_ShutdownDrives(); void VFILE_Shutdown(void); void DOS_ShutdownFiles(); void FreeBIOSDiskList(); void GFX_ShutDown(void); void MAPPER_Shutdown(); void SHELL_Init(void); #if C_DYNAMIC_X86 void CPU_Core_Dyn_X86_Shutdown(void); #endif void UpdateWindowMaximized(bool flag) { menu.maxwindow = flag; } void UpdateWindowDimensions(Bitu width, Bitu height) { currentWindowWidth = width; currentWindowHeight = height; } void UpdateWindowDimensions(void) { #if defined(WIN32) && !defined(C_SDL2) // When maximized, SDL won't actually tell us our new dimensions, so get it ourselves. // FIXME: Instead of GetHWND() we need to track our own handle or add something to SDL 1.x // to provide the handle! RECT r = { 0 }; GetClientRect(GetHWND(), &r); UpdateWindowDimensions(r.right, r.bottom); UpdateWindowMaximized(IsZoomed(GetHWND())); #endif #if defined(LINUX) && !defined(C_SDL2) void UpdateWindowDimensions_Linux(void); UpdateWindowDimensions_Linux(); #endif } #if C_OPENGL #include "SDL_opengl.h" #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifndef GL_ARB_pixel_buffer_object #define GL_ARB_pixel_buffer_object 1 #define GL_PIXEL_PACK_BUFFER_ARB 0x88EB #define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF #endif #ifndef GL_ARB_vertex_buffer_object #define GL_ARB_vertex_buffer_object 1 typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); #endif PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL; PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL; #endif //C_OPENGL /* TODO: move to general header */ static inline int int_log2(int val) { int log = 0; while ((val >>= 1) != 0) log++; return log; } enum PRIORITY_LEVELS { PRIORITY_LEVEL_PAUSE, PRIORITY_LEVEL_LOWEST, PRIORITY_LEVEL_LOWER, PRIORITY_LEVEL_NORMAL, PRIORITY_LEVEL_HIGHER, PRIORITY_LEVEL_HIGHEST }; #if defined(C_SDL2) # define MAPPERFILE "mapper-" VERSION ".sdl2.map" #else # define MAPPERFILE "mapper-" VERSION ".map" #endif #if !defined(C_SDL2) void GUI_ResetResize(bool); void GUI_LoadFonts(); void GUI_Run(bool); #endif void Restart(bool pressed); bool RENDER_GetAspect(void); bool RENDER_GetAutofit(void); const char* titlebar = NULL; extern const char* RunningProgram; extern bool CPU_CycleAutoAdjust; #if !(ENVIRON_INCLUDED) extern char** environ; #endif Bitu frames = 0; double rtdelta = 0; bool emu_paused = false; bool mouselocked = false; //Global variable for mapper bool fullscreen_switch = true; bool dos_kernel_disabled = true; bool startup_state_numlock = false; // Global for keyboard initialisation bool startup_state_capslock = false; // Global for keyboard initialisation #if defined(WIN32) && !defined(C_SDL2) extern "C" void SDL1_hax_SetMenu(HMENU menu); #endif #ifdef WIN32 # include #endif #if (HAVE_DDRAW_H) # include struct private_hwdata { LPDIRECTDRAWSURFACE3 dd_surface; LPDIRECTDRAWSURFACE3 dd_writebuf; }; #endif #if (HAVE_D3D9_H) # include "direct3d.h" #endif #if (HAVE_D3D9_H) CDirect3D* d3d = NULL; #endif #ifdef WIN32 # define STDOUT_FILE TEXT("stdout.txt") # define STDERR_FILE TEXT("stderr.txt") # define DEFAULT_CONFIG_FILE "/dosbox.conf" #elif defined(MACOSX) # define DEFAULT_CONFIG_FILE "/Library/Preferences/DOSBox Preferences" #else /*linux freebsd*/ # define DEFAULT_CONFIG_FILE "/.dosboxrc" #endif #if C_SET_PRIORITY # include # define PRIO_TOTAL (PRIO_MAX-PRIO_MIN) #endif #ifdef OS2 # include #endif enum AUTOLOCK_FEEDBACK { AUTOLOCK_FEEDBACK_NONE, AUTOLOCK_FEEDBACK_BEEP, AUTOLOCK_FEEDBACK_FLASH }; // do not specify any defaults inside, it is zeroed at start of main() struct SDL_Block { bool inited; bool active; //If this isn't set don't draw bool updating; #if defined(C_SDL2) bool update_window; bool update_display_contents; int window_desired_width, window_desired_height; #endif struct { Bit32u width; Bit32u height; Bit32u bpp; Bitu flags; double scalex,scaley; GFX_CallBack_t callback; } draw; bool wait_on_error; struct { struct { Bit16u width, height; bool fixed; bool display_res; } full; struct { Bit16u width, height; } window; Bit8u bpp; #if defined(C_SDL2) Bit32u pixelFormat; #endif bool fullscreen; bool lazy_fullscreen; bool prevent_fullscreen; bool lazy_fullscreen_req; bool doublebuf; SCREEN_TYPES type; SCREEN_TYPES want_type; } desktop; #if C_OPENGL struct { Bitu pitch; void * framebuf; GLuint buffer; GLuint texture; GLuint displaylist; GLint max_texsize; bool bilinear; bool packed_pixel; bool paletted_texture; bool pixel_buffer_object; } opengl; #endif struct { SDL_Surface * surface; #if (HAVE_DDRAW_H) && defined(WIN32) RECT rect; #endif } blit; struct { PRIORITY_LEVELS focus; PRIORITY_LEVELS nofocus; } priority; SDL_Rect clip; SDL_Surface * surface; #if defined(C_SDL2) SDL_Window * window; SDL_Renderer * renderer; const char * rendererDriver; int displayNumber; struct { SDL_Texture * texture; SDL_PixelFormat * pixelFormat; } texture; #endif SDL_cond *cond; struct { bool autolock; AUTOLOCK_FEEDBACK autolock_feedback; bool autoenable; bool requestlock; bool locked; Bitu sensitivity; } mouse; SDL_Rect updateRects[1024]; Bitu overscan_color; Bitu overscan_width; Bitu num_joysticks; #if defined (WIN32) bool using_windib; #endif // state of alt-keys for certain special handlings Bit16u laltstate; Bit16u raltstate; bool must_redraw_all; bool deferred_resize; bool init_ignore; unsigned int gfx_force_redraw_count; struct { int x; int y; double xToY; double yToX; } srcAspect; #if C_XBRZ struct { bool enable; bool postscale_bilinear; int task_granularity; int fixed_scale_factor; int max_scale_factor; std::vector renderbuf; std::vector pixbuf; bool scale_on; int scale_factor; } xBRZ; #endif }; static SDL_Block sdl; void SDL_rect_cliptoscreen(SDL_Rect &r) { if (r.x < 0) { r.w += r.x; r.x = 0; } if (r.y < 0) { r.h += r.y; r.y = 0; } if ((r.x+r.w) > sdl.surface->w) r.w = sdl.surface->w - r.x; if ((r.y+r.h) > sdl.surface->h) r.h = sdl.surface->h - r.y; /* NTS: Apparently r.w and r.h are unsigned, therefore no need to check if negative */ // if (r.w < 0) r.w = 0; // if (r.h < 0) r.h = 0; } Bitu GUI_JoystickCount(void) { return sdl.num_joysticks; } #if !defined(MACOSX) /* TODO: should move to it's own file ================================================ */ static unsigned char logo[32*32*4]= { #include "dosbox_logo.h" }; #endif #if !defined(MACOSX) static void DOSBox_SetOriginalIcon(void) { SDL_Surface *logos; #if WORDS_BIGENDIAN logos = SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0xff000000,0x00ff0000,0x0000ff00,0); #else logos = SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0); #endif #if defined(C_SDL2) SDL_SetWindowIcon(sdl.window, logos); #else SDL_WM_SetIcon(logos,NULL); #endif } #endif /* =================================================================================== */ #if defined (WIN32) bool GFX_SDLUsingWinDIB(void) { return sdl.using_windib; } #endif void GFX_SetIcon(void) { #if !defined(MACOSX) /* Set Icon (must be done before any sdl_setvideomode call) */ /* But don't set it on OS X, as we use a nicer external icon there. */ /* Made into a separate call, so it can be called again when we restart the graphics output on win32 */ if (menu_compatible) { DOSBox_SetOriginalIcon(); return; } #endif #if defined(WIN32) && !defined(C_SDL2) HICON hIcon1; hIcon1 = (HICON) LoadImage( GetModuleHandle(NULL), MAKEINTRESOURCE(dosbox_ico), IMAGE_ICON, 16,16,LR_DEFAULTSIZE); SendMessage(GetHWND(), WM_SETICON, ICON_SMALL, (LPARAM) hIcon1 ); #endif } extern std::string dosbox_title; void GFX_SetTitle(Bit32s cycles,Bits frameskip,Bits timing,bool paused){ (void)frameskip;//UNUSED (void)timing;//UNUSED // static Bits internal_frameskip=0; static Bit32s internal_cycles=0; // static Bits internal_timing=0; char title[200] = {0}; Section_prop *section = static_cast(control->GetSection("SDL")); assert(section != NULL); titlebar = section->Get_string("titlebar"); if (cycles != -1) internal_cycles = cycles; // if (timing != -1) internal_timing = timing; // if (frameskip != -1) internal_frameskip = frameskip; sprintf(title,"%s%sDOSBox-X %s, %d cyc/ms", dosbox_title.c_str(),dosbox_title.empty()?"":": ", VERSION,(int)internal_cycles); { const char *what = (titlebar != NULL && *titlebar != 0) ? titlebar : RunningProgram; if (what != NULL && *what != 0) { char *p = title + strlen(title); // append to end of string sprintf(p,", %s",what); } } if (!menu.hidecycles) { char *p = title + strlen(title); // append to end of string sprintf(p,", FPS %2d",(int)frames); } if (menu.showrt) { char *p = title + strlen(title); // append to end of string sprintf(p,", %2d%%/RT",(int)floor((rtdelta / 10) + 0.5)); } if (paused) strcat(title," PAUSED"); #if defined(C_SDL2) SDL_SetWindowTitle(sdl.window,title); #else SDL_WM_SetCaption(title,VERSION); #endif } bool warn_on_mem_write = false; void CPU_Snap_Back_To_Real_Mode(); static void KillSwitch(bool pressed) { if (!pressed) return; if (sdl.desktop.fullscreen) GFX_SwitchFullScreen(); #if 0 /* Re-enable this hack IF DOSBox continues to have problems page-faulting on kill switch */ CPU_Snap_Back_To_Real_Mode(); /* TEMPORARY HACK. There are portions of DOSBox that write to memory as if still running DOS. */ /* ^ Without this hack, when running Windows NT 3.1 this Kill Switch effectively becomes the Instant Page Fault BSOD switch * because the DOSBox code attempting to write to real mode memory causes a page fault (though hitting the kill switch a * second time shuts DOSBox down properly). It's sort of the same issue behind the INT 33h emulation causing instant BSOD * in Windows NT the instant you moved or clicked the mouse. The purpose of this hack is that, before any of that DOSBox * code has a chance, we force the CPU back into real mode so that the code doesn't trigger funny page faults and DOSBox * shuts down properly. */ #endif warn_on_mem_write = true; throw 1; } static void SDL_Overscan(void) { sdl.overscan_color=0; if (sdl.overscan_width) { Bitu border_color = GFX_GetRGB(vga.dac.rgb[vga.attr.overscan_color].red<<2, vga.dac.rgb[vga.attr.overscan_color].green<<2, vga.dac.rgb[vga.attr.overscan_color].blue<<2); if (border_color != sdl.overscan_color) { sdl.overscan_color = border_color; // Find four rectangles forming the border SDL_Rect *rect = &sdl.updateRects[0]; rect->x = 0; rect->y = 0; rect->w = sdl.draw.width+(unsigned int)(2*sdl.clip.x); rect->h = (uint16_t)sdl.clip.y; // top if ((Bitu)rect->h > (Bitu)sdl.overscan_width) { rect->y += (int)(rect->h-sdl.overscan_width); rect->h = (uint16_t)sdl.overscan_width; } if ((Bitu)sdl.clip.x > (Bitu)sdl.overscan_width) { rect->x += (int)sdl.clip.x-(int)sdl.overscan_width; rect->w -= (uint16_t)(2*((int)sdl.clip.x-(int)sdl.overscan_width)); } rect = &sdl.updateRects[1]; rect->x = 0; rect->y = sdl.clip.y; rect->w = (uint16_t)sdl.clip.x; rect->h = (uint16_t)sdl.draw.height; // left if ((unsigned int)rect->w > (unsigned int)sdl.overscan_width) { rect->x += (int)rect->w-(int)sdl.overscan_width; rect->w = (uint16_t)sdl.overscan_width; } rect = &sdl.updateRects[2]; rect->x = (int)sdl.clip.x+(int)sdl.draw.width; rect->y = sdl.clip.y; rect->w = (uint16_t)sdl.clip.x; rect->h = (uint16_t)sdl.draw.height; // right if ((unsigned int)rect->w > (unsigned int)sdl.overscan_width) { rect->w = (uint16_t)sdl.overscan_width; } rect = &sdl.updateRects[3]; rect->x = 0; rect->y = (int)sdl.clip.y+(int)sdl.draw.height; rect->w = sdl.draw.width+(unsigned int)(2*sdl.clip.x); rect->h = (uint16_t)sdl.clip.y; // bottom if ((Bitu)rect->h > (Bitu)sdl.overscan_width) { rect->h = (uint16_t)sdl.overscan_width; } if ((Bitu)sdl.clip.x > (Bitu)sdl.overscan_width) { rect->x += (int)sdl.clip.x-(int)sdl.overscan_width; rect->w -= (unsigned int)(2*((int)sdl.clip.x-(int)sdl.overscan_width)); } if (sdl.surface->format->BitsPerPixel == 8) { // SDL_FillRect seems to have some issues with palettized hw surfaces Bit8u* pixelptr = (Bit8u*)sdl.surface->pixels; Bitu linepitch = sdl.surface->pitch; for (Bitu i=0; i<4; i++) { rect = &sdl.updateRects[i]; Bit8u* start = pixelptr + (unsigned int)rect->y*(unsigned int)linepitch + (unsigned int)rect->x; for (Bitu j=0; jh; j++) { memset(start, vga.attr.overscan_color, rect->w); start += linepitch; } } } else { for (Bitu i=0; i<4; i++) SDL_FillRect(sdl.surface, &sdl.updateRects[i], border_color); #if defined(C_SDL2) SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4); #else SDL_UpdateRects(sdl.surface, 4, sdl.updateRects); #endif } } } } bool DOSBox_Paused() { return emu_paused; } bool pause_on_vsync = false; #if defined(C_SDL2) bool GFX_IsFullscreen() { if (sdl.window == NULL) return false; uint32_t windowFlags = SDL_GetWindowFlags(sdl.window); if (windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) return true; return false; } static bool IsFullscreen() { return GFX_IsFullscreen(); } #endif void PauseDOSBox(bool pressed) { bool paused = true; SDL_Event event; if (!pressed) return; /* reflect in the menu that we're paused now */ mainMenu.get_item("mapper_pause").check(true).refresh_item(mainMenu); void MAPPER_ReleaseAllKeys(void); MAPPER_ReleaseAllKeys(); GFX_SetTitle(-1,-1,-1,true); // KEYBOARD_ClrBuffer(); GFX_LosingFocus(); while (SDL_PollEvent(&event)); // flush event queue. // reset pause conditions pause_on_vsync = false; // give mouse to win32 (ex. alt-tab) #if defined(C_SDL2) SDL_SetRelativeMouseMode(SDL_FALSE); #else SDL_WM_GrabInput(SDL_GRAB_OFF); #endif while (paused) { SDL_WaitEvent(&event); // since we're not polling, cpu usage drops to 0. #ifdef __WIN32__ #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU if (event.type==SDL_SYSWMEVENT && event.syswm.msg->msg == WM_COMMAND && event.syswm.msg->wParam == (mainMenu.get_item("mapper_pause").get_master_id()+DOSBoxMenu::winMenuMinimumID)) { paused=false; GFX_SetTitle(-1,-1,-1,false); break; } #endif #endif switch (event.type) { case SDL_QUIT: KillSwitch(true); break; case SDL_KEYDOWN: // Must use Pause/Break or escape Key to resume. if(event.key.keysym.sym == SDLK_PAUSE || event.key.keysym.sym == SDLK_ESCAPE) { paused = false; GFX_SetTitle(-1,-1,-1,false); break; } else if (event.key.keysym.sym == SDLK_SPACE) { /* spacebar = single frame step */ /* resume, but let the VGA code know to call us on vertical retrace */ paused = false; pause_on_vsync = true; GFX_SetTitle(-1,-1,-1,false); break; } #if defined (MACOSX) && !defined(C_SDL2) if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) { /* On macs, all apps exit when pressing cmd-q */ KillSwitch(true); break; } #endif } } // restore mouse state void GFX_UpdateSDLCaptureState(); GFX_UpdateSDLCaptureState(); void MAPPER_ReleaseAllKeys(void); MAPPER_ReleaseAllKeys(); // KEYBOARD_ClrBuffer(); GFX_LosingFocus(); // redraw screen (ex. fullscreen - pause - alt+tab x2 - unpause) if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset ); /* reflect in the menu that we're paused now */ mainMenu.get_item("mapper_pause").check(false).refresh_item(mainMenu); } #if defined(C_SDL2) static SDL_Window * GFX_SetSDLWindowMode(Bit16u width, Bit16u height, SCREEN_TYPES screenType) { static SCREEN_TYPES lastType = SCREEN_SURFACE; if (sdl.renderer) { SDL_DestroyRenderer(sdl.renderer); sdl.renderer=0; } if (sdl.texture.pixelFormat) { SDL_FreeFormat(sdl.texture.pixelFormat); sdl.texture.pixelFormat = 0; } if (sdl.texture.texture) { SDL_DestroyTexture(sdl.texture.texture); sdl.texture.texture=0; } #if C_OPENGL if (sdl.opengl.context) { SDL_GL_DeleteContext(sdl.opengl.context); sdl.opengl.context=0; } #endif sdl.window_desired_width = width; sdl.window_desired_height = height; int currWidth, currHeight; if (sdl.window) { //SDL_GetWindowSize(sdl.window, &currWidth, &currHeight); if (!sdl.update_window) { SDL_GetWindowSize(sdl.window, &currWidth, &currHeight); sdl.update_display_contents = ((width == currWidth) && (height == currHeight)); return sdl.window; } } /* If we change screen type, recreate the window. Furthermore, if * it is our very first time then we simply create a new window. */ if (!sdl.window || (lastType != screenType) // || (currWidth != width) || (currHeight != height) // || (glwindow != (0 != (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_OPENGL))) // || (fullscreen && (0 == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN))) // || (fullscreen != (SDL_WINDOW_FULLSCREEN == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN))) // || (fullscreen && ((width != currWidth) || (height != currHeight))) ) { lastType = screenType; if (sdl.window) { SDL_DestroyWindow(sdl.window); } sdl.window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber), SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber), width, height, (GFX_IsFullscreen() ? (sdl.desktop.full.display_res ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0) | ((screenType == SCREEN_OPENGL) ? SDL_WINDOW_OPENGL : 0) | SDL_WINDOW_SHOWN); if (sdl.window) { GFX_SetTitle(-1, -1, -1, false); //refresh title. } SDL_GetWindowSize(sdl.window, &currWidth, &currHeight); sdl.update_display_contents = ((width == currWidth) && (height == currHeight)); return sdl.window; } /* Fullscreen mode switching has its limits, and is also problematic on * some window managers. For now, the following may work up to some * level. On X11, SDL_VIDEO_X11_LEGACY_FULLSCREEN=1 can also help, * although it has its own issues. * Suggestion: Use the desktop res if possible, with output=surface * if one is not interested in scaling. * On Android, desktop res is the only way. */ if (GFX_IsFullscreen()) { SDL_DisplayMode displayMode; SDL_GetWindowDisplayMode(sdl.window, &displayMode); displayMode.w = width; displayMode.h = height; SDL_SetWindowDisplayMode(sdl.window, &displayMode); SDL_SetWindowFullscreen(sdl.window, SDL_WINDOW_FULLSCREEN_DESKTOP); } else { SDL_SetWindowFullscreen(sdl.window, 0); SDL_SetWindowSize(sdl.window, width, height); } /* Maybe some requested fullscreen resolution is unsupported? */ SDL_GetWindowSize(sdl.window, &currWidth, &currHeight); sdl.update_display_contents = ((width == currWidth) && (height == currHeight)); return sdl.window; } // Used for the mapper UI and more: Creates a fullscreen window with desktop res // on Android, and a non-fullscreen window with the input dimensions otherwise. SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height) { return GFX_SetSDLWindowMode(width, height, SCREEN_SURFACE); } // Returns the rectangle in the current window to be used for scaling a // sub-window with the given dimensions, like the mapper UI. SDL_Rect GFX_GetSDLSurfaceSubwindowDims(Bit16u width, Bit16u height) { SDL_Rect rect; rect.x=rect.y=0; rect.w=width; rect.h=height; return rect; } # if !defined(C_SDL2) // Currently used for an initial test here static SDL_Window * GFX_SetSDLOpenGLWindow(Bit16u width, Bit16u height) { return GFX_SetSDLWindowMode(width, height, SCREEN_OPENGL); } # endif #endif #if C_OPENGL && !defined(C_SDL2) && DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW const unsigned int SDLDrawGenFontTextureUnitPerRow = 16; const unsigned int SDLDrawGenFontTextureRows = 16; const unsigned int SDLDrawGenFontTextureWidth = SDLDrawGenFontTextureUnitPerRow * 8; const unsigned int SDLDrawGenFontTextureHeight = SDLDrawGenFontTextureRows * 16; bool SDLDrawGenFontTextureInit = false; GLuint SDLDrawGenFontTexture = (GLuint)(~0UL); #endif #if !defined(C_SDL2) /* Reset the screen with current values in the sdl structure */ Bitu GFX_GetBestMode(Bitu flags) { Bitu testbpp,gotbpp; switch (sdl.desktop.want_type) { case SCREEN_SURFACE: check_surface: flags &= ~GFX_LOVE_8; //Disable love for 8bpp modes /* Check if we can satisfy the depth it loves */ if (flags & GFX_LOVE_8) testbpp=8; else if (flags & GFX_LOVE_15) testbpp=15; else if (flags & GFX_LOVE_16) testbpp=16; else if (flags & GFX_LOVE_32) testbpp=32; else testbpp=0; if (sdl.desktop.fullscreen) gotbpp=(unsigned int)SDL_VideoModeOK(640,480,testbpp, (unsigned int)SDL_FULLSCREEN | (unsigned int)SDL_HWSURFACE | (unsigned int)SDL_HWPALETTE); else gotbpp=sdl.desktop.bpp; /* SDL 1.x and sometimes SDL 2.x mistake 15-bit 5:5:5 RGB for 16-bit 5:6:5 RGB * which causes colors to mis-display. This seems to be common with Windows and Linux. * If SDL said 16-bit but the bit masks suggest 15-bit, then make the correction now. */ if (gotbpp == 16) { if (sdl.surface->format->Gshift == 5 && sdl.surface->format->Gmask == (31U << 5U)) { 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)"); gotbpp = 15; } } /* If we can't get our favorite mode check for another working one */ switch (gotbpp) { case 8: if (flags & GFX_CAN_8) flags&=~(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32); break; case 15: if (flags & GFX_CAN_15) flags&=~(GFX_CAN_8|GFX_CAN_16|GFX_CAN_32); break; case 16: if (flags & GFX_CAN_16) flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_32); break; case 24: case 32: if (flags & GFX_CAN_32) flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); break; } flags |= GFX_CAN_RANDOM; break; #if C_OPENGL case SCREEN_OPENGL: if (!(flags&GFX_CAN_32)) goto check_surface; flags|=GFX_SCALING; flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); break; #endif #if (HAVE_D3D9_H) && defined(WIN32) case SCREEN_DIRECT3D: flags|=GFX_SCALING; if(GCC_UNLIKELY(d3d->bpp16)) flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_32); else flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); break; #endif default: goto check_surface; break; } return flags; } #endif /* FIXME: This prepares the SDL library to accept Win32 drag+drop events from the Windows shell. * So it should be named something like EnableDragAcceptFiles() not SDL_Prepare() */ void SDL_Prepare(void) { if (menu_compatible) return; #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) // Microsoft Windows specific LOG(LOG_MISC,LOG_DEBUG)("Win32: Preparing main window to accept files dragged in from the Windows shell"); SDL_PumpEvents(); SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); DragAcceptFiles(GetHWND(), TRUE); #endif } void GFX_ForceRedrawScreen(void) { GFX_Stop(); if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset ); GFX_Start(); } void GFX_ResetScreen(void) { fullscreen_switch=false; GFX_Stop(); if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset ); GFX_Start(); CPU_Reset_AutoAdjust(); fullscreen_switch=true; #if !defined(C_SDL2) if (!sdl.desktop.fullscreen) DOSBox_RefreshMenu(); // for menu #endif } void GFX_ForceFullscreenExit(void) { if (sdl.desktop.lazy_fullscreen) { LOG_MSG("GFX LF: invalid screen change"); } else { sdl.desktop.fullscreen=false; GFX_ResetScreen(); } } uint32_t GFX_Rmask; unsigned char GFX_Rshift; uint32_t GFX_Gmask; unsigned char GFX_Gshift; uint32_t GFX_Bmask; unsigned char GFX_Bshift; uint32_t GFX_Amask; unsigned char GFX_Ashift; unsigned char GFX_bpp; unsigned int GFX_GetBShift() { return sdl.surface->format->Bshift; } void GFX_LogSDLState(void) { LOG(LOG_MISC,LOG_DEBUG)("SDL video mode: %ux%u (clip %ux%u with upper-left at %ux%u) %ubpp", (unsigned)sdl.surface->w,(unsigned)sdl.surface->h, (unsigned)sdl.clip.w,(unsigned)sdl.clip.h, (unsigned)sdl.clip.x,(unsigned)sdl.clip.y, (unsigned)sdl.surface->format->BitsPerPixel); LOG(LOG_MISC,LOG_DEBUG)(" red: shift=%u mask=0x%08lx", (unsigned)sdl.surface->format->Rshift, (unsigned long)sdl.surface->format->Rmask); LOG(LOG_MISC,LOG_DEBUG)(" green: shift=%u mask=0x%08lx", (unsigned)sdl.surface->format->Gshift, (unsigned long)sdl.surface->format->Gmask); LOG(LOG_MISC,LOG_DEBUG)(" blue: shift=%u mask=0x%08lx", (unsigned)sdl.surface->format->Bshift, (unsigned long)sdl.surface->format->Bmask); LOG(LOG_MISC,LOG_DEBUG)(" alpha: shift=%u mask=0x%08lx", (unsigned)sdl.surface->format->Ashift, (unsigned long)sdl.surface->format->Amask); GFX_bpp = sdl.surface->format->BitsPerPixel; GFX_Rmask = sdl.surface->format->Rmask; GFX_Rshift = sdl.surface->format->Rshift; GFX_Gmask = sdl.surface->format->Gmask; GFX_Gshift = sdl.surface->format->Gshift; GFX_Bmask = sdl.surface->format->Bmask; GFX_Bshift = sdl.surface->format->Bshift; GFX_Amask = sdl.surface->format->Amask; GFX_Ashift = sdl.surface->format->Ashift; } #if !defined(C_SDL2) && C_OPENGL int Voodoo_OGL_GetWidth(); int Voodoo_OGL_GetHeight(); bool Voodoo_OGL_Active(); static SDL_Surface * GFX_SetupSurfaceScaledOpenGL(Bit32u sdl_flags, Bit32u bpp) { Bit16u fixedWidth; Bit16u fixedHeight; Bit16u windowWidth; Bit16u windowHeight; if (sdl.desktop.prevent_fullscreen) /* 3Dfx openGL do not allow resize */ sdl_flags &= ~((unsigned int)SDL_RESIZABLE); if (sdl.desktop.want_type == SCREEN_OPENGL) sdl_flags |= (unsigned int)SDL_OPENGL; if (sdl.desktop.fullscreen) { fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0; fixedHeight = sdl.desktop.full.fixed ? sdl.desktop.full.height : 0; sdl_flags |= (unsigned int)(SDL_FULLSCREEN|SDL_HWSURFACE); } else { fixedWidth = sdl.desktop.window.width; fixedHeight = sdl.desktop.window.height; sdl_flags |= (unsigned int)SDL_HWSURFACE; } if (fixedWidth == 0 || fixedHeight == 0) { Bitu consider_height = menu.maxwindow ? currentWindowHeight : 0; Bitu consider_width = menu.maxwindow ? currentWindowWidth : 0; int final_height = max(consider_height,userResizeWindowHeight); int final_width = max(consider_width,userResizeWindowWidth); fixedWidth = final_width; fixedHeight = final_height; } #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* scale the menu bar if the window is large enough */ { int cw = fixedWidth,ch = fixedHeight; int scale = 1; if (cw == 0) cw = (Bit16u)(sdl.draw.width*sdl.draw.scalex); if (ch == 0) ch = (Bit16u)(sdl.draw.height*sdl.draw.scaley); while ((cw/scale) >= (640*2) && (ch/scale) >= (400*2)) scale++; LOG_MSG("menuScale=%d",scale); mainMenu.setScale((unsigned int)scale); if (mainMenu.isVisible()) fixedHeight -= mainMenu.menuBox.h; } #endif if (Voodoo_OGL_GetWidth() != 0 && Voodoo_OGL_GetHeight() != 0 && Voodoo_OGL_Active() && sdl.desktop.prevent_fullscreen) { /* 3Dfx openGL do not allow resize */ sdl.clip.x=0;sdl.clip.y=0; sdl.clip.w=windowWidth=(Bit16u)Voodoo_OGL_GetWidth(); sdl.clip.h=windowHeight=(Bit16u)Voodoo_OGL_GetHeight(); } else if (fixedWidth && fixedHeight) { sdl.clip.w = windowWidth = fixedWidth; sdl.clip.h = windowHeight = fixedHeight; // adjust resulting image aspect ratio if (render.aspect) { if (fixedWidth > sdl.srcAspect.xToY * fixedHeight) // output broader than input => black bars left and right { sdl.clip.w = static_cast(fixedHeight * sdl.srcAspect.xToY); } else // black bars top and bottom { sdl.clip.h = static_cast(fixedWidth * sdl.srcAspect.yToX); } } sdl.clip.x = (fixedWidth - sdl.clip.w) / 2; sdl.clip.y = (fixedHeight - sdl.clip.h) / 2; } else { sdl.clip.w = windowWidth = (Bit16u)(sdl.draw.width*sdl.draw.scalex); sdl.clip.h = windowHeight = (Bit16u)(sdl.draw.height*sdl.draw.scaley); if (render.aspect) { // we solve problem of aspect ratio based window extension here when window size is not set explicitly if (windowWidth*sdl.srcAspect.y != windowHeight*sdl.srcAspect.x) { // abnormal aspect ratio detected, apply correction if (windowWidth*sdl.srcAspect.y > windowHeight*sdl.srcAspect.x) { // wide pixel ratio, height should be extended to fit sdl.clip.h = windowHeight = (Bitu)floor((double)windowWidth * sdl.srcAspect.y / sdl.srcAspect.x + 0.5); } else { // long pixel ratio, width should be extended sdl.clip.w = windowWidth = (Bitu)floor((double)windowHeight * sdl.srcAspect.x / sdl.srcAspect.y + 0.5); } } } sdl.clip.x = (windowWidth - sdl.clip.w) / 2; sdl.clip.y = (windowHeight - sdl.clip.h) / 2; } LOG(LOG_MISC,LOG_DEBUG)("GFX_SetSize OpenGL window=%ux%u clip=x,y,w,h=%d,%d,%d,%d", (unsigned int)windowWidth, (unsigned int)windowHeight, (unsigned int)sdl.clip.x, (unsigned int)sdl.clip.y, (unsigned int)sdl.clip.w, (unsigned int)sdl.clip.h); #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW if (mainMenu.isVisible()) { windowHeight += mainMenu.menuBox.h; sdl.clip.y += mainMenu.menuBox.h; } #endif sdl.surface=SDL_SetVideoMode(windowWidth,windowHeight,(int)bpp,(unsigned int)sdl_flags); sdl.deferred_resize = false; sdl.must_redraw_all = true; /* There seems to be a problem with MesaGL in Linux/X11 where * the first swap buffer we do is misplaced according to the * previous window size. * * NTS: This seems to have been fixed, which is why this is * commented out. I guess not calling GFX_SetSize() * with a 0x0 widthxheight helps! */ // sdl.gfx_force_redraw_count = 2; UpdateWindowDimensions(); GFX_LogSDLState(); #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW mainMenu.screenWidth = (size_t)(sdl.surface->w); mainMenu.updateRect(); mainMenu.setRedraw(); #endif return sdl.surface; } #endif void GFX_TearDown(void) { if (sdl.updating) GFX_EndUpdate( 0 ); if (sdl.blit.surface) { SDL_FreeSurface(sdl.blit.surface); sdl.blit.surface=0; } } static void GFX_ResetSDL() { /* deprecated */ } #if defined(WIN32) && !defined(C_SDL2) extern "C" unsigned int SDL1_hax_inhibit_WM_PAINT; #endif #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW void MenuShadeRect(int x,int y,int w,int h) { if (OpenGL_using()) { #if C_OPENGL glShadeModel (GL_FLAT); glBlendFunc(GL_ONE, GL_SRC_ALPHA); glDisable (GL_DEPTH_TEST); glDisable (GL_LIGHTING); glEnable(GL_BLEND); glDisable(GL_CULL_FACE); glDisable(GL_ALPHA_TEST); glDisable(GL_FOG); glDisable(GL_SCISSOR_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_TEXTURE_2D); glColor4ub(0, 0, 0, 64); glBegin(GL_QUADS); glVertex2i(x ,y ); glVertex2i(x+w,y ); glVertex2i(x+w,y+h); glVertex2i(x ,y+h); glEnd(); glBlendFunc(GL_ONE, GL_ZERO); glEnable(GL_TEXTURE_2D); #endif } else { if (x < 0) { w += x; x = 0; } if (y < 0) { y += h; y = 0; } if ((x+w) > sdl.surface->w) w = sdl.surface->w - x; if ((y+h) > sdl.surface->h) h = sdl.surface->h - y; if (w <= 0 || h <= 0) return; if (sdl.surface->format->BitsPerPixel == 32) { unsigned char *scan; uint32_t *row,mask; mask = ((sdl.surface->format->Rmask >> 2) & sdl.surface->format->Rmask) | ((sdl.surface->format->Gmask >> 2) & sdl.surface->format->Gmask) | ((sdl.surface->format->Bmask >> 2) & sdl.surface->format->Bmask); assert(sdl.surface->pixels != NULL); scan = (unsigned char*)sdl.surface->pixels; scan += y * sdl.surface->pitch; scan += x * 4; while (h-- > 0) { row = (uint32_t*)scan; scan += sdl.surface->pitch; for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (row[c] >> 2) & mask; } } else if (sdl.surface->format->BitsPerPixel == 16) { unsigned char *scan; uint16_t *row,mask; mask = ((sdl.surface->format->Rmask >> 2) & sdl.surface->format->Rmask) | ((sdl.surface->format->Gmask >> 2) & sdl.surface->format->Gmask) | ((sdl.surface->format->Bmask >> 2) & sdl.surface->format->Bmask); assert(sdl.surface->pixels != NULL); scan = (unsigned char*)sdl.surface->pixels; scan += y * sdl.surface->pitch; scan += x * 2; while (h-- > 0) { row = (uint16_t*)scan; scan += sdl.surface->pitch; for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (row[c] >> 2) & mask; } } else { /* TODO */ } } } void MenuDrawRect(int x,int y,int w,int h,Bitu color) { if (OpenGL_using()) { #if C_OPENGL glShadeModel (GL_FLAT); glBlendFunc(GL_ONE, GL_ZERO); glDisable (GL_DEPTH_TEST); glDisable (GL_LIGHTING); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); glDisable(GL_ALPHA_TEST); glDisable(GL_FOG); glDisable(GL_SCISSOR_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_TEXTURE_2D); glColor3ub((color >> 16UL) & 0xFF,(color >> 8UL) & 0xFF,(color >> 0UL) & 0xFF); glBegin(GL_QUADS); glVertex2i(x ,y ); glVertex2i(x+w,y ); glVertex2i(x+w,y+h); glVertex2i(x ,y+h); glEnd(); glBlendFunc(GL_ONE, GL_ZERO); glEnable(GL_TEXTURE_2D); #endif } else { if (x < 0) { w += x; x = 0; } if (y < 0) { y += h; y = 0; } if ((x+w) > sdl.surface->w) w = sdl.surface->w - x; if ((y+h) > sdl.surface->h) h = sdl.surface->h - y; if (w <= 0 || h <= 0) return; if (sdl.surface->format->BitsPerPixel == 32) { unsigned char *scan; uint32_t *row; assert(sdl.surface->pixels != NULL); scan = (unsigned char*)sdl.surface->pixels; scan += y * sdl.surface->pitch; scan += x * 4; while (h-- > 0) { row = (uint32_t*)scan; scan += sdl.surface->pitch; for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (uint32_t)color; } } else if (sdl.surface->format->BitsPerPixel == 16) { unsigned char *scan; uint16_t *row; assert(sdl.surface->pixels != NULL); scan = (unsigned char*)sdl.surface->pixels; scan += y * sdl.surface->pitch; scan += x * 2; while (h-- > 0) { row = (uint16_t*)scan; scan += sdl.surface->pitch; for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (uint16_t)color; } } else { /* TODO */ } } } extern Bit8u int10_font_14[256 * 14]; extern Bit8u int10_font_16[256 * 16]; void MenuDrawTextChar(int x,int y,unsigned char c,Bitu color) { static const unsigned int fontHeight = 16; if (x < 0 || y < 0 || (unsigned int)(x+8) > (unsigned int)sdl.surface->w || (unsigned int)(y+(int)fontHeight) > (unsigned int)sdl.surface->h) return; unsigned char *bmp = (unsigned char*)int10_font_16 + (c * fontHeight); if (OpenGL_using()) { #if C_OPENGL unsigned int tx = (c % 16u) * 8u; unsigned int ty = (c / 16u) * 16u; /* MenuDrawText() has prepared OpenGL state for us */ glBegin(GL_QUADS); // lower left glTexCoord2i((int)tx+0, (int)ty ); glVertex2i((int)x, (int)y ); // lower right glTexCoord2i((int)tx+8, (int)ty ); glVertex2i((int)x+8,(int)y ); // upper right glTexCoord2i((int)tx+8, (int)ty+(int)fontHeight); glVertex2i((int)x+8,(int)y+(int)fontHeight); // upper left glTexCoord2i((int)tx+0, (int)ty+(int)fontHeight); glVertex2i((int)x, (int)y+(int)fontHeight); glEnd(); #endif } else { unsigned char *scan; assert(sdl.surface->pixels != NULL); scan = (unsigned char*)sdl.surface->pixels; scan += (unsigned int)y * (unsigned int)sdl.surface->pitch; scan += (unsigned int)x * (((unsigned int)sdl.surface->format->BitsPerPixel+7u)/8u); for (unsigned int row=0;row < fontHeight;row++) { unsigned char rb = bmp[row]; if (sdl.surface->format->BitsPerPixel == 32) { uint32_t *dp = (uint32_t*)scan; for (unsigned int colm=0x80;colm != 0;colm >>= 1) { if (rb & colm) *dp = (uint32_t)color; dp++; } } else if (sdl.surface->format->BitsPerPixel == 16) { uint16_t *dp = (uint16_t*)scan; for (unsigned int colm=0x80;colm != 0;colm >>= 1) { if (rb & colm) *dp = (uint16_t)color; dp++; } } scan += (size_t)sdl.surface->pitch; } } } void MenuDrawTextChar2x(int x,int y,unsigned char c,Bitu color) { static const unsigned int fontHeight = 16; if (x < 0 || y < 0 || (unsigned int)(x+8) > (unsigned int)sdl.surface->w || (unsigned int)(y+(int)fontHeight) > (unsigned int)sdl.surface->h) return; unsigned char *bmp = (unsigned char*)int10_font_16 + (c * fontHeight); if (OpenGL_using()) { #if C_OPENGL unsigned int tx = (c % 16u) * 8u; unsigned int ty = (c / 16u) * 16u; /* MenuDrawText() has prepared OpenGL state for us */ glBegin(GL_QUADS); // lower left glTexCoord2i((int)tx+0, (int)ty ); glVertex2i(x, y ); // lower right glTexCoord2i((int)tx+8, (int)ty ); glVertex2i(x+(8*2),y ); // upper right glTexCoord2i((int)tx+8, (int)ty+(int)fontHeight); glVertex2i(x+(8*2),y+((int)fontHeight*2)); // upper left glTexCoord2i((int)tx+0, (int)ty+(int)fontHeight); glVertex2i(x, y+((int)fontHeight*2)); glEnd(); #endif } else { unsigned char *scan; assert(sdl.surface->pixels != NULL); scan = (unsigned char*)sdl.surface->pixels; scan += y * sdl.surface->pitch; scan += x * ((sdl.surface->format->BitsPerPixel+7)/8); for (unsigned int row=0;row < (fontHeight*2);row++) { unsigned char rb = bmp[row>>1U]; if (sdl.surface->format->BitsPerPixel == 32) { uint32_t *dp = (uint32_t*)scan; for (unsigned int colm=0x80;colm != 0;colm >>= 1) { if (rb & colm) { *dp++ = (uint32_t)color; *dp++ = (uint32_t)color; } else { dp += 2; } } } else if (sdl.surface->format->BitsPerPixel == 16) { uint16_t *dp = (uint16_t*)scan; for (unsigned int colm=0x80;colm != 0;colm >>= 1) { if (rb & colm) { *dp++ = (uint16_t)color; *dp++ = (uint16_t)color; } else { dp += 2; } } } scan += sdl.surface->pitch; } } } void MenuDrawText(int x,int y,const char *text,Bitu color) { #if C_OPENGL if (OpenGL_using()) { glBindTexture(GL_TEXTURE_2D,SDLDrawGenFontTexture); glPushMatrix(); glMatrixMode (GL_TEXTURE); glLoadIdentity (); glScaled(1.0 / SDLDrawGenFontTextureWidth, 1.0 / SDLDrawGenFontTextureHeight, 1.0); glColor4ub((color >> 16UL) & 0xFF,(color >> 8UL) & 0xFF,(color >> 0UL) & 0xFF,0xFF); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glEnable(GL_BLEND); } #endif while (*text != 0) { if (mainMenu.fontCharScale >= 2) MenuDrawTextChar2x(x,y,(unsigned char)(*text++),color); else MenuDrawTextChar(x,y,(unsigned char)(*text++),color); x += (int)mainMenu.fontCharWidth; } #if C_OPENGL if (OpenGL_using()) { glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc(GL_ONE, GL_ZERO); glDisable(GL_ALPHA_TEST); glEnable(GL_TEXTURE_2D); glPopMatrix(); glBindTexture(GL_TEXTURE_2D,sdl.opengl.texture); } #endif } void DOSBoxMenu::item::drawMenuItem(DOSBoxMenu &menu) { (void)menu;//UNUSED Bitu bgcolor = GFX_GetRGB(63, 63, 63); Bitu fgcolor = GFX_GetRGB(191, 191, 191); Bitu fgshortcolor = GFX_GetRGB(127, 127, 191); Bitu fgcheckcolor = GFX_GetRGB(191, 191, 127); if (type >= separator_type_id) { /* separators never change visual state on hover/select */ } else if (!status.enabled) { fgcolor = GFX_GetRGB(144, 144, 144); fgshortcolor = GFX_GetRGB(63, 63, 144); fgcheckcolor = GFX_GetRGB(144, 144, 63); } else if (itemHilight) { bgcolor = GFX_GetRGB(0, 0, 63); fgcolor = GFX_GetRGB(255, 255, 255); fgshortcolor = GFX_GetRGB(191, 191, 255); } else if (itemHover) { bgcolor = GFX_GetRGB(127, 127, 127); fgcolor = GFX_GetRGB(255, 255, 255); fgshortcolor = GFX_GetRGB(191, 191, 255); } if (SDL_MUSTLOCK(sdl.surface)) SDL_LockSurface(sdl.surface); MenuDrawRect(screenBox.x, screenBox.y, screenBox.w, screenBox.h, bgcolor); if (checkBox.w != 0 && checkBox.h != 0) { const char *str = status.checked ? "\xFB" : " "; MenuDrawText(screenBox.x+checkBox.x, screenBox.y+checkBox.y, str, fgcheckcolor); } if (textBox.w != 0 && textBox.h != 0) MenuDrawText(screenBox.x+textBox.x, screenBox.y+textBox.y, text.c_str(), fgcolor); if (shortBox.w != 0 && shortBox.h != 0) MenuDrawText(screenBox.x+shortBox.x, screenBox.y+shortBox.y, shortcut_text.c_str(), fgshortcolor); if (type == submenu_type_id && borderTop/*not toplevel*/) MenuDrawText((int)((int)screenBox.x+(int)screenBox.w - (int)mainMenu.fontCharWidth - 1), (int)((int)screenBox.y+(int)textBox.y), "\x10", fgcheckcolor); if (type == separator_type_id) MenuDrawRect((int)screenBox.x, (int)screenBox.y + ((int)screenBox.h/2), (int)screenBox.w, 1, fgcolor); else if (type == vseparator_type_id) MenuDrawRect((int)screenBox.x + ((int)screenBox.w/2), (int)screenBox.y, 1, (int)screenBox.h, fgcolor); if (SDL_MUSTLOCK(sdl.surface)) SDL_UnlockSurface(sdl.surface); } void DOSBoxMenu::displaylist::DrawDisplayList(DOSBoxMenu &menu,bool updateScreen) { for (auto &id : disp_list) { DOSBoxMenu::item &item = menu.get_item(id); item.drawMenuItem(menu); if (updateScreen) item.updateScreenFromItem(menu); } } bool DOSBox_isMenuVisible(void); void GFX_DrawSDLMenu(DOSBoxMenu &menu,DOSBoxMenu::displaylist &dl) { if (menu.needsRedraw() && DOSBox_isMenuVisible() && (!sdl.updating || OpenGL_using()) && !sdl.desktop.fullscreen) { if (!OpenGL_using()) { if (SDL_MUSTLOCK(sdl.surface)) SDL_LockSurface(sdl.surface); } if (&dl == &menu.display_list) { /* top level menu, draw background */ MenuDrawRect(menu.menuBox.x, menu.menuBox.y, menu.menuBox.w, menu.menuBox.h - 1, GFX_GetRGB(63, 63, 63)); MenuDrawRect(menu.menuBox.x, menu.menuBox.y + menu.menuBox.h - 1, menu.menuBox.w, 1, GFX_GetRGB(31, 31, 31)); } if (!OpenGL_using()) { if (SDL_MUSTLOCK(sdl.surface)) SDL_UnlockSurface(sdl.surface); } #if 0 LOG_MSG("menudraw %u",(unsigned int)SDL_GetTicks()); #endif menu.clearRedraw(); menu.display_list.DrawDisplayList(menu,/*updateScreen*/false); if (!OpenGL_using()) { #if defined(C_SDL2) SDL_UpdateWindowSurfaceRects( sdl.window, &menu.menuBox, 1 ); #else SDL_UpdateRects( sdl.surface, 1, &menu.menuBox ); #endif } } } #endif #if C_OPENGL bool initedOpenGL = false; #endif #if C_XBRZ // returns true if scaling possible/enabled, false otherwise bool xBRZ_SetScaleParameters(int srcWidth, int srcHeight, int dstWidth, int dstHeight) { sdl.xBRZ.scale_factor = (sdl.xBRZ.fixed_scale_factor == 0) ? static_cast(std::sqrt((double)dstWidth * dstHeight / (srcWidth * srcHeight)) + 0.5) : sdl.xBRZ.fixed_scale_factor; // enable minimal scaling if upscale is still possible but requires post-downscale // having aspect ratio correction on always implies enabled scaler because it gives better quality than DOSBox own method if (sdl.xBRZ.scale_factor == 1 && (render.aspect || dstWidth > srcWidth || dstHeight > srcHeight)) sdl.xBRZ.scale_factor = 2; if (sdl.xBRZ.scale_factor >= 2) { // ok to scale, now clamp scale factor if corresponding max option is set sdl.xBRZ.scale_factor = min(sdl.xBRZ.scale_factor, sdl.xBRZ.max_scale_factor); sdl.xBRZ.scale_on = true; } else { // scaling impossible sdl.xBRZ.scale_on = false; } return sdl.xBRZ.scale_on; } #endif void RENDER_Reset(void); Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) { if (width == 0 || height == 0) { E_Exit("GFX_SetSize with width=%d height=%d zero dimensions not allowed",(int)width,(int)height); return 0; } if (sdl.updating) GFX_EndUpdate( 0 ); sdl.must_redraw_all = true; sdl.draw.width=width; sdl.draw.height=height; sdl.draw.flags=flags; sdl.draw.callback=callback; sdl.draw.scalex=scalex; sdl.draw.scaley=scaley; LOG(LOG_MISC,LOG_DEBUG)("GFX_SetSize %ux%u flags=0x%x scale=%.3fx%.3f", (unsigned int)width,(unsigned int)height, (unsigned int)flags, scalex,scaley); Bitu bpp=0; Bitu retFlags = 0; // Uint32 sdl_flags; if (sdl.blit.surface) { SDL_FreeSurface(sdl.blit.surface); sdl.blit.surface=0; } #if defined(WIN32) && !defined(C_SDL2) SDL1_hax_inhibit_WM_PAINT = 0; #endif switch (sdl.desktop.want_type) { #if defined(C_SDL2) (void)bpp; case SCREEN_SURFACE: { GFX_ResetSDL(); dosurface: sdl.desktop.type=SCREEN_SURFACE; sdl.clip.w=width; sdl.clip.h=height; if (GFX_IsFullscreen()) { if (sdl.desktop.full.fixed) { sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2); sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2); sdl.window = GFX_SetSDLWindowMode(sdl.desktop.full.width, sdl.desktop.full.height, sdl.desktop.type); if (sdl.window == NULL) E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,sdl.desktop.bpp,SDL_GetError()); } else { sdl.clip.x=0; sdl.clip.y=0; sdl.window = GFX_SetSDLWindowMode(width, height, sdl.desktop.type); if (sdl.window == NULL) LOG_MSG("Fullscreen not supported: %s", SDL_GetError()); SDL_SetWindowFullscreen(sdl.window, 0); GFX_CaptureMouse(); goto dosurface; } } else { int menuheight = 0; #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW if (mainMenu.isVisible()) menuheight = mainMenu.menuBox.h; #endif sdl.clip.x=sdl.overscan_width; sdl.clip.y=sdl.overscan_width + menuheight; sdl.window=GFX_SetSDLWindowMode(width+2*sdl.overscan_width, height+menuheight+2*sdl.overscan_width, sdl.desktop.type); if (sdl.window == NULL) E_Exit("Could not set windowed video mode %ix%i: %s",(int)width,(int)height,SDL_GetError()); } sdl.surface = SDL_GetWindowSurface(sdl.window); if (sdl.surface == NULL) E_Exit("Could not retrieve window surface: %s",SDL_GetError()); switch (sdl.surface->format->BitsPerPixel) { case 8: retFlags = GFX_CAN_8; break; case 15: retFlags = GFX_CAN_15; break; case 16: retFlags = GFX_CAN_16; break; case 32: retFlags = GFX_CAN_32; break; } /* Fix a glitch with aspect=true occuring when changing between modes with different dimensions */ SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW mainMenu.screenWidth = sdl.surface->w; mainMenu.updateRect(); mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); #endif SDL_UpdateWindowSurface(sdl.window); break; } #else case SCREEN_SURFACE: GFX_ResetSDL(); dosurface: if (flags & GFX_CAN_8) bpp=8; if (flags & GFX_CAN_15) bpp=15; if (flags & GFX_CAN_16) bpp=16; if (flags & GFX_CAN_32) bpp=32; #if defined(WIN32) && !defined(C_SDL2) /* SDL 1.x might mis-inform us on 16bpp for 15-bit color, which is bad enough. But on Windows, we're still required to ask for 16bpp to get the 15bpp mode we want. */ if (bpp == 15) { if (sdl.surface->format->Gshift == 5 && sdl.surface->format->Gmask == (31U << 5U)) { 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."); bpp = 16; } } #endif #if C_XBRZ if (sdl.xBRZ.enable) { // 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 // if we detect non-4:3 pixel ratio here with aspect correction on, we correct it so original fits into resized window properly if (render.aspect) { if (width*sdl.srcAspect.y != height * sdl.srcAspect.x) { // abnormal aspect ratio detected, apply correction if (width*sdl.srcAspect.y > height*sdl.srcAspect.x) { // wide pixel ratio, height should be extended to fit height = (Bitu)floor((double)width * sdl.srcAspect.y / sdl.srcAspect.x + 0.5); } else { // long pixel ratio, width should be extended width = (Bitu)floor((double)height * sdl.srcAspect.x / sdl.srcAspect.y + 0.5); } } } } #endif sdl.desktop.type=SCREEN_SURFACE; sdl.clip.w=width; sdl.clip.h=height; if (sdl.desktop.fullscreen) { Uint32 wflags = SDL_FULLSCREEN | SDL_HWPALETTE | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0); if (sdl.desktop.full.fixed) { sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2); sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2); sdl.surface=SDL_SetVideoMode(sdl.desktop.full.width, sdl.desktop.full.height, bpp, wflags); sdl.deferred_resize = false; sdl.must_redraw_all = true; } else { sdl.clip.x=0;sdl.clip.y=0; sdl.surface=SDL_SetVideoMode(width, height, bpp, wflags); sdl.deferred_resize = false; sdl.must_redraw_all = true; } if (sdl.surface == NULL) { LOG_MSG("Fullscreen not supported: %s", SDL_GetError()); sdl.desktop.fullscreen=false; GFX_CaptureMouse(); goto dosurface; } } else { sdl.clip.x=0; sdl.clip.y=0; #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* scale the menu bar if the window is large enough */ { Bitu consider_height = menu.maxwindow ? currentWindowHeight : height; Bitu consider_width = menu.maxwindow ? currentWindowWidth : width; Bitu final_height = max(max(consider_height,userResizeWindowHeight),(Bitu)(sdl.clip.y+sdl.clip.h)); Bitu final_width = max(max(consider_width,userResizeWindowWidth),(Bitu)(sdl.clip.x+sdl.clip.w)); Bitu scale = 1; while ((final_width/scale) >= (640*2) && (final_height/scale) >= (400*2)) scale++; LOG_MSG("menuScale=%lu",(unsigned long)scale); mainMenu.setScale(scale); } #endif /* center the screen in the window */ { int menuheight = 0; #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW if (mainMenu.isVisible()) menuheight = mainMenu.menuBox.h; #endif Bitu consider_height = menu.maxwindow ? currentWindowHeight : (height + (unsigned int)menuheight + (sdl.overscan_width * 2)); Bitu consider_width = menu.maxwindow ? currentWindowWidth : (width + (sdl.overscan_width * 2)); #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW if (mainMenu.isVisible()) { /* enforce a minimum 640x400 surface size. * the menus are useless below 640x400 */ if (consider_width < (640+(sdl.overscan_width * 2))) consider_width = (640+(sdl.overscan_width * 2)); if (consider_height < (400+(sdl.overscan_width * 2)+(unsigned int)menuheight)) consider_height = (400+(sdl.overscan_width * 2)+(unsigned int)menuheight); } #endif int final_height = (int)max(max(consider_height,userResizeWindowHeight),(Bitu)(sdl.clip.y+sdl.clip.h)) - (int)menuheight - ((int)sdl.overscan_width * 2); int final_width = (int)max(max(consider_width,userResizeWindowWidth),(Bitu)(sdl.clip.x+sdl.clip.w)) - ((int)sdl.overscan_width * 2); int ax = (final_width - (sdl.clip.x + sdl.clip.w)) / 2; int ay = (final_height - (sdl.clip.y + sdl.clip.h)) / 2; if (ax < 0) ax = 0; if (ay < 0) ay = 0; sdl.clip.x += ax + (int)sdl.overscan_width; sdl.clip.y += ay + (int)sdl.overscan_width; // sdl.clip.w = currentWindowWidth - sdl.clip.x; // sdl.clip.h = currentWindowHeight - sdl.clip.y; final_width += (int)sdl.overscan_width*2; final_height += (int)menuheight + (int)sdl.overscan_width*2; sdl.clip.y += (int)menuheight; LOG_MSG("surface consider=%ux%u final=%ux%u", (unsigned int)consider_width, (unsigned int)consider_height, (unsigned int)final_width, (unsigned int)final_height); sdl.surface = SDL_SetVideoMode(final_width, final_height, bpp, (unsigned int)((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | (unsigned int)SDL_HAX_NOREFRESH | (unsigned int)SDL_RESIZABLE); sdl.deferred_resize = false; sdl.must_redraw_all = true; if (SDL_MUSTLOCK(sdl.surface)) SDL_LockSurface(sdl.surface); memset(sdl.surface->pixels, 0, (unsigned int)sdl.surface->pitch * (unsigned int)sdl.surface->h); if (SDL_MUSTLOCK(sdl.surface)) SDL_UnlockSurface(sdl.surface); } #ifdef WIN32 if (sdl.surface == NULL) { SDL_QuitSubSystem(SDL_INIT_VIDEO); if (!sdl.using_windib) { LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with windib enabled."); putenv("SDL_VIDEODRIVER=windib"); sdl.using_windib=true; } else { LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with directx enabled."); putenv("SDL_VIDEODRIVER=directx"); sdl.using_windib=false; } SDL_InitSubSystem(SDL_INIT_VIDEO); GFX_SetIcon(); //Set Icon again sdl.surface = SDL_SetVideoMode(width,height,bpp,SDL_HWSURFACE); sdl.deferred_resize = false; sdl.must_redraw_all = true; if(sdl.surface) GFX_SetTitle(-1,-1,-1,false); //refresh title. } #endif if (sdl.surface == NULL) E_Exit("Could not set windowed video mode %ix%i-%i: %s",(int)width,(int)height,(int)bpp,SDL_GetError()); } if (sdl.surface) { switch (sdl.surface->format->BitsPerPixel) { case 8: retFlags = GFX_CAN_8; break; case 15: retFlags = GFX_CAN_15; break; case 16: if (sdl.surface->format->Gshift == 5 && sdl.surface->format->Gmask == (31U << 5U)) { retFlags = GFX_CAN_15; } else { retFlags = GFX_CAN_16; } break; case 32: retFlags = GFX_CAN_32; break; } if (retFlags && (sdl.surface->flags & SDL_HWSURFACE)) retFlags |= GFX_HARDWARE; if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF)) { sdl.blit.surface=SDL_CreateRGBSurface((Uint32)SDL_HWSURFACE, (int)sdl.draw.width, (int)sdl.draw.height, (int)sdl.surface->format->BitsPerPixel, (Uint32)sdl.surface->format->Rmask, (Uint32)sdl.surface->format->Gmask, (Uint32)sdl.surface->format->Bmask, (Uint32)0u); /* If this one fails be ready for some flickering... */ } #if C_XBRZ if (sdl.xBRZ.enable) { // pre-calculate scaling factor and adjust aspect rate correction offload state int clipWidth = sdl.surface->w; int clipHeight = sdl.surface->h; if (render.aspect) { if (clipWidth > sdl.srcAspect.xToY * clipHeight) { clipWidth = static_cast(clipHeight * sdl.srcAspect.xToY); } else // black bars top and bottom { clipHeight = static_cast(clipWidth * sdl.srcAspect.yToX); } } xBRZ_SetScaleParameters(sdl.draw.width, sdl.draw.height, clipWidth, clipHeight); if (sdl.xBRZ.scale_on != render.aspectOffload) { // when we are scaling, we ask render code not to do any aspect correction // when we are not scaling, render code is allowed to do aspect correction at will render.aspectOffload = sdl.xBRZ.scale_on; PIC_AddEvent(VGA_SetupDrawing, 50); // schedule another resize here, render has already been initialized at this point and we have just changed its option } } #endif } #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW mainMenu.screenWidth = (size_t)sdl.surface->w; mainMenu.updateRect(); mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); #endif break; #endif #if C_OPENGL case SCREEN_OPENGL: { /* NTS: Apparently calling glFinish/glFlush before setup causes a segfault within * the OpenGL library on Mac OS X. */ if (initedOpenGL) { glFinish(); glFlush(); } if (sdl.opengl.pixel_buffer_object) { glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); if (sdl.opengl.buffer) glDeleteBuffersARB(1, &sdl.opengl.buffer); } else if (sdl.opengl.framebuf) { free(sdl.opengl.framebuf); } sdl.opengl.framebuf=0; SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); #if SDL_VERSION_ATLEAST(1, 2, 11) Section_prop * sec=static_cast(control->GetSection("vsync")); if(sec) { SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, (!strcmp(sec->Get_string("vsyncmode"),"host"))?1:0 ); } #endif GFX_SetupSurfaceScaledOpenGL(SDL_RESIZABLE, 0); if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) { LOG_MSG("SDL:OPENGL:Can't open drawing surface, are you running in 16bpp(or higher) mode?"); goto dosurface; } glFinish(); glFlush(); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &sdl.opengl.max_texsize); //if (!(flags&GFX_CAN_32) || (flags & GFX_RGBONLY)) goto dosurface; Bitu adjTexWidth = sdl.draw.width; Bitu adjTexHeight = sdl.draw.height; #if C_XBRZ // we do the same as with Direct3D: precreate pixel buffer adjusted for xBRZ if (sdl.xBRZ.enable && xBRZ_SetScaleParameters(adjTexWidth, adjTexHeight, sdl.clip.w, sdl.clip.h)) { adjTexWidth = adjTexWidth * sdl.xBRZ.scale_factor; adjTexHeight = adjTexHeight * sdl.xBRZ.scale_factor; } #endif int texsize=2 << int_log2(adjTexWidth > adjTexHeight ? adjTexWidth : adjTexHeight); if (texsize>sdl.opengl.max_texsize) { LOG_MSG("SDL:OPENGL:No support for texturesize of %d (max size is %d), falling back to surface",texsize,sdl.opengl.max_texsize); goto dosurface; } /* Create the texture and display list */ if (sdl.opengl.pixel_buffer_object) { glGenBuffersARB(1, &sdl.opengl.buffer); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, adjTexWidth*adjTexHeight*4, NULL, GL_STREAM_DRAW_ARB); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); } else sdl.opengl.framebuf=calloc(adjTexWidth*adjTexHeight, 4); //32 bit color sdl.opengl.pitch=adjTexWidth*4; glBindTexture(GL_TEXTURE_2D,0); #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW if (SDLDrawGenFontTextureInit) { glDeleteTextures(1,&SDLDrawGenFontTexture); SDLDrawGenFontTexture = (GLuint)(~0UL); SDLDrawGenFontTextureInit = 0; } #endif glViewport(0,0,sdl.surface->w,sdl.surface->h); glDeleteTextures(1,&sdl.opengl.texture); glGenTextures(1,&sdl.opengl.texture); glBindTexture(GL_TEXTURE_2D,sdl.opengl.texture); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, 0); // No borders glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); if (sdl.opengl.bilinear) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0); gl_menudraw_countdown = 2; // two GL buffers gl_clear_countdown = 2; // two GL buffers glClearColor (0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); // SDL_GL_SwapBuffers(); // glClear(GL_COLOR_BUFFER_BIT); glShadeModel (GL_FLAT); glBlendFunc(GL_ONE, GL_ZERO); glDisable (GL_DEPTH_TEST); glDisable (GL_LIGHTING); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); glDisable(GL_ALPHA_TEST); glDisable(GL_FOG); glDisable(GL_SCISSOR_TEST); glDisable(GL_STENCIL_TEST); glEnable(GL_TEXTURE_2D); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); glMatrixMode (GL_PROJECTION); glLoadIdentity (); glOrtho(0, sdl.surface->w, sdl.surface->h, 0, -1, 1); glMatrixMode (GL_TEXTURE); glLoadIdentity (); glScaled(1.0 / texsize, 1.0 / texsize, 1.0); //if (glIsList(sdl.opengl.displaylist)) glDeleteLists(sdl.opengl.displaylist, 1); //sdl.opengl.displaylist = glGenLists(1); sdl.opengl.displaylist = 1; glNewList(sdl.opengl.displaylist, GL_COMPILE); glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glBegin(GL_QUADS); // lower left glTexCoord2i(0, 0 ); glVertex2i(sdl.clip.x, sdl.clip.y ); // lower right glTexCoord2i(adjTexWidth,0 ); glVertex2i(sdl.clip.x+sdl.clip.w,sdl.clip.y ); // upper right glTexCoord2i(adjTexWidth,adjTexHeight); glVertex2i(sdl.clip.x+sdl.clip.w,sdl.clip.y+sdl.clip.h); // upper left glTexCoord2i(0, adjTexHeight); glVertex2i(sdl.clip.x, sdl.clip.y+sdl.clip.h); glEnd(); glEndList(); glBindTexture(GL_TEXTURE_2D,0); #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW void GFX_DrawSDLMenu(DOSBoxMenu &menu,DOSBoxMenu::displaylist &dl); mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); // FIXME: Why do we have to reinitialize the font texture? /*if (!SDLDrawGenFontTextureInit) */{ GLuint err = 0; glGetError(); /* read and discard last error */ SDLDrawGenFontTexture = (GLuint)(~0UL); glGenTextures(1,&SDLDrawGenFontTexture); if (SDLDrawGenFontTexture == (GLuint)(~0UL) || (err=glGetError()) != 0) { LOG_MSG("WARNING: Unable to make font texture. id=%llu err=%lu", (unsigned long long)SDLDrawGenFontTexture,(unsigned long)err); } else { LOG_MSG("font texture id=%lu will make %u x %u", (unsigned long)SDLDrawGenFontTexture, (unsigned int)SDLDrawGenFontTextureWidth, (unsigned int)SDLDrawGenFontTextureHeight); SDLDrawGenFontTextureInit = 1; glBindTexture(GL_TEXTURE_2D,SDLDrawGenFontTexture); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, 0); // No borders glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, SDLDrawGenFontTextureWidth, SDLDrawGenFontTextureHeight, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0); /* load the font */ { extern Bit8u int10_font_16[256 * 16]; unsigned char *bmp; uint32_t tmp[8*16]; unsigned int x,y,c; for (c=0;c < 256;c++) { bmp = int10_font_16 + (c * 16); for (y=0;y < 16;y++) { for (x=0;x < 8;x++) { tmp[(y*8)+x] = (bmp[y] & (0x80 >> x)) ? 0xFFFFFFFFUL : 0x00000000UL; } } glTexSubImage2D(GL_TEXTURE_2D, /*level*/0, /*x*/(int)((c % 16) * 8), /*y*/(int)((c / 16) * 16), 8, 16, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, (void*)tmp); } } glBindTexture(GL_TEXTURE_2D,0); } } #endif glFinish(); glFlush(); initedOpenGL = true; sdl.desktop.type=SCREEN_OPENGL; retFlags = GFX_CAN_32 | GFX_SCALING; if (sdl.opengl.pixel_buffer_object) retFlags |= GFX_HARDWARE; break; }//OPENGL #endif //C_OPENGL #if (HAVE_D3D9_H) && defined(WIN32) case SCREEN_DIRECT3D: { Bit16u fixedWidth; Bit16u fixedHeight; Bit16u windowWidth; Bit16u windowHeight; Bitu adjTexWidth = width; Bitu adjTexHeight = height; if (sdl.desktop.fullscreen) { fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0; fixedHeight = sdl.desktop.full.fixed ? sdl.desktop.full.height : 0; } else { fixedWidth = sdl.desktop.window.width; fixedHeight = sdl.desktop.window.height; } if (fixedWidth == 0 || fixedHeight == 0) { Bitu consider_height = menu.maxwindow ? currentWindowHeight : 0; Bitu consider_width = menu.maxwindow ? currentWindowWidth : 0; int final_height = max(consider_height,userResizeWindowHeight); int final_width = max(consider_width,userResizeWindowWidth); fixedWidth = final_width; fixedHeight = final_height; } #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* scale the menu bar if the window is large enough */ { int cw = fixedWidth,ch = fixedHeight; Bitu scale = 1; if (cw == 0) cw = (Bit16u)(sdl.draw.width*sdl.draw.scalex); if (ch == 0) ch = (Bit16u)(sdl.draw.height*sdl.draw.scaley); while ((cw/scale) >= (640*2) && (ch/scale) >= (400*2)) scale++; LOG_MSG("menuScale=%lu",(unsigned long)scale); mainMenu.setScale(scale); if (mainMenu.isVisible()) fixedHeight -= mainMenu.menuBox.h; } #endif if (fixedWidth && fixedHeight) { sdl.clip.w = fixedWidth; sdl.clip.h = fixedHeight; if (render.aspect) { if (fixedWidth > sdl.srcAspect.xToY * fixedHeight) // output broader than input => black bars left and right { sdl.clip.w = static_cast(fixedHeight * sdl.srcAspect.xToY); } else // black bars top and bottom { sdl.clip.h = static_cast(fixedWidth * sdl.srcAspect.yToX); } } sdl.clip.x = (fixedWidth - sdl.clip.w) / 2; sdl.clip.y = (fixedHeight - sdl.clip.h) / 2; windowWidth = fixedWidth; windowHeight = fixedHeight; } else { sdl.clip.w=windowWidth=(Bit16u)(sdl.draw.width*sdl.draw.scalex); sdl.clip.h=windowHeight=(Bit16u)(sdl.draw.height*sdl.draw.scaley); // we solve problem of aspect ratio based window extension here when window size is not set explicitly if (windowWidth*sdl.srcAspect.y != windowHeight * sdl.srcAspect.x) { // abnormal aspect ratio detected, apply correction if (windowWidth*sdl.srcAspect.y > windowHeight*sdl.srcAspect.x) { // wide pixel ratio, height should be extended to fit sdl.clip.h = windowHeight = (Bitu)floor((double)windowWidth * sdl.srcAspect.y / sdl.srcAspect.x + 0.5); } else { // long pixel ratio, width should be extended sdl.clip.w = windowWidth = (Bitu)floor((double)windowHeight * sdl.srcAspect.x / sdl.srcAspect.y + 0.5); } } sdl.clip.x = (windowWidth - sdl.clip.w) / 2; sdl.clip.y = (windowHeight - sdl.clip.h) / 2; } // when xBRZ scaler is used, we can adjust render target size to exactly what xBRZ scaler will output, leaving final scaling to default D3D scaler / shaders #if C_XBRZ if (sdl.xBRZ.enable && xBRZ_SetScaleParameters(width, height, sdl.clip.w, sdl.clip.h)) { adjTexWidth = width * sdl.xBRZ.scale_factor; adjTexHeight = height * sdl.xBRZ.scale_factor; } #endif // Calculate texture size if ((!d3d->square) && (!d3d->pow2)) { d3d->dwTexWidth = adjTexWidth; d3d->dwTexHeight = adjTexHeight; } else if (d3d->square) { int texsize = 2 << int_log2(adjTexWidth > adjTexHeight ? adjTexWidth : adjTexHeight); d3d->dwTexWidth = d3d->dwTexHeight = texsize; } else { d3d->dwTexWidth = 2 << int_log2(adjTexWidth); d3d->dwTexHeight = 2 << int_log2(adjTexHeight); } LOG(LOG_MISC,LOG_DEBUG)("GFX_SetSize Direct3D texture=%ux%u window=%ux%u clip=x,y,w,h=%d,%d,%d,%d", (unsigned int)d3d->dwTexWidth, (unsigned int)d3d->dwTexHeight, (unsigned int)windowWidth, (unsigned int)windowHeight, (unsigned int)sdl.clip.x, (unsigned int)sdl.clip.y, (unsigned int)sdl.clip.w, (unsigned int)sdl.clip.h); #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW if (mainMenu.isVisible()) { windowHeight += mainMenu.menuBox.h; sdl.clip.y += mainMenu.menuBox.h; } #endif #if (C_D3DSHADERS) Section_prop *section=static_cast(control->GetSection("sdl")); if(section) { Prop_multival* prop = section->Get_multival("pixelshader"); std::string f = prop->GetSection()->Get_string("force"); d3d->LoadPixelShader(prop->GetSection()->Get_string("type"), scalex, scaley, (f == "forced")); } else { LOG_MSG("SDL:D3D:Could not get pixelshader info, shader disabled"); } #endif d3d->aspect=false;//RENDER_GetAspect(); d3d->autofit=false;//TODO RENDER_GetAutofit() && sdl.desktop.fullscreen; //scale to 5:4 monitors in fullscreen only // Create a dummy sdl surface // D3D will hang or crash when using fullscreen with ddraw surface, therefore we hack SDL to provide // a GDI window with an additional 0x40 flag. If this fails or stock SDL is used, use WINDIB output if(GCC_UNLIKELY(d3d->bpp16)) { sdl.surface=SDL_SetVideoMode(windowWidth, windowHeight,16,sdl.desktop.fullscreen ? SDL_FULLSCREEN|0x40 : SDL_RESIZABLE|0x40); sdl.deferred_resize = false; sdl.must_redraw_all = true; retFlags = GFX_CAN_16 | GFX_SCALING; } else { sdl.surface=SDL_SetVideoMode(windowWidth, windowHeight,0,sdl.desktop.fullscreen ? SDL_FULLSCREEN|0x40 : SDL_RESIZABLE|0x40); sdl.deferred_resize = false; sdl.must_redraw_all = true; retFlags = GFX_CAN_32 | GFX_SCALING; } if (sdl.surface == NULL) E_Exit("Could not set video mode %ix%i-%i: %s",sdl.clip.w,sdl.clip.h,d3d->bpp16 ? 16:32,SDL_GetError()); sdl.desktop.type=SCREEN_DIRECT3D; if(d3d->dynamic) retFlags |= GFX_HARDWARE; SDL1_hax_inhibit_WM_PAINT = 1; if(GCC_UNLIKELY(d3d->Resize3DEnvironment(windowWidth,windowHeight,sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h,adjTexWidth, adjTexHeight,sdl.desktop.fullscreen) != S_OK)) { retFlags = 0; } #if LOG_D3D LOG_MSG("SDL:D3D:Display mode set to: %dx%d with %fx%f scale", sdl.clip.w, sdl.clip.h,sdl.draw.scalex, sdl.draw.scaley); #endif #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW mainMenu.screenWidth = sdl.surface->w; mainMenu.updateRect(); mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); #endif break; } #endif default: goto dosurface; break; }//CASE GFX_LogSDLState(); if (retFlags) GFX_Start(); if (!sdl.mouse.autoenable) SDL_ShowCursor(sdl.mouse.autolock?SDL_DISABLE:SDL_ENABLE); UpdateWindowDimensions(); return retFlags; } #if defined(WIN32) && !defined(HX_DOS) // WARNING: Not recommended, there is danger you cannot exit emulator because mouse+keyboard are taken static bool enable_hook_everything = false; #endif // Whether or not to hook the keyboard and block special keys. // Setting this is recommended so that your keyboard is fully usable in the guest OS when you // enable the mouse+keyboard capture. But hooking EVERYTHING is not recommended because there is a // danger you become trapped in the DOSBox emulator! static bool enable_hook_special_keys = true; #if defined(WIN32) && !defined(HX_DOS) // Whether or not to hook Num/Scroll/Caps lock in order to give the guest OS full control of the // LEDs on the keyboard (i.e. the LEDs do not change until the guest OS changes their state). // This flag also enables code to set the LEDs to guest state when setting mouse+keyboard capture, // and restoring LED state when releasing capture. static bool enable_hook_lock_toggle_keys = true; #endif #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) // and this is where we store host LED state when capture is set. static bool on_capture_num_lock_was_on = true; // reasonable guess static bool on_capture_scroll_lock_was_on = false; static bool on_capture_caps_lock_was_on = false; #endif static bool exthook_enabled = false; #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) static HHOOK exthook_winhook = NULL; #if !defined(__MINGW32__) extern "C" void SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(unsigned char x); #endif static LRESULT CALLBACK WinExtHookKeyboardHookProc(int nCode,WPARAM wParam,LPARAM lParam) { if (nCode == HC_ACTION) { HWND myHwnd = GetHWND(); if (exthook_enabled && GetFocus() == myHwnd) { /* intercept only if DOSBox-X is the focus and the keyboard is hooked */ if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN || wParam == WM_SYSKEYUP || wParam == WM_KEYUP) { KBDLLHOOKSTRUCT *st_hook = (KBDLLHOOKSTRUCT*)lParam; if (st_hook->flags & LLKHF_INJECTED) { // injected keys are automatically allowed, especially if we are injecting keyboard input into ourself // to control Num/Scroll/Caps Lock LEDs. If we don't check this we cannot control the LEDs. Injecting // keydown/keyup for Num Lock is the only means provided by Windows to control those LEDs. } else if (st_hook->vkCode == VK_MENU/*alt*/ || st_hook->vkCode == VK_CONTROL || st_hook->vkCode == VK_LSHIFT || st_hook->vkCode == VK_RSHIFT) { // always allow modifier keys through, so other applications are not left with state inconsistent from // actual keyboard state. } else { bool nopass = enable_hook_everything; // if the user wants us to hook ALL keys then that's where this signals it bool alternate_message = false; // send as WM_USER+0x100 instead of WM_KEYDOWN if (!nopass) { // hook only certain keys Windows is likely to act on by itself. // FIXME: Hooking the keyboard does NOT prevent Fn+SPACE (zoom) from triggering screen resolution // changes in Windows 10! How do we stop that? // FIXME: It might be nice to let the user decide whether or not Print Screen is intercepted. // TODO: We do not hook the volume up/down/mute keys. This is to be kind to the user. They may // appreciate the ability to dial down the volume if a loud DOS program comes up. But // if the user WANTS us to, we should allow hooking those keys. // TODO: Allow (if instructed) hooking the VK_SLEEP key so pushing the sleep key (the // one with the icon of the moon on Microsoft keyboards) can be sent instead to the // guest OS. Also add code where if we're not hooking the key, then we should listen // for signals the guest OS is suspending or hibernating and auto-disconnect the // mouse capture and keyboard hook. switch (st_hook->vkCode) { case VK_LWIN: // left Windows key (normally triggers Start menu) case VK_RWIN: // right Windows key (normally triggers Start menu) case VK_APPS: // Application key (normally open to the user, but just in case) case VK_PAUSE: // pause key case VK_SNAPSHOT: // print screen case VK_TAB: // try to catch ALT+TAB too (not blocking VK_TAB will allow host OS to switch tasks) case VK_ESCAPE: // try to catch CTRL+ESC as well (so Windows 95 Start Menu is accessible) case VK_SPACE: // and space (catching VK_ZOOM isn't enough to prevent Windows 10 from changing res) // these keys have no meaning to DOSBox and so we hook them by default to allow the guest OS to use them case VK_BROWSER_BACK: // Browser Back key case VK_BROWSER_FORWARD: // Browser Forward key case VK_BROWSER_REFRESH: // Browser Refresh key case VK_BROWSER_STOP: // Browser Stop key case VK_BROWSER_SEARCH: // Browser Search key case VK_BROWSER_FAVORITES: // Browser Favorites key case VK_BROWSER_HOME: // Browser Start and Home key case VK_MEDIA_NEXT_TRACK: // Next Track key case VK_MEDIA_PREV_TRACK: // Previous Track key case VK_MEDIA_STOP: // Stop Media key case VK_MEDIA_PLAY_PAUSE: // Play / Pause Media key case VK_LAUNCH_MAIL: // Start Mail key case VK_LAUNCH_MEDIA_SELECT: // Select Media key case VK_LAUNCH_APP1: // Start Application 1 key case VK_LAUNCH_APP2: // Start Application 2 key case VK_PLAY: // Play key case VK_ZOOM: // Zoom key (the (+) magnifying glass keyboard shortcut laptops have these days on the spacebar?) nopass = true; break; // IME Hiragana key, otherwise inaccessible to us case 0xF2: nopass = true; // FIXME: This doesn't (yet) cause a SDL key event. break; // we allow hooking Num/Scroll/Caps Lock keys so that pressing them does not toggle the LED. // we then take Num/Scroll/Caps LED state from the guest and let THAT control the LED state. case VK_CAPITAL: case VK_NUMLOCK: case VK_SCROLL: nopass = enable_hook_lock_toggle_keys; alternate_message = true; break; } } if (nopass) { // convert WM_KEYDOWN/WM_KEYUP if obfuscating the message to distinguish between real and injected events if (alternate_message) { if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) wParam = WM_USER + 0x100; else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) wParam = WM_USER + 0x101; } DWORD lParam = (st_hook->scanCode << 8U) + ((st_hook->flags & LLKHF_EXTENDED) ? 0x01000000 : 0) + ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) ? 0xC0000000 : 0); // catch the keystroke, post it to ourself, do not pass it on PostMessage(myHwnd, (UINT)wParam, st_hook->vkCode, lParam); return TRUE; } } } } } return CallNextHookEx(exthook_winhook, nCode, wParam, lParam); } // Microsoft doesn't have an outright "set toggle key state" call, they expect you // to know the state and then fake input to toggle. Blegh. Fine. void WinSetKeyToggleState(unsigned int vkCode, bool state) { bool curState = (GetKeyState(vkCode) & 1) ? true : false; INPUT inps; // if we're already in that state, then there is nothing to do. if (curState == state) return; // fake keyboard input. memset(&inps, 0, sizeof(inps)); inps.type = INPUT_KEYBOARD; inps.ki.wVk = vkCode; inps.ki.dwFlags = KEYEVENTF_EXTENDEDKEY; // pressed, use wVk. SendInput(1, &inps, sizeof(INPUT)); memset(&inps, 0, sizeof(inps)); inps.type = INPUT_KEYBOARD; inps.ki.wVk = vkCode; inps.ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; // release, use wVk. SendInput(1, &inps, sizeof(INPUT)); } #endif Bitu Keyboard_Guest_LED_State(); void UpdateKeyboardLEDState(Bitu led_state/* in the same bitfield arrangement as using command 0xED on PS/2 keyboards */); void UpdateKeyboardLEDState(Bitu led_state/* in the same bitfield arrangement as using command 0xED on PS/2 keyboards */) { (void)led_state;//POSSIBLY UNUSED #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) /* Microsoft Windows */ if (exthook_enabled) { // ONLY if ext hook is enabled, else we risk infinite loops with keyboard events WinSetKeyToggleState(VK_NUMLOCK, !!(led_state & 2)); WinSetKeyToggleState(VK_SCROLL, !!(led_state & 1)); WinSetKeyToggleState(VK_CAPITAL, !!(led_state & 4)); } #endif } void DoExtendedKeyboardHook(bool enable) { if (exthook_enabled == enable) return; #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) if (enable) { if (!exthook_winhook) { exthook_winhook = SetWindowsHookEx(WH_KEYBOARD_LL, WinExtHookKeyboardHookProc, GetModuleHandle(NULL), 0); if (exthook_winhook == NULL) return; } // it's on exthook_enabled = enable; // flush out and handle pending keyboard I/O { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } #if !defined(__MINGW32__) // Enable the SDL hack for Win32 to handle Num/Scroll/Caps SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(1); #endif // if hooking Num/Scroll/Caps Lock then record the toggle state of those keys. // then read from the keyboard emulation the LED state set by the guest and apply it to the host keyboard. if (enable_hook_lock_toggle_keys) { // record state on_capture_num_lock_was_on = (GetKeyState(VK_NUMLOCK) & 1) ? true : false; on_capture_scroll_lock_was_on = (GetKeyState(VK_SCROLL) & 1) ? true : false; on_capture_caps_lock_was_on = (GetKeyState(VK_CAPITAL) & 1) ? true : false; // change to guest state (FIXME: Read emulated keyboard state and apply!) UpdateKeyboardLEDState(Keyboard_Guest_LED_State()); } } else { if (exthook_winhook) { if (enable_hook_lock_toggle_keys) { // restore state WinSetKeyToggleState(VK_NUMLOCK, on_capture_num_lock_was_on); WinSetKeyToggleState(VK_SCROLL, on_capture_scroll_lock_was_on); WinSetKeyToggleState(VK_CAPITAL, on_capture_caps_lock_was_on); } { MSG msg; // before we disable the SDL hack make sure we flush out and handle any pending keyboard events. // if we don't do this the posted Num/Scroll/Caps events will stay in the queue and will be handled // by SDL after turning off the toggle key hack. Sleep(1); // make sure Windows posts the keystrokes while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } #if !defined(__MINGW32__) // Disable the SDL hack for Win32 to handle Num/Scroll/Caps SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(0); #endif UnhookWindowsHookEx(exthook_winhook); exthook_winhook = NULL; } exthook_enabled = enable; } #endif } void GFX_ReleaseMouse(void) { if (sdl.mouse.locked) GFX_CaptureMouse(); } void GFX_CaptureMouse(void) { sdl.mouse.locked=!sdl.mouse.locked; if (sdl.mouse.locked) { #if defined(C_SDL2) SDL_SetRelativeMouseMode(SDL_TRUE); #else SDL_WM_GrabInput(SDL_GRAB_ON); #endif if (enable_hook_special_keys) DoExtendedKeyboardHook(true); SDL_ShowCursor(SDL_DISABLE); } else { DoExtendedKeyboardHook(false); #if defined(C_SDL2) SDL_SetRelativeMouseMode(SDL_FALSE); #else SDL_WM_GrabInput(SDL_GRAB_OFF); #endif if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE); } mouselocked=sdl.mouse.locked; #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */ if (sdl.mouse.locked) { void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id); void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id); GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle); GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle); } #endif /* keep the menu updated (it might not exist yet) */ if (mainMenu.item_exists("mapper_capmouse")) mainMenu.get_item("mapper_capmouse").check(sdl.mouse.locked).refresh_item(mainMenu); } void GFX_UpdateSDLCaptureState(void) { if (sdl.mouse.locked) { #if defined(C_SDL2) SDL_SetRelativeMouseMode(SDL_TRUE); #else SDL_WM_GrabInput(SDL_GRAB_ON); #endif if (enable_hook_special_keys) DoExtendedKeyboardHook(true); SDL_ShowCursor(SDL_DISABLE); } else { DoExtendedKeyboardHook(false); #if defined(C_SDL2) SDL_SetRelativeMouseMode(SDL_FALSE); #else SDL_WM_GrabInput(SDL_GRAB_OFF); #endif if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE); } CPU_Reset_AutoAdjust(); GFX_SetTitle(-1,-1,-1,false); } #if WIN32 void CaptureMouseNotifyWin32() { const auto lck = sdl.mouse.locked; switch (sdl.mouse.autolock_feedback) { case AUTOLOCK_FEEDBACK_NONE: break; case AUTOLOCK_FEEDBACK_BEEP: { const auto lo = 1000; const auto hi = 2000; const auto t1 = 50; const auto t2 = 25; const auto f1 = lck ? hi : lo; const auto f2 = lck ? lo : hi; const auto tt = lck ? t1 : t2; Beep(f1, tt); Beep(f2, tt); } break; case AUTOLOCK_FEEDBACK_FLASH: { const auto cnt = lck ? 4 : 2; const auto tim = lck ? 80 : 40; const auto wnd = GetHWND(); if (wnd != nullptr) { FLASHWINFO fi; fi.cbSize = sizeof(FLASHWINFO); fi.hwnd = wnd; fi.dwFlags = FLASHW_CAPTION; fi.uCount = cnt; fi.dwTimeout = tim; FlashWindowEx(&fi); } break; } default: ; } } #endif void CaptureMouseNotify() { #if WIN32 CaptureMouseNotifyWin32(); #else // TODO #endif } static void CaptureMouse(bool pressed) { if (!pressed) return; CaptureMouseNotify(); GFX_CaptureMouse(); } #if defined (WIN32) STICKYKEYS stick_keys = {sizeof(STICKYKEYS), 0}; void sticky_keys(bool restore){ static bool inited = false; if (!inited){ inited = true; SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0); } if (restore) { SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0); return; } //Get current sticky keys layout: STICKYKEYS s = {sizeof(STICKYKEYS), 0}; SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0); if ( !(s.dwFlags & SKF_STICKYKEYSON)) { //Not on already s.dwFlags &= ~SKF_HOTKEYACTIVE; SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0); } } #else #define sticky_keys(a) #endif #ifdef __WIN32__ static void d3d_init(void) { #if !(HAVE_D3D9_H) E_Exit("D3D not supported"); #else sdl.desktop.want_type=SCREEN_DIRECT3D; if(!sdl.using_windib) { LOG_MSG("Resetting to WINDIB mode"); SDL_QuitSubSystem(SDL_INIT_VIDEO); putenv("SDL_VIDEODRIVER=windib"); sdl.using_windib=true; if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) E_Exit("Can't init SDL Video %s",SDL_GetError()); GFX_SetIcon(); GFX_SetTitle(-1,-1,-1,false); if(!sdl.desktop.fullscreen) DOSBox_RefreshMenu(); } SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); if(!SDL_GetWMInfo(&wmi)) { LOG_MSG("SDL:Error retrieving window information"); LOG_MSG("Failed to get window info"); sdl.desktop.want_type=SCREEN_SURFACE; } else { if(sdl.desktop.fullscreen) { GFX_CaptureMouse(); } if(d3d) delete d3d; d3d = new CDirect3D(640,400); if(!d3d) { LOG_MSG("Failed to create d3d object"); sdl.desktop.want_type=SCREEN_SURFACE; } else if(d3d->InitializeDX(wmi.child_window,sdl.desktop.doublebuf) != S_OK) { LOG_MSG("Unable to initialize DirectX"); sdl.desktop.want_type=SCREEN_SURFACE; } } #endif } #endif void GetDesktopResolution(int* width, int* height) { #ifdef WIN32 RECT rDdesk; auto hDesk = GetDesktopWindow(); GetWindowRect(hDesk, &rDdesk); *width = rDdesk.right - rDdesk.left; *height = rDdesk.bottom - rDdesk.top; #elif defined(LINUX) void Linux_GetDesktopResolution(int *width,int *height); Linux_GetDesktopResolution(width,height); /* this is MESSY but there's too much namespace collision going on here */ #else *width = 1024; // guess *height = 768; #endif } void res_init(void) { Section * sec = control->GetSection("sdl"); Section_prop * section=static_cast(sec); sdl.desktop.full.fixed=false; const char* fullresolution=section->Get_string("fullresolution"); sdl.desktop.full.width = 0; sdl.desktop.full.height = 0; if(fullresolution && *fullresolution) { char res[100]; safe_strncpy( res, fullresolution, sizeof( res )); fullresolution = lowcase (res);//so x and X are allowed if (strcmp(fullresolution,"original")) { sdl.desktop.full.fixed = true; if (strcmp(fullresolution,"desktop")) { //desktop = 0x0 char* height = const_cast(strchr(fullresolution,'x')); if(height && * height) { *height = 0; sdl.desktop.full.height = atoi(height+1); sdl.desktop.full.width = atoi(res); } } } } sdl.desktop.window.width = 0; sdl.desktop.window.height = 0; const char* windowresolution=section->Get_string("windowresolution"); if(windowresolution && *windowresolution) { //if(sdl.desktop.type==SCREEN_SURFACE) return; char res[100]; safe_strncpy( res,windowresolution, sizeof( res )); windowresolution = lowcase (res);//so x and X are allowed if(strcmp(windowresolution,"original")) { char* height = const_cast(strchr(windowresolution,'x')); if(height && *height) { *height = 0; sdl.desktop.window.height = (Bit16u)atoi(height+1); sdl.desktop.window.width = (Bit16u)atoi(res); } } } sdl.desktop.doublebuf=section->Get_bool("fulldouble"); int width = 1024; int height = 768; // fullresolution == desktop -> get/set desktop size auto sdlSection = control->GetSection("sdl"); auto sdlSectionProp = static_cast(sdlSection); auto fullRes = sdlSectionProp->Get_string("fullresolution"); if (!strcmp(fullRes, "desktop")) GetDesktopResolution(&width, &height); if (!sdl.desktop.full.width) { sdl.desktop.full.width=width; } if (!sdl.desktop.full.height) { sdl.desktop.full.height=height; } if(sdl.desktop.type==SCREEN_SURFACE && !sdl.desktop.fullscreen) return; else { GFX_Stop(); if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset ); GFX_Start(); } } void res_input(bool type, const char * res) { Section* sec = control->GetSection("sdl"); char win_res[11]; if(sec) { strcpy(win_res,res); if(type) { std::string tmp("windowresolution="); tmp.append(win_res); sec->HandleInputline(tmp); } else { std::string tmp("fullresolution="); tmp.append(win_res); sec->HandleInputline(tmp); } res_init(); } } void change_output(int output) { GFX_Stop(); Section * sec = control->GetSection("sdl"); Section_prop * section=static_cast(sec); sdl.overscan_width=(unsigned int)section->Get_int("overscan"); UpdateOverscanMenu(); switch (output) { case 0: sdl.desktop.want_type=SCREEN_SURFACE; break; case 1: sdl.desktop.want_type=SCREEN_SURFACE; break; case 2: /* do nothing */ break; case 3: #if C_OPENGL change_output(2); sdl.desktop.want_type=SCREEN_OPENGL; #if !defined(C_SDL2) sdl.opengl.bilinear = true; #endif #endif break; case 4: #if C_OPENGL change_output(2); sdl.desktop.want_type=SCREEN_OPENGL; #if !defined(C_SDL2) sdl.opengl.bilinear = false; //NB #endif #endif break; #if defined(__WIN32__) && !defined(C_SDL2) case 5: sdl.desktop.want_type=SCREEN_DIRECT3D; d3d_init(); break; #endif case 6: break; case 7: // do not set want_type break; case 8: if(sdl.desktop.want_type==SCREEN_OPENGL) { } #ifdef WIN32 else if(sdl.desktop.want_type==SCREEN_DIRECT3D) { if(sdl.desktop.fullscreen) GFX_CaptureMouse(); d3d_init(); } #endif break; default: LOG_MSG("SDL:Unsupported output device %d, switching back to surface",output); sdl.desktop.want_type=SCREEN_SURFACE; break; } const char* windowresolution=section->Get_string("windowresolution"); if(windowresolution && *windowresolution) { char res[100]; safe_strncpy( res,windowresolution, sizeof( res )); windowresolution = lowcase (res);//so x and X are allowed if(strcmp(windowresolution,"original")) { if(output == 0) { std::string tmp("windowresolution=original"); sec->HandleInputline(tmp); } } } res_init(); if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset ); GFX_SetTitle(CPU_CycleMax,-1,-1,false); GFX_LogSDLState(); UpdateWindowDimensions(); } void GFX_SwitchFullScreen(void) { #if !defined(HX_DOS) if (sdl.desktop.prevent_fullscreen) return; menu.resizeusing = true; sdl.desktop.fullscreen = !sdl.desktop.fullscreen; auto full = sdl.desktop.fullscreen; // if we're going fullscreen and current scaler exceeds screen size, // cancel the fullscreen change -> fixes scaler crashes // TODO this will need further changes to accomodate different outputs (e.g. stretched) if (full) { int width, height; GetDesktopResolution(&width, &height); auto width1 = sdl.draw.width; auto height1 = sdl.draw.height; if ((unsigned int)width < width1 || (unsigned int)height < height1) { sdl.desktop.fullscreen = false; LOG_MSG("WARNING: full screen canceled, surface size (%ix%i) exceeds screen size (%ix%i).", width1, height1, width, height); return; } } LOG_MSG("INFO: switched to %s mode", full ? "full screen" : "window"); #if !defined(C_SDL2) // (re-)assign menu to window void DOSBox_SetSysMenu(void); DOSBox_SetSysMenu(); #endif // ensure mouse capture when fullscreen || (re-)capture if user said so when windowed auto locked = sdl.mouse.locked; if ((full && !locked) || (!full && locked)) GFX_CaptureMouse(); // disable/enable sticky keys for fullscreen/desktop #if defined (WIN32) sticky_keys(!full); #endif GFX_ResetScreen(); // set vsync to host // NOTE why forcing ??? #ifdef WIN32 if (menu.startup) // NOTE should be always true I suppose ??? { auto vsync = static_cast(control->GetSection("vsync")); if (vsync) { auto vsyncMode = vsync->Get_string("vsyncmode"); if (!strcmp(vsyncMode, "host")) SetVal("vsync", "vsyncmode", "host"); } } #endif #endif } static void SwitchFullScreen(bool pressed) { if (!pressed) return; GFX_LosingFocus(); if (sdl.desktop.lazy_fullscreen) { LOG_MSG("GFX LF: fullscreen switching not supported"); } else { GFX_SwitchFullScreen(); } } void GFX_SwitchLazyFullscreen(bool lazy) { sdl.desktop.lazy_fullscreen=lazy; sdl.desktop.lazy_fullscreen_req=false; } void GFX_SwitchFullscreenNoReset(void) { sdl.desktop.fullscreen=!sdl.desktop.fullscreen; } bool GFX_LazyFullscreenRequested(void) { if (sdl.desktop.lazy_fullscreen) return sdl.desktop.lazy_fullscreen_req; return false; } bool GFX_GetPreventFullscreen(void) { return sdl.desktop.prevent_fullscreen; } #if defined(WIN32) && !defined(C_SDL2) extern "C" unsigned char SDL1_hax_RemoveMinimize; #endif void GFX_PreventFullscreen(bool lockout) { if (sdl.desktop.prevent_fullscreen != lockout) { sdl.desktop.prevent_fullscreen = lockout; #if defined(WIN32) && !defined(C_SDL2) void DOSBox_SetSysMenu(void); int Reflect_Menu(void); SDL1_hax_RemoveMinimize = lockout ? 1 : 0; DOSBox_SetSysMenu(); Reflect_Menu(); #endif } } void GFX_RestoreMode(void) { if (sdl.draw.width == 0 || sdl.draw.height == 0) return; GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback); GFX_UpdateSDLCaptureState(); GFX_ResetScreen(); } #if !defined(C_SDL2) static bool GFX_GetSurfacePtrLock = false; unsigned char *GFX_GetSurfacePtr(size_t *pitch, unsigned int x, unsigned int y) { if (sdl.surface->pixels == NULL) { if (!GFX_GetSurfacePtrLock) { if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface)) return NULL; GFX_GetSurfacePtrLock = true; } } *pitch = sdl.surface->pitch; if (sdl.surface->pixels != NULL) { unsigned char *p = (unsigned char*)(sdl.surface->pixels); p += y * sdl.surface->pitch; p += x * ((unsigned int)sdl.surface->format->BitsPerPixel >> 3U); return p; } return NULL; } void GFX_ReleaseSurfacePtr(void) { if (GFX_GetSurfacePtrLock) { if (SDL_MUSTLOCK(sdl.surface)) SDL_UnlockSurface(sdl.surface); GFX_GetSurfacePtrLock = false; } } #endif bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { if (!sdl.active || sdl.updating) return false; switch (sdl.desktop.type) { case SCREEN_SURFACE: #if C_XBRZ if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { sdl.xBRZ.renderbuf.resize(sdl.draw.width * sdl.draw.height); pixels = sdl.xBRZ.renderbuf.empty() ? nullptr : reinterpret_cast(&sdl.xBRZ.renderbuf[0]); pitch = sdl.draw.width * sizeof(uint32_t); } else #endif { if (sdl.blit.surface) { if (SDL_MUSTLOCK(sdl.blit.surface) && SDL_LockSurface(sdl.blit.surface)) return false; pixels = (Bit8u *)sdl.blit.surface->pixels; pitch = sdl.blit.surface->pitch; } else { if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface)) return false; pixels = (Bit8u *)sdl.surface->pixels; pixels += sdl.clip.y*sdl.surface->pitch; pixels += sdl.clip.x*sdl.surface->format->BytesPerPixel; pitch = sdl.surface->pitch; } } SDL_Overscan(); sdl.updating=true; return true; #if C_OPENGL case SCREEN_OPENGL: #if C_XBRZ if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { sdl.xBRZ.renderbuf.resize(sdl.draw.width * sdl.draw.height); pixels = sdl.xBRZ.renderbuf.empty() ? nullptr : reinterpret_cast(&sdl.xBRZ.renderbuf[0]); pitch = sdl.draw.width * sizeof(uint32_t); } else #endif { if (sdl.opengl.pixel_buffer_object) { glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); pixels = (Bit8u *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); } else pixels = (Bit8u *)sdl.opengl.framebuf; pitch = sdl.opengl.pitch; } sdl.updating=true; return true; #endif #if (HAVE_D3D9_H) && defined(WIN32) case SCREEN_DIRECT3D: #if C_XBRZ if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { sdl.xBRZ.renderbuf.resize(sdl.draw.width * sdl.draw.height); pixels = sdl.xBRZ.renderbuf.empty() ? nullptr : reinterpret_cast(&sdl.xBRZ.renderbuf[0]); pitch = sdl.draw.width * sizeof(uint32_t); sdl.updating = true; } else #endif sdl.updating = d3d->LockTexture(pixels, pitch); return sdl.updating; #endif default: break; } return false; } void GFX_OpenGLRedrawScreen(void) { #if C_OPENGL if (OpenGL_using()) { if (gl_clear_countdown > 0) { gl_clear_countdown--; glClearColor (0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); } if (sdl.opengl.pixel_buffer_object) { glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT); glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); glCallList(sdl.opengl.displaylist); } else { glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glCallList(sdl.opengl.displaylist); } } #endif } #if C_XBRZ void xBRZ_Render(const uint32_t* renderBuf, uint32_t* xbrzBuf, const Bit16u *changedLines, const int srcWidth, const int srcHeight, int scalingFactor) { if (changedLines) // perf: in worst case similar to full input scaling { concurrency::task_group tg; // perf: task_group is slightly faster than pure prallel_for int yLast = 0; Bitu y = 0, index = 0; while (y < sdl.draw.height) { if (!(index & 1)) y += changedLines[index]; else { const int sliceFirst = y; const int sliceLast = y + changedLines[index]; y += changedLines[index]; int yFirst = max(yLast, sliceFirst - 2); // we need to update two adjacent lines as well since they are analyzed by xBRZ! yLast = min(srcHeight, sliceLast + 2); // (and make sure to not overlap with last slice!) for (int i = yFirst; i < yLast; i += sdl.xBRZ.task_granularity) { const int iLast = min(i + sdl.xBRZ.task_granularity, yLast); tg.run([=] { xbrz::scale(scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), i, iLast); }); } } index++; } tg.wait(); } else // process complete input image { concurrency::task_group tg; for (int i = 0; i < srcHeight; i += sdl.xBRZ.task_granularity) tg.run([=] { const int iLast = min(i + sdl.xBRZ.task_granularity, srcHeight); xbrz::scale(scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), i, iLast); }); tg.wait(); } } #endif void GFX_EndUpdate( const Bit16u *changedLines ) { /* don't present our output if 3Dfx is in OpenGL mode */ if (sdl.desktop.prevent_fullscreen) return; #if (HAVE_D3D9_H) && defined(WIN32) if (d3d && d3d->getForceUpdate()); // continue else #endif if (!sdl.updating) return; sdl.updating=false; switch (sdl.desktop.type) { case SCREEN_SURFACE: #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); #endif #if C_XBRZ if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { const int srcWidth = sdl.draw.width; const int srcHeight = sdl.draw.height; if (sdl.xBRZ.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0) { // we assume render buffer is *not* scaled! const int outputHeight = sdl.surface->h; const int outputWidth = sdl.surface->w; int clipWidth = outputWidth; int clipHeight = outputHeight; int clipX = 0; int clipY = 0; if (render.aspect) { if (outputWidth > sdl.srcAspect.xToY * outputHeight) // output broader than input => black bars left and right { clipWidth = static_cast(outputHeight * sdl.srcAspect.xToY); clipX = (outputWidth - clipWidth) / 2; } else // black bars top and bottom { clipHeight = static_cast(outputWidth * sdl.srcAspect.yToX); clipY = (outputHeight - clipHeight) / 2; } } // 1. xBRZ-scale render buffer into xbrz pixel buffer int xbrzWidth = 0; int xbrzHeight = 0; uint32_t* xbrzBuf; xbrzWidth = srcWidth * sdl.xBRZ.scale_factor; xbrzHeight = srcHeight * sdl.xBRZ.scale_factor; sdl.xBRZ.pixbuf.resize(xbrzWidth * xbrzHeight); const uint32_t* renderBuf = &sdl.xBRZ.renderbuf[0]; // help VS compiler a little + support capture by value xbrzBuf = &sdl.xBRZ.pixbuf[0]; xBRZ_Render(renderBuf, xbrzBuf, changedLines, srcWidth, srcHeight, sdl.xBRZ.scale_factor); // 2. nearest neighbor/bilinear scale xbrz buffer into output surface clipping area const bool mustLock = SDL_MUSTLOCK(sdl.surface); if (mustLock) SDL_LockSurface(sdl.surface); if (sdl.surface->pixels) // if locking fails, this can be nullptr, also check if we really need to draw { uint32_t* clipTrg = reinterpret_cast(static_cast(sdl.surface->pixels) + clipY * sdl.surface->pitch + clipX * sizeof(uint32_t)); if (sdl.xBRZ.postscale_bilinear) { concurrency::task_group tg; for (int i = 0; i < clipHeight; i += sdl.xBRZ.task_granularity) tg.run([=] { const int iLast = min(i + sdl.xBRZ.task_granularity, clipHeight); xbrz::bilinearScale(&xbrzBuf[0], // const uint32_t* src, xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), // int srcWidth, int srcHeight, int srcPitch, clipTrg, //PixTrg* trg, clipWidth, clipHeight, sdl.surface->pitch, // int trgWidth, int trgHeight, int trgPitch, i, iLast, // int yFirst, int yLast, [](uint32_t pix) { return pix; }); // PixConverter pixCvrt }); tg.wait(); } else { concurrency::task_group tg; for (int i = 0; i < clipHeight; i += sdl.xBRZ.task_granularity) tg.run([=] { const int iLast = min(i + sdl.xBRZ.task_granularity, clipHeight); xbrz::nearestNeighborScale(&xbrzBuf[0], xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), clipTrg, clipWidth, clipHeight, sdl.surface->pitch, i, iLast, [](uint32_t pix) { return pix; }); // perf: going over target is by factor 4 faster than going over source for similar image sizes }); tg.wait(); } } if (mustLock) SDL_UnlockSurface(sdl.surface); if (!menu.hidecycles && !sdl.desktop.fullscreen) frames++; #if defined(C_SDL2) SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 1); #else SDL_Flip(sdl.surface); #endif } } else #endif /*C_XBRZ*/ { if (SDL_MUSTLOCK(sdl.surface)) { if (sdl.blit.surface) { SDL_UnlockSurface(sdl.blit.surface); int Blit = SDL_BlitSurface(sdl.blit.surface, 0, sdl.surface, &sdl.clip); LOG(LOG_MISC, LOG_WARN)("BlitSurface returned %d", Blit); } else { SDL_UnlockSurface(sdl.surface); } if (changedLines && (changedLines[0] == sdl.draw.height)) return; if (!menu.hidecycles && !sdl.desktop.fullscreen) frames++; #if !defined(C_SDL2) SDL_Flip(sdl.surface); #endif } else if (sdl.must_redraw_all) { #if !defined(C_SDL2) if (changedLines != NULL) SDL_Flip(sdl.surface); #endif } else if (changedLines) { if (changedLines[0] == sdl.draw.height) return; if (!menu.hidecycles && !sdl.desktop.fullscreen) frames++; Bitu y = 0, index = 0, rectCount = 0; while (y < sdl.draw.height) { if (!(index & 1)) { y += changedLines[index]; } else { SDL_Rect *rect = &sdl.updateRects[rectCount++]; rect->x = sdl.clip.x; rect->y = sdl.clip.y + y; rect->w = (Bit16u)sdl.draw.width; rect->h = changedLines[index]; y += changedLines[index]; SDL_rect_cliptoscreen(*rect); } index++; } if (rectCount) { #if defined(C_SDL2) SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, rectCount); #else SDL_UpdateRects(sdl.surface, rectCount, sdl.updateRects); #endif } } } break; #if C_OPENGL case SCREEN_OPENGL: if (sdl.must_redraw_all && changedLines == NULL) { } else { if (gl_clear_countdown > 0) { gl_clear_countdown--; glClearColor (0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); } if (gl_menudraw_countdown > 0) { gl_menudraw_countdown--; #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); #endif } #if C_XBRZ if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { // OpenGL pixel buffer is precreated for direct xBRZ output, while xBRZ render buffer is used for rendering const int srcWidth = sdl.draw.width; const int srcHeight = sdl.draw.height; if (sdl.xBRZ.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0) { // we assume render buffer is *not* scaled! const uint32_t* renderBuf = &sdl.xBRZ.renderbuf[0]; // help VS compiler a little + support capture by value uint32_t* trgTex; if (sdl.opengl.pixel_buffer_object) { glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer); trgTex = (uint32_t *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); } else trgTex = reinterpret_cast(static_cast(sdl.opengl.framebuf)); if (trgTex) xBRZ_Render(renderBuf, trgTex, changedLines, srcWidth, srcHeight, sdl.xBRZ.scale_factor); } // and here we go repeating some stuff with xBRZ related modifications if (sdl.opengl.pixel_buffer_object) { glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT); glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sdl.draw.width * sdl.xBRZ.scale_factor, sdl.draw.height * sdl.xBRZ.scale_factor, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, 0); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); } else { glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sdl.draw.width * sdl.xBRZ.scale_factor, sdl.draw.height * sdl.xBRZ.scale_factor, GL_BGRA_EXT, #if defined (MACOSX) // needed for proper looking graphics on macOS 10.12, 10.13 GL_UNSIGNED_INT_8_8_8_8, #else // works on Linux GL_UNSIGNED_INT_8_8_8_8_REV, #endif (Bit8u *)sdl.opengl.framebuf); } glCallList(sdl.opengl.displaylist); SDL_GL_SwapBuffers(); } else #endif /*C_XBRZ*/ if (sdl.opengl.pixel_buffer_object) { if(changedLines && (changedLines[0] == sdl.draw.height)) return; glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT); glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (int)sdl.draw.width, (int)sdl.draw.height, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, (void*)0); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); glCallList(sdl.opengl.displaylist); SDL_GL_SwapBuffers(); } else if (changedLines) { if(changedLines[0] == sdl.draw.height) return; Bitu y = 0, index = 0; glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); while (y < sdl.draw.height) { if (!(index & 1)) { y += changedLines[index]; } else { Bit8u *pixels = (Bit8u *)sdl.opengl.framebuf + y * sdl.opengl.pitch; Bitu height = changedLines[index]; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, (int)y, (int)sdl.draw.width, (int)height, GL_BGRA_EXT, #if defined (MACOSX) // needed for proper looking graphics on macOS 10.12, 10.13 GL_UNSIGNED_INT_8_8_8_8, #else // works on Linux GL_UNSIGNED_INT_8_8_8_8_REV, #endif (void*)pixels ); y += height; } index++; } glCallList(sdl.opengl.displaylist); #if 0 /* DEBUG Prove to me that you're drawing the damn texture */ glBindTexture(GL_TEXTURE_2D,SDLDrawGenFontTexture); glPushMatrix(); glMatrixMode (GL_TEXTURE); glLoadIdentity (); glScaled(1.0 / SDLDrawGenFontTextureWidth, 1.0 / SDLDrawGenFontTextureHeight, 1.0); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_ALPHA_TEST); glEnable(GL_BLEND); glBegin(GL_QUADS); // lower left glTexCoord2i(0, 0 ); glVertex2i( 0, 0 ); // lower right glTexCoord2i(SDLDrawGenFontTextureWidth,0 ); glVertex2i( SDLDrawGenFontTextureWidth,0 ); // upper right glTexCoord2i(SDLDrawGenFontTextureWidth,SDLDrawGenFontTextureHeight); glVertex2i( SDLDrawGenFontTextureWidth,SDLDrawGenFontTextureHeight); // upper left glTexCoord2i(0, SDLDrawGenFontTextureHeight); glVertex2i( 0, SDLDrawGenFontTextureHeight); glEnd(); glBlendFunc(GL_ONE, GL_ZERO); glDisable(GL_ALPHA_TEST); glEnable(GL_TEXTURE_2D); glPopMatrix(); glBindTexture(GL_TEXTURE_2D,sdl.opengl.texture); #endif SDL_GL_SwapBuffers(); } if(!menu.hidecycles && !sdl.desktop.fullscreen) frames++; } break; #endif #if (HAVE_D3D9_H) && defined(WIN32) case SCREEN_DIRECT3D: #if C_XBRZ if (sdl.xBRZ.enable && sdl.xBRZ.scale_on) { // we have xBRZ pseudo render buffer to be output to the pre-sized texture, do the xBRZ part const int srcWidth = sdl.draw.width; const int srcHeight = sdl.draw.height; if (sdl.xBRZ.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0) { // we assume render buffer is *not* scaled! int xbrzWidth = srcWidth * sdl.xBRZ.scale_factor; int xbrzHeight = srcHeight * sdl.xBRZ.scale_factor; sdl.xBRZ.pixbuf.resize(xbrzWidth * xbrzHeight); const uint32_t* renderBuf = &sdl.xBRZ.renderbuf[0]; // help VS compiler a little + support capture by value uint32_t* xbrzBuf = &sdl.xBRZ.pixbuf[0]; xBRZ_Render(renderBuf, xbrzBuf, changedLines, srcWidth, srcHeight, sdl.xBRZ.scale_factor); // now copy xBRZ buffer to the texture, adjusting for texture pitch Bit8u *tgtPix; Bitu tgtPitch; if (d3d->LockTexture(tgtPix, tgtPitch) && tgtPix) // if locking fails, target texture can be nullptr { uint32_t* tgtTex = reinterpret_cast(static_cast(tgtPix)); concurrency::task_group tg; for (int i = 0; i < xbrzHeight; i += sdl.xBRZ.task_granularity) tg.run([=] { const int iLast = min(i + sdl.xBRZ.task_granularity, xbrzHeight); xbrz::pitchChange(&xbrzBuf[0], &tgtTex[0], xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), tgtPitch, i, iLast, [](uint32_t pix) { return pix; }); }); tg.wait(); } } } #endif if(!menu.hidecycles) frames++; //implemented if(GCC_UNLIKELY(!d3d->UnlockTexture(changedLines))) { E_Exit("Failed to draw screen!"); } break; #endif default: break; } if (changedLines != NULL) { sdl.must_redraw_all = false; #if !defined(C_SDL2) sdl.surface->flags &= ~((unsigned int)SDL_HAX_NOREFRESH); #endif if (changedLines != NULL && sdl.deferred_resize) { sdl.deferred_resize = false; #if defined(C_SDL2) #else void GFX_RedrawScreen(Bit32u nWidth, Bit32u nHeight); GFX_RedrawScreen(sdl.draw.width, sdl.draw.height); #endif } else if (sdl.gfx_force_redraw_count > 0) { void RENDER_CallBack( GFX_CallBackFunctions_t function ); RENDER_CallBack(GFX_CallBackRedraw); sdl.gfx_force_redraw_count--; } } } void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) { (void)start; (void)count; (void)entries; #if !defined(C_SDL2) /* I should probably not change the GFX_PalEntry :) */ if (sdl.surface->flags & SDL_HWPALETTE) { if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,start,count)) { E_Exit("SDL:Can't set palette"); } } else { if (!SDL_SetPalette(sdl.surface,SDL_LOGPAL,(SDL_Color *)entries,start,count)) { E_Exit("SDL:Can't set palette"); } } #endif } Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue) { switch (sdl.desktop.type) { case SCREEN_SURFACE: return SDL_MapRGB(sdl.surface->format,red,green,blue); case SCREEN_OPENGL: return (((unsigned int)blue << 0u) | ((unsigned int)green << 8u) | ((unsigned int)red << 16u)) | (255u << 24u); case SCREEN_DIRECT3D: return SDL_MapRGB(sdl.surface->format,red,green,blue); default: break; } return 0; } void GFX_Stop() { if (sdl.updating) GFX_EndUpdate( 0 ); sdl.active=false; } void GFX_Start() { sdl.active=true; } static void GUI_ShutDown(Section * /*sec*/) { GFX_Stop(); if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop ); if (sdl.mouse.locked) GFX_CaptureMouse(); if (sdl.desktop.fullscreen) GFX_SwitchFullScreen(); #if (HAVE_D3D9_H) && defined(WIN32) if ((sdl.desktop.type==SCREEN_DIRECT3D) && (d3d)) delete d3d; #endif } static void SetPriority(PRIORITY_LEVELS level) { #if C_SET_PRIORITY // Do nothing if priorties are not the same and not root, else the highest // priority can not be set as users can only lower priority (not restore it) if((sdl.priority.focus != sdl.priority.nofocus ) && (getuid()!=0) ) return; #endif switch (level) { #ifdef WIN32 case PRIORITY_LEVEL_PAUSE: // if DOSBox is paused, assume idle priority case PRIORITY_LEVEL_LOWEST: SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS); break; case PRIORITY_LEVEL_LOWER: SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS); break; case PRIORITY_LEVEL_NORMAL: SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS); break; case PRIORITY_LEVEL_HIGHER: SetPriorityClass(GetCurrentProcess(),ABOVE_NORMAL_PRIORITY_CLASS); break; case PRIORITY_LEVEL_HIGHEST: SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS); break; #elif C_SET_PRIORITY /* Linux use group as dosbox has mulitple threads under linux */ case PRIORITY_LEVEL_PAUSE: // if DOSBox is paused, assume idle priority case PRIORITY_LEVEL_LOWEST: setpriority (PRIO_PGRP, 0,PRIO_MAX); break; case PRIORITY_LEVEL_LOWER: setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/3)); break; case PRIORITY_LEVEL_NORMAL: setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/2)); break; case PRIORITY_LEVEL_HIGHER: setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/5) ); break; case PRIORITY_LEVEL_HIGHEST: setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/4) ); break; #endif default: break; } } extern Bit8u int10_font_14[256 * 14]; static void OutputString(Bitu x,Bitu y,const char * text,Bit32u color,Bit32u color2,SDL_Surface * output_surface) { Bit32u * draw=(Bit32u*)(((Bit8u *)output_surface->pixels)+((y)*output_surface->pitch))+x; while (*text) { Bit8u * font=&int10_font_14[(*text)*14]; Bitu i,j; Bit32u * draw_line=draw; for (i=0;i<14;i++) { Bit8u map=*font++; for (j=0;j<8;j++) { if (map & 0x80) *((Bit32u*)(draw_line+j))=color; else *((Bit32u*)(draw_line+j))=color2; map<<=1; } draw_line+=output_surface->pitch/4; } text++; draw+=8; } } #if (HAVE_D3D9_H) && defined(WIN32) # include "SDL_syswm.h" #endif #if (HAVE_D3D9_H) && defined(WIN32) static void D3D_reconfigure() { # if (C_D3DSHADERS) if (d3d) { Section_prop *section=static_cast(control->GetSection("sdl")); Prop_multival* prop = section->Get_multival("pixelshader"); if(SUCCEEDED(d3d->LoadPixelShader(prop->GetSection()->Get_string("type"), 0, 0))) { GFX_ResetScreen(); } } # endif } #endif void ResetSystem(bool pressed) { if (!pressed) return; throw int(3); } bool has_GUI_StartUp = false; static void GUI_StartUp() { DOSBoxMenu::item *item; if (has_GUI_StartUp) return; has_GUI_StartUp = true; LOG(LOG_GUI,LOG_DEBUG)("Starting GUI"); #if defined(C_SDL2) LOG(LOG_GUI,LOG_DEBUG)("This version compiled against SDL 2.x"); #else LOG(LOG_GUI,LOG_DEBUG)("This version compiled against SDL 1.x"); #endif AddExitFunction(AddExitFunctionFuncPair(GUI_ShutDown)); #if !defined(C_SDL2) GUI_LoadFonts(); #endif sdl.active=false; sdl.updating=false; #if defined(C_SDL2) sdl.update_window=true; #endif GFX_SetIcon(); sdl.desktop.lazy_fullscreen=false; sdl.desktop.lazy_fullscreen_req=false; sdl.desktop.prevent_fullscreen=false; Section_prop * section=static_cast(control->GetSection("sdl")); assert(section != NULL); sdl.desktop.fullscreen=section->Get_bool("fullscreen"); sdl.wait_on_error=section->Get_bool("waitonerror"); Prop_multival* p=section->Get_multival("priority"); std::string focus = p->GetSection()->Get_string("active"); std::string notfocus = p->GetSection()->Get_string("inactive"); if (focus == "lowest") { sdl.priority.focus = PRIORITY_LEVEL_LOWEST; } else if (focus == "lower") { sdl.priority.focus = PRIORITY_LEVEL_LOWER; } else if (focus == "normal") { sdl.priority.focus = PRIORITY_LEVEL_NORMAL; } else if (focus == "higher") { sdl.priority.focus = PRIORITY_LEVEL_HIGHER; } else if (focus == "highest") { sdl.priority.focus = PRIORITY_LEVEL_HIGHEST; } if (notfocus == "lowest") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWEST; } else if (notfocus == "lower") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWER; } else if (notfocus == "normal") { sdl.priority.nofocus=PRIORITY_LEVEL_NORMAL; } else if (notfocus == "higher") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHER; } else if (notfocus == "highest") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHEST; } else if (notfocus == "pause") { /* we only check for pause here, because it makes no sense * for DOSBox to be paused while it has focus */ sdl.priority.nofocus=PRIORITY_LEVEL_PAUSE; } SetPriority(sdl.priority.focus); //Assume focus on startup sdl.mouse.locked=false; mouselocked=false; //Global for mapper sdl.mouse.requestlock=false; sdl.desktop.full.fixed=false; const char* fullresolution=section->Get_string("fullresolution"); sdl.desktop.full.width = 0; sdl.desktop.full.height = 0; if(fullresolution && *fullresolution) { char res[100]; strncpy( res, fullresolution, sizeof( res )); fullresolution = lowcase (res);//so x and X are allowed if (strcmp(fullresolution,"original")) { sdl.desktop.full.fixed = true; if (strcmp(fullresolution,"desktop")) { //desktop = 0x0 char* height = const_cast(strchr(fullresolution,'x')); if (height && * height) { *height = 0; sdl.desktop.full.height = (Bit16u)atoi(height+1); sdl.desktop.full.width = (Bit16u)atoi(res); } } } } sdl.desktop.window.width = 0; sdl.desktop.window.height = 0; const char* windowresolution=section->Get_string("windowresolution"); if(windowresolution && *windowresolution) { char res[100]; strncpy( res,windowresolution, sizeof( res )); windowresolution = lowcase (res);//so x and X are allowed if(strcmp(windowresolution,"original")) { char* height = const_cast(strchr(windowresolution,'x')); if(height && *height) { *height = 0; sdl.desktop.window.height = (Bit16u)atoi(height+1); sdl.desktop.window.width = (Bit16u)atoi(res); } } } sdl.desktop.doublebuf=section->Get_bool("fulldouble"); #if !defined(C_SDL2) #if SDL_VERSION_ATLEAST(1, 2, 10) if (!sdl.desktop.full.width || !sdl.desktop.full.height){ //Can only be done on the very first call! Not restartable. const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo(); if (vidinfo) { sdl.desktop.full.width = vidinfo->current_w; sdl.desktop.full.height = vidinfo->current_h; } } #endif #endif int width=1024;// int height=768; if (!sdl.desktop.full.width) { sdl.desktop.full.width=width; } if (!sdl.desktop.full.height) { sdl.desktop.full.height=width; } sdl.mouse.autoenable=section->Get_bool("autolock"); if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE); sdl.mouse.autolock=false; const std::string feedback = section->Get_string("autolock_feedback"); if (feedback == "none") sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_NONE; else if (feedback == "beep") sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_BEEP; else if (feedback == "flash") sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_FLASH; sdl.mouse.sensitivity=(unsigned int)section->Get_int("sensitivity"); std::string output=section->Get_string("output"); /* Setup Mouse correctly if fullscreen */ if(sdl.desktop.fullscreen) GFX_CaptureMouse(); // pre-set render aspect offload to false, altered by using xBRZ scaler or Direct3D/OpenGL modes that do aspect correction themselves and don't need render code to mess with it render.aspectOffload = false; #if C_XBRZ // yes, we read render section settings here, because xBRZ is integrated here but has settings in "render" { Section_prop * r_section = static_cast(control->GetSection("render")); Prop_multival* r_prop = r_section->Get_multival("scaler"); std::string r_scaler = r_prop->GetSection()->Get_string("type"); sdl.xBRZ.enable = ((r_scaler == "xbrz") || (r_scaler == "xbrz_bilinear")); sdl.xBRZ.postscale_bilinear = (r_scaler == "xbrz_bilinear"); sdl.xBRZ.task_granularity = r_section->Get_int("xbrz slice"); sdl.xBRZ.fixed_scale_factor = r_section->Get_int("xbrz fixed scale factor"); sdl.xBRZ.max_scale_factor = r_section->Get_int("xbrz max scale factor"); if ((sdl.xBRZ.max_scale_factor < 2) || (sdl.xBRZ.max_scale_factor > xbrz::SCALE_FACTOR_MAX)) sdl.xBRZ.max_scale_factor = xbrz::SCALE_FACTOR_MAX; if ((sdl.xBRZ.fixed_scale_factor < 2) || (sdl.xBRZ.fixed_scale_factor > xbrz::SCALE_FACTOR_MAX)) sdl.xBRZ.fixed_scale_factor = 0; } if (sdl.xBRZ.enable) { // xBRZ requirements if ((output != "surface") && (output != "direct3d") && (output != "opengl") && (output != "openglhq") && (output != "openglnb")) output = "surface"; render.aspectOffload = true; // render aspect ratio correction voids xBRZ algorithm, so scaler does aspect correction itself (and resorts to render code when no scaling is applied) } #endif // output type selection // "overlay" was removed, pre-map to Direct3D or OpenGL or surface if (output == "overlay") { #if (HAVE_D3D9_H) && defined(WIN32) output = "direct3d"; #elif C_OPENGL output = "opengl"; #else output = "surface"; #endif } if (output == "surface") { sdl.desktop.want_type=SCREEN_SURFACE; } else if (output == "ddraw") { sdl.desktop.want_type=SCREEN_SURFACE; #if C_OPENGL } else if (output == "opengl" || output == "openglhq") { sdl.desktop.want_type=SCREEN_OPENGL; sdl.opengl.bilinear = true; render.aspectOffload = true; // OpenGL code does aspect correction itself, we don't need render thread to do it } else if (output == "openglnb") { sdl.desktop.want_type=SCREEN_OPENGL; sdl.opengl.bilinear = false; render.aspectOffload = true; // OpenGL code does aspect correction itself, we don't need render thread to do it #endif #if (HAVE_D3D9_H) && defined(WIN32) } else if (output == "direct3d") { sdl.desktop.want_type=SCREEN_DIRECT3D; render.aspectOffload = true; // Direct3D code does aspect correction itself, we don't need render thread to do it #if LOG_D3D LOG_MSG("SDL:Direct3D activated"); #endif #endif } else { LOG_MSG("SDL:Unsupported output device %s, switching back to surface",output.c_str()); sdl.desktop.want_type=SCREEN_SURFACE; // SHOULDN'T BE POSSIBLE anymore } sdl.overscan_width=(unsigned int)section->Get_int("overscan"); // sdl.overscan_color=section->Get_int("overscancolor"); #if defined(C_SDL2) /* Initialize screen for first time */ if (!GFX_SetSDLSurfaceWindow(640,400)) E_Exit("Could not initialize video: %s",SDL_GetError()); sdl.surface = SDL_GetWindowSurface(sdl.window); // SDL_Rect splash_rect=GFX_GetSDLSurfaceSubwindowDims(640,400); sdl.desktop.pixelFormat = SDL_GetWindowPixelFormat(sdl.window); LOG_MSG("SDL:Current window pixel format: %s", SDL_GetPixelFormatName(sdl.desktop.pixelFormat)); sdl.desktop.bpp=8*SDL_BYTESPERPIXEL(sdl.desktop.pixelFormat); if (SDL_BITSPERPIXEL(sdl.desktop.pixelFormat) == 24) { LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!"); } #else /* Initialize screen for first time */ sdl.surface=SDL_SetVideoMode(640,400,0,SDL_RESIZABLE); if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError()); sdl.deferred_resize = false; sdl.must_redraw_all = true; sdl.desktop.bpp=sdl.surface->format->BitsPerPixel; if (sdl.desktop.bpp==24) { LOG_MSG("SDL:You are running in 24 bpp mode, this will slow down things!"); } #endif #if (HAVE_D3D9_H) && defined(WIN32) if(sdl.desktop.want_type==SCREEN_DIRECT3D) { SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); if(!SDL_GetWMInfo(&wmi)) { LOG_MSG("SDL:Error retrieving window information"); LOG_MSG("Failed to get window info"); sdl.desktop.want_type=SCREEN_SURFACE; } else { if(d3d) delete d3d; d3d = new CDirect3D(640,400); if(!d3d) { LOG_MSG("Failed to create d3d object"); sdl.desktop.want_type=SCREEN_SURFACE; } else if(d3d->InitializeDX(wmi.child_window,sdl.desktop.doublebuf) != S_OK) { LOG_MSG("Unable to initialize DirectX"); sdl.desktop.want_type=SCREEN_SURFACE; } } } #endif GFX_LogSDLState(); GFX_Stop(); #if defined(C_SDL2) SDL_SetWindowTitle(sdl.window,"DOSBox"); #else SDL_WM_SetCaption("DOSBox",VERSION); #endif /* Please leave the Splash screen stuff in working order in DOSBox. We spend a lot of time making DOSBox. */ //ShowSplashScreen(); /* I will keep the splash screen alive. But now, the BIOS will do it --J.C. */ /* Get some Event handlers */ #if defined(__WIN32__) && !defined(C_SDL2) MAPPER_AddHandler(ToggleMenu,MK_return,MMOD1|MMOD2,"togglemenu","ToggleMenu"); #endif // WIN32 MAPPER_AddHandler(ResetSystem, MK_r, MMODHOST, "reset", "Reset", &item); /* Host+R (Host+CTRL+R acts funny on my Linux system) */ item->set_text("Reset guest system"); MAPPER_AddHandler(KillSwitch,MK_f9,MMOD1,"shutdown","ShutDown", &item); /* KEEP: Most DOSBox-X users may have muscle memory for this */ item->set_text("Quit"); MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Cap Mouse", &item); /* KEEP: Most DOSBox-X users may have muscle memory for this */ item->set_text("Capture mouse"); MAPPER_AddHandler(SwitchFullScreen,MK_f,MMODHOST,"fullscr","Fullscreen", &item); item->set_text("Toggle fullscreen"); MAPPER_AddHandler(Restart,MK_nothing,0,"restart","Restart", &item); /* This is less useful, and now has no default binding */ item->set_text("Restart DOSBox-X"); void PasteClipboard(bool bPressed); // emendelson from dbDOS adds MMOD2 to this for Ctrl-Alt-F5 for PasteClipboard MAPPER_AddHandler(PasteClipboard, MK_nothing, 0, "paste", "Paste Clipboard"); //end emendelson #if C_DEBUG /* Pause binds with activate-debugger */ MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD1, "pause", "Pause"); #else MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause"); #endif #if !defined(C_SDL2) MAPPER_AddHandler(&GUI_Run, MK_nothing, 0, "gui", "ShowGUI", &item); item->set_text("Configuration GUI"); MAPPER_AddHandler(&GUI_ResetResize, MK_nothing, 0, "resetsize", "ResetSize", &item); item->set_text("Reset window size"); #endif /* Get Keyboard state of numlock and capslock */ #if defined(C_SDL2) SDL_Keymod keystate = SDL_GetModState(); #else SDLMod keystate = SDL_GetModState(); #endif if(keystate&KMOD_NUM) startup_state_numlock = true; if(keystate&KMOD_CAPS) startup_state_capslock = true; UpdateWindowDimensions(); } void Mouse_AutoLock(bool enable) { sdl.mouse.autolock=enable; if (sdl.mouse.autoenable) sdl.mouse.requestlock=enable; else { SDL_ShowCursor(enable?SDL_DISABLE:SDL_ENABLE); sdl.mouse.requestlock=false; } } static void RedrawScreen(Bit32u nWidth, Bit32u nHeight) { (void)nWidth;//UNUSED (void)nHeight;//UNUSED // int width; // int height; #ifdef __WIN32__ // width=sdl.clip.w; // height=sdl.clip.h; #else // width=sdl.draw.width; // height=sdl.draw.height; #endif void RENDER_CallBack( GFX_CallBackFunctions_t function ); #if 0 while (sdl.desktop.fullscreen) { int temp_size; temp_size=render.scale.size; if(!sdl.desktop.fullscreen) { render.scale.size=temp_size; RENDER_CallBack( GFX_CallBackReset); return; } } #endif #ifdef WIN32 if(menu.resizeusing) { RENDER_CallBack( GFX_CallBackReset); return; } #endif #if 0 /* FIXME: This code misbehaves when doublescan=false on Linux/X11 */ if((Bitu)nWidth == (Bitu)width && (Bitu)nHeight == (Bitu)height) { RENDER_CallBack( GFX_CallBackReset); return; } Section_prop * section=static_cast(control->GetSection("sdl")); if ((!strcmp(section->Get_string("windowresolution"),"original") || (!strcmp(section->Get_string("windowresolution"),"desktop"))) && (render.src.dblw && render.src.dblh)) { switch (render.scale.op) { case scalerOpNormal: if(!render.scale.hardware) { if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) { if (render.scale.size <= 4 && render.scale.size >=1) ++render.scale.size; break; } else { if (render.scale.size <= 5 && render.scale.size >= 2) --render.scale.size; break; } } else { if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) { if (render.scale.size == 1) { render.scale.size=4; break; } if (render.scale.size == 4) { render.scale.size=6; break; } if (render.scale.size == 6) { render.scale.size=8; break; } if (render.scale.size == 8) { render.scale.size=10; break; } } if((Bitu)nWidth<(Bitu)width || (Bitu)nHeight<(Bitu)height) { if (render.scale.size == 10) { render.scale.size=8; break; } if (render.scale.size == 8) { render.scale.size=6; break; } if (render.scale.size == 6) { render.scale.size=4; break; } if (render.scale.size == 4) { render.scale.size=1; break; } } } break; case scalerOpAdvMame: case scalerOpHQ: case scalerOpAdvInterp: case scalerOpTV: case scalerOpRGB: case scalerOpScan: if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) { if (render.scale.size == 2) ++render.scale.size; } if((Bitu)nWidth<(Bitu)width || (Bitu)nHeight<(Bitu)height) { if (render.scale.size == 3) --render.scale.size; } break; case scalerOpSaI: case scalerOpSuperSaI: case scalerOpSuperEagle: default: // other scalers break; } } #endif RENDER_CallBack( GFX_CallBackReset); } void GFX_RedrawScreen(Bit32u nWidth, Bit32u nHeight) { RedrawScreen(nWidth, nHeight); } #if defined(C_SDL2) void GFX_HandleVideoResize(int width, int height) { /* Maybe a screen rotation has just occurred, so we simply resize. There may be a different cause for a forced resized, though. */ if (sdl.desktop.full.display_res && IsFullscreen()) { /* Note: We should not use GFX_ObtainDisplayDimensions (SDL_GetDisplayBounds) on Android after a screen rotation: The older values from application startup are returned. */ sdl.desktop.full.width = width; sdl.desktop.full.height = height; } /* Even if the new window's dimensions are actually the desired ones * we may still need to re-obtain a new window surface or do * a different thing. So we basically call GFX_SetSize, but without * touching the window itself (or else we may end in an infinite loop). * * Furthermore, if the new dimensions are *not* the desired ones, we * don't fight it. Rather than attempting to resize it back, we simply * keep the window as-is and disable screen updates. This is done * in SDL_SetSDLWindowSurface by setting sdl.update_display_contents * to false. */ sdl.update_window = false; GFX_ResetScreen(); sdl.update_window = true; } #else bool GFX_MustActOnResize() { if (!GFX_IsFullscreen()) return false; return true; } static void HandleVideoResize(void * event) { if(sdl.desktop.fullscreen) return; /* don't act on resize events if we made the window non-resizeable. * especially if 3Dfx voodoo emulation is active. */ if (!(sdl.surface->flags & SDL_RESIZABLE)) return; /* don't act if 3Dfx OpenGL emulation is active */ if (GFX_GetPreventFullscreen()) return; SDL_ResizeEvent* ResizeEvent = (SDL_ResizeEvent*)event; /* assume the resize comes from user preference UNLESS the window * is fullscreen or maximized */ if (!menu.maxwindow && !sdl.desktop.fullscreen && !sdl.init_ignore && NonUserResizeCounter == 0 && !window_was_maximized) { UpdateWindowDimensions(); UpdateWindowDimensions((unsigned int)ResizeEvent->w, (unsigned int)ResizeEvent->h); /* if the dimensions actually changed from our surface dimensions, then assume it's the user's input. Linux/X11 is good at doing this anyway, but the Windows SDL 1.x support will return us a resize event for the window size change resulting from SDL mode set. */ if (ResizeEvent->w != sdl.surface->w || ResizeEvent->h != sdl.surface->h) { userResizeWindowWidth = (unsigned int)ResizeEvent->w; userResizeWindowHeight = (unsigned int)ResizeEvent->h; } } else { UpdateWindowDimensions(); } window_was_maximized = menu.maxwindow; if (NonUserResizeCounter > 0) NonUserResizeCounter--; if (sdl.updating && !GFX_MustActOnResize()) { /* act on resize when updating is complete */ sdl.deferred_resize = true; } else { sdl.deferred_resize = false; RedrawScreen((unsigned int)ResizeEvent->w, (unsigned int)ResizeEvent->h); } /* if(sdl.desktop.want_type!=SCREEN_DIRECT3D) { HWND hwnd=GetHWND(); RECT myrect; GetClientRect(hwnd,&myrect); if(myrect.right==GetSystemMetrics(SM_CXSCREEN)) GFX_SwitchFullScreen(); } */ #ifdef WIN32 menu.resizeusing=false; #endif } #endif extern unsigned int mouse_notify_mode; bool user_cursor_locked = false; bool user_cursor_synced = false; int user_cursor_x = 0,user_cursor_y = 0; int user_cursor_sw = 640,user_cursor_sh = 480; #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */ DOSBoxMenu::item_handle_t DOSBoxMenu::displaylist::itemFromPoint(DOSBoxMenu &menu,int x,int y) { for (auto &id : disp_list) { DOSBoxMenu::item &item = menu.get_item(id); if (x >= item.screenBox.x && y >= item.screenBox.y) { int sx = x - item.screenBox.x; int sy = y - item.screenBox.y; int adj = (this != &menu.display_list && item.get_type() == DOSBoxMenu::submenu_type_id) ? 2 : 0; if (sx < (item.screenBox.w+adj) && sy < item.screenBox.h) return id; } } return unassigned_item_handle; } void DOSBoxMenu::item::updateScreenFromItem(DOSBoxMenu &menu) { (void)menu;//UNUSED if (!OpenGL_using()) { SDL_Rect uprect = screenBox; SDL_rect_cliptoscreen(uprect); #if defined(C_SDL2) SDL_UpdateWindowSurfaceRects(sdl.window, &uprect, 1); #else SDL_UpdateRects( sdl.surface, 1, &uprect ); #endif } } void DOSBoxMenu::item::updateScreenFromPopup(DOSBoxMenu &menu) { (void)menu;//UNUSED if (!OpenGL_using()) { SDL_Rect uprect = popupBox; uprect.w += DOSBoxMenu::dropshadowX; uprect.h += DOSBoxMenu::dropshadowY; SDL_rect_cliptoscreen(uprect); #if defined(C_SDL2) SDL_UpdateWindowSurfaceRects(sdl.window, &uprect, 1); #else SDL_UpdateRects( sdl.surface, 1, &uprect ); #endif } } void DOSBoxMenu::item::drawBackground(DOSBoxMenu &menu) { (void)menu;//UNUSED Bitu bordercolor = GFX_GetRGB(31, 31, 31); Bitu bgcolor = GFX_GetRGB(63, 63, 63); if (popupBox.w <= 1 || popupBox.h <= 1) return; MenuDrawRect(popupBox.x, popupBox.y, popupBox.w, popupBox.h, bgcolor); if (borderTop) MenuDrawRect(popupBox.x, popupBox.y, popupBox.w, 1, bordercolor); MenuDrawRect(popupBox.x, popupBox.y + popupBox.h - 1, popupBox.w, 1, bordercolor); MenuDrawRect(popupBox.x, popupBox.y, 1, popupBox.h, bordercolor); MenuDrawRect(popupBox.x + popupBox.w - 1, popupBox.y, 1, popupBox.h, bordercolor); if (type == DOSBoxMenu::submenu_type_id) { MenuShadeRect((int)popupBox.x + (int)popupBox.w, (int)popupBox.y + (int)DOSBoxMenu::dropshadowY, (int)DOSBoxMenu::dropshadowX, (int)popupBox.h); MenuShadeRect((int)popupBox.x + (int)DOSBoxMenu::dropshadowX, (int)popupBox.y + (int)popupBox.h, (int)popupBox.w - (int)DOSBoxMenu::dropshadowX, (int)DOSBoxMenu::dropshadowY); } } #endif #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */ void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id) { (void)menu;//UNUSED if (mainMenu.menuUserHoverAt != item_id) { if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) { mainMenu.get_item(mainMenu.menuUserHoverAt).setHover(mainMenu,false); mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu); mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu); } mainMenu.menuUserHoverAt = item_id; if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) { mainMenu.get_item(mainMenu.menuUserHoverAt).setHover(mainMenu,true); mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu); mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu); } if (OpenGL_using()) mainMenu.setRedraw(); } } void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id) { (void)menu;//UNUSED if (mainMenu.menuUserAttentionAt != item_id) { if (mainMenu.menuUserAttentionAt != DOSBoxMenu::unassigned_item_handle) { mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,false); mainMenu.get_item(mainMenu.menuUserAttentionAt).drawMenuItem(mainMenu); mainMenu.get_item(mainMenu.menuUserAttentionAt).updateScreenFromItem(mainMenu); } mainMenu.menuUserAttentionAt = item_id; if (mainMenu.menuUserAttentionAt != DOSBoxMenu::unassigned_item_handle) { mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,true); mainMenu.get_item(mainMenu.menuUserAttentionAt).drawMenuItem(mainMenu); mainMenu.get_item(mainMenu.menuUserAttentionAt).updateScreenFromItem(mainMenu); } if (OpenGL_using()) mainMenu.setRedraw(); } } #endif uint8_t Mouse_GetButtonState(void); static void HandleMouseMotion(SDL_MouseMotionEvent * motion) { #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */ if (!sdl.mouse.locked && !sdl.desktop.fullscreen && mainMenu.isVisible() && motion->y < mainMenu.menuBox.h && Mouse_GetButtonState() == 0) { GFX_SDLMenuTrackHover(mainMenu,mainMenu.display_list.itemFromPoint(mainMenu,motion->x,motion->y)); SDL_ShowCursor(SDL_ENABLE); if (OpenGL_using() && mainMenu.needsRedraw()) { #if C_OPENGL gl_menudraw_countdown = 2; // two GL buffers GFX_OpenGLRedrawScreen(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); SDL_GL_SwapBuffers(); #endif } return; } else { GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle); if (OpenGL_using() && mainMenu.needsRedraw()) { #if C_OPENGL gl_menudraw_countdown = 2; // two GL buffers GFX_OpenGLRedrawScreen(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); SDL_GL_SwapBuffers(); #endif } } #endif user_cursor_x = motion->x - sdl.clip.x; user_cursor_y = motion->y - sdl.clip.y; user_cursor_locked = sdl.mouse.locked; user_cursor_synced = !user_cursor_locked; user_cursor_sw = sdl.clip.w; user_cursor_sh = sdl.clip.h; auto xrel = static_cast(motion->xrel) * sdl.mouse.sensitivity / 100.0f; auto yrel = static_cast(motion->yrel) * sdl.mouse.sensitivity / 100.0f; auto x = static_cast(motion->x - sdl.clip.x) / (sdl.clip.w - 1) * sdl.mouse.sensitivity / 100.0f; auto y = static_cast(motion->y - sdl.clip.y) / (sdl.clip.h - 1) * sdl.mouse.sensitivity / 100.0f; auto emu = sdl.mouse.locked; if (mouse_notify_mode != 0) { /* for mouse integration driver */ xrel = yrel = x = y = 0.0f; emu = sdl.mouse.locked; const auto isdown = Mouse_GetButtonState() != 0; const auto inside = motion->x >= sdl.clip.x && motion->x < sdl.clip.x + sdl.clip.w && motion->y >= sdl.clip.y && motion->y < sdl.clip.y + sdl.clip.h; SDL_ShowCursor(isdown || inside ? SDL_DISABLE : SDL_ENABLE); /* TODO: If guest has not read mouse cursor position within 250ms show cursor again */ } bool MOUSE_IsHidden(); if (!user_cursor_locked) { /* Show only when DOS app is not using mouse */ SDL_ShowCursor(MOUSE_IsHidden() ? SDL_ENABLE : SDL_DISABLE); } Mouse_CursorMoved(xrel, yrel, x, y, emu); } #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */ void MenuFullScreenRedraw(void) { #if defined(C_SDL2) SDL_UpdateWindowSurface(sdl.window); #else SDL_Flip(sdl.surface); #endif } #if defined(C_SDL2) static const SDL_TouchID no_touch_id = (SDL_TouchID)(~0ULL); static const SDL_FingerID no_finger_id = (SDL_FingerID)(~0ULL); static SDL_FingerID touchscreen_finger_lock = no_finger_id; static SDL_TouchID touchscreen_touch_lock = no_touch_id; #endif static struct { unsigned char* bmp = NULL; unsigned int stride = 0,height = 0; } menuSavedScreen; void MenuSaveScreen(void) { if (!OpenGL_using()) { if (menuSavedScreen.bmp == NULL) { menuSavedScreen.height = (unsigned int)sdl.surface->h; menuSavedScreen.stride = (unsigned int)sdl.surface->pitch; menuSavedScreen.bmp = new unsigned char[menuSavedScreen.height * menuSavedScreen.stride]; } if (SDL_MUSTLOCK(sdl.surface)) SDL_LockSurface(sdl.surface); memcpy(menuSavedScreen.bmp, sdl.surface->pixels, menuSavedScreen.height * menuSavedScreen.stride); if (SDL_MUSTLOCK(sdl.surface)) SDL_UnlockSurface(sdl.surface); } } void MenuRestoreScreen(void) { if (!OpenGL_using()) { if (menuSavedScreen.bmp == NULL) return; if (SDL_MUSTLOCK(sdl.surface)) SDL_LockSurface(sdl.surface); memcpy(sdl.surface->pixels, menuSavedScreen.bmp, menuSavedScreen.height * menuSavedScreen.stride); if (SDL_MUSTLOCK(sdl.surface)) SDL_UnlockSurface(sdl.surface); } } void MenuFreeScreen(void) { if (menuSavedScreen.bmp == NULL) return; delete[] menuSavedScreen.bmp; menuSavedScreen.bmp = NULL; } #endif static void HandleMouseButton(SDL_MouseButtonEvent * button) { bool inMenu = false; #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */ if (!sdl.mouse.locked && !sdl.desktop.fullscreen && mainMenu.isVisible() && button->y < mainMenu.menuBox.h) { GFX_SDLMenuTrackHover(mainMenu,mainMenu.display_list.itemFromPoint(mainMenu,button->x,button->y)); inMenu = true; } else { GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle); } if (button->button == SDL_BUTTON_LEFT) { if (button->state == SDL_PRESSED) { GFX_SDLMenuTrackHilight(mainMenu,mainMenu.menuUserHoverAt); if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) { std::vector popup_stack; DOSBoxMenu::item_handle_t choice_item; DOSBoxMenu::item_handle_t psel_item; DOSBoxMenu::item_handle_t sel_item; bool button_holding=true; bool redrawAll=false; bool resized=false; bool runloop=true; SDL_Rect uprect; SDL_Event event; psel_item = DOSBoxMenu::unassigned_item_handle; choice_item = mainMenu.menuUserHoverAt = mainMenu.menuUserAttentionAt; popup_stack.push_back(mainMenu.menuUserAttentionAt); #if (HAVE_D3D9_H) && defined(WIN32) if (sdl.desktop.want_type == SCREEN_DIRECT3D) { /* In output=direct3d mode, SDL still has a surface but this code ignores SDL * and draws directly to a Direct3D9 backbuffer which is presented to the window * client area. However, GDI output to the window still works, and this code * uses the SDL surface still. Therefore, for menus to draw correctly atop the * Direct3D output, this code copies the Direct3D backbuffer to the SDL surface * first. * * WARNING: This happens to work with Windows (even Windows 10 build 18xx as of * 2018/05/21) because Windows appears to permit mixing Direct3D and GDI rendering * to the window. * * Someday, if Microsoft should break that ability, this code will need to be * revised to send screen "updates" to the Direct3D backbuffer first, then * Present to the window client area. */ if (d3d) d3d->UpdateRectToSDLSurface(0, 0, sdl.surface->w, sdl.surface->h); } #endif if (OpenGL_using()) { #if C_OPENGL mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,false); mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,false); /* show the menu */ mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,true); mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,true); glClearColor (0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); GFX_OpenGLRedrawScreen(); /* give the menu bar a drop shadow */ MenuShadeRect( (int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX, (int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h, (int)mainMenu.menuBox.w, (int)DOSBoxMenu::dropshadowY - 1/*menubar border*/); mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); for (auto i=popup_stack.begin();i!=popup_stack.end();i++) { if (mainMenu.get_item(*i).get_type() == DOSBoxMenu::submenu_type_id) { mainMenu.get_item(*i).drawBackground(mainMenu); mainMenu.get_item(*i).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false); } } SDL_GL_SwapBuffers(); #endif } else { mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,false); mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,false); mainMenu.get_item(mainMenu.menuUserAttentionAt).drawMenuItem(mainMenu); MenuSaveScreen(); /* give the menu bar a drop shadow */ MenuShadeRect( (int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX, (int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h, (int)mainMenu.menuBox.w, (int)DOSBoxMenu::dropshadowY - 1/*menubar border*/); uprect.x = 0; uprect.y = mainMenu.menuBox.y + mainMenu.menuBox.h; uprect.w = mainMenu.menuBox.w; uprect.h = DOSBoxMenu::dropshadowY; #if defined(C_SDL2) SDL_UpdateWindowSurfaceRects(sdl.window, &uprect, 1); #else SDL_UpdateRects( sdl.surface, 1, &uprect ); #endif /* show the menu */ mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,true); mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,true); mainMenu.get_item(mainMenu.menuUserAttentionAt).drawBackground(mainMenu); mainMenu.get_item(mainMenu.menuUserAttentionAt).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false); mainMenu.get_item(mainMenu.menuUserAttentionAt).updateScreenFromPopup(mainMenu); } /* hack */ mainMenu.menuUserAttentionAt = DOSBoxMenu::unassigned_item_handle; /* fall into another loop to process the menu */ while (runloop) { if (!SDL_WaitEvent(&event)) break; #if defined(C_SDL2) switch (event.type) { case SDL_FINGERDOWN: if (touchscreen_finger_lock == no_finger_id && touchscreen_touch_lock == no_touch_id) { touchscreen_finger_lock = event.tfinger.fingerId; touchscreen_touch_lock = event.tfinger.touchId; Sint32 x,y; #if defined(WIN32) /* NTS: Windows versions of SDL2 do normalize the coordinates */ x = (Sint32)(event.tfinger.x * sdl.clip.w); y = (Sint32)(event.tfinger.y * sdl.clip.h); #else /* NTS: Linux versions of SDL2 don't normalize the coordinates? */ x = event.tfinger.x; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ y = event.tfinger.y; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ #endif memset(&event.button,0,sizeof(event.button)); event.type = SDL_MOUSEBUTTONDOWN; event.button.x = x; event.button.y = y; } else { event.type = -1; } break; case SDL_FINGERUP: if (touchscreen_finger_lock == event.tfinger.fingerId && touchscreen_touch_lock == event.tfinger.touchId) { touchscreen_finger_lock = no_finger_id; touchscreen_touch_lock = no_touch_id; Sint32 x,y; #if defined(WIN32) /* NTS: Windows versions of SDL2 do normalize the coordinates */ x = (Sint32)(event.tfinger.x * sdl.clip.w); y = (Sint32)(event.tfinger.y * sdl.clip.h); #else /* NTS: Linux versions of SDL2 don't normalize the coordinates? */ x = event.tfinger.x; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ y = event.tfinger.y; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ #endif memset(&event.button,0,sizeof(event.button)); event.type = SDL_MOUSEBUTTONUP; event.button.x = x; event.button.y = y; } else { event.type = -1; } break; case SDL_FINGERMOTION: if (touchscreen_finger_lock == event.tfinger.fingerId && touchscreen_touch_lock == event.tfinger.touchId) { Sint32 x,y; #if defined(WIN32) /* NTS: Windows versions of SDL2 do normalize the coordinates */ x = (Sint32)(event.tfinger.x * sdl.clip.w); y = (Sint32)(event.tfinger.y * sdl.clip.h); #else /* NTS: Linux versions of SDL2 don't normalize the coordinates? */ x = event.tfinger.x; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ y = event.tfinger.y; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ #endif memset(&event.button,0,sizeof(event.button)); event.type = SDL_MOUSEMOTION; event.button.x = x; event.button.y = y; } else if (touchscreen_finger_lock != no_finger_id || touchscreen_touch_lock != no_touch_id) { event.type = -1; } break; } #endif switch (event.type) { case SDL_QUIT: throw(0); break; case SDL_KEYUP: if (event.key.keysym.sym == SDLK_ESCAPE) { choice_item = DOSBoxMenu::unassigned_item_handle; runloop = false; } break; #if !defined(C_SDL2) case SDL_VIDEORESIZE: UpdateWindowDimensions(); // FIXME: Use SDL window dimensions, except that on Windows, SDL won't tell us our actual dimensions HandleVideoResize(&event.resize); runloop = false; resized = true; break; #endif case SDL_MOUSEBUTTONDOWN: button_holding=true; choice_item = mainMenu.menuUserHoverAt; if (choice_item != DOSBoxMenu::unassigned_item_handle) { DOSBoxMenu::item &item = mainMenu.get_item(choice_item); item.setHilight(mainMenu,true); item.drawMenuItem(mainMenu); if (OpenGL_using()) redrawAll = true; else item.updateScreenFromItem(mainMenu); } else { /* clicking on nothing should dismiss */ runloop = false; } break; case SDL_MOUSEBUTTONUP: button_holding=false; choice_item = mainMenu.menuUserHoverAt; if (choice_item != DOSBoxMenu::unassigned_item_handle) { if (choice_item == psel_item) { /* clicking something twice should dismiss */ runloop = false; } else { DOSBoxMenu::item &item = mainMenu.get_item(choice_item); if (item.get_type() == DOSBoxMenu::item_type_id && item.is_enabled()) runloop = false; } psel_item = choice_item; } else { /* not selecting anything counts as a reason to exit */ runloop = false; } break; case SDL_MOUSEMOTION: { sel_item = DOSBoxMenu::unassigned_item_handle; auto search = popup_stack.end(); if (search != popup_stack.begin()) { do { search--; sel_item = mainMenu.get_item(*search).display_list.itemFromPoint(mainMenu,event.button.x,event.button.y); if (sel_item != DOSBoxMenu::unassigned_item_handle) { assert(search != popup_stack.end()); search++; break; } } while (search != popup_stack.begin()); } if (sel_item == DOSBoxMenu::unassigned_item_handle) sel_item = mainMenu.display_list.itemFromPoint(mainMenu,event.button.x,event.button.y); /* at this point: * sel_item = item under cursor, or unassigned if no item * search = iterator just past the item's level (to remove items if changing) */ if (mainMenu.menuUserHoverAt != sel_item) { if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) { mainMenu.get_item(mainMenu.menuUserHoverAt).setHover(mainMenu,false); if (mainMenu.get_item(mainMenu.menuUserHoverAt).get_type() == DOSBoxMenu::item_type_id) mainMenu.get_item(mainMenu.menuUserHoverAt).setHilight(mainMenu,false); } if (sel_item != DOSBoxMenu::unassigned_item_handle) { if (mainMenu.get_item(sel_item).get_type() == DOSBoxMenu::submenu_type_id) { if (!mainMenu.get_item(sel_item).isHilight()) { /* use a copy of the iterator to scan forward and un-hilight the menu items. * then use the original iterator to erase from the vector. */ for (auto ss=search;ss != popup_stack.end();ss++) { for (auto &id : mainMenu.get_item(*ss).display_list.get_disp_list()) mainMenu.get_item(id).setHilight(mainMenu,false).setHover(mainMenu,false); mainMenu.get_item(*ss).setHilight(mainMenu,false).setHover(mainMenu,false); } popup_stack.erase(search,popup_stack.end()); mainMenu.get_item(sel_item).setHilight(mainMenu,true).setHover(mainMenu,true); popup_stack.push_back(sel_item); redrawAll = true; } } else { /* use a copy of the iterator to scan forward and un-hilight the menu items. * then use the original iterator to erase from the vector. */ for (auto ss=search;ss != popup_stack.end();ss++) { for (auto &id : mainMenu.get_item(*ss).display_list.get_disp_list()) mainMenu.get_item(id).setHilight(mainMenu,false).setHover(mainMenu,false); mainMenu.get_item(*ss).setHilight(mainMenu,false).setHover(mainMenu,false); redrawAll = true; } popup_stack.erase(search,popup_stack.end()); } if (OpenGL_using()) redrawAll = true; mainMenu.get_item(sel_item).setHover(mainMenu,true); if (mainMenu.get_item(sel_item).get_type() == DOSBoxMenu::item_type_id && button_holding) mainMenu.get_item(sel_item).setHilight(mainMenu,true); } else { if (OpenGL_using()) redrawAll = true; } if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle && !OpenGL_using() && !redrawAll) { mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu); mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu); } mainMenu.menuUserHoverAt = sel_item; if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle && !OpenGL_using() && !redrawAll) { mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu); mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu); } } } break; } if (redrawAll) { redrawAll = false; #if 0/*DEBUG*/ LOG_MSG("Redraw %u",(unsigned int)SDL_GetTicks()); #endif if (OpenGL_using()) { #if C_OPENGL glClearColor (0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); GFX_OpenGLRedrawScreen(); mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); #endif } else { MenuRestoreScreen(); mainMenu.display_list.DrawDisplayList(mainMenu,/*updateScreen*/false); } /* give the menu bar a drop shadow */ MenuShadeRect( (int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX, (int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h, (int)mainMenu.menuBox.w, (int)DOSBoxMenu::dropshadowY - 1/*menubar border*/); for (auto i=popup_stack.begin();i!=popup_stack.end();i++) { if (mainMenu.get_item(*i).get_type() == DOSBoxMenu::submenu_type_id) { mainMenu.get_item(*i).drawBackground(mainMenu); mainMenu.get_item(*i).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false); } } #if C_OPENGL if (OpenGL_using()) SDL_GL_SwapBuffers(); else #endif MenuFullScreenRedraw(); } } #if defined(C_SDL2) /* force touchscreen mapping to let go */ touchscreen_finger_lock = no_finger_id; touchscreen_touch_lock = no_touch_id; #endif /* then return */ GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle); GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle); if (!resized) { MenuRestoreScreen(); if (!OpenGL_using()) MenuFullScreenRedraw(); } MenuFreeScreen(); while (!popup_stack.empty()) { DOSBoxMenu::item &item = mainMenu.get_item(popup_stack.back()); for (auto &id : item.display_list.get_disp_list()) { mainMenu.get_item(id).setHilight(mainMenu,false); mainMenu.get_item(id).setHover(mainMenu,false); } item.setHilight(mainMenu,false); item.setHover(mainMenu,false); popup_stack.pop_back(); } if (OpenGL_using()) { #if C_OPENGL glClearColor (0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); GFX_OpenGLRedrawScreen(); mainMenu.setRedraw(); GFX_DrawSDLMenu(mainMenu,mainMenu.display_list); SDL_GL_SwapBuffers(); gl_clear_countdown = 2; gl_menudraw_countdown = 2; // two GL buffers #endif } /* action! */ if (!resized && choice_item != DOSBoxMenu::unassigned_item_handle) { DOSBoxMenu::item &item = mainMenu.get_item(choice_item); if (item.get_type() == DOSBoxMenu::item_type_id && item.is_enabled()) mainMenu.dispatchItemCommand(item); } return; } } else { GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle); } } #endif switch (button->state) { case SDL_PRESSED: if (inMenu) return; if (sdl.mouse.requestlock && !sdl.mouse.locked && mouse_notify_mode == 0) { CaptureMouseNotify(); GFX_CaptureMouse(); // Dont pass klick to mouse handler break; } if (!sdl.mouse.autoenable && sdl.mouse.autolock && mouse_notify_mode == 0 && button->button == SDL_BUTTON_MIDDLE) { GFX_CaptureMouse(); break; } switch (button->button) { case SDL_BUTTON_LEFT: Mouse_ButtonPressed(0); break; case SDL_BUTTON_RIGHT: Mouse_ButtonPressed(1); break; case SDL_BUTTON_MIDDLE: Mouse_ButtonPressed(2); break; #if !defined(C_SDL2) case SDL_BUTTON_WHEELUP: /* Ick, really SDL? */ Mouse_ButtonPressed(100-1); break; case SDL_BUTTON_WHEELDOWN: /* Ick, really SDL? */ Mouse_ButtonPressed(100+1); break; #endif } break; case SDL_RELEASED: switch (button->button) { case SDL_BUTTON_LEFT: Mouse_ButtonReleased(0); break; case SDL_BUTTON_RIGHT: Mouse_ButtonReleased(1); break; case SDL_BUTTON_MIDDLE: Mouse_ButtonReleased(2); break; #if !defined(C_SDL2) case SDL_BUTTON_WHEELUP: /* Ick, really SDL? */ Mouse_ButtonReleased(100-1); break; case SDL_BUTTON_WHEELDOWN: /* Ick, really SDL? */ Mouse_ButtonReleased(100+1); break; #endif } break; } } void GFX_LosingFocus(void) { sdl.laltstate=SDL_KEYUP; sdl.raltstate=SDL_KEYUP; MAPPER_LosingFocus(); DoExtendedKeyboardHook(false); } #if !defined(C_SDL2) static bool PasteClipboardNext(); // added emendelson from dbDOS #endif #if !defined(C_SDL2) bool GFX_IsFullscreen(void) { return sdl.desktop.fullscreen; } #endif #if defined(__WIN32__) && !defined(C_SDL2) && !defined(HX_DOS) void OpenFileDialog( char * path_arg ) { if(control->SecureMode()) { LOG_MSG(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); return; } DOS_MCB mcb(dos.psp()-1); static char pcname[9]; mcb.GetFileName(pcname); if(strlen(pcname)) return; OPENFILENAME OpenFileName; char szFile[MAX_PATH]; char CurrentDir[MAX_PATH]; const char * Temp_CurrentDir = CurrentDir; if (Drives['C'-'A']) { if (MessageBox(GetHWND(), "Quick launch automatically mounts drive C in DOSBox.\nDrive C has already been mounted. Do you want to continue?", "Warning",MB_YESNO)==IDNO) return; } if(path_arg) goto search; szFile[0] = 0; GetCurrentDirectory( MAX_PATH, CurrentDir ); OpenFileName.lStructSize = sizeof( OPENFILENAME ); OpenFileName.hwndOwner = NULL; if(DOSBox_Kor()) OpenFileName.lpstrFilter = "실행 파일(*.com, *.exe, *.bat)\0*.com;*.exe;*.bat\0모든 파일(*.*)\0*.*\0"; else OpenFileName.lpstrFilter = "Executable files(*.com, *.exe, *.bat)\0*.com;*.exe;*.bat\0All files(*.*)\0*.*\0"; OpenFileName.lpstrCustomFilter = NULL; OpenFileName.nMaxCustFilter = 0; OpenFileName.nFilterIndex = 0; OpenFileName.lpstrFile = szFile; OpenFileName.nMaxFile = sizeof( szFile ); OpenFileName.lpstrFileTitle = NULL; OpenFileName.nMaxFileTitle = 0; OpenFileName.lpstrInitialDir = CurrentDir; OpenFileName.lpstrTitle = "Select an executable"; OpenFileName.nFileOffset = 0; OpenFileName.nFileExtension = 0; OpenFileName.lpstrDefExt = NULL; OpenFileName.lCustData = 0; OpenFileName.lpfnHook = NULL; OpenFileName.lpTemplateName = NULL; OpenFileName.Flags = OFN_EXPLORER; search: if(GetOpenFileName( &OpenFileName ) || path_arg) { WIN32_FIND_DATA FindFileData; HANDLE hFind; char drive [_MAX_DRIVE]; char dir [_MAX_DIR]; char fname [_MAX_FNAME]; char ext [_MAX_EXT]; char * path = 0; if(path_arg) { szFile[0] = 0; sprintf(szFile,path_arg); } path = szFile; _splitpath (path, drive, dir, fname, ext); char ext_temp [_MAX_EXT]; ext_temp[0] = 0; sprintf(ext_temp,ext); hFind = FindFirstFile(szFile, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { if(strcasecmp(ext,"")) goto search; szFile[0] = 0; ext[0] = 0; sprintf(ext,".com"); sprintf(szFile,"%s%s%s%s",drive,dir,fname,".com"); hFind = FindFirstFile(szFile, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { szFile[0] = 0; ext[0] = 0; sprintf(ext,".exe"); sprintf(szFile,"%s%s%s%s",drive,dir,fname,".exe"); hFind = FindFirstFile(szFile, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { szFile[0] = 0; ext[0] = 0; sprintf(ext,".bat"); sprintf(szFile,"%s%s%s%s",drive,dir,fname,".bat"); hFind = FindFirstFile(szFile, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { szFile[0] = 0; ext[0]=0; goto search; } } } } if((!strcmp(ext,".com")) || (!strcmp(ext,".exe")) || (!strcmp(ext,".bat"))) { char pathname[DOS_PATHLENGTH]; sprintf(pathname,"%s%s",drive,dir); MountDrive_2('C',pathname,"LOCAL"); DOS_SetDrive(toupper('C') - 'A'); } else { LOG_MSG("GUI: Unsupported filename extension."); goto godefault; } #define DOSNAMEBUF 256 char name1[DOSNAMEBUF+1]; sprintf(name1,"%s%s",fname,ext); Bit16u n=1; Bit8u c='\n'; DOS_WriteFile(STDOUT,&c,&n); DOS_Shell shell; DOS_MCB mcb(dos.psp()-1); static char name[9]; mcb.GetFileName(name); SetCurrentDirectory( Temp_CurrentDir ); do { shell.Execute(name1,(char*)(" ")); if(!strcmp(ext,".bat")) shell.RunInternal(); if (!strlen(name)) break; } while (1); if(strcmp(ext,".bat")) DOS_WriteFile(STDOUT,&c,&n); shell.ShowPrompt(); } godefault: SetCurrentDirectory( Temp_CurrentDir ); return; } void Go_Boot(const char boot_drive[_MAX_DRIVE]) { if(control->SecureMode()) { LOG_MSG(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); return; } OPENFILENAME OpenFileName; char szFile[MAX_PATH]; char CurrentDir[MAX_PATH]; const char * Temp_CurrentDir = CurrentDir; szFile[0] = 0; GetCurrentDirectory( MAX_PATH, CurrentDir ); OpenFileName.lStructSize = sizeof( OPENFILENAME ); OpenFileName.hwndOwner = NULL; if(DOSBox_Kor()) OpenFileName.lpstrFilter = "이미지 파일(*.img, *.ima, *.pcjr, *.jrc)\0*.pcjr;*.img;*.ima;*.jrc\0모든 파일(*.*)\0*.*\0"; else OpenFileName.lpstrFilter = "Image files(*.img, *.ima, *.pcjr, *.jrc)\0*.pcjr;*.img;*.ima;*.jrc\0All files(*.*)\0*.*\0"; OpenFileName.lpstrCustomFilter = NULL; OpenFileName.nMaxCustFilter = 0; OpenFileName.nFilterIndex = 0; OpenFileName.lpstrFile = szFile; OpenFileName.nMaxFile = sizeof( szFile ); OpenFileName.lpstrFileTitle = NULL; OpenFileName.nMaxFileTitle = 0; OpenFileName.lpstrInitialDir = CurrentDir; OpenFileName.lpstrTitle = "Select an image file"; OpenFileName.nFileOffset = 0; OpenFileName.nFileExtension = 0; OpenFileName.lpstrDefExt = NULL; OpenFileName.lCustData = 0; OpenFileName.lpfnHook = NULL; OpenFileName.lpTemplateName = NULL; OpenFileName.Flags = OFN_EXPLORER; search: if(GetOpenFileName( &OpenFileName )) { WIN32_FIND_DATA FindFileData; HANDLE hFind; char drive [_MAX_DRIVE]; char dir [_MAX_DIR]; char fname [_MAX_FNAME]; char ext [_MAX_EXT]; char * path = 0; path = szFile; _splitpath (path, drive, dir, fname, ext); char ext_temp [_MAX_EXT]; ext_temp[0] = 0; sprintf(ext_temp,ext); hFind = FindFirstFile(szFile, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) goto search; if((!strcmp(ext,".img")) || (!strcmp(ext,".pcjr")) || (!strcmp(ext,".jrc")) || (!strcmp(ext,".ima"))) { extern Bitu ZDRIVE_NUM; char root[4] = {(char)('A'+ZDRIVE_NUM),':','\\',0}; char cmd[20]; DOS_Shell shell; Bit16u n=1; Bit8u c='\n'; if(strcmp(boot_drive,"a")) { char szFile_pre[MAX_PATH]; szFile_pre[0] = 0; strcpy(szFile_pre,boot_drive); strcat(szFile_pre," -t hdd "); strcat(szFile_pre,szFile); DOS_WriteFile(STDOUT,&c,&n); cmd[0] = 0; strcpy(cmd,root); strcat(cmd,"imgmount.com"); shell.Execute(cmd,szFile_pre); shell.RunInternal(); } DOS_WriteFile(STDOUT,&c,&n); strcat(szFile," -l "); strcat(szFile,boot_drive); cmd[0] = 0; strcpy(cmd,root); strcat(cmd,"boot.com"); shell.Execute(cmd,szFile); shell.RunInternal(); DOS_WriteFile(STDOUT,&c,&n); shell.ShowPrompt(); // if failed } else { LOG_MSG("GUI: Unsupported filename extension."); goto godefault; } } godefault: SetCurrentDirectory( Temp_CurrentDir ); return; } void Go_Boot2(const char boot_drive[_MAX_DRIVE]) { Bit16u n=1; Bit8u c='\n'; DOS_WriteFile(STDOUT,&c,&n); char temp[7]; extern Bitu ZDRIVE_NUM; char root[4] = {(char)('A'+ZDRIVE_NUM),':','\\',0}; char cmd[20]; temp[0] = 0; cmd[0] = 0; strcpy(cmd,root); strcat(cmd,"boot.com"); strcpy(temp,"-l "); strcat(temp,boot_drive); DOS_Shell shell; shell.Execute(cmd,temp); shell.RunInternal(); DOS_WriteFile(STDOUT,&c,&n); shell.ShowPrompt(); // if failed } /* FIXME: Unused */ void Drag_Drop( char * path_arg ) { if(control->SecureMode()) { LOG_MSG(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); return; } DOS_MCB mcb(dos.psp()-1); static char name[9]; mcb.GetFileName(name); if((!path_arg) || (strlen(name))) return; WIN32_FIND_DATA FindFileData; HANDLE hFind; char drive [_MAX_DRIVE]; char dir [_MAX_DIR]; char fname [_MAX_FNAME]; char ext [_MAX_EXT]; char szFile[MAX_PATH]; szFile[0] = 0; sprintf(szFile,path_arg); char * path = szFile; _splitpath (path, drive, dir, fname, ext); char ext_temp [_MAX_EXT]; ext_temp[0] = 0; sprintf(ext_temp,ext); hFind = FindFirstFile(szFile, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) return; if((!strcmp(ext,".com")) || (!strcmp(ext,".exe")) || (!strcmp(ext,".bat"))) OpenFileDialog(path_arg); else LOG_MSG("GUI: Unsupported filename extension."); } HHOOK hhk; LRESULT CALLBACK CBTProc(INT nCode, WPARAM wParam, LPARAM lParam) { (void)lParam; if( HCBT_ACTIVATE == nCode ) { HWND hChildWnd; hChildWnd = (HWND)wParam; SetDlgItemText(hChildWnd,IDYES,"CD-ROM"); SetDlgItemText(hChildWnd,IDNO,"Floppy"); SetDlgItemText(hChildWnd,IDCANCEL,"Harddisk"); UnhookWindowsHookEx(hhk); } CallNextHookEx(hhk, nCode, wParam, lParam); return 0; } int MountMessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType ) { hhk = SetWindowsHookEx( WH_CBT, &CBTProc, 0, GetCurrentThreadId() ); const int iRes = MessageBox( hWnd, lpText, lpCaption, uType | MB_SETFOREGROUND ); return iRes; } void OpenFileDialog_Img( char drive ) { if(control->SecureMode()) { LOG_MSG(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW")); return; } if (Drives[drive-'A']) { LOG_MSG("GUI: Unmount drive %c first, and then try again.",drive); return; } OPENFILENAME OpenFileName; char szFile[MAX_PATH]; char CurrentDir[MAX_PATH]; const char * Temp_CurrentDir = CurrentDir; szFile[0] = 0; GetCurrentDirectory( MAX_PATH, CurrentDir ); OpenFileName.lStructSize = sizeof( OPENFILENAME ); OpenFileName.hwndOwner = NULL; if(DOSBox_Kor()) OpenFileName.lpstrFilter = "이미지/ZIP 파일(*.ima, *.img, *.iso, *.cue, *.bin, *.mdf, *.zip, *.7z)\0*.ima;*.img;*.iso;*.mdf;*.zip;*.cue;*.bin;*.7z\0모든 파일(*.*)\0*.*\0"; else OpenFileName.lpstrFilter = "Image/Zip files(*.ima, *.img, *.iso, *.cue, *.bin, *.mdf, *.zip, *.7z)\0*.ima;*.img;*.iso;*.mdf;*.zip;*.cue;*.bin;*.7z\0All files(*.*)\0*.*\0"; OpenFileName.lpstrCustomFilter = NULL; OpenFileName.nMaxCustFilter = 0; OpenFileName.nFilterIndex = 0; OpenFileName.lpstrFile = szFile; OpenFileName.nMaxFile = sizeof( szFile ); OpenFileName.lpstrFileTitle = NULL; OpenFileName.nMaxFileTitle = 0; OpenFileName.lpstrInitialDir = CurrentDir; OpenFileName.lpstrTitle = "Select an image file"; OpenFileName.nFileOffset = 0; OpenFileName.nFileExtension = 0; OpenFileName.lpstrDefExt = NULL; OpenFileName.lCustData = 0; OpenFileName.lpfnHook = NULL; OpenFileName.lpTemplateName = NULL; OpenFileName.Flags = OFN_EXPLORER; search: if(GetOpenFileName( &OpenFileName )) { WIN32_FIND_DATA FindFileData; HANDLE hFind; hFind = FindFirstFile(szFile, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) goto search; char drive2 [_MAX_DRIVE]; char dir [_MAX_DIR]; char fname [_MAX_FNAME]; char ext [_MAX_EXT]; char * path = szFile; _splitpath (path, drive2, dir, fname, ext); if((!strcmp(ext,".img")) || (!strcmp(ext,".iso")) || (!strcmp(ext,".cue")) || (!strcmp(ext,".bin")) || (!strcmp(ext,".mdf"))) { if(!strcmp(ext,".img")) { int whichval=MountMessageBox(GetHWND(),"Drive type:","Mount as Image",MB_YESNOCANCEL); if(whichval == IDYES) Mount_Img(drive,path);// CD-ROM else if(whichval == IDNO) Mount_Img_Floppy(drive,path); // Floppy else if(whichval == IDCANCEL) Mount_Img_HDD(drive,path);// Harddisk } else Mount_Img(drive,path); } else if(!strcmp(ext,".ima")) { Mount_Img_Floppy(drive,path); } else LOG_MSG("GUI: Unsupported filename extension."); } SetCurrentDirectory( Temp_CurrentDir ); } void D3D_PS(void) { OPENFILENAME OpenFileName; char szFile[MAX_PATH]; char CurrentDir[MAX_PATH]; const char * Temp_CurrentDir = CurrentDir; szFile[0] = 0; GetCurrentDirectory( MAX_PATH, CurrentDir ); OpenFileName.lStructSize = sizeof( OPENFILENAME ); OpenFileName.hwndOwner = NULL; if(DOSBox_Kor()) OpenFileName.lpstrFilter = "효과 파일(*.fx)\0*.fx\0모든 파일(*.*)\0*.*\0"; else OpenFileName.lpstrFilter = "Effect files(*.fx)\0*.fx\0All files(*.*)\0*.*\0"; OpenFileName.lpstrCustomFilter = NULL; OpenFileName.nMaxCustFilter = 0; OpenFileName.nFilterIndex = 0; OpenFileName.lpstrFile = szFile; OpenFileName.nMaxFile = sizeof( szFile ); OpenFileName.lpstrFileTitle = NULL; OpenFileName.nMaxFileTitle = 0; OpenFileName.lpstrInitialDir = ".\\Shaders";; OpenFileName.lpstrTitle = "Select an effect file"; OpenFileName.nFileOffset = 0; OpenFileName.nFileExtension = 0; OpenFileName.lpstrDefExt = NULL; OpenFileName.lCustData = 0; OpenFileName.lpfnHook = NULL; OpenFileName.lpTemplateName = NULL; OpenFileName.Flags = OFN_EXPLORER; //search: if(GetOpenFileName( &OpenFileName )) { // WIN32_FIND_DATA FindFileData; // HANDLE hFind; char drive [_MAX_DRIVE]; char dir [_MAX_DIR]; char fname [_MAX_FNAME]; char ext [_MAX_EXT]; char * path = 0; path = szFile; _splitpath (path, drive, dir, fname, ext); if(!strcmp(ext,".fx")) { if(!strcmp(fname,"none")) { SetVal("sdl","pixelshader","none"); goto godefault; } if (sdl.desktop.want_type != SCREEN_DIRECT3D) MessageBox(GetHWND(), "Set output to Direct3D for the changes to take effect", "Warning", 0); if (MessageBox(GetHWND(), "Always enable this pixelshader under any circumstances in Direct3D?" \ "\nIf yes, the shader will be used even if the result might not be desired.", fname, MB_YESNO) == IDYES) strcat(fname, ".fx forced"); else strcat(fname, ".fx"); SetVal("sdl","pixelshader",fname); } else { LOG_MSG("GUI: Unsupported filename extension."); goto godefault; } } godefault: SetCurrentDirectory( Temp_CurrentDir ); return; } #endif void* GetSetSDLValue(int isget, std::string target, void* setval) { if (target == "wait_on_error") { if (isget) return (void*) sdl.wait_on_error; else sdl.wait_on_error = setval; } else if (target == "opengl.bilinear") { #if C_OPENGL if (isget) return (void*) sdl.opengl.bilinear; else sdl.opengl.bilinear = setval; #else if (isget) return (void*) 0; #endif /* } else if (target == "draw.callback") { if (isget) return (void*) sdl.draw.callback; else sdl.draw.callback = *static_cast(setval); } else if (target == "desktop.full.width") { if (isget) return (void*) sdl.desktop.full.width; else sdl.desktop.full.width = *static_cast(setval); } else if (target == "desktop.full.height") { if (isget) return (void*) sdl.desktop.full.height; else sdl.desktop.full.height = *static_cast(setval); } else if (target == "desktop.full.fixed") { if (isget) return (void*) sdl.desktop.full.fixed; else sdl.desktop.full.fixed = setval; } else if (target == "desktop.window.width") { if (isget) return (void*) sdl.desktop.window.width; else sdl.desktop.window.width = *static_cast(setval); } else if (target == "desktop.window.height") { if (isget) return (void*) sdl.desktop.window.height; else sdl.desktop.window.height = *static_cast(setval); */ } else if (target == "desktop.fullscreen") { if (isget) return (void*) sdl.desktop.fullscreen; else sdl.desktop.fullscreen = setval; } else if (target == "desktop.doublebuf") { if (isget) return (void*) sdl.desktop.doublebuf; else sdl.desktop.doublebuf = setval; /* } else if (target == "desktop.type") { if (isget) return (void*) sdl.desktop.type; else sdl.desktop.type = *static_cast(setval); */ } else if (target == "desktop.want_type") { if (isget) return (void*) sdl.desktop.want_type; else sdl.desktop.want_type = *static_cast(setval); /* } else if (target == "surface") { if (isget) return (void*) sdl.surface; else sdl.surface = static_cast(setval); } else if (target == "overlay") { if (isget) return (void*) sdl.overlay; else sdl.overlay = static_cast(setval); */ } else if (target == "mouse.autoenable") { if (isget) return (void*) sdl.mouse.autoenable; else sdl.mouse.autoenable = setval; /* } else if (target == "overscan_width") { if (isget) return (void*) sdl.overscan_width; else sdl.overscan_width = *static_cast(setval); */ #if defined (WIN32) } else if (target == "using_windib") { if (isget) return (void*) sdl.using_windib; else sdl.using_windib = setval; #endif } return NULL; } #if defined(C_SDL2) static void FingerToFakeMouseMotion(SDL_TouchFingerEvent * finger) { SDL_MouseMotionEvent fake; memset(&fake,0,sizeof(fake)); #if defined(WIN32) /* NTS: Windows versions of SDL2 do normalize the coordinates */ fake.x = (Sint32)(finger->x * sdl.clip.w); fake.y = (Sint32)(finger->y * sdl.clip.h); #else /* NTS: Linux versions of SDL2 don't normalize the coordinates? */ fake.x = finger->x; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ fake.y = finger->y; /* Contrary to SDL_events.h the x/y coordinates are NOT normalized to 0...1 */ #endif fake.xrel = (Sint32)finger->dx; fake.yrel = (Sint32)finger->dy; HandleMouseMotion(&fake); if (finger->type == SDL_FINGERDOWN || finger->type == SDL_FINGERUP) { SDL_MouseButtonEvent fakeb; memset(&fakeb,0,sizeof(fakeb)); fakeb.state = (finger->type == SDL_FINGERDOWN) ? SDL_PRESSED : SDL_RELEASED; fakeb.button = SDL_BUTTON_LEFT; fakeb.x = fake.x; fakeb.y = fake.y; HandleMouseButton(&fakeb); } } static void HandleTouchscreenFinger(SDL_TouchFingerEvent * finger) { /* Now that SDL2 can tell my mouse from my laptop touchscreen, let's * map tap events to the left mouse button. Now I can use my laptop * touchscreen with Windows 3.11 again! --J.C. */ /* Now let's handle The Finger (har har) */ /* NTS: This code is written to map ONLY one finger to the mouse. * If multiple fingers are touching the screen, this code will * only respond to the first finger that touched the screen. */ if (finger->type == SDL_FINGERDOWN) { if (touchscreen_finger_lock == no_finger_id && touchscreen_touch_lock == no_touch_id) { touchscreen_finger_lock = finger->fingerId; touchscreen_touch_lock = finger->touchId; FingerToFakeMouseMotion(finger); Mouse_ButtonPressed(0); } } else if (finger->type == SDL_FINGERUP) { if (touchscreen_finger_lock == finger->fingerId && touchscreen_touch_lock == finger->touchId) { touchscreen_finger_lock = no_finger_id; touchscreen_touch_lock = no_touch_id; FingerToFakeMouseMotion(finger); Mouse_ButtonReleased(0); } } else if (finger->type == SDL_FINGERMOTION) { if (touchscreen_finger_lock == finger->fingerId && touchscreen_touch_lock == finger->touchId) { FingerToFakeMouseMotion(finger); } } } #endif #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) void MSG_WM_COMMAND_handle(SDL_SysWMmsg &Message); #endif struct mouse_pos { long x = 0; long y = 0; } mouse_pos; bool mouse_inside = false; void GFX_EventsMouseProcess(const long x, const long y, const long rx, const long ry) { const auto x1 = sdl.clip.x; const auto x2 = x1 + sdl.clip.w - 1; const auto y1 = sdl.clip.y; const auto y2 = y1 + sdl.clip.h - 1; const auto in = x >= x1 && x <= x2 && y >= y1 && y <= y2; if (mouse_inside && !in) { const auto x3 = max((int)x1, min((int)x2, (int)x)); const auto y3 = max((int)y1, min((int)y2, (int)y)); SDL_Event evt; evt.type = SDL_MOUSEMOTION; evt.motion.state = 0; evt.motion.which = 0; evt.motion.x = x3; evt.motion.y = y3; evt.motion.xrel = rx; evt.motion.yrel = ry; SDL_PushEvent(&evt); } mouse_inside = in; } #if defined(WIN32) void GFX_EventsMouseWin32() { /* Compute relative mouse movement */ POINT point; SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); if (!SDL_GetWMInfo(&wmi)) return; if (!GetCursorPos(&point)) return; if (!ScreenToClient(wmi.child_window, &point)) return; const auto x = point.x; const auto y = point.y; const auto rx = x - mouse_pos.x; const auto ry = y - mouse_pos.y; mouse_pos.x = x; mouse_pos.y = y; /* Let the method do the heavy uplifting */ GFX_EventsMouseProcess(x, y, rx, ry); } #endif /** * \brief Processes mouse movements when outside the window. * * This method will send an extra mouse event to the SDL pump * when some relative movement has occurred. */ void GFX_EventsMouse() { if (sdl.desktop.fullscreen || sdl.mouse.locked) return; #if WIN32 GFX_EventsMouseWin32(); #else // TODO #endif } void GFX_Events() { CheckMapperKeyboardLayout(); #if defined(C_SDL2) /* SDL 2.x---------------------------------- */ SDL_Event event; #if defined (REDUCE_JOYSTICK_POLLING) static int poll_delay=0; int time=GetTicks(); if (time-poll_delay>20) { poll_delay=time; if (sdl.num_joysticks>0) { SDL_JoystickUpdate(); MAPPER_UpdateJoysticks(); } } #endif while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_WINDOWEVENT: switch (event.window.event) { case SDL_WINDOWEVENT_RESTORED: GFX_ResetScreen(); continue; case SDL_WINDOWEVENT_RESIZED: GFX_HandleVideoResize(event.window.data1, event.window.data2); continue; case SDL_WINDOWEVENT_EXPOSED: if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw ); continue; case SDL_WINDOWEVENT_FOCUS_GAINED: if (IsFullscreen() && !sdl.mouse.locked) GFX_CaptureMouse(); SetPriority(sdl.priority.focus); CPU_Disable_SkipAutoAdjust(); break; case SDL_WINDOWEVENT_FOCUS_LOST: if (sdl.mouse.locked) { GFX_CaptureMouse(); } SetPriority(sdl.priority.nofocus); GFX_LosingFocus(); CPU_Enable_SkipAutoAdjust(); break; default: ; } /* Non-focus priority is set to pause; check to see if we've lost window or input focus * i.e. has the window been minimised or made inactive? */ if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) { if ((event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (event.window.event == SDL_WINDOWEVENT_MINIMIZED)) { /* Window has lost focus, pause the emulator. * This is similar to what PauseDOSBox() does, but the exit criteria is different. * Instead of waiting for the user to hit Alt-Break, we wait for the window to * regain window or input focus. */ bool paused = true; SDL_Event ev; GFX_SetTitle(-1,-1,-1,true); KEYBOARD_ClrBuffer(); // SDL_Delay(500); // while (SDL_PollEvent(&ev)) { // flush event queue. // } while (paused) { // WaitEvent waits for an event rather than polling, so CPU usage drops to zero SDL_WaitEvent(&ev); switch (ev.type) { case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event. case SDL_WINDOWEVENT: // wait until we get window focus back if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (ev.window.event == SDL_WINDOWEVENT_MINIMIZED) || (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) { // We've got focus back, so unpause and break out of the loop if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) { paused = false; GFX_SetTitle(-1,-1,-1,false); } /* Now poke a "release ALT" command into the keyboard buffer * we have to do this, otherwise ALT will 'stick' and cause * problems with the app running in the DOSBox. */ KEYBOARD_AddKey(KBD_leftalt, false); KEYBOARD_AddKey(KBD_rightalt, false); if (ev.window.event == SDL_WINDOWEVENT_RESTORED) { // We may need to re-create a texture and more GFX_ResetScreen(); } } break; } } } } break; case SDL_MOUSEMOTION: #if defined(C_SDL2) if (touchscreen_finger_lock == no_finger_id && touchscreen_touch_lock == no_touch_id && event.motion.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */ HandleMouseMotion(&event.motion); } #else HandleMouseMotion(&event.motion); #endif break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: #if defined(C_SDL2) if (touchscreen_finger_lock == no_finger_id && touchscreen_touch_lock == no_touch_id && event.button.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */ HandleMouseButton(&event.button); } #else HandleMouseButton(&event.button); #endif break; case SDL_FINGERDOWN: case SDL_FINGERUP: case SDL_FINGERMOTION: HandleTouchscreenFinger(&event.tfinger); break; case SDL_QUIT: throw(0); break; #if defined (MACOSX) case SDL_KEYDOWN: case SDL_KEYUP: /* On macs CMD-Q is the default key to close an application */ if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RGUI || event.key.keysym.mod == KMOD_LGUI) ) { KillSwitch(true); break; } #endif default: void MAPPER_CheckEvent(SDL_Event * event); MAPPER_CheckEvent(&event); } } #else /* SDL 1.x---------------------------------- */ SDL_Event event; #if defined (REDUCE_JOYSTICK_POLLING) static uint32_t poll_delay=0; uint32_t time=GetTicks(); if ((int32_t)(time-poll_delay)>20) { poll_delay=time; if (sdl.num_joysticks>0) { SDL_JoystickUpdate(); MAPPER_UpdateJoysticks(); } } #endif GFX_EventsMouse(); while (SDL_PollEvent(&event)) { switch (event.type) { #ifdef __WIN32__ case SDL_SYSWMEVENT : { switch( event.syswm.msg->msg ) { #if !defined(HX_DOS) case WM_COMMAND: MSG_WM_COMMAND_handle(/*&*/(*event.syswm.msg)); break; #endif case WM_SYSCOMMAND: switch (event.syswm.msg->wParam) { case 0xF032: // FIXME: What is this? case SC_MAXIMIZE: userResizeWindowWidth = 0; userResizeWindowHeight = 0; menu.maxwindow = true; break; case 0xF122: // FIXME: What is this? case SC_RESTORE: if (sdl.desktop.fullscreen) GFX_SwitchFullScreen(); menu.maxwindow = false; UpdateWindowDimensions(); RENDER_Reset(); if (OpenGL_using()) { UpdateWindowDimensions(); RENDER_Reset(); } break; case ID_WIN_SYSMENU_RESTOREMENU: /* prevent removing the menu in 3Dfx mode */ if (!GFX_GetPreventFullscreen()) { DOSBox_SetMenu(); mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu); } break; case ID_WIN_SYSMENU_TOGGLEMENU: /* prevent removing the menu in 3Dfx mode */ if (!GFX_GetPreventFullscreen()) { if (menu.toggle) DOSBox_NoMenu(); else DOSBox_SetMenu(); mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu); } break; } default: break; } } #endif case SDL_ACTIVEEVENT: if (event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) { if (event.active.gain) { if (sdl.desktop.fullscreen && !sdl.mouse.locked) GFX_CaptureMouse(); SetPriority(sdl.priority.focus); CPU_Disable_SkipAutoAdjust(); BIOS_SynchronizeNumLock(); } else { if (sdl.mouse.locked) { CaptureMouseNotify(); GFX_CaptureMouse(); } #if defined(WIN32) if (sdl.desktop.fullscreen) GFX_ForceFullscreenExit(); #endif SetPriority(sdl.priority.nofocus); GFX_LosingFocus(); CPU_Enable_SkipAutoAdjust(); } } /* Non-focus priority is set to pause; check to see if we've lost window or input focus * i.e. has the window been minimised or made inactive? */ if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) { if ((event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) && (!event.active.gain)) { /* Window has lost focus, pause the emulator. * This is similar to what PauseDOSBox() does, but the exit criteria is different. * Instead of waiting for the user to hit Alt-Break, we wait for the window to * regain window or input focus. */ bool paused = true; SDL_Event ev; GFX_SetTitle(-1,-1,-1,true); KEYBOARD_ClrBuffer(); // SDL_Delay(500); // while (SDL_PollEvent(&ev)) { // flush event queue. // } while (paused) { // WaitEvent waits for an event rather than polling, so CPU usage drops to zero SDL_WaitEvent(&ev); switch (ev.type) { case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event. case SDL_ACTIVEEVENT: // wait until we get window focus back if (ev.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) { // We've got focus back, so unpause and break out of the loop if (ev.active.gain) { paused = false; GFX_SetTitle(-1,-1,-1,false); } /* Now poke a "release ALT" command into the keyboard buffer * we have to do this, otherwise ALT will 'stick' and cause * problems with the app running in the DOSBox. */ KEYBOARD_AddKey(KBD_leftalt, false); KEYBOARD_AddKey(KBD_rightalt, false); } break; } } } } break; case SDL_MOUSEMOTION: HandleMouseMotion(&event.motion); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: HandleMouseButton(&event.button); break; case SDL_VIDEORESIZE: UpdateWindowDimensions(); // FIXME: Use SDL window dimensions, except that on Windows, SDL won't tell us our actual dimensions HandleVideoResize(&event.resize); break; case SDL_QUIT: throw(0); break; case SDL_VIDEOEXPOSE: if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw ); break; #ifdef WIN32 case SDL_KEYDOWN: case SDL_KEYUP: // ignore event alt+tab if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type; if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type; if (((event.key.keysym.sym==SDLK_TAB)) && ((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) { MAPPER_LosingFocus(); break; } #endif #if defined (MACOSX) case SDL_KEYDOWN: case SDL_KEYUP: /* On macs CMD-Q is the default key to close an application */ if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) { KillSwitch(true); break; } #endif default: void MAPPER_CheckEvent(SDL_Event * event); MAPPER_CheckEvent(&event); } } // start emendelson from dbDOS // Disabled multiple characters per dispatch b/c occasionally // keystrokes get lost in the spew. (Prob b/c of DI usage on Win32, sadly..) // while (PasteClipboardNext()); // Doesn't really matter though, it's fast enough as it is... static Bitu iPasteTicker = 0; if ((iPasteTicker++ % 20) == 0) // emendelson: was %2, %20 is good for WP51 PasteClipboardNext(); // end added emendelson from dbDOS #endif } // added emendelson from dbDos #if defined(WIN32) && !defined(C_SDL2) && !defined(__MINGW32__) #include // Ripped from SDL's SDL_dx5events.c, since there's no API to access it... #define DIRECTINPUT_VERSION 0x0800 #include #ifndef DIK_PAUSE #define DIK_PAUSE 0xC5 #endif #ifndef DIK_OEM_102 #define DIK_OEM_102 0x56 /* < > | on UK/Germany keyboards */ #endif static SDLKey aryScanCodeToSDLKey[0xFF]; static bool bScanCodeMapInited = false; static void PasteInitMapSCToSDLKey() { /* Map the DIK scancodes to SDL keysyms */ for (int i = 0; i 0); const bool bModCntrlOn = ((sdlmModsOn & (KMOD_LCTRL | KMOD_RCTRL)) > 0); const bool bModAltOn = ((sdlmModsOn & (KMOD_LALT | KMOD_RALT)) > 0); const UINT uiScanCodeShift = MapVirtualKey(VK_SHIFT, MAPVK_VK_TO_VSC); const UINT uiScanCodeCntrl = MapVirtualKey(VK_CONTROL, MAPVK_VK_TO_VSC); const UINT uiScanCodeAlt = MapVirtualKey(VK_MENU, MAPVK_VK_TO_VSC); const SDLMod sdlmMods = (SDLMod)((sdlmModsOn & ~(KMOD_LSHIFT | KMOD_RSHIFT | KMOD_LCTRL | KMOD_RCTRL | KMOD_LALT | KMOD_RALT)) | (bModShiftOn ? KMOD_LSHIFT : 0) | (bModCntrlOn ? KMOD_LCTRL : 0) | (bModAltOn ? KMOD_LALT : 0)); /// \note Currently pasteing a character is a two step affair, because if /// you do it too quickly DI can miss a key press/release. // Could be made more efficient, but would require tracking of more state, // so let's forgot that for now... size_t sStrokesRequired = 2; // At least the key & up/down if (bModShift != bModShiftOn) sStrokesRequired += 2; // To press/release Shift if (bModCntrl != bModCntrlOn) sStrokesRequired += 2; // To press/release Control if (bModAlt != bModAltOn) sStrokesRequired += 2; // To press/release Alt /// \fixme Should check if key is already pressed or not so it can toggle press /// but since we don't actually have any mappings from VK/SC to DI codes /// (which SDL (can) use(s) internally as actually scancodes), we can't /// actually check that ourselves, sadly... if (KEYBOARD_BufferSpaceAvail() < (sStrokesRequired + kPasteMinBufExtra)) return false; if (bModShift != bModShiftOn) GenKBStroke(uiScanCodeShift, !bModShiftOn, sdlmMods); if (bModCntrl != bModCntrlOn) GenKBStroke(uiScanCodeCntrl, !bModCntrlOn, sdlmMods); if (bModAlt != bModAltOn) GenKBStroke(uiScanCodeAlt, !bModAltOn, sdlmMods); GenKBStroke(uiScanCode, true, sdlmMods); GenKBStroke(uiScanCode, false, sdlmMods); if (bModShift != bModShiftOn) GenKBStroke(uiScanCodeShift, bModShiftOn, sdlmMods); if (bModCntrl != bModCntrlOn) GenKBStroke(uiScanCodeCntrl, bModCntrlOn, sdlmMods); if (bModAlt != bModAltOn) GenKBStroke(uiScanCodeAlt, bModAltOn, sdlmMods); //putchar(cKey); // For debugging dropped strokes } // Pop head. Could be made more efficient, but this is neater. strPasteBuffer = strPasteBuffer.substr(1, strPasteBuffer.length()); // technically -1, but it clamps by itself anyways... return true; } void PasteClipboard(bool bPressed) { if (!bPressed) return; SDL_SysWMinfo wmiInfo; SDL_VERSION(&wmiInfo.version); if (SDL_GetWMInfo(&wmiInfo) != 1) return; if (!::OpenClipboard(wmiInfo.window)) return; if (!::IsClipboardFormatAvailable(CF_TEXT)) return; HANDLE hContents = ::GetClipboardData(CF_TEXT); if (!hContents) return; const char* szClipboard = (const char*)::GlobalLock(hContents); if (szClipboard) { // Create a copy of the string, and filter out Linefeed characters (ASCII '10') size_t sClipboardLen = strlen(szClipboard); char* szFilteredText = reinterpret_cast(alloca(sClipboardLen + 1)); char* szFilterNextChar = szFilteredText; for (size_t i = 0; i < sClipboardLen; ++i) if (szClipboard[i] != 0x0A) // Skip linefeeds { *szFilterNextChar = szClipboard[i]; ++szFilterNextChar; } *szFilterNextChar = '\0'; // Cap it. strPasteBuffer.append(szFilteredText); ::GlobalUnlock(hContents); } ::CloseClipboard(); } /// TODO: add menu items here #else // end emendelson from dbDOS void PasteClipboard(bool bPressed) { (void)bPressed;//UNUSED // stub } # if !defined(C_SDL2) bool PasteClipboardNext() { // stub return false; } # endif #endif #if defined (WIN32) static BOOL WINAPI ConsoleEventHandler(DWORD event) { switch (event) { case CTRL_SHUTDOWN_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: raise(SIGTERM); return TRUE; case CTRL_C_EVENT: default: //pass to the next handler return FALSE; } } #endif void Null_Init(Section *sec); void SDL_SetupConfigSection() { Section_prop * sdl_sec=control->AddSection_prop("sdl",&Null_Init); Prop_bool* Pbool; Prop_string* Pstring; Prop_int* Pint; Prop_multival* Pmulti; Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false); Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)"); Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false); Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox."); //Pbool = sdl_sec->Add_bool("sdlresize",Property::Changeable::Always,false); //Pbool->Set_help("Makes window resizable (depends on scalers)"); Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"desktop"); Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n" " Using your monitor's native resolution with aspect=true might give the best results.\n" " If you end up with small window on a large screen, try an output different from surface."); Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original"); Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n" " (output=surface does not!)"); const char* outputs[] = { "surface", "overlay", #if C_OPENGL "opengl", "openglnb", "openglhq", #endif "ddraw", #if (HAVE_D3D9_H) && defined(WIN32) "direct3d", #endif 0 }; #ifdef __WIN32__ # if defined(HX_DOS) Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "surface"); /* HX DOS should stick to surface */ # elif defined(__MINGW32__) && !(HAVE_D3D9_H) && !defined(C_SDL2) /* NTS: OpenGL output never seems to work in VirtualBox under Windows XP */ Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, isVirtualBox ? "surface" : "opengl"); /* MinGW builds do not yet have Direct3D */ # else Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "direct3d"); #endif #else Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "surface"); #endif Pstring->Set_help("What video system to use for output."); Pstring->Set_values(outputs); Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always, false); Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)"); const char* feeds[] = { "none", "beep", "flash", nullptr}; Pstring = sdl_sec->Add_string("autolock_feedback", Property::Changeable::Always, feeds[1]); Pstring->Set_help("Autolock status feedback type, i.e. visual, auditive, none."); Pstring->Set_values(feeds); Pint = sdl_sec->Add_int("sensitivity",Property::Changeable::Always,100); Pint->SetMinMax(1,1000); Pint->Set_help("Mouse sensitivity."); Pbool = sdl_sec->Add_bool("waitonerror",Property::Changeable::Always, true); Pbool->Set_help("Wait before closing the console if dosbox has an error."); Pmulti = sdl_sec->Add_multi("priority", Property::Changeable::Always, ","); Pmulti->SetValue("higher,normal",/*init*/true); Pmulti->Set_help("Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized.\n" " pause is only valid for the second entry."); const char* actt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0}; Pstring = Pmulti->GetSection()->Add_string("active",Property::Changeable::Always,"higher"); Pstring->Set_values(actt); const char* inactt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0}; Pstring = Pmulti->GetSection()->Add_string("inactive",Property::Changeable::Always,"normal"); Pstring->Set_values(inactt); Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE); Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the default value."); #if (HAVE_D3D9_H) && (C_D3DSHADERS) && defined(WIN32) Pmulti = sdl_sec->Add_multi("pixelshader",Property::Changeable::Always," "); Pmulti->SetValue("none",/*init*/true); Pmulti->Set_help("Pixelshader program (effect file must be in Shaders subdirectory). If 'forced' is appended,\n" "then the shader will be used even if the result might not be desired."); Pstring = Pmulti->GetSection()->Add_string("type",Property::Changeable::Always,"none"); Pstring = Pmulti->GetSection()->Add_string("force",Property::Changeable::Always,""); #endif Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,false); Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems."); Pint = sdl_sec->Add_int("overscan",Property::Changeable::Always, 0); Pint->SetMinMax(0,10); Pint->Set_help("Width of overscan border (0 to 10). (works only if output=surface)"); Pstring = sdl_sec->Add_string("titlebar", Property::Changeable::Always, ""); Pstring->Set_help("Change the string displayed in the DOSBox title bar."); Pbool = sdl_sec->Add_bool("showmenu", Property::Changeable::Always, true); Pbool->Set_help("Whether to show the menu bar (if supported). Default true."); // Pint = sdl_sec->Add_int("overscancolor",Property::Changeable::Always, 0); // Pint->SetMinMax(0,1000); // Pint->Set_help("Value of overscan color."); } static void show_warning(char const * const message) { bool textonly = true; #ifdef WIN32 textonly = false; if ( !sdl.inited && SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE) < 0 ) textonly = true; sdl.inited = true; #endif LOG_MSG( "Warning: %s", message); if(textonly) return; #if defined(C_SDL2) if (!sdl.window) if (!GFX_SetSDLSurfaceWindow(640,400)) return; sdl.surface = SDL_GetWindowSurface(sdl.window); #else if(!sdl.surface) sdl.surface = SDL_SetVideoMode(640,400,0,SDL_RESIZABLE); sdl.deferred_resize = false; sdl.must_redraw_all = true; #endif if(!sdl.surface) return; #if SDL_BYTEORDER == SDL_BIG_ENDIAN Bit32u rmask = 0xff000000; Bit32u gmask = 0x00ff0000; Bit32u bmask = 0x0000ff00; #else Bit32u rmask = 0x000000ff; Bit32u gmask = 0x0000ff00; Bit32u bmask = 0x00ff0000; #endif SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0); if (!splash_surf) return; int x = 120,y = 20; std::string m(message),m2; std::string::size_type a,b,c,d; while(m.size()) { //Max 50 characters. break on space before or on a newline c = m.find('\n'); d = m.rfind(' ',50); if(c>d) a=b=d; else a=b=c; if( a != std::string::npos) b++; m2 = m.substr(0,a); m.erase(0,b); OutputString((unsigned int)x,(unsigned int)y,m2.c_str(),0xffffffffu,0,splash_surf); y += 20; } SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); #if defined(C_SDL2) SDL_UpdateWindowSurface(sdl.window); #else SDL_Flip(sdl.surface); #endif SDL_Delay(12000); } static void launcheditor(std::string edit) { std::string path,file; Cross::CreatePlatformConfigDir(path); Cross::GetPlatformConfigName(file); path += file; FILE* f = fopen(path.c_str(),"r"); if(!f && !control->PrintConfig(path.c_str())) { printf("tried creating %s. but failed.\n",path.c_str()); exit(1); } if(f) fclose(f); execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0); //if you get here the launching failed! printf("can't find editor(s) specified at the command line.\n"); exit(1); } #if C_DEBUG extern void DEBUG_ShutDown(Section * /*sec*/); #endif void restart_program(std::vector & parameters) { char** newargs = new char* [parameters.size()+1]; // parameter 0 is the executable path // contents of the vector follow // last one is NULL for(Bitu i = 0; i < parameters.size(); i++) newargs[i]=(char*)parameters[i].c_str(); newargs[parameters.size()] = NULL; if(sdl.desktop.fullscreen) SwitchFullScreen(1); putenv((char*)("SDL_VIDEODRIVER=")); #ifndef WIN32 SDL_CloseAudio(); SDL_Delay(50); SDL_Quit(); #if C_DEBUG // shutdown curses DEBUG_ShutDown(NULL); #endif #endif #ifndef WIN32 execvp(newargs[0], newargs); #endif #ifdef __MINGW32__ #ifdef WIN32 // if failed under win32 PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory(&si,sizeof(si)); si.cb=sizeof(si); ZeroMemory(&pi,sizeof(pi)); if(CreateProcess(NULL, newargs[0], NULL, NULL, false, 0, NULL, NULL, &si, &pi)) { CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); SDL_CloseAudio(); SDL_Delay(50); throw(0); SDL_Quit(); #if C_DEBUG // shutdown curses DEBUG_ShutDown(NULL); #endif } #endif #else // if not MINGW #ifdef WIN32 char newargs_temp[32767]; strcpy(newargs_temp, ""); for(Bitu i = 1; i < parameters.size(); i++) { strcat(newargs_temp, " "); strcat(newargs_temp, newargs[i]); } if(ShellExecute(NULL, "open", newargs[0], newargs_temp, NULL, SW_SHOW)) { SDL_CloseAudio(); SDL_Delay(50); throw(0); SDL_Quit(); #if C_DEBUG // shutdown curses DEBUG_ShutDown(NULL); #endif } #endif #endif free(newargs); } void Restart(bool pressed) { // mapper handler (void)pressed;//UNUSED restart_program(control->startup_params); } static void launchcaptures(std::string const& edit) { std::string path,file; struct stat cstat; Section* t = control->GetSection("dosbox"); if(t) file = t->GetPropValue("captures"); if(!t || file == NO_SUCH_PROPERTY) { printf("Config system messed up.\n"); exit(1); } path = "."; path += CROSS_FILESPLIT; path += file; stat(path.c_str(),&cstat); if(cstat.st_mode & S_IFDIR) { execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0); //if you get here the launching failed! printf("can't find filemanager %s\n",edit.c_str()); exit(1); } else { path = ""; Cross::CreatePlatformConfigDir(path); path += file; Cross::CreateDir(path); stat(path.c_str(),&cstat); if((cstat.st_mode & S_IFDIR) == 0) { printf("%s doesn't exist or isn't a directory.\n",path.c_str()); exit(1); } execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0); //if you get here the launching failed! printf("can't find filemanager %s\n",edit.c_str()); exit(1); } } static void launchsaves(std::string const& edit) { std::string path,file; struct stat cstat; file="SAVE"; path = "."; path += CROSS_FILESPLIT; path += file; stat(path.c_str(),&cstat); if(cstat.st_mode & S_IFDIR) { execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0); //if you get here the launching failed! printf("can't find filemanager %s\n",edit.c_str()); exit(1); } else { path = ""; Cross::CreatePlatformConfigDir(path); path += file; Cross::CreateDir(path); stat(path.c_str(),&cstat); if((cstat.st_mode & S_IFDIR) == 0) { printf("%s doesn't exists or isn't a directory.\n",path.c_str()); exit(1); } execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0); //if you get here the launching failed! printf("can't find filemanager %s\n",edit.c_str()); exit(1); } } static void printconfiglocation() { std::string path,file; Cross::CreatePlatformConfigDir(path); Cross::GetPlatformConfigName(file); path += file; FILE* f = fopen(path.c_str(),"r"); if(!f && !control->PrintConfig(path.c_str())) { printf("tried creating %s. but failed",path.c_str()); exit(1); } if(f) fclose(f); printf("%s\n",path.c_str()); exit(0); } static void eraseconfigfile() { FILE* f = fopen("dosbox.conf","r"); if(f) { fclose(f); show_warning("Warning: dosbox.conf exists in current working directory.\nThis will override the configuration file at runtime.\n"); } std::string path,file; Cross::GetPlatformConfigDir(path); Cross::GetPlatformConfigName(file); path += file; f = fopen(path.c_str(),"r"); if(!f) exit(0); fclose(f); unlink(path.c_str()); exit(0); } static void erasemapperfile() { FILE* g = fopen("dosbox.conf","r"); if(g) { fclose(g); show_warning("Warning: dosbox.conf exists in current working directory.\nKeymapping might not be properly reset.\n" "Please reset configuration as well and delete the dosbox.conf.\n"); } std::string path,file=MAPPERFILE; Cross::GetPlatformConfigDir(path); path += file; FILE* f = fopen(path.c_str(),"r"); if(!f) exit(0); fclose(f); unlink(path.c_str()); exit(0); } void SetNumLock(void) { #ifdef WIN32 if (control->opt_disable_numlock_check) return; // Simulate a key press keybd_event(VK_NUMLOCK,0x45,KEYEVENTF_EXTENDEDKEY | 0,0); // Simulate a key release keybd_event(VK_NUMLOCK,0x45,KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,0); #endif } #ifdef WIN32 bool numlock_stat=false; #endif void CheckNumLockState(void) { #ifdef WIN32 BYTE keyState[256]; GetKeyboardState((LPBYTE)(&keyState)); if (keyState[VK_NUMLOCK] & 1) { numlock_stat = true; startup_state_numlock = true; } #endif } extern bool log_keyboard_scan_codes; bool showconsole_init = false; #if C_DEBUG bool DEBUG_IsDebuggerConsoleVisible(void); #endif void DOSBox_ShowConsole() { #if defined(WIN32) && !defined(HX_DOS) CONSOLE_SCREEN_BUFFER_INFO csbi; COORD crd; HWND hwnd; #if C_DEBUG /* if the debugger has already taken the console, do nothing */ if (DEBUG_IsDebuggerConsoleVisible()) return; #endif /* if WE have already opened the console, do nothing */ if (showconsole_init) return; /* Microsoft Windows: Allocate a console and begin spewing to it. DOSBox is compiled on Windows platforms as a Win32 application, and therefore, no console. */ /* FIXME: What about "file handles" 0/1/2 emulated by C library, for use with _open/_close/_lseek/etc? */ AllocConsole(); GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); crd = csbi.dwSize; crd.X = 130; SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), crd); hwnd = GetConsoleWindow(); ShowWindow(hwnd, SW_MAXIMIZE); freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); showconsole_init = true; #endif } void DOSBox_ConsolePauseWait() { char c; printf("Hit ENTER to continue\n"); do { if (fread(&c, 1, 1, stdin) != 1) break; } while (!(c == 13 || c == 10)); /* wait for Enter key */ } bool DOSBOX_parse_argv() { std::string optname,tmp; assert(control != NULL); assert(control->cmdline != NULL); control->cmdline->BeginOpt(); while (control->cmdline->GetOpt(optname)) { std::transform(optname.begin(), optname.end(), optname.begin(), ::tolower); if (optname == "version") { DOSBox_ShowConsole(); fprintf(stderr,"\nDOSBox version %s, copyright 2002-2015 DOSBox Team.\n\n",VERSION); fprintf(stderr,"DOSBox is written by the DOSBox Team (See AUTHORS file))\n"); fprintf(stderr,"DOSBox comes with ABSOLUTELY NO WARRANTY. This is free software,\n"); fprintf(stderr,"and you are welcome to redistribute it under certain conditions;\n"); fprintf(stderr,"please read the COPYING file thoroughly before doing so.\n\n"); #if defined(WIN32) DOSBox_ConsolePauseWait(); #endif return 0; } else if (optname == "h" || optname == "help") { DOSBox_ShowConsole(); fprintf(stderr,"\ndosbox [options]\n"); fprintf(stderr,"\nDOSBox version %s, copyright 2002-2015 DOSBox Team.\n\n",VERSION); fprintf(stderr," -h -help Show this help\n"); fprintf(stderr," -editconf Launch editor\n"); fprintf(stderr," -opencaptures Launch captures\n"); fprintf(stderr," -opensaves Launch saves\n"); fprintf(stderr," -eraseconf Erase config file\n"); fprintf(stderr," -resetconf Erase config file\n"); fprintf(stderr," -printconf Print config file location\n"); fprintf(stderr," -erasemapper Erase mapper file\n"); fprintf(stderr," -resetmapper Erase mapper file\n"); fprintf(stderr," -console Show console (win32)\n"); fprintf(stderr," -noconsole Don't show console (debug+win32 only)\n"); fprintf(stderr," -nogui Don't show gui (win32 only)\n"); fprintf(stderr," -nomenu Don't show menu (win32 only)\n"); fprintf(stderr," -userconf Create user level config file\n"); fprintf(stderr," -conf Use config file \n"); fprintf(stderr," -startui -startgui Start DOSBox-X with UI\n"); fprintf(stderr," -startmapper Start DOSBox-X with mapper\n"); fprintf(stderr," -showcycles Show cycles count\n"); fprintf(stderr," -showrt Show emulation speed relative to realtime\n"); fprintf(stderr," -fullscreen Start in fullscreen\n"); fprintf(stderr," -savedir Save path\n"); fprintf(stderr," -disable-numlock-check Disable numlock check (win32 only)\n"); fprintf(stderr," -date-host-forced Force synchronization of date with host\n"); fprintf(stderr," -debug Set all logging levels to debug\n"); fprintf(stderr," -early-debug Log early initialization messages in DOSBox (implies -console)\n"); fprintf(stderr," -keydbg Log all SDL key events (debugging)\n"); fprintf(stderr," -lang Use specific message file instead of language= setting\n"); fprintf(stderr," -nodpiaware Ignore (don't signal) Windows DPI awareness\n"); fprintf(stderr," -securemode Enable secure mode\n"); fprintf(stderr," -noautoexec Don't execute AUTOEXEC.BAT config section\n"); fprintf(stderr," -exit Exit after executing AUTOEXEC.BAT\n"); fprintf(stderr," -c Execute this command in addition to AUTOEXEC.BAT.\n"); fprintf(stderr," Make sure to surround the command in quotes to cover spaces.\n"); fprintf(stderr," -break-start Break into debugger at startup\n"); fprintf(stderr," -time-limit Kill the emulator after 'n' seconds\n"); fprintf(stderr," -fastbioslogo Fast BIOS logo (skip 1-second pause)\n"); fprintf(stderr," -log-con Log CON output to a log file\n"); #if defined(WIN32) DOSBox_ConsolePauseWait(); #endif return 0; } else if (optname == "c") { if (!control->cmdline->NextOptArgv(tmp)) return false; control->opt_c.push_back(tmp); } else if (optname == "log-con") { control->opt_log_con = true; } else if (optname == "time-limit") { if (!control->cmdline->NextOptArgv(tmp)) return false; control->opt_time_limit = atof(tmp.c_str()); } else if (optname == "break-start") { control->opt_break_start = true; } else if (optname == "exit") { control->opt_exit = true; } else if (optname == "noautoexec") { control->opt_noautoexec = true; } else if (optname == "securemode") { control->opt_securemode = true; } else if (optname == "nodpiaware") { control->opt_disable_dpi_awareness = true; } else if (optname == "keydbg") { log_keyboard_scan_codes = true; } else if (optname == "date-host-forced" || optname == "date_host_forced") { control->opt_date_host_forced = true; } else if (optname == "showrt") { control->opt_showrt = true; } else if (optname == "showcycles") { control->opt_showcycles = true; } else if (optname == "startmapper") { control->opt_startmapper = true; } else if (optname == "fullscreen") { control->opt_fullscreen = true; } else if (optname == "startui" || optname == "startgui") { control->opt_startui = true; } else if (optname == "disable-numlock-check" || optname == "disable_numlock_check") { /* mainline DOSBox expects -disable_numlock_check so we support that here too */ control->opt_disable_numlock_check = true; } else if (optname == "savedir") { if (!control->cmdline->NextOptArgv(custom_savedir)) return false; } else if (optname == "userconf") { control->opt_userconf = true; } else if (optname == "lang") { if (!control->cmdline->NextOptArgv(control->opt_lang)) return false; } else if (optname == "fastbioslogo") { control->opt_fastbioslogo = true; } else if (optname == "conf") { if (!control->cmdline->NextOptArgv(tmp)) return false; control->config_file_list.push_back(tmp); } else if (optname == "editconf") { if (!control->cmdline->NextOptArgv(control->opt_editconf)) return false; } else if (optname == "opencaptures") { if (!control->cmdline->NextOptArgv(control->opt_opencaptures)) return false; } else if (optname == "opensaves") { if (!control->cmdline->NextOptArgv(control->opt_opensaves)) return false; } else if (optname == "eraseconf") { control->opt_eraseconf = true; } else if (optname == "resetconf") { control->opt_resetconf = true; } else if (optname == "printconf") { control->opt_printconf = true; } else if (optname == "erasemapper") { control->opt_erasemapper = true; } else if (optname == "resetmapper") { control->opt_resetmapper = true; } else if (optname == "noconsole") { control->opt_noconsole = true; control->opt_console = false; } else if (optname == "console") { control->opt_noconsole = false; control->opt_console = true; } else if (optname == "nomenu") { control->opt_nomenu = true; } else if (optname == "nogui") { control->opt_nogui = true; } else if (optname == "debug") { control->opt_debug = true; } else if (optname == "early-debug") { control->opt_earlydebug = true; control->opt_console = true; } else { printf("WARNING: Unknown option %s (first parsing stage)\n",optname.c_str()); } } /* now that the above loop has eaten all the options from the command * line, scan the command line for batch files to run. * https://github.com/joncampbell123/dosbox-x/issues/369 */ control->cmdline->BeginOpt(/*don't eat*/false); while (!control->cmdline->CurrentArgvEnd()) { control->cmdline->GetCurrentArgv(tmp); { struct stat st; const char *ext = strrchr(tmp.c_str(),'.'); if (ext != NULL) { /* if it looks like a file... with an extension */ if (stat(tmp.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { if (!strcasecmp(ext,".bat")) { /* .BAT files given on the command line trigger automounting C: to run it */ control->auto_bat_additional.push_back(tmp); control->cmdline->EatCurrentArgv(); continue; } } } } control->cmdline->NextArgv(); } return true; } void MSG_Init(); void DOSBOX_RealInit(); void DOSBOX_InitTickLoop(); void TIMER_ShutdownTickHandlers(); void DOSBOX_SetupConfigSections(void); void PAGING_Init(); void IO_Init(); void Init_VGABIOS(); void Init_AddressLimitAndGateMask(); void Init_RAM(); void Init_MemHandles(); void Init_MemoryAccessArray(); void Init_PCJR_CartridgeROM(); void Init_PS2_Port_92h(); void Init_A20_Gate(); void HARDWARE_Init(); void CAPTURE_Init(); void ROMBIOS_Init(); void CALLBACK_Init(); void Init_DMA(); void Init_PIC(); void PCIBUS_Init(); void PROGRAMS_Init(); void RENDER_Init(); void TIMER_Init(); void CMOS_Init(); void VGA_Init(); void CPU_Init(); void ISAPNP_Cfg_Init(); #if C_FPU void FPU_Init(); #endif void KEYBOARD_Init(); void VOODOO_Init(); void MIXER_Init(); void MIDI_Init(); /* Init all the sections */ void MPU401_Init(); #if C_DEBUG void DEBUG_Init(); #endif void SBLASTER_Init(); void GUS_Init(); void INNOVA_Init(); void PCSPEAKER_Init(); void TANDYSOUND_Init(); void DISNEY_Init(); void PS1SOUND_Init(); void BIOS_Init(); void INT10_Init(); void JOYSTICK_Init(); void SERIAL_Init(); void PARALLEL_Init(); void DONGLE_Init(); void DOS_Init(); void XMS_Init(); void EMS_Init(); void MOUSE_Init(); void DOS_KeyboardLayout_Init(); void MSCDEX_Init(); void DRIVES_Init(); void IPX_Init(); void IDE_Init(); void NE2K_Init(); void FDC_Primary_Init(); void AUTOEXEC_Init(); #if defined(WIN32) extern bool dpi_aware_enable; // NTS: I intend to add code that not only indicates High DPI awareness but also queries the monitor DPI // and then factor the DPI into DOSBox's scaler and UI decisions. void Windows_DPI_Awareness_Init() { // if the user says not to from the command line, or disables it from dosbox.conf, then don't enable DPI awareness. if (!dpi_aware_enable || control->opt_disable_dpi_awareness) return; /* log it */ LOG(LOG_MISC,LOG_DEBUG)("Win32: I will announce High DPI awareness to Windows to eliminate upscaling"); // turn off DPI scaling so DOSBox-X doesn't look so blurry on Windows 8 & Windows 10. // use GetProcAddress and LoadLibrary so that these functions are not hard dependencies that prevent us from // running under Windows 7 or XP. // // I'm also told that Windows 8.1 has SetProcessDPIAwareness but nobody seems to know where it is. // Perhaps the tooth fairy can find it for me. Come on, Microsoft get your act together! [https://msdn.microsoft.com/en-us/library/windows/desktop/dn302122(v=vs.85).aspx] BOOL (WINAPI *__SetProcessDPIAware)(void) = NULL; // vista/7/8/10 HMODULE __user32; __user32 = GetModuleHandle("USER32.DLL"); if (__user32) __SetProcessDPIAware = (BOOL(WINAPI *)(void))GetProcAddress(__user32, "SetProcessDPIAware"); if (__SetProcessDPIAware) { LOG(LOG_MISC,LOG_DEBUG)("USER32.DLL exports SetProcessDPIAware function, calling it to signal we are DPI aware."); __SetProcessDPIAware(); } } #endif bool VM_Boot_DOSBox_Kernel() { if (!dos_kernel_disabled) { RemoveEMSPageFrame(); RemoveUMBBlock(); DisableINT33(); DOS_GetMemory_unmap(); VFILE_Shutdown(); PROGRAMS_Shutdown(); DOS_UninstallMisc(); SBLASTER_DOS_Shutdown(); GUS_DOS_Shutdown(); EMS_DoShutDown(); XMS_DoShutDown(); DOS_DoShutDown(); DispatchVMEvent(VM_EVENT_DOS_SURPRISE_REBOOT); // <- apparently we rebooted without any notification (such as jmp'ing to FFFF:0000) dos_kernel_disabled = true; #if defined(WIN32) && !defined(C_SDL2) int Reflect_Menu(void); Reflect_Menu(); #endif } if (dos_kernel_disabled) { DispatchVMEvent(VM_EVENT_DOS_BOOT); // <- just starting the DOS kernel now /* DOS kernel init */ dos_kernel_disabled = false; // FIXME: DOS_Init should install VM callback handler to set this void DOS_Startup(Section* sec); DOS_Startup(NULL); #if defined(WIN32) && !defined(C_SDL2) int Reflect_Menu(void); Reflect_Menu(); #endif void update_pc98_function_row(bool enable); void DRIVES_Startup(Section *s); DRIVES_Startup(NULL); /* NEC's function key row seems to be deeply embedded in the CON driver. Am I wrong? */ if (IS_PC98_ARCH) update_pc98_function_row(true); DispatchVMEvent(VM_EVENT_DOS_INIT_KERNEL_READY); // <- kernel is ready /* keyboard mapping, at this point in CONFIG.SYS parsing, right? */ void DOS_KeyboardLayout_Startup(Section* sec); DOS_KeyboardLayout_Startup(NULL); /* Most MS-DOS installations have a DEVICE=C:\HIMEM.SYS somewhere near the top of their CONFIG.SYS */ void XMS_Startup(Section *sec); XMS_Startup(NULL); /* And then after that, usually a DEVICE=C:\EMM386.EXE just after HIMEM.SYS */ void EMS_Startup(Section* sec); EMS_Startup(NULL); DispatchVMEvent(VM_EVENT_DOS_INIT_CONFIG_SYS_DONE); // <- we just finished executing CONFIG.SYS SHELL_Init(); // <- NTS: this will change CPU instruction pointer! DispatchVMEvent(VM_EVENT_DOS_INIT_SHELL_READY); // <- we just finished loading the shell (COMMAND.COM) /* it's time to init parsing AUTOEXEC.BAT */ void AUTOEXEC_Startup(Section *sec); AUTOEXEC_Startup(NULL); /* Most MS-DOS installations run MSCDEX.EXE from somewhere in AUTOEXEC.BAT. We do the same here, in a fashion. */ /* TODO: Can we make this an OPTION if the user doesn't want to make MSCDEX.EXE resident? */ /* TODO: When we emulate executing AUTOEXEC.BAT between INIT_SHELL_READY and AUTOEXEC_BAT_DONE, can we make a fake MSCDEX.EXE within drive Z:\ * and auto-add a Z:\MSCDEX.EXE to the top of AUTOEXEC.BAT, command line switches and all. if the user has not already added it? */ void MSCDEX_Startup(Section* sec); MSCDEX_Startup(NULL); /* Some installations load the MOUSE.COM driver from AUTOEXEC.BAT as well */ /* TODO: Can we make this an option? Can we add a fake MOUSE.COM to the Z:\ drive as well? */ void MOUSE_Startup(Section *sec); MOUSE_Startup(NULL); DispatchVMEvent(VM_EVENT_DOS_INIT_AUTOEXEC_BAT_DONE); // <- we just finished executing AUTOEXEC.BAT DispatchVMEvent(VM_EVENT_DOS_INIT_AT_PROMPT); // <- now, we're at the DOS prompt SHELL_Run(); } return true; } bool VM_PowerOn() { if (!guest_machine_power_on) { // powering on means power on event, followed by reset assert, then reset deassert guest_machine_power_on = true; DispatchVMEvent(VM_EVENT_POWERON); DispatchVMEvent(VM_EVENT_RESET); DispatchVMEvent(VM_EVENT_RESET_END); } return true; } void update_pc98_clock_pit_menu(void) { Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); int pc98rate = dosbox_section->Get_int("pc-98 timer master frequency"); if (pc98rate > 6) pc98rate /= 2; if (pc98rate == 0) pc98rate = 5; /* Pick the most likely to work with DOS games (FIXME: This is a GUESS!! Is this correct?) */ else if (pc98rate < 5) pc98rate = 4; else pc98rate = 5; mainMenu.get_item("dos_pc98_pit_4mhz").check(pc98rate == 4).refresh_item(mainMenu); mainMenu.get_item("dos_pc98_pit_5mhz").check(pc98rate == 5).refresh_item(mainMenu); } bool dos_pc98_clock_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED void TIMER_OnEnterPC98_Phase2(Section*); void TIMER_OnEnterPC98_Phase2_UpdateBDA(void); const char *ts = menuitem->get_name().c_str(); if (!strncmp(ts,"dos_pc98_pit_",13)) ts += 13; else return true; std::string tmp = "pc-98 timer master frequency="; { char tmp1[64]; sprintf(tmp1,"%u",atoi(ts)); tmp += tmp1; } Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); dosbox_section->HandleInputline(tmp.c_str()); TIMER_OnEnterPC98_Phase2(NULL); TIMER_OnEnterPC98_Phase2_UpdateBDA(); update_pc98_clock_pit_menu(); return true; } void SetScaleForced(bool forced); void OutputSettingMenuUpdate(void); bool scaler_forced_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED SetScaleForced(!render.scale.forced); return true; } void MENU_swapstereo(bool enabled); bool MENU_get_swapstereo(void); bool mixer_swapstereo_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED MENU_swapstereo(!MENU_get_swapstereo()); return true; } void MENU_mute(bool enabled); bool MENU_get_mute(void); bool mixer_mute_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED MENU_mute(!MENU_get_mute()); return true; } bool dos_mouse_enable_int33_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED extern bool Mouse_Drv; Mouse_Drv = !Mouse_Drv; mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu); return true; } bool dos_mouse_y_axis_reverse_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED extern bool Mouse_Vertical; Mouse_Vertical = !Mouse_Vertical; mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu); return true; } bool dos_mouse_sensitivity_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED #if !defined(C_SDL2) GUI_Shortcut(2); #endif return true; } bool vid_pc98_5mhz_gdc_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED if (IS_PC98_ARCH) { void gdc_5mhz_mode_update_vars(void); extern bool gdc_5mhz_mode; gdc_5mhz_mode = !gdc_5mhz_mode; gdc_5mhz_mode_update_vars(); Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); if (gdc_5mhz_mode) dosbox_section->HandleInputline("pc-98 start gdc at 5mhz=1"); else dosbox_section->HandleInputline("pc-98 start gdc at 5mhz=0"); mainMenu.get_item("pc98_5mhz_gdc").check(gdc_5mhz_mode).refresh_item(mainMenu); } return true; } bool vid_pc98_200scanline_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED if (IS_PC98_ARCH) { extern bool pc98_allow_scanline_effect; pc98_allow_scanline_effect = !pc98_allow_scanline_effect; Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); if (pc98_allow_scanline_effect) dosbox_section->HandleInputline("pc-98 allow scanline effect=1"); else dosbox_section->HandleInputline("pc-98 allow scanline effect=0"); mainMenu.get_item("pc98_allow_200scanline").check(pc98_allow_scanline_effect).refresh_item(mainMenu); } return true; } bool vid_pc98_4parts_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED if (IS_PC98_ARCH) { extern bool pc98_allow_4_display_partitions; void updateGDCpartitions4(bool enable); updateGDCpartitions4(!pc98_allow_4_display_partitions); Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); if (pc98_allow_4_display_partitions) dosbox_section->HandleInputline("pc-98 allow 4 display partition graphics=1"); else dosbox_section->HandleInputline("pc-98 allow 4 display partition graphics=0"); mainMenu.get_item("pc98_allow_4partitions").check(pc98_allow_4_display_partitions).refresh_item(mainMenu); } return true; } bool vid_pc98_enable_egc_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED void gdc_egc_enable_update_vars(void); extern bool enable_pc98_egc; extern bool enable_pc98_grcg; extern bool enable_pc98_16color; if(IS_PC98_ARCH) { enable_pc98_egc = !enable_pc98_egc; gdc_egc_enable_update_vars(); Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); if (enable_pc98_egc) { dosbox_section->HandleInputline("pc-98 enable egc=1"); if(!enable_pc98_grcg) { //Also enable GRCG if GRCG is disabled when enabling EGC enable_pc98_grcg = !enable_pc98_grcg; mem_writeb(0x54C,(enable_pc98_grcg ? 0x02 : 0x00) | (enable_pc98_16color ? 0x04 : 0x00)); dosbox_section->HandleInputline("pc-98 enable grcg=1"); } } else dosbox_section->HandleInputline("pc-98 enable egc=0"); mainMenu.get_item("pc98_enable_egc").check(enable_pc98_egc).refresh_item(mainMenu); mainMenu.get_item("pc98_enable_grcg").check(enable_pc98_grcg).refresh_item(mainMenu); } return true; } bool vid_pc98_enable_grcg_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED extern bool enable_pc98_grcg; extern bool enable_pc98_egc; void gdc_grcg_enable_update_vars(void); if(IS_PC98_ARCH) { enable_pc98_grcg = !enable_pc98_grcg; gdc_grcg_enable_update_vars(); Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); if (enable_pc98_grcg) dosbox_section->HandleInputline("pc-98 enable grcg=1"); else dosbox_section->HandleInputline("pc-98 enable grcg=0"); if ((!enable_pc98_grcg) && enable_pc98_egc) { // Also disable EGC if switching off GRCG void gdc_egc_enable_update_vars(void); enable_pc98_egc = !enable_pc98_egc; gdc_egc_enable_update_vars(); dosbox_section->HandleInputline("pc-98 enable egc=0"); } mainMenu.get_item("pc98_enable_egc").check(enable_pc98_egc).refresh_item(mainMenu); mainMenu.get_item("pc98_enable_grcg").check(enable_pc98_grcg).refresh_item(mainMenu); } return true; } bool vid_pc98_enable_analog_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED //NOTE: I thought that even later PC-9801s and some PC-9821s could use EGC features in digital 8-colors mode? extern bool enable_pc98_16color; void gdc_16color_enable_update_vars(void); if(IS_PC98_ARCH) { enable_pc98_16color = !enable_pc98_16color; gdc_16color_enable_update_vars(); Section_prop * dosbox_section = static_cast(control->GetSection("dosbox")); if (enable_pc98_16color) dosbox_section->HandleInputline("pc-98 enable 16-color=1"); else dosbox_section->HandleInputline("pc-98 enable 16-color=0"); mainMenu.get_item("pc98_enable_analog").check(enable_pc98_16color).refresh_item(mainMenu); } return true; } bool vid_pc98_cleartext_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED void pc98_clear_text(void); if (IS_PC98_ARCH) pc98_clear_text(); return true; } bool vid_pc98_graphics_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED void pc98_clear_graphics(void); if (IS_PC98_ARCH) pc98_clear_graphics(); return true; } bool overscan_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED int f = atoi(menuitem->get_text().c_str()); /* Off becomes 0 */ char tmp[64]; sprintf(tmp,"%u",f); SetVal("sdl", "overscan", tmp); change_output(7); return true; } void UpdateOverscanMenu(void) { for (size_t i=0;i <= 10;i++) { char tmp[64]; sprintf(tmp,"overscan_%zu",i); mainMenu.get_item(tmp).check(sdl.overscan_width == i).refresh_item(mainMenu); } } bool vsync_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED #if !defined(C_SDL2) const char *val = menuitem->get_name().c_str(); if (!strncmp(val,"vsync_",6)) val += 6; else return true; SetVal("vsync", "vsyncmode", val); void change_output(int output); change_output(8); VGA_Vsync VGA_Vsync_Decode(const char *vsyncmodestr); void VGA_VsyncUpdateMode(VGA_Vsync vsyncmode); VGA_VsyncUpdateMode(VGA_Vsync_Decode(val)); #endif return true; } bool vsync_set_syncrate_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED #if !defined(C_SDL2) GUI_Shortcut(17); #endif return true; } bool output_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED const char *what = menuitem->get_name().c_str(); if (!strncmp(what,"output_",7)) what += 7; else return true; if (!strcmp(what,"surface")) { if (sdl.desktop.want_type == SCREEN_SURFACE) return true; change_output(0); } else if (!strcmp(what,"opengl")) { #if C_OPENGL if (sdl.desktop.want_type == SCREEN_OPENGL && sdl.opengl.bilinear) return true; change_output(3); #endif } else if (!strcmp(what,"openglnb")) { #if C_OPENGL if (sdl.desktop.want_type == SCREEN_OPENGL && !sdl.opengl.bilinear) return true; change_output(4); #endif } else if (!strcmp(what,"direct3d")) { if (sdl.desktop.want_type == SCREEN_DIRECT3D) return true; change_output(5); } SetVal("sdl", "output", what); OutputSettingMenuUpdate(); return true; } bool MENU_SetBool(std::string secname, std::string value); bool vga_9widetext_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED MENU_SetBool("render", "char9"); return true; } bool doublescan_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED MENU_SetBool("render", "doublescan"); return true; } bool scaler_set_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED const char *scaler = menuitem->get_name().c_str(); if (!strncmp(scaler,"scaler_set_",11)) scaler += 11; else abort(); auto value = std::string(scaler) + (render.scale.forced ? " forced" : ""); SetVal("render", "scaler", value); void RENDER_UpdateFromScalerSetting(void); RENDER_UpdateFromScalerSetting(); void RENDER_UpdateScalerMenu(void); RENDER_UpdateScalerMenu(); void RENDER_CallBack( GFX_CallBackFunctions_t function ); RENDER_CallBack(GFX_CallBackReset); return true; } bool video_frameskip_common_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED int f = atoi(menuitem->get_text().c_str()); /* Off becomes 0 */ char tmp[64]; sprintf(tmp,"%u",f); SetVal("render", "frameskip", tmp); return true; } bool show_console_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED DOSBox_ShowConsole(); mainMenu.get_item("show_console").check(true).refresh_item(mainMenu); return true; } bool wait_on_error_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED sdl.wait_on_error = !sdl.wait_on_error; mainMenu.get_item("wait_on_error").check(sdl.wait_on_error).refresh_item(mainMenu); return true; } bool autolock_mouse_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED sdl.mouse.autoenable = !sdl.mouse.autoenable; mainMenu.get_item("auto_lock_mouse").check(sdl.mouse.autoenable).refresh_item(mainMenu); return true; } bool doublebuf_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED SetVal("sdl", "fulldouble", (GetSetSDLValue(1, "desktop.doublebuf", 0)) ? "false" : "true"); res_init(); mainMenu.get_item("doublebuf").check(!!GetSetSDLValue(1, "desktop.doublebuf", 0)).refresh_item(mainMenu); return true; } #if defined(LINUX) && !defined(C_SDL2) bool x11_on_top = false; #endif bool is_always_on_top(void) { #if defined(_WIN32) && !defined(C_SDL2) DWORD dwExStyle = ::GetWindowLong(GetHWND(), GWL_EXSTYLE); return !!(dwExStyle & WS_EX_TOPMOST); #elif defined(LINUX) && !defined(C_SDL2) return x11_on_top; #else return false; #endif } #if defined(_WIN32) && !defined(C_SDL2) extern "C" void sdl1_hax_set_topmost(unsigned char topmost); #endif void toggle_always_on_top(void) { bool cur = is_always_on_top(); #if defined(_WIN32) && !defined(C_SDL2) sdl1_hax_set_topmost(!cur); #elif defined(LINUX) && !defined(C_SDL2) void LinuxX11_OnTop(bool f); LinuxX11_OnTop(x11_on_top = (!cur)); #else (void)cur; #endif } bool showdetails_menu_callback(DOSBoxMenu * const xmenu, DOSBoxMenu::item * const menuitem) { (void)xmenu;//UNUSED (void)menuitem;//UNUSED menu.hidecycles = !menu.hidecycles; GFX_SetTitle(CPU_CycleMax, -1, -1, false); mainMenu.get_item("showdetails").check(!menu.hidecycles).refresh_item(mainMenu); return true; } bool alwaysontop_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED (void)menuitem;//UNUSED toggle_always_on_top(); mainMenu.get_item("alwaysontop").check(is_always_on_top()).refresh_item(mainMenu); return true; } bool sendkey_preset_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) { (void)menu;//UNUSED if (menuitem->get_name() == "sendkey_ctrlesc") { KEYBOARD_AddKey(KBD_leftctrl, true); KEYBOARD_AddKey(KBD_esc, true); KEYBOARD_AddKey(KBD_leftctrl, false); KEYBOARD_AddKey(KBD_esc, false); } else if (menuitem->get_name() == "sendkey_alttab") { KEYBOARD_AddKey(KBD_leftalt, true); KEYBOARD_AddKey(KBD_tab, true); KEYBOARD_AddKey(KBD_leftalt, false); KEYBOARD_AddKey(KBD_tab, false); } else if (menuitem->get_name() == "sendkey_winlogo") { KEYBOARD_AddKey(KBD_lwindows, true); KEYBOARD_AddKey(KBD_lwindows, false); } else if (menuitem->get_name() == "sendkey_winmenu") { KEYBOARD_AddKey(KBD_rwinmenu, true); KEYBOARD_AddKey(KBD_rwinmenu, false); } else if (menuitem->get_name() == "sendkey_cad") { KEYBOARD_AddKey(KBD_leftctrl, true); KEYBOARD_AddKey(KBD_leftalt, true); KEYBOARD_AddKey(KBD_delete, true); KEYBOARD_AddKey(KBD_leftctrl, false); KEYBOARD_AddKey(KBD_leftalt, false); KEYBOARD_AddKey(KBD_delete, false); } return true; } void SetCyclesCount_mapper_shortcut_RunInternal(void) { #if !defined(C_SDL2) void MAPPER_ReleaseAllKeys(void); MAPPER_ReleaseAllKeys(); GFX_LosingFocus(); GUI_Shortcut(16); void MAPPER_ReleaseAllKeys(void); MAPPER_ReleaseAllKeys(); GFX_LosingFocus(); #endif } void SetCyclesCount_mapper_shortcut_RunEvent(Bitu /*val*/) { KEYBOARD_ClrBuffer(); //Clear buffer GFX_LosingFocus(); //Release any keys pressed (buffer gets filled again). SetCyclesCount_mapper_shortcut_RunInternal(); } void SetCyclesCount_mapper_shortcut(bool pressed) { if (!pressed) return; PIC_AddEvent(SetCyclesCount_mapper_shortcut_RunEvent, 0.0001f); //In case mapper deletes the key object that ran it } void AspectRatio_mapper_shortcut(bool pressed) { if (!pressed) return; if (!GFX_GetPreventFullscreen()) { SetVal("render", "aspect", render.aspect ? "false" : "true"); mainMenu.get_item("mapper_aspratio").check(render.aspect).refresh_item(mainMenu); } } void HideMenu_mapper_shortcut(bool pressed) { if (!pressed) return; void ToggleMenu(bool pressed); ToggleMenu(true); mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu); } void OutputSettingMenuUpdate(void) { mainMenu.get_item("output_surface").check(sdl.desktop.want_type==SCREEN_SURFACE).refresh_item(mainMenu); mainMenu.get_item("output_direct3d").check(sdl.desktop.want_type==SCREEN_DIRECT3D).refresh_item(mainMenu); #if C_OPENGL mainMenu.get_item("output_opengl").check(sdl.desktop.want_type==SCREEN_OPENGL && sdl.opengl.bilinear).refresh_item(mainMenu); mainMenu.get_item("output_openglnb").check(sdl.desktop.want_type==SCREEN_OPENGL && !sdl.opengl.bilinear).refresh_item(mainMenu); #endif } bool custom_bios = false; //extern void UI_Init(void); int main(int argc, char* argv[]) SDL_MAIN_NOEXCEPT { CommandLine com_line(argc,argv); Config myconf(&com_line); bitop::self_test(); ptrop::self_test(); memset(&sdl,0,sizeof(sdl)); // struct sdl isn't initialized anywhere that I can tell // initialize some defaults in SDL structure here sdl.srcAspect.x = 4; sdl.srcAspect.y = 3; sdl.srcAspect.xToY = (double)sdl.srcAspect.x / sdl.srcAspect.y; sdl.srcAspect.yToX = (double)sdl.srcAspect.y / sdl.srcAspect.x; #if C_XBRZ sdl.xBRZ.task_granularity = 16; sdl.xBRZ.max_scale_factor = xbrz::SCALE_FACTOR_MAX; #endif control=&myconf; #if defined(WIN32) && !defined(HX_DOS) /* Microsoft's IME does not play nice with DOSBox */ ImmDisableIME((DWORD)(-1)); #endif #if defined(MACOSX) /* The resource system of DOSBox-X relies on being able to locate the Resources subdirectory within the DOSBox-X .app bundle. To do this, we have to first know where our own executable is, which Mac OS X helpfully puts int argv[0] for us */ /* NTS: Experimental testing shows that when we are run from the desktop (double-clicking on the .app bundle from the Finder) the current working directory is / (fs root). */ extern std::string MacOSXEXEPath; extern std::string MacOSXResPath; MacOSXEXEPath = argv[0]; /* The path should be something like /blah/blah/dosbox-x.app/Contents/MacOS/DosBox */ /* If that's true, then we can move one level up the tree and look for */ /* /blah/blah/dosbox-x.app/Contents/Resources */ { const char *ref = argv[0]; const char *s = strrchr(ref,'/'); if (s != NULL) { if (s > ref) s--; while (s > ref && *s != '/') s--; if (!strncasecmp(s,"/MacOS/",7)) { MacOSXResPath = std::string(ref,(size_t)(s-ref)) + "/Resources"; } } } /* If we were launched by the Finder, the current working directory will usually be the root of the filesystem (/) which is useless. If we see that, change instead to the user's home directory */ { char *home = getenv("HOME"); char cwd[512]; cwd[0]=0; getcwd(cwd,sizeof(cwd)-1); if (!strcmp(cwd,"/")) { /* Only the Finder would do that. Even if the user somehow did this from the Terminal app, it's still worth changing to the home directory because certain directories including / are locked readonly even for sudo in Mac OS X */ /* NTS: HOME is usually an absolute path */ if (home != NULL) chdir(home); } } #endif { std::string tmp,config_path; /* -- parse command line arguments */ if (!DOSBOX_parse_argv()) return 1; if (control->opt_time_limit > 0) time_limit_ms = (Bitu)(control->opt_time_limit * 1000); if (control->opt_console) DOSBox_ShowConsole(); /* -- Handle some command line options */ if (control->opt_eraseconf || control->opt_resetconf) eraseconfigfile(); if (control->opt_printconf) printconfiglocation(); if (control->opt_erasemapper || control->opt_resetmapper) erasemapperfile(); /* -- Early logging init, in case these details are needed to debug problems at this level */ /* If --early-debug was given this opens up logging to STDERR until Log::Init() */ LOG::EarlyInit(); #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) { DISPLAY_DEVICE dd; unsigned int i = 0; do { memset(&dd, 0, sizeof(dd)); dd.cb = sizeof(dd); if (!EnumDisplayDevices(NULL, i, &dd, 0)) break; LOG_MSG("Win32 EnumDisplayDevices #%d: name=%s string=%s", i, dd.DeviceName, dd.DeviceString); i++; if (strstr(dd.DeviceString, "VirtualBox") != NULL) isVirtualBox = true; } while (1); } #endif /* -- Init the configuration system and add default values */ CheckNumLockState(); /* -- setup the config sections for config parsing */ LOG::SetupConfigSection(); SDL_SetupConfigSection(); DOSBOX_SetupConfigSections(); /* -- Parse configuration files */ Cross::GetPlatformConfigDir(config_path); /* -- -- first the user config file */ if (control->opt_userconf) { tmp.clear(); Cross::GetPlatformConfigDir(config_path); Cross::GetPlatformConfigName(tmp); config_path += tmp; LOG(LOG_MISC,LOG_DEBUG)("Loading config file according to -userconf from %s",config_path.c_str()); control->ParseConfigFile(config_path.c_str()); if (!control->configfiles.size()) { //Try to create the userlevel configfile. tmp.clear(); Cross::CreatePlatformConfigDir(config_path); Cross::GetPlatformConfigName(tmp); config_path += tmp; LOG(LOG_MISC,LOG_DEBUG)("Attempting to write config file according to -userconf, to %s",config_path.c_str()); if (control->PrintConfig(config_path.c_str())) { LOG(LOG_MISC,LOG_NORMAL)("Generating default configuration. Writing it to %s",config_path.c_str()); //Load them as well. Makes relative paths much easier control->ParseConfigFile(config_path.c_str()); } } } /* -- -- second the -conf switches from the command line */ for (size_t si=0;si < control->config_file_list.size();si++) { std::string &cfg = control->config_file_list[si]; if (!control->ParseConfigFile(cfg.c_str())) { // try to load it from the user directory control->ParseConfigFile((config_path + cfg).c_str()); } } /* -- -- if none found, use dosbox.conf */ if (!control->configfiles.size()) control->ParseConfigFile("dosbox.conf"); /* -- -- if none found, use userlevel conf */ if (!control->configfiles.size()) { tmp.clear(); Cross::GetPlatformConfigName(tmp); control->ParseConfigFile((config_path + tmp).c_str()); } #if (ENVIRON_LINKED) /* -- parse environment block (why?) */ control->ParseEnv(environ); #endif /* -- initialize logging first, so that higher level inits can report problems to the log file */ LOG::Init(); #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) { DISPLAY_DEVICE dd; unsigned int i = 0; do { memset(&dd, 0, sizeof(dd)); dd.cb = sizeof(dd); if (!EnumDisplayDevices(NULL, i, &dd, 0)) break; LOG_MSG("Win32 EnumDisplayDevices #%d: name=%s string=%s", i, dd.DeviceName, dd.DeviceString); i++; } while (1); } if (isVirtualBox) LOG_MSG("Win32 VirtualBox graphics adapter detected"); #endif /* -- Welcome to DOSBox-X! */ LOG_MSG("DOSBox-X version %s",VERSION); LOG(LOG_MISC,LOG_NORMAL)("Copyright 2002-2015 enhanced branch by The Great Codeholio, forked from the main project by the DOSBox Team, published under GNU GPL."); #if defined(MACOSX) LOG_MSG("Mac OS X EXE path: %s",MacOSXEXEPath.c_str()); LOG_MSG("Mac OS X Resource path: %s",MacOSXResPath.c_str()); #endif /* -- [debug] setup console */ #if C_DEBUG # if defined(WIN32) && !defined(HX_DOS) /* Can't disable the console with debugger enabled */ if (control->opt_noconsole) { LOG(LOG_MISC,LOG_DEBUG)("-noconsole: hiding Win32 console window"); ShowWindow(GetConsoleWindow(), SW_HIDE); DestroyWindow(GetConsoleWindow()); } # endif #endif #if defined(WIN32) /* -- Windows: set console control handler */ SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleEventHandler,TRUE); #endif #if !defined(C_SDL2) { int id, major, minor; DOSBox_CheckOS(id, major, minor); if (id == 1) menu.compatible=true; /* use all variables to shut up the compiler about unused vars */ LOG(LOG_MISC,LOG_DEBUG)("DOSBox_CheckOS results: id=%u major=%u minor=%u",id,major,minor); } #endif /* -- SDL init hackery */ #if SDL_VERSION_ATLEAST(1, 2, 14) /* hack: On debian/ubuntu with older libsdl version as they have done this themselves, but then differently. * with this variable they will work correctly. I've only tested the 1.2.14 behaviour against the windows version of libsdl */ putenv(const_cast("SDL_DISABLE_LOCK_KEYS=1")); LOG(LOG_GUI,LOG_DEBUG)("SDL 1.2.14 hack: SDL_DISABLE_LOCK_KEYS=1"); #endif #ifdef WIN32 /* hack: Encourage SDL to use windib if not otherwise specified */ if (getenv("SDL_VIDEODRIVER") == NULL) { #if defined(C_SDL2) LOG(LOG_GUI, LOG_DEBUG)("Win32 hack: setting SDL_VIDEODRIVER=windows because environ variable is not set"); putenv("SDL_VIDEODRIVER=windows"); #else LOG(LOG_GUI,LOG_DEBUG)("Win32 hack: setting SDL_VIDEODRIVER=windib because environ variable is not set"); putenv("SDL_VIDEODRIVER=windib"); #endif sdl.using_windib=true; } #endif #if defined(WIN32) && defined(C_SDL2) /* HACK: WASAPI output on Windows 10 isn't working... */ if (getenv("SDL_AUDIODRIVER") == NULL) { LOG(LOG_GUI, LOG_DEBUG)("Win32: using directsound audio driver"); putenv("SDL_AUDIODRIVER=directsound"); } #endif sdl.init_ignore = true; #ifdef WIN32 /* Windows Vista/7/8/10 DPI awareness. If we don't tell Windows we're high DPI aware, the DWM will * upscale our window to emulate a 96 DPI display which on high res screen will make our UI look blurry. * But we obey the user if they don't want us to do that. */ Windows_DPI_Awareness_Init(); #endif /* -- SDL init */ #if defined(C_SDL2) if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER|/*SDL_INIT_CDROM|*/SDL_INIT_NOPARACHUTE) >= 0) #else if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_CDROM|SDL_INIT_NOPARACHUTE) >= 0) #endif sdl.inited = true; else E_Exit("Can't init SDL %s",SDL_GetError()); /* -- -- decide whether to show menu in GUI */ if (control->opt_nogui || menu.compatible) menu.gui=false; /* -- -- helpful advice */ LOG(LOG_GUI,LOG_NORMAL)("Press Ctrl-F10 to capture/release mouse, Alt-F10 for configuration."); /* -- -- other steps to prepare SDL window/output */ SDL_Prepare(); /* -- -- Keyboard layout detection and setup */ KeyboardLayoutDetect(); SetMapperKeyboardLayout(host_keyboard_layout); /* -- -- Initialise Joystick seperately. This way we can warn when it fails instead of exiting the application */ LOG(LOG_MISC,LOG_DEBUG)("Initializing SDL joystick subsystem..."); if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0) { sdl.num_joysticks = (Bitu)SDL_NumJoysticks(); LOG(LOG_MISC,LOG_DEBUG)("SDL reports %u joysticks",(unsigned int)sdl.num_joysticks); } else { LOG(LOG_GUI,LOG_WARN)("Failed to init joystick support"); sdl.num_joysticks = 0; } /* must redraw after modeset */ sdl.must_redraw_all = true; sdl.deferred_resize = false; /* assume L+R ALT keys are up */ sdl.laltstate = SDL_KEYUP; sdl.raltstate = SDL_KEYUP; #if defined(WIN32) && !defined(C_SDL2) # if SDL_VERSION_ATLEAST(1, 2, 10) sdl.using_windib=true; # else sdl.using_windib=false; # endif if (getenv("SDL_VIDEODRIVER")==NULL) { putenv("SDL_VIDEODRIVER=windib"); if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) E_Exit("Can't init SDL Video %s",SDL_GetError()); sdl.using_windib=true; } else { char* sdl_videodrv = getenv("SDL_VIDEODRIVER"); LOG(LOG_MISC,LOG_DEBUG)("Win32: SDL_VIDEODRIVER is '%s', so I will obey it",sdl_videodrv); if (strcmp(sdl_videodrv,"directx")==0) sdl.using_windib = false; else if (strcmp(sdl_videodrv,"windib")==0) sdl.using_windib = true; } #endif /* GUI init */ GUI_StartUp(); /* FIXME: We need a more general "init list", outside of the section-based design, * that we then execute serially here. */ /* TODO: Each section currently uses "AddDestroyFunction" per section. We need to * change over that code to a global destroy callback list instead. */ /* TODO: Get rid of "init" and "destroy" callback lists per section. */ /* TODO: Add a global (within the Config object) init and destroy callback list. * On each call, init functions are added to the end of the list, and * destroy functions added to the beginning of the list. That way, init * is lowest level to highest, destroy is highest level to lowest. */ /* TODO: Config object should also have a "reset" callback list. On system * reset each device would be notified so that it can emulate hardware * reset (the RESET line on ISA/PCI bus), lowest level to highest. */ /* TODO: Each "init" function should do the work of getting the section object, * whatever section it wants to read, instead of us doing the work. When * that's complete, the call to init should be without parameters (void). * The hope is that the init functions can read whatever sections it wants, * both newer DOSBox-X sections and existing DOSBox (mainline) compatible * sections. */ /* The order is important here: * Init functions are called low-level first to high level last, * because some init functions rely on others. */ #if !defined(C_SDL2) # if defined(WIN32) Reflect_Menu(); # endif if (control->opt_startui) GUI_Run(false); #endif if (control->opt_editconf.length() != 0) launcheditor(control->opt_editconf); if (control->opt_opencaptures.length() != 0) launchcaptures(control->opt_opencaptures); if (control->opt_opensaves.length() != 0) launchsaves(control->opt_opensaves); { /* Some extra SDL Functions */ Section_prop *sdl_sec = static_cast(control->GetSection("sdl")); if (control->opt_fullscreen || sdl_sec->Get_bool("fullscreen")) { LOG(LOG_MISC,LOG_DEBUG)("Going fullscreen immediately, during startup"); #if !defined(C_SDL2) void DOSBox_SetSysMenu(void); DOSBox_SetSysMenu(); #endif //only switch if not already in fullscreen if (!sdl.desktop.fullscreen) GFX_SwitchFullScreen(); } } /* stock top-level menu items */ { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"MainMenu"); item.set_text("Main"); { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"MainSendKey"); item.set_text("Send Key"); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CpuMenu"); item.set_text("CPU"); { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CpuCoreMenu"); item.set_text("CPU core"); } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CpuTypeMenu"); item.set_text("CPU type"); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoMenu"); item.set_text("Video"); { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoFrameskipMenu"); item.set_text("Frameskip"); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"frameskip_0").set_text("Off"). set_callback_function(video_frameskip_common_menu_callback); for (unsigned int f=1;f <= 10;f++) { char tmp1[64],tmp2[64]; sprintf(tmp1,"frameskip_%u",f); sprintf(tmp2,"%u frame",f); mainMenu.alloc_item(DOSBoxMenu::item_type_id,tmp1).set_text(tmp2). set_callback_function(video_frameskip_common_menu_callback); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoScalerMenu"); item.set_text("Scaler"); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"scaler_forced").set_text("Force scaler"). set_callback_function(scaler_forced_menu_callback); for (size_t i=0;scaler_menu_opts[i][0] != NULL;i++) { const std::string name = std::string("scaler_set_") + scaler_menu_opts[i][0]; mainMenu.alloc_item(DOSBoxMenu::item_type_id,name).set_text(scaler_menu_opts[i][1]). set_callback_function(scaler_set_menu_callback); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoCompatMenu"); item.set_text("Compatibility"); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vga_9widetext").set_text("Allow 9-pixel wide text mode"). set_callback_function(vga_9widetext_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"doublescan").set_text("Doublescan"). set_callback_function(doublescan_menu_callback); } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoOutputMenu"); item.set_text("Output"); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_surface").set_text("Surface"). set_callback_function(output_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_direct3d").set_text("Direct3D"). set_callback_function(output_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_opengl").set_text("OpenGL"). set_callback_function(output_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_openglnb").set_text("OpenGL NB"). set_callback_function(output_menu_callback); } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoVsyncMenu"); item.set_text("V-Sync"); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_on").set_text("On"). set_callback_function(vsync_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_force").set_text("Force"). set_callback_function(vsync_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_host").set_text("Host"). set_callback_function(vsync_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_off").set_text("Off"). set_callback_function(vsync_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_set_syncrate").set_text("Set syncrate"). set_callback_function(vsync_set_syncrate_menu_callback); } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoOverscanMenu"); item.set_text("Overscan"); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"overscan_0").set_text("Off"). set_callback_function(overscan_menu_callback); for (size_t i=1;i <= 10;i++) { char tmp1[64],tmp2[64]; sprintf(tmp1,"overscan_%zu",i); sprintf(tmp2,"%zu",i); mainMenu.alloc_item(DOSBoxMenu::item_type_id,tmp1).set_text(tmp2). set_callback_function(overscan_menu_callback); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoPC98Menu"); item.set_text("PC-98"); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_5mhz_gdc").set_text("5MHz GDC clock"). set_callback_function(vid_pc98_5mhz_gdc_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_allow_200scanline").set_text("Allow 200-line scanline effect"). set_callback_function(vid_pc98_200scanline_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_allow_4partitions").set_text("Allow 4 display partitions in graphics layer"). set_callback_function(vid_pc98_4parts_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_egc").set_text("Enable EGC"). set_callback_function(vid_pc98_enable_egc_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_grcg").set_text("Enable GRCG"). set_callback_function(vid_pc98_enable_grcg_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_analog").set_text("Enable analog display"). set_callback_function(vid_pc98_enable_analog_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_clear_text").set_text("Clear text layer"). set_callback_function(vid_pc98_cleartext_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_clear_graphics").set_text("Clear graphics layer"). set_callback_function(vid_pc98_graphics_menu_callback); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"SoundMenu"); item.set_text("Sound"); { mainMenu.alloc_item(DOSBoxMenu::item_type_id,"mixer_swapstereo").set_text("Swap stereo"). set_callback_function(mixer_swapstereo_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"mixer_mute").set_text("Mute"). set_callback_function(mixer_mute_menu_callback); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"DOSMenu"); item.set_text("DOS"); { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"DOSMouseMenu"); item.set_text("Mouse"); { mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_mouse_enable_int33").set_text("Internal Emulation"). set_callback_function(dos_mouse_enable_int33_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_mouse_y_axis_reverse").set_text("Y-axis Reverse"). set_callback_function(dos_mouse_y_axis_reverse_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_mouse_sensitivity").set_text("Sensitivity"). set_callback_function(dos_mouse_sensitivity_menu_callback); } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"DOSPC98Menu"); item.set_text("PC-98 PIT master clock"); { mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_pc98_pit_4mhz").set_text("4MHz/8MHz"). set_callback_function(dos_pc98_clock_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_pc98_pit_5mhz").set_text("5MHz/10MHz"). set_callback_function(dos_pc98_clock_menu_callback); } } } { DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CaptureMenu"); item.set_text("Capture"); } /* more */ { DOSBoxMenu::item *item; MAPPER_AddHandler(&SetCyclesCount_mapper_shortcut, MK_nothing, 0, "editcycles", "EditCycles", &item); item->set_text("Edit cycles"); MAPPER_AddHandler(&HideMenu_mapper_shortcut, MK_escape, MMODHOST, "togmenu", "TogMenu", &item); item->set_text("Hide/show menu bar"); item->check(!menu.toggle); } #if (HAVE_D3D9_H) && defined(WIN32) D3D_reconfigure(); #endif /* Start up main machine */ // Shows menu bar (window) menu.startup = true; menu.showrt = control->opt_showrt; menu.hidecycles = (control->opt_showcycles ? false : true); #if defined(WIN32) && !defined(C_SDL2) { Section_prop *sec = static_cast(control->GetSection("dosbox")); enable_hook_special_keys = sec->Get_bool("keyboard hook"); } #endif MSG_Init(); MAPPER_StartUp(); DOSBOX_InitTickLoop(); DOSBOX_RealInit(); /* at this point: If the machine type is PC-98, and the mapper keyboard layout was "Japanese", * then change the mapper layout to "Japanese PC-98" */ if (host_keyboard_layout == DKM_JPN && IS_PC98_ARCH) SetMapperKeyboardLayout(DKM_JPN_PC98); RENDER_Init(); { /* Depends on RENDER_Init */ DOSBoxMenu::item *item; MAPPER_AddHandler(&AspectRatio_mapper_shortcut, MK_nothing, 0, "aspratio", "AspRatio", &item); item->set_text("Fit to aspect ratio"); item->check(render.aspect); } CAPTURE_Init(); IO_Init(); HARDWARE_Init(); Init_AddressLimitAndGateMask(); /* <- need to init address mask so Init_RAM knows the maximum amount of RAM possible */ Init_MemoryAccessArray(); /* <- NTS: In DOSBox-X this is the "cache" of devices that responded to memory access */ Init_A20_Gate(); // FIXME: Should be handled by motherboard! Init_PS2_Port_92h(); // FIXME: Should be handled by motherboard! Init_RAM(); Init_DMA(); Init_PIC(); TIMER_Init(); PCIBUS_Init(); PAGING_Init(); /* <- NTS: At this time, must come before memory init because paging is so well integrated into emulation code */ CMOS_Init(); ROMBIOS_Init(); CALLBACK_Init(); /* <- NTS: This relies on ROM BIOS allocation and it must happen AFTER ROMBIOS init */ #if C_DEBUG DEBUG_Init(); /* <- NTS: Relies on callback system */ #endif Init_VGABIOS(); VOODOO_Init(); PROGRAMS_Init(); /* <- NTS: Does not init programs, it inits the callback used later when creating the .COM programs on drive Z: */ PCSPEAKER_Init(); TANDYSOUND_Init(); MPU401_Init(); MIXER_Init(); MIDI_Init(); CPU_Init(); #if C_FPU FPU_Init(); #endif VGA_Init(); ISAPNP_Cfg_Init(); FDC_Primary_Init(); KEYBOARD_Init(); SBLASTER_Init(); JOYSTICK_Init(); PS1SOUND_Init(); DISNEY_Init(); GUS_Init(); IDE_Init(); INNOVA_Init(); BIOS_Init(); INT10_Init(); SERIAL_Init(); DONGLE_Init(); PARALLEL_Init(); #if C_NE2000 NE2K_Init(); #endif #if defined(WIN32) && !defined(C_SDL2) Reflect_Menu(); #endif /* If PCjr emulation, map cartridge ROM */ if (machine == MCH_PCJR) Init_PCJR_CartridgeROM(); /* let's assume motherboards are sane on boot because A20 gate is ENABLED on first boot */ MEM_A20_Enable(true); /* OS init now */ DOS_Init(); DRIVES_Init(); DOS_KeyboardLayout_Init(); MOUSE_Init(); // FIXME: inits INT 15h and INT 33h at the same time. Also uses DOS_GetMemory() which is why DOS_Init must come first XMS_Init(); EMS_Init(); AUTOEXEC_Init(); #if C_IPX IPX_Init(); #endif MSCDEX_Init(); /* Init memhandle system. This part is used by DOSBox's XMS/EMS emulation to associate handles * per page. FIXME: I would like to push this down to the point that it's never called until * XMS/EMS emulation needs it. I would also like the code to free the mhandle array immediately * upon booting into a guest OS, since memory handles no longer have meaning in the guest OS * memory layout. */ Init_MemHandles(); /* finally, the mapper */ MAPPER_Init(); /* stop at this point, and show the mapper, if instructed */ if (control->opt_startmapper) { LOG(LOG_MISC,LOG_DEBUG)("Running mapper interface, during startup, as instructed"); MAPPER_RunInternal(); } /* more */ mainMenu.alloc_item(DOSBoxMenu::item_type_id,"show_console").set_text("Show console").set_callback_function(show_console_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"wait_on_error").set_text("Wait on error").set_callback_function(wait_on_error_menu_callback).check(sdl.wait_on_error); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"auto_lock_mouse").set_text("Autolock mouse").set_callback_function(autolock_mouse_menu_callback).check(sdl.mouse.autoenable); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_ctrlesc").set_text("Ctrl+Esc").set_callback_function(sendkey_preset_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_alttab").set_text("Alt+Tab").set_callback_function(sendkey_preset_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_winlogo").set_text("Logo key").set_callback_function(sendkey_preset_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_winmenu").set_text("Menu key").set_callback_function(sendkey_preset_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_cad").set_text("Ctrl+Alt+Del").set_callback_function(sendkey_preset_menu_callback); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"doublebuf").set_text("Double Buffering (Fullscreen)").set_callback_function(doublebuf_menu_callback).check(!!GetSetSDLValue(1, "desktop.doublebuf", 0)); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"alwaysontop").set_text("Always on top").set_callback_function(alwaysontop_menu_callback).check(is_always_on_top()); mainMenu.alloc_item(DOSBoxMenu::item_type_id,"showdetails").set_text("Show details").set_callback_function(showdetails_menu_callback).check(!menu.hidecycles); bool MENU_get_swapstereo(void); mainMenu.get_item("mixer_swapstereo").check(MENU_get_swapstereo()).refresh_item(mainMenu); bool MENU_get_mute(void); mainMenu.get_item("mixer_mute").check(MENU_get_mute()).refresh_item(mainMenu); mainMenu.get_item("scaler_forced").check(render.scale.forced); mainMenu.get_item("vga_9widetext").enable(!IS_PC98_ARCH); mainMenu.get_item("doublescan").enable(!IS_PC98_ARCH); mainMenu.get_item("pc98_5mhz_gdc").enable(IS_PC98_ARCH); mainMenu.get_item("pc98_allow_200scanline").enable(IS_PC98_ARCH); mainMenu.get_item("pc98_allow_4partitions").enable(IS_PC98_ARCH); mainMenu.get_item("pc98_enable_egc").enable(IS_PC98_ARCH); mainMenu.get_item("pc98_enable_grcg").enable(IS_PC98_ARCH); mainMenu.get_item("pc98_enable_analog").enable(IS_PC98_ARCH); mainMenu.get_item("pc98_clear_text").enable(IS_PC98_ARCH); mainMenu.get_item("pc98_clear_graphics").enable(IS_PC98_ARCH); mainMenu.get_item("dos_pc98_pit_4mhz").enable(IS_PC98_ARCH); mainMenu.get_item("dos_pc98_pit_5mhz").enable(IS_PC98_ARCH); extern bool Mouse_Vertical; extern bool Mouse_Drv; mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu); mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu); mainMenu.get_item("show_console").check(showconsole_init).refresh_item(mainMenu); OutputSettingMenuUpdate(); update_pc98_clock_pit_menu(); /* The machine just "powered on", and then reset finished */ if (!VM_PowerOn()) E_Exit("VM failed to power on"); /* go! */ sdl.init_ignore = false; UpdateWindowDimensions(); userResizeWindowWidth = 0; userResizeWindowHeight = 0; UpdateOverscanMenu(); #if !defined(C_SDL2) void GUI_ResetResize(bool pressed); GUI_ResetResize(true); #endif void ConstructMenu(void); ConstructMenu(); mainMenu.dump_log_debug(); /*DEBUG*/ mainMenu.rebuild(); #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* -- menu */ MainMenu = mainMenu.getWinMenu(); #endif #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW mainMenu.screenWidth = (unsigned int)sdl.surface->w; mainMenu.updateRect(); #endif { Section_prop *section = static_cast(control->GetSection("SDL")); assert(section != NULL); bool cfg_want_menu = section->Get_bool("showmenu"); /* -- -- decide whether to set menu */ if (menu_gui && !control->opt_nomenu && cfg_want_menu) DOSBox_SetMenu(); else DOSBox_NoMenu(); } #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU int Reflect_Menu(void); Reflect_Menu(); #endif bool reboot_dos; bool run_machine; bool wait_debugger; bool reboot_machine; bool dos_kernel_shutdown; fresh_boot: reboot_dos = false; run_machine = false; wait_debugger = false; reboot_machine = false; dos_kernel_shutdown = false; /* NTS: CPU reset handler, and BIOS init, has the instruction pointer poised to run through BIOS initialization, * which will then "boot" into the DOSBox kernel, and then the shell, by calling VM_Boot_DOSBox_Kernel() */ /* FIXME: throwing int() is a stupid and nondescriptive way to signal shutdown/reset. */ try { #if C_DEBUG if (control->opt_break_start) DEBUG_EnableDebugger(); #endif DOSBOX_RunMachine(); } catch (int x) { if (x == 2) { /* booting a guest OS. "boot" has already done the work to load the image and setup CPU registers */ LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to boot guest OS"); run_machine = true; /* make note. don't run the whole shebang from an exception handler! */ dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */ } else if (x == 3) { /* reboot the system */ LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot the system"); reboot_machine = true; dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */ } else if (x == 5) { /* go to PC-98 mode */ E_Exit("Obsolete int signal"); } else if (x == 6) { /* reboot DOS kernel */ LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot DOS kernel"); reboot_dos = true; dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */ } else if (x == 7) { /* DOS kernel corruption error (need to restart the DOS kernel) */ LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot DOS kernel"); reboot_dos = true; wait_debugger = true; dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */ } else if (x == 8) { /* Booting to a BIOS, shutting down DOSBox BIOS */ LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to boot into BIOS image"); reboot_machine = true; dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */ } else { LOG(LOG_MISC,LOG_DEBUG)("Emulation threw DOSBox kill switch signal"); // kill switch (see instances of throw(0) and throw(1) elsewhere in DOSBox) run_machine = false; dos_kernel_shutdown = false; } } catch (...) { throw; } #if defined(WIN32) && !defined(C_SDL2) int Reflect_Menu(void); Reflect_Menu(); #endif if (dos_kernel_shutdown) { /* NTS: we take different paths depending on whether we're just shutting down DOS * or doing a hard reboot. */ if (wait_debugger) { #if C_DEBUG Bitu DEBUG_EnableDebugger(void); void DEBUG_WaitNoExecute(void); LOG_MSG("Starting debugger."); DEBUG_EnableDebugger(); DEBUG_WaitNoExecute(); #endif } /* new code: fire event */ if (reboot_machine) DispatchVMEvent(VM_EVENT_DOS_EXIT_REBOOT_BEGIN); else DispatchVMEvent(VM_EVENT_DOS_EXIT_BEGIN); /* older shutdown code */ RemoveEMSPageFrame(); /* remove UMB block */ if (!keep_umb_on_boot) RemoveUMBBlock(); /* disable INT 33h mouse services. it can interfere with guest OS paging and control of the mouse */ DisableINT33(); /* unmap the DOSBox kernel private segment. if the user told us not to, * but the segment exists below 640KB, then we must, because the guest OS * will trample it and assume control of that region of RAM. */ if (!keep_private_area_on_boot || reboot_machine) DOS_GetMemory_unmap(); else if (DOS_PRIVATE_SEGMENT < 0xA000) DOS_GetMemory_unmap(); /* revector some dos-allocated interrupts */ if (!reboot_machine) { real_writed(0,0x01*4,BIOS_DEFAULT_HANDLER_LOCATION); real_writed(0,0x03*4,BIOS_DEFAULT_HANDLER_LOCATION); } /* shutdown DOSBox's virtual drive Z */ VFILE_Shutdown(); /* shutdown the programs */ PROGRAMS_Shutdown(); /* FIXME: Is this safe? Or will this cause use-after-free bug? */ /* remove environment variables for some components */ DOS_UninstallMisc(); SBLASTER_DOS_Shutdown(); GUS_DOS_Shutdown(); /* disable Expanded Memory. EMM is a DOS API, not a BIOS API */ EMS_DoShutDown(); /* and XMS, also a DOS API */ XMS_DoShutDown(); /* and the DOS API in general */ DOS_DoShutDown(); /* set the "disable DOS kernel" flag so other parts of this program * do not attempt to manipulate now-defunct parts of the kernel * such as the environment block */ dos_kernel_disabled = true; /* new code: fire event */ if (reboot_machine) DispatchVMEvent(VM_EVENT_DOS_EXIT_REBOOT_KERNEL); else DispatchVMEvent(VM_EVENT_DOS_EXIT_KERNEL); #if defined(WIN32) && !defined(C_SDL2) int Reflect_Menu(void); Reflect_Menu(); #endif } #if defined(WIN32) && !defined(C_SDL2) int Reflect_Menu(void); Reflect_Menu(); #endif if (run_machine) { bool disable_a20 = static_cast(control->GetSection("dosbox"))->Get_bool("turn off a20 gate on boot"); /* if instructed, turn off A20 at boot */ if (disable_a20) MEM_A20_Enable(false); /* new code: fire event */ DispatchVMEvent(VM_EVENT_GUEST_OS_BOOT); LOG_MSG("Alright: DOS kernel shutdown, booting a guest OS\n"); LOG_MSG(" CS:IP=%04x:%04x SS:SP=%04x:%04x AX=%04x BX=%04x CX=%04x DX=%04x\n", SegValue(cs),reg_ip, SegValue(ss),reg_sp, reg_ax,reg_bx,reg_cx,reg_dx); #if C_DEBUG if (boot_debug_break) { boot_debug_break = false; void DEBUG_Enable(bool pressed); DEBUG_Enable(true); } #endif /* run again */ goto fresh_boot; } #if defined(WIN32) && !defined(C_SDL2) int Reflect_Menu(void); Reflect_Menu(); #endif if (reboot_machine) { LOG_MSG("Rebooting the system\n"); void CPU_Snap_Back_Forget(); /* Shutdown everything. For shutdown to work properly we must force CPU to real mode */ CPU_Snap_Back_To_Real_Mode(); CPU_Snap_Back_Forget(); /* new code: fire event */ DispatchVMEvent(VM_EVENT_RESET); DispatchVMEvent(VM_EVENT_RESET_END); /* HACK: EGA/VGA modes will need VGA BIOS mapped in, ready to go */ if (IS_EGAVGA_ARCH) { void INT10_Startup(Section *sec); INT10_Startup(NULL); } #if C_DEBUG if (boot_debug_break) { boot_debug_break = false; void DEBUG_Enable(bool pressed); DEBUG_Enable(true); } #endif /* run again */ goto fresh_boot; } else if (reboot_dos) { /* typically (at this time) to enter/exit PC-98 mode */ LOG_MSG("Rebooting DOS\n"); void CPU_Snap_Back_Forget(); /* Shutdown everything. For shutdown to work properly we must force CPU to real mode */ CPU_Snap_Back_To_Real_Mode(); CPU_Snap_Back_Forget(); /* all hardware devices need to know to reregister themselves PC-98 style */ /* begin booting DOS again. */ void BIOS_Enter_Boot_Phase(void); BIOS_Enter_Boot_Phase(); /* run again */ goto fresh_boot; } #if defined(WIN32) && !defined(C_SDL2) int Reflect_Menu(void); Reflect_Menu(); #endif /* and then shutdown */ GFX_ShutDown(); void CPU_Snap_Back_Forget(); /* Shutdown everything. For shutdown to work properly we must force CPU to real mode */ CPU_Snap_Back_To_Real_Mode(); CPU_Snap_Back_Forget(); /* NTS: The "control" object destructor is called here because the "myconf" object leaves scope. * The destructor calls all section destroy functions here. After this point, all sections have * freed resources. */ } void CALLBACK_Dump(void); CALLBACK_Dump(); /* GUI font registry shutdown */ #if !defined(C_SDL2) GUI::Font::registry_freeall(); #endif DOS_ShutdownDrives(); DOS_ShutdownFiles(); DOS_ShutdownDevices(); CALLBACK_Shutdown(); #if C_DYNAMIC_X86 CPU_Core_Dyn_X86_Shutdown(); #endif FreeBIOSDiskList(); MAPPER_Shutdown(); VFILE_Shutdown(); PROGRAMS_Shutdown(); TIMER_ShutdownTickHandlers(); #if C_DEBUG DEBUG_ShutDown(NULL); #endif sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt //Force visible mouse to end user. Somehow this sometimes doesn't happen #if defined(C_SDL2) SDL_SetRelativeMouseMode(SDL_FALSE); #else SDL_WM_GrabInput(SDL_GRAB_OFF); #endif SDL_ShowCursor(SDL_ENABLE); /* Exit functions */ while (!exitfunctions.empty()) { Function_wrapper &ent = exitfunctions.front(); LOG(LOG_MISC,LOG_DEBUG)("Calling exit function (%p) '%s'",(void*)((uintptr_t)ent.function),ent.name.c_str()); ent.function(NULL); exitfunctions.pop_front(); } LOG::Exit(); #if defined(WIN32) && !defined(C_SDL2) # if !defined(HX_DOS) ShowWindow(GetHWND(), SW_HIDE); SDL1_hax_SetMenu(NULL);/* detach menu from window, or else Windows will destroy the menu out from under the C++ class */ # endif #endif #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU void sdl_hax_macosx_setmenu(void *nsMenu); sdl_hax_macosx_setmenu(NULL); #endif SDL_Quit();//Let's hope sdl will quit as well when it catches an exception mainMenu.unbuild(); mainMenu.clear_all_menu_items(); return 0; } void GFX_GetSize(int &width, int &height, bool &fullscreen) { width = sdl.clip.w; // draw.width height = sdl.clip.h; // draw.height fullscreen = sdl.desktop.fullscreen; } void GFX_ShutDown(void) { LOG(LOG_MISC,LOG_DEBUG)("Shutting down GFX renderer"); GFX_Stop(); if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop ); if (sdl.mouse.locked) GFX_CaptureMouse(); if (sdl.desktop.fullscreen) GFX_SwitchFullScreen(); } bool OpenGL_using(void) { #if C_OPENGL return (sdl.desktop.want_type==SCREEN_OPENGL?true:false); #else return false; #endif } bool Get_Custom_SaveDir(std::string& savedir) { (void)savedir;//UNUSED if (custom_savedir.length() != 0) return true; return false; } #if !defined(C_SDL2) void GUI_ResetResize(bool pressed) { void RENDER_CallBack( GFX_CallBackFunctions_t function ); if (!pressed) return; userResizeWindowWidth = 0; userResizeWindowHeight = 0; if (GFX_GetPreventFullscreen()) return; if (sdl.updating && !GFX_MustActOnResize()) { /* act on resize when updating is complete */ sdl.deferred_resize = true; } else { sdl.deferred_resize = false; RENDER_CallBack(GFX_CallBackReset); } } #endif