DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/sdlmain.cpp
00001 
00011 /*
00012  *  Copyright (C) 2002-2019  The DOSBox Team
00013  *
00014  *  This program is free software; you can redistribute it and/or modify
00015  *  it under the terms of the GNU General Public License as published by
00016  *  the Free Software Foundation; either version 2 of the License, or
00017  *  (at your option) any later version.
00018  *
00019  *  This program is distributed in the hope that it will be useful,
00020  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00021  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022  *  GNU General Public License for more details.
00023  *
00024  *  You should have received a copy of the GNU General Public License
00025  *  along with this program; if not, write to the Free Software
00026  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA.
00027  */
00028 
00029 #ifdef WIN32
00030 # ifndef WIN32_LEAN_AND_MEAN
00031 #  define WIN32_LEAN_AND_MEAN
00032 # endif
00033 #endif
00034 
00035 #ifdef OS2
00036 # define INCL_DOS
00037 # define INCL_WIN
00038 #endif
00039 
00040 extern bool dpi_aware_enable;
00041 
00042 bool OpenGL_using(void);
00043 void GFX_OpenGLRedrawScreen(void);
00044 
00045 #ifndef _GNU_SOURCE
00046 # define _GNU_SOURCE
00047 #endif
00048 
00049 // Tell Mac OS X to shut up about deprecated OpenGL calls
00050 #ifndef GL_SILENCE_DEPRECATION
00051 #define GL_SILENCE_DEPRECATION
00052 #endif
00053 
00054 #include <stdlib.h>
00055 #include <string.h>
00056 #include <stdio.h>
00057 #include <unistd.h>
00058 #include <assert.h>
00059 #include <stdarg.h>
00060 #include <sys/types.h>
00061 #include <algorithm> // std::transform
00062 #include <fcntl.h>
00063 #ifdef WIN32
00064 # include <signal.h>
00065 # include <sys/stat.h>
00066 # include <process.h>
00067 # if !defined(__MINGW32__) /* MinGW does not have these headers */
00068 #  include <shcore.h>
00069 #  include <shellscalingapi.h>
00070 # endif
00071 #endif
00072 
00073 #include "dosbox.h"
00074 #include "pic.h"
00075 #include "timer.h"
00076 #include "setup.h"
00077 #include "bios.h"
00078 #include "support.h"
00079 #include "debug.h"
00080 #include "ide.h"
00081 #include "bitop.h"
00082 #include "ptrop.h"
00083 #include "mapper.h"
00084 #include "sdlmain.h"
00085 #include "zipfile.h"
00086 #include "shell.h"
00087 
00088 #if defined(WIN32) && !defined(HX_DOS)
00089 # include <shobjidl.h>
00090 #endif
00091 
00092 #if defined(WIN32) && defined(__MINGW32__) /* MinGW does not have IID_ITaskbarList3 */
00093 /* MinGW now contains this, the older MinGW for HX-DOS does not.
00094  * Keep things simple and just #define around it like this */
00095 static const GUID __my_CLSID_TaskbarList ={ 0x56FDF344,0xFD6D,0x11d0,{0x95,0x8A,0x00,0x60,0x97,0xC9,0xA0,0x90}};
00096 # define CLSID_TaskbarList __my_CLSID_TaskbarList
00097 
00098 static const GUID __my_IID_ITaskbarList3 = { 0xEA1AFB91ul,0x9E28u,0x4B86u,0x90u,0xE9u,0x9Eu,0x9Fu,0x8Au,0x5Eu,0xEFu,0xAFu };
00099 # define IID_ITaskbarList3 __my_IID_ITaskbarList3
00100 #endif
00101 
00102 #if defined(WIN32) && defined(__MINGW32__) /* MinGW does not have this */
00103 typedef enum PROCESS_DPI_AWARENESS {
00104     PROCESS_DPI_UNAWARE             = 0,
00105     PROCESS_SYSTEM_DPI_AWARE        = 1,
00106     PROCESS_PER_MONITOR_DPI_AWARE   = 2
00107 } PROCESS_DPI_AWARENESS;
00108 #endif
00109 
00110 #if C_EMSCRIPTEN
00111 # include <emscripten.h>
00112 #endif
00113 
00114 #include "../src/libs/gui_tk/gui_tk.h"
00115 
00116 #ifdef __WIN32__
00117 # include "callback.h"
00118 # include "dos_inc.h"
00119 # include <malloc.h>
00120 # include "Commdlg.h"
00121 # include "windows.h"
00122 # include "Shellapi.h"
00123 # include "shell.h"
00124 # include <cstring>
00125 # include <fstream>
00126 # include <sstream>
00127 # if defined(__MINGW32__) && !defined(HX_DOS)
00128 #  include <imm.h> // input method editor
00129 # endif
00130 #endif // WIN32
00131 
00132 #include "mapper.h"
00133 #include "vga.h"
00134 #include "keyboard.h"
00135 #include "cpu.h"
00136 #include "fpu.h"
00137 #include "cross.h"
00138 #include "keymap.h"
00139 
00140 #ifdef _MSC_VER
00141 # define MIN(a,b) ((a) < (b) ? (a) : (b))
00142 # define MAX(a,b) ((a) > (b) ? (a) : (b))
00143 #else
00144 # define MIN(a,b) std::min(a,b)
00145 # define MAX(a,b) std::max(a,b)
00146 #endif
00147 
00148 #if !defined(C_SDL2) && !defined(RISCOS)
00149 # include "SDL_version.h"
00150 # ifndef SDL_DOSBOX_X_SPECIAL
00151 #  error This code must be compiled using the SDL 1.x library provided in this source repository
00152 # endif
00153 #endif
00154 
00155 #include "sdlmain.h"
00156 
00157 #ifdef MACOSX
00158 extern bool has_touch_bar_support;
00159 bool osx_detect_nstouchbar(void);
00160 void osx_init_touchbar(void);
00161 #endif
00162 
00163 void ShutDownMemHandles(Section * sec);
00164 
00165 SDL_Block sdl;
00166 Bitu frames = 0;
00167 
00168 ScreenSizeInfo          screen_size_info;
00169 
00170 const char *scaler_menu_opts[][2] = {
00171     { "none",                   "None" },
00172     { "normal2x",               "Normal 2X" },
00173     { "normal3x",               "Normal 3X" },
00174     { "normal4x",               "Normal 4X" },
00175     { "normal5x",               "Normal 5X" },
00176     { "hardware_none",          "Hardware None" },
00177     { "hardware2x",             "Hardware 2X" },
00178     { "hardware3x",             "Hardware 3X" },
00179     { "hardware4x",             "Hardware 4X" },
00180     { "hardware5x",             "Hardware 5X" },
00181     { "gray",                   "Grayscale" },
00182     { "gray2x",                 "Grayscale 2X" },
00183     { "tv2x",                   "TV 2X" },
00184     { "tv3x",                   "TV 3X" },
00185     { "scan2x",                 "Scan 2X" },
00186     { "scan3x",                 "Scan 3X" },
00187     { "rgb2x",                  "RGB 2X" },
00188     { "rgb3x",                  "RGB 3X" },
00189     { "advmame2x",              "Advanced MAME 2X" },
00190     { "advmame3x",              "Advanced MAME 3X" },
00191     { "hq2x",                   "HQ 2X" },
00192     { "hq3x",                   "HQ 3X" },
00193     { "advinterp2x",            "Advanced Interpolation 2X" },
00194     { "advinterp3x",            "Advanced Interpolation 3X" },
00195     { "2xsai",                  "2xSai" },
00196     { "super2xsai",             "Super2xSai" },
00197     { "supereagle",             "SuperEagle" },
00198 #if C_XBRZ
00199     { "xbrz",                   "xBRZ" },
00200     { "xbrz_bilinear",          "xBRZ Bilinear" },
00201 #endif
00202     { NULL, NULL }
00203 };
00204 
00205 #if defined(WIN32) && !defined(C_SDL2)
00206 bool isVirtualBox = false; /* OpenGL never works with Windows XP inside VirtualBox */
00207 #endif
00208 
00209 bool OpenGL_using(void);
00210 
00211 #if defined(WIN32) && !defined(S_ISREG)
00212 # define S_ISREG(x) ((x & S_IFREG) == S_IFREG)
00213 #endif
00214 
00215 using namespace std;
00216 
00217 void UpdateOverscanMenu(void);
00218 
00219 const char *DKM_to_string(const unsigned int dkm) {
00220     switch (dkm) {
00221         case DKM_US:        return "us";
00222         case DKM_DEU:       return "ger";
00223         case DKM_JPN_PC98:  return "jpn_pc98";
00224         case DKM_JPN:       return "jpn";
00225         default:            break;
00226     }
00227 
00228     return "";
00229 }
00230 
00231 const char *DKM_to_descriptive_string(const unsigned int dkm) {
00232     switch (dkm) {
00233         case DKM_US:        return "US English";
00234         case DKM_DEU:       return "German";
00235         case DKM_JPN_PC98:  return "Japanese (PC-98)";
00236         case DKM_JPN:       return "Japanese";
00237         default:            break;
00238     }
00239 
00240     return "";
00241 }
00242 
00243 #if defined(WIN32) && !defined(HX_DOS)
00244 ITaskbarList3 *winTaskbarList = NULL;
00245 #endif
00246 
00247 #if defined(WIN32) && !defined(HX_DOS)
00248 void WindowsTaskbarUpdatePreviewRegion(void) {
00249     if (winTaskbarList != NULL) {
00250         /* Windows 7/8/10: Tell the taskbar which part of our window contains the DOS screen */
00251         RECT r;
00252 
00253         r.top = sdl.clip.y;
00254         r.left = sdl.clip.x;
00255         r.right = sdl.clip.x + sdl.clip.w;
00256         r.bottom = sdl.clip.y + sdl.clip.h;
00257 
00258         /* NTS: The MSDN documentation is misleading. Apparently, despite 30+ years of Windows SDK
00259                 behavior where the "client area" is the area below the menu bar and inside the frame,
00260                 ITaskbarList3's idea of the "client area" is the the area inside the frame INCLUDING
00261                 the menu bar. Why? */
00262         if (GetMenu(GetHWND()) != NULL) {
00263             r.top += GetSystemMetrics(SM_CYMENU);//HACK
00264             r.bottom += GetSystemMetrics(SM_CYMENU);//HACK
00265         }
00266 
00267         if (winTaskbarList->SetThumbnailClip(GetHWND(), &r) != S_OK)
00268             LOG_MSG("WARNING: ITaskbarList3::SetThumbnailClip() failed");
00269     }
00270 }
00271 
00272 void WindowsTaskbarResetPreviewRegion(void) {
00273     if (winTaskbarList != NULL) {
00274         /* Windows 7/8/10: Tell the taskbar which part of our window contains the client area (not including the menu bar) */
00275         RECT r;
00276 
00277         GetClientRect(GetHWND(), &r);
00278 
00279         /* NTS: The MSDN documentation is misleading. Apparently, despite 30+ years of Windows SDK
00280                 behavior where the "client area" is the area below the menu bar and inside the frame,
00281                 ITaskbarList3's idea of the "client area" is the the area inside the frame INCLUDING
00282                 the menu bar. Why? */
00283         if (GetMenu(GetHWND()) != NULL) {
00284             r.top += GetSystemMetrics(SM_CYMENU);//HACK
00285             r.bottom += GetSystemMetrics(SM_CYMENU);//HACK
00286         }
00287 
00288         if (winTaskbarList->SetThumbnailClip(GetHWND(), &r) != S_OK)
00289             LOG_MSG("WARNING: ITaskbarList3::SetThumbnailClip() failed");
00290     }
00291 }
00292 #endif
00293 
00294 unsigned int mapper_keyboard_layout = DKM_US;
00295 unsigned int host_keyboard_layout = DKM_US;
00296 
00297 void KeyboardLayoutDetect(void) {
00298     unsigned int nlayout = DKM_US;
00299 
00300 #if defined(LINUX)
00301     unsigned int Linux_GetKeyboardLayout(void);
00302     nlayout = Linux_GetKeyboardLayout();
00303 
00304     /* BUGFIX: The xkbmap for 'jp' in Linux/X11 has a problem that maps both
00305      *         Ro and Yen to backslash, which in SDL's default state makes
00306      *         it impossible to map them properly in the mapper. */
00307     if (nlayout == DKM_JPN) {
00308         LOG_MSG("Engaging Linux/X11 fix for jp xkbmap in order to handle Ro/Yen keys");
00309 
00310         void Linux_JPXKBFix(void);
00311         Linux_JPXKBFix();
00312     }
00313 #elif defined(WIN32)
00314     WORD lid = LOWORD(GetKeyboardLayout(0));
00315 
00316     LOG_MSG("Windows keyboard layout ID is 0x%04x", lid);
00317 
00318     switch (lid) {
00319         case 0x0407:    nlayout = DKM_DEU; break;
00320         case 0x0409:    nlayout = DKM_US; break;
00321         case 0x0411:    nlayout = DKM_JPN; break;
00322         default:        break;
00323     }
00324 #endif
00325 
00326     host_keyboard_layout = nlayout;
00327 
00328     LOG_MSG("Host keyboard layout is now %s (%s)",
00329         DKM_to_string(host_keyboard_layout),
00330         DKM_to_descriptive_string(host_keyboard_layout));
00331 }
00332 
00333 void SetMapperKeyboardLayout(const unsigned int dkm) {
00334     /* TODO: Make mapper re-initialize layout. If the mapper interface is visible, redraw it. */
00335     mapper_keyboard_layout = dkm;
00336 
00337     LOG_MSG("Mapper keyboard layout is now %s (%s)",
00338         DKM_to_string(mapper_keyboard_layout),
00339         DKM_to_descriptive_string(mapper_keyboard_layout));
00340 }
00341 
00342 #if defined(WIN32) && !defined(C_SDL2)
00343 extern "C" unsigned char SDL1_hax_hasLayoutChanged(void);
00344 extern "C" void SDL1_hax_ackLayoutChanged(void);
00345 #endif
00346 
00347 void CheckMapperKeyboardLayout(void) {
00348 #if defined(WIN32) && !defined(C_SDL2)
00349     if (SDL1_hax_hasLayoutChanged()) {
00350         SDL1_hax_ackLayoutChanged();
00351         LOG_MSG("Keyboard layout changed");
00352         KeyboardLayoutDetect();
00353 
00354         if (host_keyboard_layout == DKM_JPN && IS_PC98_ARCH)
00355             SetMapperKeyboardLayout(DKM_JPN_PC98);
00356         else
00357             SetMapperKeyboardLayout(host_keyboard_layout);
00358     }
00359 #endif
00360 }
00361 
00362 /* yksoft1 says that older MinGW headers lack this value --Jonathan C. */
00363 #ifndef MAPVK_VK_TO_VSC
00364 #define MAPVK_VK_TO_VSC 0
00365 #endif
00366 
00367 bool boot_debug_break = false;
00368 
00369 bool window_was_maximized = false;
00370 
00371 /* this flag is needed in order to know if we're AT the shell,
00372    or if we're in a program running under the shell. */
00373 bool dos_shell_running_program = false;
00374 
00375 Bitu userResizeWindowWidth = 0, userResizeWindowHeight = 0;
00376 Bitu currentWindowWidth = 640, currentWindowHeight = 480;
00377 
00378 int NonUserResizeCounter = 0;
00379 
00380 Bitu time_limit_ms = 0;
00381 
00382 extern bool keep_umb_on_boot;
00383 extern bool keep_private_area_on_boot;
00384 extern bool dos_kernel_disabled;
00385 bool guest_machine_power_on = false;
00386 
00387 std::string custom_savedir;
00388 
00389 void SHELL_Run();
00390 void DisableINT33();
00391 void EMS_DoShutDown();
00392 void XMS_DoShutDown();
00393 void DOS_DoShutDown();
00394 void GUS_DOS_Shutdown();
00395 void SBLASTER_DOS_Shutdown();
00396 void DOS_ShutdownDevices(void);
00397 void RemoveEMSPageFrame(void);
00398 void RemoveUMBBlock();
00399 void DOS_GetMemory_unmap();
00400 void VFILE_Shutdown(void);
00401 void PROGRAMS_Shutdown(void);
00402 void DOS_UninstallMisc(void);
00403 void CALLBACK_Shutdown(void);
00404 void DOS_ShutdownDrives();
00405 void VFILE_Shutdown(void);
00406 void DOS_ShutdownFiles();
00407 void FreeBIOSDiskList();
00408 void GFX_ShutDown(void);
00409 void MAPPER_Shutdown();
00410 void SHELL_Init(void);
00411 #if C_DYNAMIC_X86
00412 void CPU_Core_Dyn_X86_Shutdown(void);
00413 #endif
00414 
00415 void UpdateWindowMaximized(bool flag) {
00416     menu.maxwindow = flag;
00417 }
00418 
00419 void UpdateWindowDimensions(Bitu width, Bitu height)
00420 {
00421     currentWindowWidth = width;
00422     currentWindowHeight = height;
00423 }
00424 
00425 void PrintScreenSizeInfo(void) {
00426 #if 1
00427     const char *method = "?";
00428 
00429     switch (screen_size_info.method) {
00430         case ScreenSizeInfo::METHOD_NONE:       method = "None";        break;
00431         case ScreenSizeInfo::METHOD_X11:        method = "X11";         break;
00432         case ScreenSizeInfo::METHOD_XRANDR:     method = "XRandR";      break;
00433         case ScreenSizeInfo::METHOD_WIN98BASE:  method = "Win98base";   break;
00434         case ScreenSizeInfo::METHOD_COREGRAPHICS:method = "CoreGraphics";break;
00435         default:                                                        break;
00436     }
00437 
00438     LOG_MSG("Screen report: Method '%s' (%.3f x %.3f pixels) at (%.3f x %.3f) (%.3f x %.3f mm) (%.3f x %.3f in) (%.3f x %.3f DPI)",
00439             method,
00440 
00441             screen_size_info.screen_dimensions_pixels.width,
00442             screen_size_info.screen_dimensions_pixels.height,
00443 
00444             screen_size_info.screen_position_pixels.x,
00445             screen_size_info.screen_position_pixels.y,
00446 
00447             screen_size_info.screen_dimensions_mm.width,
00448             screen_size_info.screen_dimensions_mm.height,
00449 
00450             screen_size_info.screen_dimensions_mm.width / 25.4,
00451             screen_size_info.screen_dimensions_mm.height / 25.4,
00452 
00453             screen_size_info.screen_dpi.width,
00454             screen_size_info.screen_dpi.height);
00455 #endif
00456 }
00457 
00458 #if defined(WIN32)
00459 void Windows_GetWindowDPI(ScreenSizeInfo &info) {
00460     info.clear();
00461 
00462 # if !defined(HX_DOS)
00463     HMONITOR mon;
00464     HWND hwnd;
00465 
00466     info.method = ScreenSizeInfo::METHOD_WIN98BASE;
00467 
00468     hwnd = GetHWND();
00469     if (hwnd == NULL) return;
00470 
00471     mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
00472     if (mon == NULL) mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
00473     if (mon == NULL) return;
00474 
00475     MONITORINFO mi;
00476     memset(&mi,0,sizeof(mi));
00477     mi.cbSize = sizeof(mi);
00478     if (!GetMonitorInfo(mon,&mi)) return;
00479 
00480     info.screen_position_pixels.x        = mi.rcMonitor.left;
00481     info.screen_position_pixels.y        = mi.rcMonitor.top;
00482 
00483     info.screen_dimensions_pixels.width  = mi.rcMonitor.right - mi.rcMonitor.left;
00484     info.screen_dimensions_pixels.height = mi.rcMonitor.bottom - mi.rcMonitor.top;
00485 
00486     /* Windows 10 build 1607 and later offer a "Get DPI of window" function */
00487     {
00488         HMODULE __user32;
00489 
00490         __user32 = GetModuleHandle("USER32.DLL");
00491         if (__user32) {
00492             UINT (WINAPI *__GetDpiForWindow)(HWND) = NULL;
00493 
00494             __GetDpiForWindow = (UINT (WINAPI *)(HWND))GetProcAddress(__user32,"GetDpiForWindow");
00495             if (__GetDpiForWindow) {
00496                 UINT dpi = __GetDpiForWindow(hwnd);
00497 
00498                 if (dpi != 0) {
00499                     info.screen_dpi.width = dpi;
00500                     info.screen_dpi.height = dpi;
00501 
00502                     info.screen_dimensions_mm.width = (25.4 * screen_size_info.screen_dimensions_pixels.width) / dpi;
00503                     info.screen_dimensions_mm.height = (25.4 * screen_size_info.screen_dimensions_pixels.height) / dpi;
00504                 }
00505             }
00506         }
00507     }
00508 # endif
00509 }
00510 #endif
00511 
00512 void UpdateWindowDimensions(void)
00513 {
00514 #if defined(C_SDL2)
00515     int w = 640,h = 480;
00516     SDL_GetWindowSize(sdl.window, &w, &h);
00517     UpdateWindowDimensions(w,h);
00518 
00519     Uint32 fl = SDL_GetWindowFlags(sdl.window);
00520     UpdateWindowMaximized((fl & SDL_WINDOW_MAXIMIZED) != 0);
00521 #endif
00522 #if defined(MACOSX)
00523     void MacOSX_GetWindowDPI(ScreenSizeInfo &info);
00524     MacOSX_GetWindowDPI(/*&*/screen_size_info);
00525 #endif
00526 #if defined(WIN32)
00527     // When maximized, SDL won't actually tell us our new dimensions, so get it ourselves.
00528     // FIXME: Instead of GetHWND() we need to track our own handle or add something to SDL 1.x
00529     //        to provide the handle!
00530     RECT r = { 0 };
00531 
00532     GetClientRect(GetHWND(), &r);
00533     UpdateWindowDimensions(r.right, r.bottom);
00534     UpdateWindowMaximized(IsZoomed(GetHWND()));
00535     Windows_GetWindowDPI(/*&*/screen_size_info);
00536 #endif
00537 #if defined(LINUX)
00538     void UpdateWindowDimensions_Linux(void);
00539     UpdateWindowDimensions_Linux();
00540     void Linux_GetWindowDPI(ScreenSizeInfo &info);
00541     Linux_GetWindowDPI(/*&*/screen_size_info);
00542 #endif
00543     PrintScreenSizeInfo();
00544 }
00545 
00546 #if defined(C_SDL2)
00547 # define MAPPERFILE             "mapper-" VERSION ".sdl2.map"
00548 #else
00549 # define MAPPERFILE             "mapper-" VERSION ".map"
00550 #endif
00551 
00552 void                        GUI_ResetResize(bool);
00553 void                        GUI_LoadFonts();
00554 void                        GUI_Run(bool);
00555 
00556 const char*                 titlebar = NULL;
00557 extern const char*              RunningProgram;
00558 extern bool                 CPU_CycleAutoAdjust;
00559 #if !(ENVIRON_INCLUDED)
00560 extern char**                   environ;
00561 #endif
00562 
00563 double                      rtdelta = 0;
00564 bool                        emu_paused = false;
00565 bool                        mouselocked = false; //Global variable for mapper
00566 bool                        fullscreen_switch = true;
00567 bool                        dos_kernel_disabled = true;
00568 bool                        startup_state_numlock = false; // Global for keyboard initialisation
00569 bool                        startup_state_capslock = false; // Global for keyboard initialisation
00570 bool                        startup_state_scrlock = false; // Global for keyboard initialisation
00571 
00572 #if defined(WIN32) && !defined(C_SDL2)
00573 extern "C" void SDL1_hax_SetMenu(HMENU menu);
00574 #endif
00575 
00576 #ifdef WIN32
00577 # include <windows.h>
00578 #endif
00579 
00580 #ifdef WIN32
00581 # define STDOUT_FILE                TEXT("stdout.txt")
00582 # define STDERR_FILE                TEXT("stderr.txt")
00583 # define DEFAULT_CONFIG_FILE            "/dosbox.conf"
00584 #elif defined(MACOSX)
00585 # define DEFAULT_CONFIG_FILE            "/Library/Preferences/DOSBox Preferences"
00586 #elif defined(HAIKU)
00587 #define DEFAULT_CONFIG_FILE "~/config/settings/dosbox/dosbox.conf"
00588 #else /*linux freebsd*/
00589 # define DEFAULT_CONFIG_FILE            "/.dosboxrc"
00590 #endif
00591 
00592 #if C_SET_PRIORITY
00593 # include <sys/resource.h>
00594 # define PRIO_TOTAL             (PRIO_MAX-PRIO_MIN)
00595 #endif
00596 
00597 #ifdef OS2
00598 # include <os2.h>
00599 #endif
00600 
00601 #if defined(C_SDL2)
00602 # if defined(WIN32)
00603 HWND GetHWND()
00604 {
00605     SDL_SysWMinfo wmi;
00606     SDL_VERSION(&wmi.version);
00607     if (sdl.window == NULL)
00608         return nullptr;
00609     if (!SDL_GetWindowWMInfo(sdl.window, &wmi))
00610         return nullptr;
00611     return wmi.info.win.window;
00612 }
00613 
00614 HWND GetSurfaceHWND()
00615 {
00616     return GetHWND();
00617 }
00618 # endif
00619 #endif
00620 void SDL_rect_cliptoscreen(SDL_Rect &r) {
00621     if (r.x < 0) {
00622         r.w += r.x;
00623         r.x = 0;
00624     }
00625     if (r.y < 0) {
00626         r.h += r.y;
00627         r.y = 0;
00628     }
00629     if ((r.x+r.w) > sdl.surface->w)
00630         r.w = sdl.surface->w - r.x;
00631     if ((r.y+r.h) > sdl.surface->h)
00632         r.h = sdl.surface->h - r.y;
00633     /* NTS: Apparently r.w and r.h are unsigned, therefore no need to check if negative */
00634 //    if (r.w < 0) r.w = 0;
00635 //    if (r.h < 0) r.h = 0;
00636 }
00637 
00638 Bitu GUI_JoystickCount(void) {
00639     return sdl.num_joysticks;
00640 }
00641 
00642 #if !defined(MACOSX)
00643 /* TODO: should move to it's own file ================================================ */
00644 static unsigned char logo[32*32*4]= {
00645 #include "dosbox_logo.h"
00646 };
00647 #endif
00648 
00649 #if !defined(MACOSX)
00650 static void DOSBox_SetOriginalIcon(void) {
00651     SDL_Surface *logos;
00652 
00653 #ifdef WORDS_BIGENDIAN
00654         logos = SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0xff000000,0x00ff0000,0x0000ff00,0);
00655 #else
00656         logos = SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0);
00657 #endif
00658 
00659 #if defined(C_SDL2)
00660         SDL_SetWindowIcon(sdl.window, logos);
00661 #else
00662         SDL_WM_SetIcon(logos,NULL);
00663 #endif
00664 }
00665 #endif
00666 /* =================================================================================== */
00667 
00668 #if defined (WIN32)
00669 bool GFX_SDLUsingWinDIB(void) {
00670     return sdl.using_windib;
00671 }
00672 #endif
00673 
00674 void GFX_SetIcon(void) 
00675 {
00676 #if !defined(MACOSX)
00677     /* Set Icon (must be done before any sdl_setvideomode call) */
00678     /* But don't set it on OS X, as we use a nicer external icon there. */
00679     /* Made into a separate call, so it can be called again when we restart the graphics output on win32 */
00680     if (menu_compatible) { DOSBox_SetOriginalIcon(); return; }
00681 #endif
00682 
00683 #if defined(WIN32) && !defined(C_SDL2)
00684     HICON hIcon1;
00685 
00686     hIcon1 = (HICON) LoadImage( GetModuleHandle(NULL), MAKEINTRESOURCE(dosbox_ico), IMAGE_ICON,
00687         16,16,LR_DEFAULTSIZE);
00688 
00689     SendMessage(GetHWND(), WM_SETICON, ICON_SMALL, (LPARAM) hIcon1 ); 
00690 #endif
00691 }
00692 
00693 #if C_DEBUG
00694 bool IsDebuggerActive(void);
00695 #endif
00696 
00697 extern std::string dosbox_title;
00698 
00699 void GFX_SetTitle(Bit32s cycles,Bits frameskip,Bits timing,bool paused){
00700     (void)frameskip;//UNUSED
00701     (void)timing;//UNUSED
00702 //  static Bits internal_frameskip=0;
00703     static Bit32s internal_cycles=0;
00704 //  static Bits internal_timing=0;
00705     char title[200] = {0};
00706 
00707     Section_prop *section = static_cast<Section_prop *>(control->GetSection("SDL"));
00708     assert(section != NULL);
00709     titlebar = section->Get_string("titlebar");
00710 
00711     if (cycles != -1) internal_cycles = cycles;
00712 //  if (timing != -1) internal_timing = timing;
00713 //  if (frameskip != -1) internal_frameskip = frameskip;
00714 
00715     if (CPU_CycleAutoAdjust) {
00716         sprintf(title,"%s%sDOSBox-X %s, %d%%",
00717             dosbox_title.c_str(),dosbox_title.empty()?"":": ",
00718             VERSION,(int)internal_cycles);
00719     }
00720     else {
00721         sprintf(title,"%s%sDOSBox-X %s, %d cyc/ms",
00722             dosbox_title.c_str(),dosbox_title.empty()?"":": ",
00723             VERSION,(int)internal_cycles);
00724     }
00725 
00726     {
00727         const char *what = (titlebar != NULL && *titlebar != 0) ? titlebar : RunningProgram;
00728 
00729         if (what != NULL && *what != 0) {
00730             char *p = title + strlen(title); // append to end of string
00731 
00732             sprintf(p,", %s",what);
00733         }
00734     }
00735 
00736     if (!menu.hidecycles) {
00737         char *p = title + strlen(title); // append to end of string
00738 
00739         sprintf(p,", FPS %2d",(int)frames);
00740     }
00741 
00742     if (menu.showrt) {
00743         char *p = title + strlen(title); // append to end of string
00744 
00745         sprintf(p,", %2d%%/RT",(int)floor((rtdelta / 10) + 0.5));
00746     }
00747 
00748     if (paused) strcat(title," PAUSED");
00749 #if C_DEBUG
00750     if (IsDebuggerActive()) strcat(title," DEBUGGER");
00751 #endif
00752 #if defined(C_SDL2)
00753     SDL_SetWindowTitle(sdl.window,title);
00754 #else
00755     SDL_WM_SetCaption(title,VERSION);
00756 #endif
00757 }
00758 
00759 bool warn_on_mem_write = false;
00760 
00761 void CPU_Snap_Back_To_Real_Mode();
00762 
00763 static void KillSwitch(bool pressed) {
00764     if (!pressed) return;
00765     if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
00766 #if 0 /* Re-enable this hack IF DOSBox continues to have problems page-faulting on kill switch */
00767     CPU_Snap_Back_To_Real_Mode(); /* TEMPORARY HACK. There are portions of DOSBox that write to memory as if still running DOS. */
00768     /* ^ Without this hack, when running Windows NT 3.1 this Kill Switch effectively becomes the Instant Page Fault BSOD switch
00769      * because the DOSBox code attempting to write to real mode memory causes a page fault (though hitting the kill switch a
00770      * second time shuts DOSBox down properly). It's sort of the same issue behind the INT 33h emulation causing instant BSOD
00771      * in Windows NT the instant you moved or clicked the mouse. The purpose of this hack is that, before any of that DOSBox
00772      * code has a chance, we force the CPU back into real mode so that the code doesn't trigger funny page faults and DOSBox
00773      * shuts down properly. */
00774 #endif
00775     warn_on_mem_write = true;
00776     throw 1;
00777 }
00778 
00779 void DoKillSwitch(void) {
00780     KillSwitch(true);
00781 }
00782 
00783 void BlankDisplay(void) {
00784     if (OpenGL_using()) {
00785         LOG_MSG("FIXME: BlankDisplay() not implemented for OpenGL mode");
00786     }
00787     else {
00788         SDL_FillRect(sdl.surface,0,0);
00789 #if defined(C_SDL2)
00790         SDL_UpdateWindowSurface(sdl.window);
00791 #else
00792         SDL_Flip(sdl.surface);
00793 #endif
00794     }
00795 }
00796 
00797 void GFX_SDL_Overscan(void) {
00798     sdl.overscan_color=0;
00799     if (sdl.overscan_width) {
00800         Bitu border_color =  GFX_GetRGB(vga.dac.rgb[vga.attr.overscan_color].red<<2,
00801             vga.dac.rgb[vga.attr.overscan_color].green<<2, vga.dac.rgb[vga.attr.overscan_color].blue<<2);
00802         if (border_color != sdl.overscan_color) {
00803             sdl.overscan_color = border_color;
00804 
00805         // Find four rectangles forming the border
00806             SDL_Rect *rect = &sdl.updateRects[0];
00807             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
00808             if ((Bitu)rect->h > (Bitu)sdl.overscan_width) { rect->y += (int)(rect->h-sdl.overscan_width); rect->h = (uint16_t)sdl.overscan_width; }
00809             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)); }
00810             rect = &sdl.updateRects[1];
00811             rect->x = 0; rect->y = sdl.clip.y; rect->w = (uint16_t)sdl.clip.x; rect->h = (uint16_t)sdl.draw.height; // left
00812             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; }
00813             rect = &sdl.updateRects[2];
00814             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
00815             if ((unsigned int)rect->w > (unsigned int)sdl.overscan_width) { rect->w = (uint16_t)sdl.overscan_width; }
00816             rect = &sdl.updateRects[3];
00817             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
00818             if ((Bitu)rect->h > (Bitu)sdl.overscan_width) { rect->h = (uint16_t)sdl.overscan_width; }
00819             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)); }
00820 
00821             if (sdl.surface->format->BitsPerPixel == 8) { // SDL_FillRect seems to have some issues with palettized hw surfaces
00822                 Bit8u* pixelptr = (Bit8u*)sdl.surface->pixels;
00823                 Bitu linepitch = sdl.surface->pitch;
00824                 for (Bitu i=0; i<4; i++) {
00825                     rect = &sdl.updateRects[i];
00826                     Bit8u* start = pixelptr + (unsigned int)rect->y*(unsigned int)linepitch + (unsigned int)rect->x;
00827                     for (Bitu j=0; j<(unsigned int)rect->h; j++) {
00828                         memset(start, vga.attr.overscan_color, rect->w);
00829                         start += linepitch;
00830                     }
00831                 }
00832             } else {
00833                 for (Bitu i=0; i<4; i++)
00834                     SDL_FillRect(sdl.surface, &sdl.updateRects[i], (Uint32)border_color);
00835 
00836 #if defined(C_SDL2)
00837                 SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
00838 #else
00839                 SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
00840 #endif
00841             }
00842         }
00843     }
00844 }
00845 
00846 bool DOSBox_Paused()
00847 {
00848     return emu_paused;
00849 }
00850 
00851 bool pause_on_vsync = false;
00852 
00853 #if defined(C_SDL2)
00854 static bool IsFullscreen() {
00855     if (sdl.window == NULL) return false;
00856     uint32_t windowFlags = SDL_GetWindowFlags(sdl.window);
00857     if (windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) return true;
00858     return false;
00859 }
00860 #endif
00861 
00862 bool is_paused = false;
00863 bool unpause_now = false;
00864 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
00865 int pause_menu_item_tag = -1;
00866 #endif
00867 
00868 void PushDummySDL(void) {
00869     SDL_Event event;
00870 
00871     memset(&event,0,sizeof(event));
00872     event.type = SDL_KEYUP;
00873     SDL_PushEvent(&event);
00874 }
00875 
00876 static void HandleMouseMotion(SDL_MouseMotionEvent * motion);
00877 static void HandleMouseButton(SDL_MouseButtonEvent * button);
00878 
00879 #if defined(C_SDL2)
00880 # if !defined(IGNORE_TOUCHSCREEN)
00881 static void HandleTouchscreenFinger(SDL_TouchFingerEvent * finger);
00882 # endif
00883 #endif
00884 
00885 #if defined(C_SDL2)
00886 static const SDL_TouchID no_touch_id = (SDL_TouchID)(~0ULL);
00887 static const SDL_FingerID no_finger_id = (SDL_FingerID)(~0ULL);
00888 static SDL_FingerID touchscreen_finger_lock = no_finger_id;
00889 static SDL_TouchID touchscreen_touch_lock = no_touch_id;
00890 #endif
00891 
00892 void PauseDOSBoxLoop(Bitu /*unused*/) {
00893     bool paused = true;
00894     SDL_Event event;
00895 
00896     /* reflect in the menu that we're paused now */
00897     mainMenu.get_item("mapper_pause").check(true).refresh_item(mainMenu);
00898 
00899     void MAPPER_ReleaseAllKeys(void);
00900     MAPPER_ReleaseAllKeys();
00901 
00902     GFX_SetTitle(-1,-1,-1,true);
00903 //  KEYBOARD_ClrBuffer();
00904     GFX_LosingFocus();
00905     while (SDL_PollEvent(&event)); // flush event queue.
00906 
00907     // reset pause conditions
00908     pause_on_vsync = false;
00909 
00910     // give mouse to win32 (ex. alt-tab)
00911 #if defined(C_SDL2)
00912     SDL_SetRelativeMouseMode(SDL_FALSE);
00913 #else
00914     SDL_WM_GrabInput(SDL_GRAB_OFF);
00915 #endif
00916 
00917     is_paused = true;
00918 
00919     while (paused) {
00920         if (unpause_now) {
00921             unpause_now = false;
00922             break;
00923         }
00924 
00925 #if C_EMSCRIPTEN
00926         emscripten_sleep_with_yield(0);
00927         SDL_PollEvent(&event);
00928 #else
00929         SDL_WaitEvent(&event);    // since we're not polling, cpu usage drops to 0.
00930 #endif
00931 
00932 #ifdef __WIN32__
00933   #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
00934         if (event.type==SDL_SYSWMEVENT && event.syswm.msg->msg == WM_COMMAND && (LOWORD(event.syswm.msg->wParam) == ID_WIN_SYSMENU_PAUSE || LOWORD(event.syswm.msg->wParam) == (mainMenu.get_item("mapper_pause").get_master_id()+DOSBoxMenu::winMenuMinimumID))) {
00935             paused=false;
00936             GFX_SetTitle(-1,-1,-1,false);   
00937             break;
00938         }
00939         if (event.type == SDL_SYSWMEVENT && event.syswm.msg->msg == WM_SYSCOMMAND && LOWORD(event.syswm.msg->wParam) == ID_WIN_SYSMENU_PAUSE) {
00940             paused = false;
00941             GFX_SetTitle(-1, -1, -1, false);
00942             break;
00943         }
00944   #endif
00945 #endif
00946         switch (event.type) {
00947 
00948             case SDL_QUIT: KillSwitch(true); break;
00949             case SDL_KEYDOWN:   // Must use Pause/Break or escape Key to resume.
00950             if(event.key.keysym.sym == SDLK_PAUSE || event.key.keysym.sym == SDLK_ESCAPE) {
00951 
00952                 paused = false;
00953                 GFX_SetTitle(-1,-1,-1,false);
00954                 break;
00955             }
00956             else if (event.key.keysym.sym == SDLK_SPACE) { /* spacebar = single frame step */
00957                 /* resume, but let the VGA code know to call us on vertical retrace */
00958                 paused = false;
00959                 pause_on_vsync = true;
00960                 GFX_SetTitle(-1,-1,-1,false);
00961                 break;
00962             }
00963 #if defined (MACOSX) && !defined(C_SDL2)
00964             if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
00965                 /* On macs, all apps exit when pressing cmd-q */
00966                 KillSwitch(true);
00967                 break;
00968             } 
00969 #endif
00970         case SDL_MOUSEMOTION:
00971 #if defined(C_SDL2)
00972             if (touchscreen_finger_lock == no_finger_id &&
00973                 touchscreen_touch_lock == no_touch_id &&
00974                 event.motion.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
00975                 HandleMouseMotion(&event.motion);
00976             }
00977 #else
00978             HandleMouseMotion(&event.motion);
00979 #endif
00980             break;
00981         case SDL_MOUSEBUTTONDOWN:
00982         case SDL_MOUSEBUTTONUP:
00983 #if defined(C_SDL2)
00984             if (touchscreen_finger_lock == no_finger_id &&
00985                 touchscreen_touch_lock == no_touch_id &&
00986                 event.button.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
00987                 HandleMouseButton(&event.button);
00988             }
00989 #else
00990             HandleMouseButton(&event.button);
00991 #endif
00992             break;
00993 #if defined(C_SDL2)
00994 # if !defined(IGNORE_TOUCHSCREEN)
00995         case SDL_FINGERDOWN:
00996         case SDL_FINGERUP:
00997         case SDL_FINGERMOTION:
00998             HandleTouchscreenFinger(&event.tfinger);
00999             break;
01000 # endif
01001 #endif
01002         }
01003     }
01004 
01005     // restore mouse state
01006     void GFX_UpdateSDLCaptureState();
01007     GFX_UpdateSDLCaptureState();
01008 
01009     void MAPPER_ReleaseAllKeys(void);
01010     MAPPER_ReleaseAllKeys();
01011 
01012 //  KEYBOARD_ClrBuffer();
01013     GFX_LosingFocus();
01014 
01015     // redraw screen (ex. fullscreen - pause - alt+tab x2 - unpause)
01016     if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset );
01017 
01018     /* reflect in the menu that we're paused now */
01019     mainMenu.get_item("mapper_pause").check(false).refresh_item(mainMenu);
01020 
01021     is_paused = false;
01022 }
01023 
01024 void PauseDOSBox(bool pressed) {
01025     if (pressed) {
01026         if (is_paused) {
01027             unpause_now = true;
01028             PushDummySDL();
01029         }
01030         else {
01031             PIC_AddEvent(PauseDOSBoxLoop,0.001);
01032         }
01033     }
01034 }
01035 
01036 #if defined(C_SDL2)
01037 static bool SDL2_resize_enable = false;
01038 
01039 SDL_Window* GFX_GetSDLWindow(void) {
01040     return sdl.window;
01041 }
01042 
01043 SDL_Window* GFX_SetSDLWindowMode(Bit16u width, Bit16u height, SCREEN_TYPES screenType) 
01044 {
01045     static SCREEN_TYPES lastType = SCREEN_SURFACE;
01046     if (sdl.renderer) {
01047         SDL_DestroyRenderer(sdl.renderer);
01048         sdl.renderer=0;
01049     }
01050     if (sdl.texture.pixelFormat) {
01051         SDL_FreeFormat(sdl.texture.pixelFormat);
01052         sdl.texture.pixelFormat = 0;
01053     }
01054     if (sdl.texture.texture) {
01055         SDL_DestroyTexture(sdl.texture.texture);
01056         sdl.texture.texture=0;
01057     }
01058     sdl.window_desired_width = width;
01059     sdl.window_desired_height = height;
01060     int currWidth, currHeight;
01061     if (sdl.window) {
01062         //SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
01063         if (!sdl.update_window) {
01064             SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
01065             sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
01066 
01067             currentWindowWidth = currWidth;
01068             currentWindowHeight = currHeight;
01069 
01070             return sdl.window;
01071         }
01072     }
01073 
01074 #if C_OPENGL
01075     if (sdl_opengl.context) {
01076         SDL_GL_DeleteContext(sdl_opengl.context);
01077         sdl_opengl.context=0;
01078     }
01079 #endif
01080 
01081     /* If we change screen type, recreate the window. Furthermore, if
01082      * it is our very first time then we simply create a new window.
01083      */
01084     if (!sdl.window
01085             || (lastType != screenType)
01086 //          || (currWidth != width) || (currHeight != height)
01087 //          || (glwindow != (0 != (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_OPENGL)))
01088 //          || (fullscreen && (0 == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN)))
01089 //          || (fullscreen != (SDL_WINDOW_FULLSCREEN == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN)))
01090 //          || (fullscreen && ((width != currWidth) || (height != currHeight)))
01091        ) {
01092         lastType = screenType;
01093         if (sdl.window) {
01094             SDL_DestroyWindow(sdl.window);
01095         }
01096 
01097         sdl.window = SDL_CreateWindow("",
01098                                       SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber),
01099                                       SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber),
01100                                       width, height,
01101                                       (GFX_IsFullscreen() ? (sdl.desktop.full.display_res ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0)
01102                                       | ((screenType == SCREEN_OPENGL) ? SDL_WINDOW_OPENGL : 0) | SDL_WINDOW_SHOWN
01103                                       | (SDL2_resize_enable ? SDL_WINDOW_RESIZABLE : 0)
01104                                       | (dpi_aware_enable ? SDL_WINDOW_ALLOW_HIGHDPI : 0));
01105         if (sdl.window) {
01106             GFX_SetTitle(-1, -1, -1, false); //refresh title.
01107         }
01108         sdl.surface = SDL_GetWindowSurface(sdl.window);
01109         SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
01110         sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
01111 
01112         currentWindowWidth = currWidth;
01113         currentWindowHeight = currHeight;
01114 
01115 #if C_OPENGL
01116         if (screenType == SCREEN_OPENGL) {
01117             sdl_opengl.context = SDL_GL_CreateContext(sdl.window);
01118             if (sdl_opengl.context == NULL) LOG_MSG("WARNING: SDL2 unable to create GL context");
01119             if (SDL_GL_MakeCurrent(sdl.window, sdl_opengl.context) != 0) LOG_MSG("WARNING: SDL2 unable to make current GL context");
01120         }
01121 #endif
01122 
01123         return sdl.window;
01124     }
01125     /* Fullscreen mode switching has its limits, and is also problematic on
01126      * some window managers. For now, the following may work up to some
01127      * level. On X11, SDL_VIDEO_X11_LEGACY_FULLSCREEN=1 can also help,
01128      * although it has its own issues.
01129      * Suggestion: Use the desktop res if possible, with output=surface
01130      * if one is not interested in scaling.
01131      * On Android, desktop res is the only way.
01132      */
01133     SDL_SetWindowResizable(sdl.window, SDL2_resize_enable ? SDL_TRUE : SDL_FALSE);
01134     if (GFX_IsFullscreen()) {
01135         SDL_DisplayMode displayMode;
01136         SDL_GetWindowDisplayMode(sdl.window, &displayMode);
01137         displayMode.w = width;
01138         displayMode.h = height;
01139         SDL_SetWindowDisplayMode(sdl.window, &displayMode);
01140 
01141         SDL_SetWindowFullscreen(sdl.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
01142     } else {
01143         SDL_SetWindowFullscreen(sdl.window, 0);
01144 
01145         SDL_SetWindowSize(sdl.window, width, height);
01146     }
01147     /* Maybe some requested fullscreen resolution is unsupported? */
01148     SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
01149     sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
01150     sdl.surface = SDL_GetWindowSurface(sdl.window);
01151 
01152     currentWindowWidth = currWidth;
01153     currentWindowHeight = currHeight;
01154 
01155 #if C_OPENGL
01156     if (screenType == SCREEN_OPENGL) {
01157         sdl_opengl.context = SDL_GL_CreateContext(sdl.window);
01158         if (sdl_opengl.context == NULL) LOG_MSG("WARNING: SDL2 unable to create GL context");
01159         if (SDL_GL_MakeCurrent(sdl.window, sdl_opengl.context) != 0) LOG_MSG("WARNING: SDL2 unable to make current GL context");
01160     }
01161 #endif
01162 
01163     return sdl.window;
01164 }
01165 
01166 void GFX_SetResizeable(bool enable) {
01167     if (SDL2_resize_enable != enable) {
01168         SDL2_resize_enable = enable;
01169 
01170         if (sdl.window != NULL)
01171             SDL_SetWindowResizable(sdl.window, SDL2_resize_enable ? SDL_TRUE : SDL_FALSE);
01172     }
01173 }
01174 
01175 // Used for the mapper UI and more: Creates a fullscreen window with desktop res
01176 // on Android, and a non-fullscreen window with the input dimensions otherwise.
01177 SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height) {
01178     return GFX_SetSDLWindowMode(width, height, SCREEN_SURFACE);
01179 }
01180 
01181 // Returns the rectangle in the current window to be used for scaling a
01182 // sub-window with the given dimensions, like the mapper UI.
01183 SDL_Rect GFX_GetSDLSurfaceSubwindowDims(Bit16u width, Bit16u height) {
01184     SDL_Rect rect;
01185     rect.x=rect.y=0;
01186     rect.w=width;
01187     rect.h=height;
01188     return rect;
01189 }
01190 
01191 # if !defined(C_SDL2)
01192 // Currently used for an initial test here
01193 static SDL_Window * GFX_SetSDLOpenGLWindow(Bit16u width, Bit16u height) {
01194     return GFX_SetSDLWindowMode(width, height, SCREEN_OPENGL);
01195 }
01196 # endif
01197 #endif
01198 
01199 #if C_OPENGL && DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01200 unsigned int SDLDrawGenFontTextureUnitPerRow = 16;
01201 unsigned int SDLDrawGenFontTextureRows = 16;
01202 unsigned int SDLDrawGenFontTextureWidth = SDLDrawGenFontTextureUnitPerRow * 8;
01203 unsigned int SDLDrawGenFontTextureHeight = SDLDrawGenFontTextureRows * 16;
01204 bool SDLDrawGenFontTextureInit = false;
01205 GLuint SDLDrawGenFontTexture = (GLuint)(~0UL);
01206 #endif
01207 
01208 #if !defined(C_SDL2)
01209 /* Reset the screen with current values in the sdl structure */
01210 Bitu GFX_GetBestMode(Bitu flags) 
01211 {
01212     Bitu retFlags = 0;
01213 
01214     switch (sdl.desktop.want_type) 
01215     {
01216         case SCREEN_SURFACE:
01217             retFlags = OUTPUT_SURFACE_GetBestMode(flags);
01218             break;
01219 
01220 #if C_OPENGL
01221         case SCREEN_OPENGL:
01222             retFlags = OUTPUT_OPENGL_GetBestMode(flags);
01223             break;
01224 #endif
01225 
01226 #if C_DIRECT3D
01227         case SCREEN_DIRECT3D:
01228             retFlags = OUTPUT_DIRECT3D_GetBestMode(flags);
01229             break;
01230 #endif
01231 
01232         default:
01233             // we should never reach here
01234             retFlags = 0;
01235             break;
01236     }
01237 
01238     if (!retFlags)
01239     {
01240         if (sdl.desktop.want_type != SCREEN_SURFACE)
01241         {
01242             // try falling back down to surface
01243             OUTPUT_SURFACE_Select();
01244             retFlags = OUTPUT_SURFACE_GetBestMode(flags);
01245         }
01246         if (retFlags == 0)
01247             LOG_MSG("SDL:Failed everything including falling back to surface GFX_GetBestMode"); // completely failed it seems
01248     }
01249 
01250     return retFlags;
01251 }
01252 #endif
01253 
01254 /* FIXME: This prepares the SDL library to accept Win32 drag+drop events from the Windows shell.
01255  *        So it should be named something like EnableDragAcceptFiles() not SDL_Prepare() */
01256 void SDL_Prepare(void) {
01257     if (menu_compatible) return;
01258 
01259 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) // Microsoft Windows specific
01260     LOG(LOG_MISC,LOG_DEBUG)("Win32: Preparing main window to accept files dragged in from the Windows shell");
01261 
01262     SDL_PumpEvents(); SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
01263     DragAcceptFiles(GetHWND(), TRUE);
01264 #endif
01265 }
01266 
01267 void GFX_ForceRedrawScreen(void) {
01268     GFX_Stop();
01269     if (sdl.draw.callback)
01270         (sdl.draw.callback)( GFX_CallBackReset );
01271     GFX_Start();
01272 }
01273 
01274 void GFX_ResetScreen(void) {
01275     fullscreen_switch=false; 
01276     GFX_Stop();
01277     if (sdl.draw.callback)
01278         (sdl.draw.callback)( GFX_CallBackReset );
01279     GFX_Start();
01280     CPU_Reset_AutoAdjust();
01281     fullscreen_switch=true;
01282 #if !defined(C_SDL2)
01283     if (!sdl.desktop.fullscreen) DOSBox_RefreshMenu(); // for menu
01284 #endif
01285 }
01286 
01287 void GFX_ForceFullscreenExit(void) {
01288     if (sdl.desktop.lazy_fullscreen) {
01289         LOG_MSG("GFX LF: invalid screen change");
01290     } else {
01291         sdl.desktop.fullscreen=false;
01292         GFX_ResetScreen();
01293     }
01294 }
01295 
01296 uint32_t GFX_Rmask;
01297 unsigned char GFX_Rshift;
01298 uint32_t GFX_Gmask;
01299 unsigned char GFX_Gshift;
01300 uint32_t GFX_Bmask;
01301 unsigned char GFX_Bshift;
01302 uint32_t GFX_Amask;
01303 unsigned char GFX_Ashift;
01304 unsigned char GFX_bpp;
01305 
01306 unsigned int GFX_GetBShift() {
01307     return sdl.surface->format->Bshift;
01308 }
01309 
01310 void GFX_LogSDLState(void) 
01311 {
01312     LOG(LOG_MISC,LOG_DEBUG)("SDL video mode: %ux%u (clip %ux%u with upper-left at %ux%u) %ubpp",
01313         (unsigned)sdl.surface->w,(unsigned)sdl.surface->h,
01314         (unsigned)sdl.clip.w,(unsigned)sdl.clip.h,
01315         (unsigned)sdl.clip.x,(unsigned)sdl.clip.y,
01316         (unsigned)sdl.surface->format->BitsPerPixel);
01317     LOG(LOG_MISC,LOG_DEBUG)("   red: shift=%u mask=0x%08lx",
01318         (unsigned)sdl.surface->format->Rshift,
01319         (unsigned long)sdl.surface->format->Rmask);
01320     LOG(LOG_MISC,LOG_DEBUG)("   green: shift=%u mask=0x%08lx",
01321         (unsigned)sdl.surface->format->Gshift,
01322         (unsigned long)sdl.surface->format->Gmask);
01323     LOG(LOG_MISC,LOG_DEBUG)("   blue: shift=%u mask=0x%08lx",
01324         (unsigned)sdl.surface->format->Bshift,
01325         (unsigned long)sdl.surface->format->Bmask);
01326     LOG(LOG_MISC,LOG_DEBUG)("   alpha: shift=%u mask=0x%08lx",
01327         (unsigned)sdl.surface->format->Ashift,
01328         (unsigned long)sdl.surface->format->Amask);
01329 
01330     GFX_bpp = sdl.surface->format->BitsPerPixel;
01331     GFX_Rmask = sdl.surface->format->Rmask;
01332     GFX_Rshift = sdl.surface->format->Rshift;
01333     GFX_Gmask = sdl.surface->format->Gmask;
01334     GFX_Gshift = sdl.surface->format->Gshift;
01335     GFX_Bmask = sdl.surface->format->Bmask;
01336     GFX_Bshift = sdl.surface->format->Bshift;
01337     GFX_Amask = sdl.surface->format->Amask;
01338     GFX_Ashift = sdl.surface->format->Ashift;
01339 }
01340 
01341 void GFX_TearDown(void) {
01342     if (sdl.updating)
01343         GFX_EndUpdate( 0 );
01344 
01345     if (sdl.blit.surface) {
01346         SDL_FreeSurface(sdl.blit.surface);
01347         sdl.blit.surface=0;
01348     }
01349 }
01350 
01351 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01352 void MenuShadeRect(int x,int y,int w,int h) {
01353     if (OpenGL_using()) {
01354 #if C_OPENGL
01355         glShadeModel (GL_FLAT);
01356         glBlendFunc(GL_ONE, GL_SRC_ALPHA);
01357         glDisable (GL_DEPTH_TEST);
01358         glDisable (GL_LIGHTING);
01359         glEnable(GL_BLEND);
01360         glDisable(GL_CULL_FACE);
01361         glDisable(GL_ALPHA_TEST);
01362         glDisable(GL_FOG);
01363         glDisable(GL_SCISSOR_TEST);
01364         glDisable(GL_STENCIL_TEST);
01365         glDisable(GL_TEXTURE_2D);
01366 
01367         glColor4ub(0, 0, 0, 64);
01368         glBegin(GL_QUADS);
01369         glVertex2i(x  ,y  );
01370         glVertex2i(x+w,y  );
01371         glVertex2i(x+w,y+h);
01372         glVertex2i(x  ,y+h);
01373         glEnd();
01374 
01375         glBlendFunc(GL_ONE, GL_ZERO);
01376         glEnable(GL_TEXTURE_2D);
01377 #endif
01378     }
01379     else {
01380         if (x < 0) {
01381             w += x;
01382             x = 0;
01383         }
01384         if (y < 0) {
01385             y += h;
01386             y = 0;
01387         }
01388         if ((x+w) > sdl.surface->w)
01389             w = sdl.surface->w - x;
01390         if ((y+h) > sdl.surface->h)
01391             h = sdl.surface->h - y;
01392         if (w <= 0 || h <= 0)
01393             return;
01394 
01395         if (sdl.surface->format->BitsPerPixel == 32) {
01396             unsigned char *scan;
01397             uint32_t *row,mask;
01398 
01399             mask = ((sdl.surface->format->Rmask >> 2) & sdl.surface->format->Rmask) |
01400                 ((sdl.surface->format->Gmask >> 2) & sdl.surface->format->Gmask) |
01401                 ((sdl.surface->format->Bmask >> 2) & sdl.surface->format->Bmask);
01402 
01403             assert(sdl.surface->pixels != NULL);
01404 
01405             scan  = (unsigned char*)sdl.surface->pixels;
01406             scan += y * sdl.surface->pitch;
01407             scan += x * 4;
01408             while (h-- > 0) {
01409                 row = (uint32_t*)scan;
01410                 scan += sdl.surface->pitch;
01411                 for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (row[c] >> 2) & mask;
01412             }
01413         }
01414         else if (sdl.surface->format->BitsPerPixel == 16) {
01415             unsigned char *scan;
01416             uint16_t *row,mask;
01417 
01418             mask = ((sdl.surface->format->Rmask >> 2) & sdl.surface->format->Rmask) |
01419                 ((sdl.surface->format->Gmask >> 2) & sdl.surface->format->Gmask) |
01420                 ((sdl.surface->format->Bmask >> 2) & sdl.surface->format->Bmask);
01421 
01422             assert(sdl.surface->pixels != NULL);
01423 
01424             scan  = (unsigned char*)sdl.surface->pixels;
01425             scan += y * sdl.surface->pitch;
01426             scan += x * 2;
01427             while (h-- > 0) {
01428                 row = (uint16_t*)scan;
01429                 scan += sdl.surface->pitch;
01430                 for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (row[c] >> 2) & mask;
01431             }
01432         }
01433         else {
01434             /* TODO */
01435         }
01436     }
01437 }
01438 
01439 void MenuDrawRect(int x,int y,int w,int h,Bitu color) {
01440     if (OpenGL_using()) {
01441 #if C_OPENGL
01442         glShadeModel (GL_FLAT);
01443         glBlendFunc(GL_ONE, GL_ZERO);
01444         glDisable (GL_DEPTH_TEST);
01445         glDisable (GL_LIGHTING);
01446         glDisable(GL_BLEND);
01447         glDisable(GL_CULL_FACE);
01448         glDisable(GL_ALPHA_TEST);
01449         glDisable(GL_FOG);
01450         glDisable(GL_SCISSOR_TEST);
01451         glDisable(GL_STENCIL_TEST);
01452         glDisable(GL_TEXTURE_2D);
01453 
01454         glColor3ub((color >> 16UL) & 0xFF,(color >> 8UL) & 0xFF,(color >> 0UL) & 0xFF);
01455         glBegin(GL_QUADS);
01456         glVertex2i(x  ,y  );
01457         glVertex2i(x+w,y  );
01458         glVertex2i(x+w,y+h);
01459         glVertex2i(x  ,y+h);
01460         glEnd();
01461 
01462         glBlendFunc(GL_ONE, GL_ZERO);
01463         glEnable(GL_TEXTURE_2D);
01464 #endif
01465     }
01466     else {
01467         if (x < 0) {
01468             w += x;
01469             x = 0;
01470         }
01471         if (y < 0) {
01472             y += h;
01473             y = 0;
01474         }
01475         if ((x+w) > sdl.surface->w)
01476             w = sdl.surface->w - x;
01477         if ((y+h) > sdl.surface->h)
01478             h = sdl.surface->h - y;
01479         if (w <= 0 || h <= 0)
01480             return;
01481 
01482         if (sdl.surface->format->BitsPerPixel == 32) {
01483             unsigned char *scan;
01484             uint32_t *row;
01485 
01486             assert(sdl.surface->pixels != NULL);
01487 
01488             scan  = (unsigned char*)sdl.surface->pixels;
01489             scan += y * sdl.surface->pitch;
01490             scan += x * 4;
01491             while (h-- > 0) {
01492                 row = (uint32_t*)scan;
01493                 scan += sdl.surface->pitch;
01494                 for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (uint32_t)color;
01495             }
01496         }
01497         else if (sdl.surface->format->BitsPerPixel == 16) {
01498             unsigned char *scan;
01499             uint16_t *row;
01500 
01501             assert(sdl.surface->pixels != NULL);
01502 
01503             scan  = (unsigned char*)sdl.surface->pixels;
01504             scan += y * sdl.surface->pitch;
01505             scan += x * 2;
01506             while (h-- > 0) {
01507                 row = (uint16_t*)scan;
01508                 scan += sdl.surface->pitch;
01509                 for (unsigned int c=0;c < (unsigned int)w;c++) row[c] = (uint16_t)color;
01510             }
01511         }
01512         else {
01513             /* TODO */
01514         }
01515     }
01516 }
01517 
01518 extern Bit8u int10_font_14[256 * 14];
01519 extern Bit8u int10_font_16[256 * 16];
01520 
01521 void MenuDrawTextChar(int x,int y,unsigned char c,Bitu color) {
01522     static const unsigned int fontHeight = 16;
01523 
01524     if (x < 0 || y < 0 ||
01525         (unsigned int)(x+8) > (unsigned int)sdl.surface->w ||
01526         (unsigned int)(y+(int)fontHeight) > (unsigned int)sdl.surface->h)
01527         return;
01528 
01529     unsigned char *bmp = (unsigned char*)int10_font_16 + (c * fontHeight);
01530 
01531     if (OpenGL_using()) {
01532 #if C_OPENGL
01533         unsigned int tx = (c % 16u) * 8u;
01534         unsigned int ty = (c / 16u) * 16u;
01535 
01536         /* MenuDrawText() has prepared OpenGL state for us */
01537         glBegin(GL_QUADS);
01538         // lower left
01539         glTexCoord2i((int)tx+0,    (int)ty                ); glVertex2i((int)x,  (int)y                );
01540         // lower right
01541         glTexCoord2i((int)tx+8,    (int)ty                ); glVertex2i((int)x+8,(int)y                );
01542         // upper right
01543         glTexCoord2i((int)tx+8,    (int)ty+(int)fontHeight); glVertex2i((int)x+8,(int)y+(int)fontHeight);
01544         // upper left
01545         glTexCoord2i((int)tx+0,    (int)ty+(int)fontHeight); glVertex2i((int)x,  (int)y+(int)fontHeight);
01546         glEnd();
01547 #endif
01548     }
01549     else {
01550         unsigned char *scan;
01551 
01552         assert(sdl.surface->pixels != NULL);
01553 
01554         if (x < 0 || y < 0)
01555             return;
01556         if ((x + 8) > sdl.surface->w)
01557             return;
01558         if ((y + (int)fontHeight) > sdl.surface->h)
01559             return;
01560 
01561         scan  = (unsigned char*)sdl.surface->pixels;
01562         scan += (unsigned int)y * (unsigned int)sdl.surface->pitch;
01563         scan += (unsigned int)x * (((unsigned int)sdl.surface->format->BitsPerPixel+7u)/8u);
01564 
01565         for (unsigned int row=0;row < fontHeight;row++) {
01566             unsigned char rb = bmp[row];
01567 
01568             if (sdl.surface->format->BitsPerPixel == 32) {
01569                 uint32_t *dp = (uint32_t*)scan;
01570                 for (unsigned int colm=0x80;colm != 0;colm >>= 1) {
01571                     if (rb & colm) *dp = (uint32_t)color;
01572                     dp++;
01573                 }
01574             }
01575             else if (sdl.surface->format->BitsPerPixel == 16) {
01576                 uint16_t *dp = (uint16_t*)scan;
01577                 for (unsigned int colm=0x80;colm != 0;colm >>= 1) {
01578                     if (rb & colm) *dp = (uint16_t)color;
01579                     dp++;
01580                 }
01581             }
01582 
01583             scan += (size_t)sdl.surface->pitch;
01584         }
01585     }
01586 }
01587 
01588 void MenuDrawTextChar2x(int x,int y,unsigned char c,Bitu color) {
01589     static const unsigned int fontHeight = 16;
01590 
01591     if (x < 0 || y < 0 ||
01592         (unsigned int)(x+8) > (unsigned int)sdl.surface->w ||
01593         (unsigned int)(y+(int)fontHeight) > (unsigned int)sdl.surface->h)
01594         return;
01595 
01596     unsigned char *bmp = (unsigned char*)int10_font_16 + (c * fontHeight);
01597 
01598     if (OpenGL_using()) {
01599 #if C_OPENGL
01600         unsigned int tx = (c % 16u) * 8u;
01601         unsigned int ty = (c / 16u) * 16u;
01602 
01603         /* MenuDrawText() has prepared OpenGL state for us */
01604         glBegin(GL_QUADS);
01605         // lower left
01606         glTexCoord2i((int)tx+0,    (int)ty                ); glVertex2i(x,      y                    );
01607         // lower right
01608         glTexCoord2i((int)tx+8,    (int)ty                ); glVertex2i(x+(8*2),y                    );
01609         // upper right
01610         glTexCoord2i((int)tx+8,    (int)ty+(int)fontHeight); glVertex2i(x+(8*2),y+((int)fontHeight*2));
01611         // upper left
01612         glTexCoord2i((int)tx+0,    (int)ty+(int)fontHeight); glVertex2i(x,      y+((int)fontHeight*2));
01613         glEnd();
01614 #endif
01615     }
01616     else { 
01617         unsigned char *scan;
01618 
01619         assert(sdl.surface->pixels != NULL);
01620 
01621         if (x < 0 || y < 0)
01622             return;
01623         if ((x + 16) > sdl.surface->w)
01624             return;
01625         if ((y + ((int)fontHeight * 2)) > sdl.surface->h)
01626             return;
01627 
01628         scan  = (unsigned char*)sdl.surface->pixels;
01629         scan += y * sdl.surface->pitch;
01630         scan += x * ((sdl.surface->format->BitsPerPixel+7)/8);
01631 
01632         for (unsigned int row=0;row < (fontHeight*2);row++) {
01633             unsigned char rb = bmp[row>>1U];
01634 
01635             if (sdl.surface->format->BitsPerPixel == 32) {
01636                 uint32_t *dp = (uint32_t*)scan;
01637                 for (unsigned int colm=0x80;colm != 0;colm >>= 1) {
01638                     if (rb & colm) {
01639                         *dp++ = (uint32_t)color;
01640                         *dp++ = (uint32_t)color;
01641                     }
01642                     else {
01643                         dp += 2;
01644                     }
01645                 }
01646             }
01647             else if (sdl.surface->format->BitsPerPixel == 16) {
01648                 uint16_t *dp = (uint16_t*)scan;
01649                 for (unsigned int colm=0x80;colm != 0;colm >>= 1) {
01650                     if (rb & colm) {
01651                         *dp++ = (uint16_t)color;
01652                         *dp++ = (uint16_t)color;
01653                     }
01654                     else {
01655                         dp += 2;
01656                     }
01657                 }
01658             }
01659 
01660             scan += sdl.surface->pitch;
01661         }
01662     }
01663 }
01664 
01665 void MenuDrawText(int x,int y,const char *text,Bitu color) {
01666 #if C_OPENGL
01667     if (OpenGL_using()) {
01668         glBindTexture(GL_TEXTURE_2D,SDLDrawGenFontTexture);
01669 
01670         glPushMatrix();
01671 
01672         glMatrixMode (GL_TEXTURE);
01673         glLoadIdentity ();
01674         glScaled(1.0 / SDLDrawGenFontTextureWidth, 1.0 / SDLDrawGenFontTextureHeight, 1.0);
01675 
01676         glColor4ub((color >> 16UL) & 0xFF,(color >> 8UL) & 0xFF,(color >> 0UL) & 0xFF,0xFF);
01677         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
01678 
01679         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
01680         glEnable(GL_TEXTURE_2D);
01681         glEnable(GL_ALPHA_TEST);
01682         glEnable(GL_BLEND);
01683     }
01684 #endif
01685 
01686     while (*text != 0) {
01687         if (mainMenu.fontCharScale >= 2)
01688             MenuDrawTextChar2x(x,y,(unsigned char)(*text++),color);
01689         else
01690             MenuDrawTextChar(x,y,(unsigned char)(*text++),color);
01691 
01692         x += (int)mainMenu.fontCharWidth;
01693     }
01694 
01695 #if C_OPENGL
01696     if (OpenGL_using()) {
01697         glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
01698         glBlendFunc(GL_ONE, GL_ZERO);
01699         glDisable(GL_ALPHA_TEST);
01700         glEnable(GL_TEXTURE_2D);
01701 
01702         glPopMatrix();
01703 
01704         glBindTexture(GL_TEXTURE_2D,sdl_opengl.texture);
01705     }
01706 #endif
01707 }
01708 
01709 void DOSBoxMenu::item::drawMenuItem(DOSBoxMenu &menu) {
01710     (void)menu;//UNUSED
01711 
01712     Bitu bgcolor = GFX_GetRGB(63, 63, 63);
01713     Bitu fgcolor = GFX_GetRGB(191, 191, 191);
01714     Bitu fgshortcolor = GFX_GetRGB(127, 127, 191);
01715     Bitu fgcheckcolor = GFX_GetRGB(191, 191, 127);
01716 
01717     if (type >= separator_type_id) {
01718         /* separators never change visual state on hover/select */
01719     }
01720     else if (!status.enabled) {
01721         if (itemHover)
01722             bgcolor = GFX_GetRGB(79, 79, 79);
01723 
01724         fgcolor = GFX_GetRGB(144, 144, 144);
01725         fgshortcolor = GFX_GetRGB(63, 63, 144);
01726         fgcheckcolor = GFX_GetRGB(144, 144, 63);
01727     }
01728     else if (itemHilight) {
01729         bgcolor = GFX_GetRGB(0, 0, 63);
01730         fgcolor = GFX_GetRGB(255, 255, 255);
01731         fgshortcolor = GFX_GetRGB(191, 191, 255);
01732     }
01733     else if (itemHover) {
01734         bgcolor = GFX_GetRGB(127, 127, 127);
01735         fgcolor = GFX_GetRGB(255, 255, 255);
01736         fgshortcolor = GFX_GetRGB(191, 191, 255);
01737     }
01738 
01739     itemHoverDrawn = itemHover;
01740     itemHilightDrawn = itemHilight;
01741 
01742     if (SDL_MUSTLOCK(sdl.surface))
01743         SDL_LockSurface(sdl.surface);
01744 
01745     MenuDrawRect(screenBox.x, screenBox.y, screenBox.w, screenBox.h, bgcolor);
01746     if (checkBox.w != 0 && checkBox.h != 0) {
01747         const char *str = status.checked ? "\xFB" : " ";
01748 
01749         MenuDrawText(screenBox.x+checkBox.x, screenBox.y+checkBox.y, str, fgcheckcolor);
01750     }
01751     if (textBox.w != 0 && textBox.h != 0)
01752         MenuDrawText(screenBox.x+textBox.x, screenBox.y+textBox.y, text.c_str(), fgcolor);
01753     if (shortBox.w != 0 && shortBox.h != 0)
01754         MenuDrawText(screenBox.x+shortBox.x, screenBox.y+shortBox.y, shortcut_text.c_str(), fgshortcolor);
01755 
01756     if (type == submenu_type_id && borderTop/*not toplevel*/)
01757         MenuDrawText((int)((int)screenBox.x+(int)screenBox.w - (int)mainMenu.fontCharWidth - 1), (int)((int)screenBox.y+(int)textBox.y), "\x10", fgcheckcolor);
01758 
01759     if (type == separator_type_id)
01760         MenuDrawRect((int)screenBox.x, (int)screenBox.y + ((int)screenBox.h/2), (int)screenBox.w, 1, fgcolor);
01761     else if (type == vseparator_type_id)
01762         MenuDrawRect((int)screenBox.x + ((int)screenBox.w/2), (int)screenBox.y, 1, (int)screenBox.h, fgcolor);
01763 
01764     if (SDL_MUSTLOCK(sdl.surface))
01765         SDL_UnlockSurface(sdl.surface);
01766 }
01767 
01768 void DOSBoxMenu::displaylist::DrawDisplayList(DOSBoxMenu &menu,bool updateScreen) {
01769     for (auto &id : disp_list) {
01770         DOSBoxMenu::item &item = menu.get_item(id);
01771 
01772         item.drawMenuItem(menu);
01773         if (updateScreen) item.updateScreenFromItem(menu);
01774     }
01775 }
01776 
01777 bool DOSBox_isMenuVisible(void);
01778 
01779 void GFX_DrawSDLMenu(DOSBoxMenu &menu, DOSBoxMenu::displaylist &dl) {
01780     if (!menu.needsRedraw() || (sdl.updating && !OpenGL_using())) {
01781         return;
01782     }
01783     if (!DOSBox_isMenuVisible() || sdl.desktop.fullscreen) {
01784         // BUGFIX: If the menu is hidden then silently clear "needs redraw" to avoid excess redraw of nothing
01785         menu.clearRedraw();
01786         return;
01787     }
01788 
01789     bool mustLock = !OpenGL_using() && SDL_MUSTLOCK(sdl.surface);
01790 
01791     if (mustLock) {
01792         SDL_LockSurface(sdl.surface);
01793     }
01794 
01795     if (&dl == &menu.display_list) { /* top level menu, draw background */
01796         MenuDrawRect(menu.menuBox.x, menu.menuBox.y, menu.menuBox.w, menu.menuBox.h - 1, GFX_GetRGB(63, 63, 63));
01797         MenuDrawRect(menu.menuBox.x, menu.menuBox.y + menu.menuBox.h - 1, menu.menuBox.w, 1,
01798                      GFX_GetRGB(31, 31, 31));
01799     }
01800 
01801     if (mustLock) {
01802         SDL_UnlockSurface(sdl.surface);
01803     }
01804 
01805 #if 0
01806     LOG_MSG("menudraw %u",(unsigned int)SDL_GetTicks());
01807 #endif
01808 
01809     menu.clearRedraw();
01810     menu.display_list.DrawDisplayList(menu,/*updateScreen*/false);
01811 
01812     if (!OpenGL_using()) {
01813 #if defined(C_SDL2)
01814         SDL_UpdateWindowSurfaceRects( sdl.window, &menu.menuBox, 1 );
01815 #else
01816         SDL_UpdateRects(sdl.surface, 1, &menu.menuBox);
01817 #endif
01818     }
01819 }
01820 #endif
01821 
01822 void RENDER_Reset(void);
01823 
01824 Bitu GFX_SetSize(Bitu width, Bitu height, Bitu flags, double scalex, double scaley, GFX_CallBack_t callback) 
01825 {
01826     if (width == 0 || height == 0) {
01827         E_Exit("GFX_SetSize with width=%d height=%d zero dimensions not allowed",(int)width,(int)height);
01828         return 0;
01829     }
01830 
01831     if (sdl.updating)
01832         GFX_EndUpdate( 0 );
01833 
01834     sdl.must_redraw_all = true;
01835 
01836     sdl.draw.width = (Bit32u)width;
01837     sdl.draw.height = (Bit32u)height;
01838     sdl.draw.flags = flags;
01839     sdl.draw.callback = callback;
01840     sdl.draw.scalex = scalex;
01841     sdl.draw.scaley = scaley;
01842 
01843     LOG(LOG_MISC,LOG_DEBUG)("GFX_SetSize %ux%u flags=0x%x scale=%.3fx%.3f",
01844         (unsigned int)width,(unsigned int)height,
01845         (unsigned int)flags,
01846         scalex,scaley);
01847 
01848 //    Bitu bpp = 0;
01849     Bitu retFlags = 0;
01850 
01851     if (sdl.blit.surface) {
01852         SDL_FreeSurface(sdl.blit.surface);
01853         sdl.blit.surface=0;
01854     }
01855 
01856     switch (sdl.desktop.want_type) {
01857         case SCREEN_SURFACE:
01858             retFlags = OUTPUT_SURFACE_SetSize();
01859             break;
01860 
01861 #if C_OPENGL
01862         case SCREEN_OPENGL:
01863             retFlags = OUTPUT_OPENGL_SetSize();
01864             break;
01865 #endif
01866 
01867 #if C_DIRECT3D
01868         case SCREEN_DIRECT3D: 
01869             retFlags = OUTPUT_DIRECT3D_SetSize();
01870             break;
01871 #endif
01872 
01873         default:
01874             // we should never reach here
01875             retFlags = 0;
01876             break;
01877     }
01878 
01879     if (!retFlags)
01880     {
01881         if (sdl.desktop.want_type != SCREEN_SURFACE)
01882         {
01883             // try falling back down to surface
01884             OUTPUT_SURFACE_Select();
01885             retFlags = OUTPUT_SURFACE_SetSize();
01886         }
01887         if (retFlags == 0)
01888             LOG_MSG("SDL:Failed everything including falling back to surface in GFX_GetSize"); // completely failed it seems
01889     }
01890 
01891     // we have selected an actual desktop type
01892     sdl.desktop.type = sdl.desktop.want_type;
01893 
01894     GFX_LogSDLState();
01895 
01896     if (retFlags) 
01897         GFX_Start();
01898 
01899     if (!sdl.mouse.autoenable && !sdl.mouse.locked)
01900         SDL_ShowCursor(sdl.mouse.autolock?SDL_DISABLE:SDL_ENABLE);
01901 
01902     UpdateWindowDimensions();
01903 
01904 #if defined(WIN32) && !defined(HX_DOS)
01905     WindowsTaskbarUpdatePreviewRegion();
01906 #endif
01907 
01908     return retFlags;
01909 }
01910 
01911 #if defined(WIN32) && !defined(HX_DOS)
01912 // WARNING: Not recommended, there is danger you cannot exit emulator because mouse+keyboard are taken
01913 static bool enable_hook_everything = false;
01914 #endif
01915 
01916 // Whether or not to hook the keyboard and block special keys.
01917 // Setting this is recommended so that your keyboard is fully usable in the guest OS when you
01918 // enable the mouse+keyboard capture. But hooking EVERYTHING is not recommended because there is a
01919 // danger you become trapped in the DOSBox emulator!
01920 static bool enable_hook_special_keys = true;
01921 
01922 #if defined(WIN32) && !defined(HX_DOS)
01923 // Whether or not to hook Num/Scroll/Caps lock in order to give the guest OS full control of the
01924 // LEDs on the keyboard (i.e. the LEDs do not change until the guest OS changes their state).
01925 // This flag also enables code to set the LEDs to guest state when setting mouse+keyboard capture,
01926 // and restoring LED state when releasing capture.
01927 static bool enable_hook_lock_toggle_keys = true;
01928 #endif
01929 
01930 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
01931 // and this is where we store host LED state when capture is set.
01932 static bool on_capture_num_lock_was_on = true; // reasonable guess
01933 static bool on_capture_scroll_lock_was_on = false;
01934 static bool on_capture_caps_lock_was_on = false;
01935 #endif
01936 
01937 static bool exthook_enabled = false;
01938 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
01939 static HHOOK exthook_winhook = NULL;
01940 
01941 #if !defined(__MINGW32__)
01942 extern "C" void SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(unsigned char x);
01943 #endif
01944 
01945 static LRESULT CALLBACK WinExtHookKeyboardHookProc(int nCode,WPARAM wParam,LPARAM lParam) {
01946     if (nCode == HC_ACTION) {
01947         HWND myHwnd = GetHWND();
01948 
01949         if (exthook_enabled && GetFocus() == myHwnd) { /* intercept only if DOSBox-X is the focus and the keyboard is hooked */
01950             if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN || wParam == WM_SYSKEYUP || wParam == WM_KEYUP) {
01951                 KBDLLHOOKSTRUCT *st_hook = (KBDLLHOOKSTRUCT*)lParam;
01952 
01953                 if (st_hook->flags & LLKHF_INJECTED) {
01954                     // injected keys are automatically allowed, especially if we are injecting keyboard input into ourself
01955                     // to control Num/Scroll/Caps Lock LEDs. If we don't check this we cannot control the LEDs. Injecting
01956                     // keydown/keyup for Num Lock is the only means provided by Windows to control those LEDs.
01957                 }
01958                 else if (st_hook->vkCode == VK_MENU/*alt*/ || st_hook->vkCode == VK_CONTROL ||
01959                     st_hook->vkCode == VK_LSHIFT || st_hook->vkCode == VK_RSHIFT) {
01960                     // always allow modifier keys through, so other applications are not left with state inconsistent from
01961                     // actual keyboard state.
01962                 }
01963                 else {
01964                     bool nopass = enable_hook_everything; // if the user wants us to hook ALL keys then that's where this signals it
01965                     bool alternate_message = false; // send as WM_USER+0x100 instead of WM_KEYDOWN
01966 
01967                     if (!nopass) {
01968                         // hook only certain keys Windows is likely to act on by itself.
01969 
01970                         // FIXME: Hooking the keyboard does NOT prevent Fn+SPACE (zoom) from triggering screen resolution
01971                         //        changes in Windows 10! How do we stop that?
01972 
01973                         // FIXME: It might be nice to let the user decide whether or not Print Screen is intercepted.
01974 
01975                         // TODO: We do not hook the volume up/down/mute keys. This is to be kind to the user. They may
01976                         // appreciate the ability to dial down the volume if a loud DOS program comes up. But
01977                         // if the user WANTS us to, we should allow hooking those keys.
01978 
01979                         // TODO: Allow (if instructed) hooking the VK_SLEEP key so pushing the sleep key (the
01980                         // one with the icon of the moon on Microsoft keyboards) can be sent instead to the
01981                         // guest OS. Also add code where if we're not hooking the key, then we should listen
01982                         // for signals the guest OS is suspending or hibernating and auto-disconnect the
01983                         // mouse capture and keyboard hook.
01984 
01985                         switch (st_hook->vkCode) {
01986                         case VK_LWIN:   // left Windows key (normally triggers Start menu)
01987                         case VK_RWIN:   // right Windows key (normally triggers Start menu)
01988                         case VK_APPS:   // Application key (normally open to the user, but just in case)
01989                         case VK_PAUSE:  // pause key
01990                         case VK_SNAPSHOT: // print screen
01991                         case VK_TAB:    // try to catch ALT+TAB too (not blocking VK_TAB will allow host OS to switch tasks)
01992                         case VK_ESCAPE: // try to catch CTRL+ESC as well (so Windows 95 Start Menu is accessible)
01993                         case VK_SPACE:  // and space (catching VK_ZOOM isn't enough to prevent Windows 10 from changing res)
01994                         // these keys have no meaning to DOSBox and so we hook them by default to allow the guest OS to use them
01995                         case VK_BROWSER_BACK: // Browser Back key
01996                         case VK_BROWSER_FORWARD: // Browser Forward key
01997                         case VK_BROWSER_REFRESH: // Browser Refresh key
01998                         case VK_BROWSER_STOP: // Browser Stop key
01999                         case VK_BROWSER_SEARCH: // Browser Search key
02000                         case VK_BROWSER_FAVORITES: // Browser Favorites key
02001                         case VK_BROWSER_HOME: // Browser Start and Home key
02002                         case VK_MEDIA_NEXT_TRACK: // Next Track key
02003                         case VK_MEDIA_PREV_TRACK: // Previous Track key
02004                         case VK_MEDIA_STOP: // Stop Media key
02005                         case VK_MEDIA_PLAY_PAUSE: // Play / Pause Media key
02006                         case VK_LAUNCH_MAIL: // Start Mail key
02007                         case VK_LAUNCH_MEDIA_SELECT: // Select Media key
02008                         case VK_LAUNCH_APP1: // Start Application 1 key
02009                         case VK_LAUNCH_APP2: // Start Application 2 key
02010                         case VK_PLAY: // Play key
02011                         case VK_ZOOM: // Zoom key (the (+) magnifying glass keyboard shortcut laptops have these days on the spacebar?)
02012                             nopass = true;
02013                             break;
02014 
02015                             // IME Hiragana key, otherwise inaccessible to us
02016                         case 0xF2:
02017                             nopass = true; // FIXME: This doesn't (yet) cause a SDL key event.
02018                             break;
02019 
02020                             // we allow hooking Num/Scroll/Caps Lock keys so that pressing them does not toggle the LED.
02021                             // we then take Num/Scroll/Caps LED state from the guest and let THAT control the LED state.
02022                         case VK_CAPITAL:
02023                         case VK_NUMLOCK:
02024                         case VK_SCROLL:
02025                             nopass = enable_hook_lock_toggle_keys;
02026                             alternate_message = true;
02027                             break;
02028                         }
02029                     }
02030 
02031                     if (nopass) {
02032                         // convert WM_KEYDOWN/WM_KEYUP if obfuscating the message to distinguish between real and injected events
02033                         if (alternate_message) {
02034                             if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
02035                                 wParam = WM_USER + 0x100;
02036                             else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
02037                                 wParam = WM_USER + 0x101;
02038                         }
02039 
02040                         DWORD lParam =
02041                             (st_hook->scanCode << 8U) +
02042                             ((st_hook->flags & LLKHF_EXTENDED) ? 0x01000000 : 0) +
02043                             ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) ? 0xC0000000 : 0);
02044 
02045                         // catch the keystroke, post it to ourself, do not pass it on
02046                         PostMessage(myHwnd, (UINT)wParam, st_hook->vkCode, lParam);
02047                         return TRUE;
02048                     }
02049                 }
02050             }
02051         }
02052     }
02053 
02054     return CallNextHookEx(exthook_winhook, nCode, wParam, lParam);
02055 }
02056 
02057 // Microsoft doesn't have an outright "set toggle key state" call, they expect you
02058 // to know the state and then fake input to toggle. Blegh. Fine.
02059 void WinSetKeyToggleState(unsigned int vkCode, bool state) {
02060     bool curState = (GetKeyState(vkCode) & 1) ? true : false;
02061     INPUT inps;
02062 
02063     // if we're already in that state, then there is nothing to do.
02064     if (curState == state) return;
02065 
02066     // fake keyboard input.
02067     memset(&inps, 0, sizeof(inps));
02068     inps.type = INPUT_KEYBOARD;
02069     inps.ki.wVk = vkCode;
02070     inps.ki.dwFlags = KEYEVENTF_EXTENDEDKEY; // pressed, use wVk.
02071     SendInput(1, &inps, sizeof(INPUT));
02072 
02073     memset(&inps, 0, sizeof(inps));
02074     inps.type = INPUT_KEYBOARD;
02075     inps.ki.wVk = vkCode;
02076     inps.ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; // release, use wVk.
02077     SendInput(1, &inps, sizeof(INPUT));
02078 }
02079 #endif
02080 
02081 Bitu Keyboard_Guest_LED_State();
02082 void UpdateKeyboardLEDState(Bitu led_state/* in the same bitfield arrangement as using command 0xED on PS/2 keyboards */);
02083 
02084 void UpdateKeyboardLEDState(Bitu led_state/* in the same bitfield arrangement as using command 0xED on PS/2 keyboards */) {
02085     (void)led_state;//POSSIBLY UNUSED
02086 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) /* Microsoft Windows */
02087     if (exthook_enabled) { // ONLY if ext hook is enabled, else we risk infinite loops with keyboard events
02088         //WinSetKeyToggleState(VK_NUMLOCK, !!(led_state & 2));
02089         //WinSetKeyToggleState(VK_SCROLL, !!(led_state & 1));
02090         //WinSetKeyToggleState(VK_CAPITAL, !!(led_state & 4));
02091     }
02092 #endif
02093 }
02094 
02095 void DoExtendedKeyboardHook(bool enable) {
02096     if (exthook_enabled == enable)
02097         return;
02098 
02099 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
02100     if (enable) {
02101         if (!exthook_winhook) {
02102             exthook_winhook = SetWindowsHookEx(WH_KEYBOARD_LL, WinExtHookKeyboardHookProc, GetModuleHandle(NULL), 0);
02103             if (exthook_winhook == NULL) return;
02104         }
02105 
02106         // it's on
02107         exthook_enabled = enable;
02108 
02109         // flush out and handle pending keyboard I/O
02110         {
02111             MSG msg;
02112 
02113             while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
02114                 TranslateMessage(&msg);
02115                 DispatchMessage(&msg);
02116             }
02117         }
02118 
02119 #if !defined(__MINGW32__)
02120         // Enable the SDL hack for Win32 to handle Num/Scroll/Caps
02121         SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(1);
02122 #endif
02123 
02124         // if hooking Num/Scroll/Caps Lock then record the toggle state of those keys.
02125         // then read from the keyboard emulation the LED state set by the guest and apply it to the host keyboard.
02126         if (enable_hook_lock_toggle_keys) {
02127             // record state
02128             on_capture_num_lock_was_on = (GetKeyState(VK_NUMLOCK) & 1) ? true : false;
02129             on_capture_scroll_lock_was_on = (GetKeyState(VK_SCROLL) & 1) ? true : false;
02130             on_capture_caps_lock_was_on = (GetKeyState(VK_CAPITAL) & 1) ? true : false;
02131             // change to guest state (FIXME: Read emulated keyboard state and apply!)
02132             UpdateKeyboardLEDState(Keyboard_Guest_LED_State());
02133         }
02134     }
02135     else {
02136         if (exthook_winhook) {
02137             if (enable_hook_lock_toggle_keys) {
02138                 // restore state
02139                 //WinSetKeyToggleState(VK_NUMLOCK, on_capture_num_lock_was_on);
02140                 //WinSetKeyToggleState(VK_SCROLL, on_capture_scroll_lock_was_on);
02141                 //WinSetKeyToggleState(VK_CAPITAL, on_capture_caps_lock_was_on);
02142             }
02143 
02144             {
02145                 MSG msg;
02146 
02147                 // before we disable the SDL hack make sure we flush out and handle any pending keyboard events.
02148                 // if we don't do this the posted Num/Scroll/Caps events will stay in the queue and will be handled
02149                 // by SDL after turning off the toggle key hack.
02150                 Sleep(1); // make sure Windows posts the keystrokes
02151                 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
02152                     TranslateMessage(&msg);
02153                     DispatchMessage(&msg);
02154                 }
02155             }
02156 
02157 #if !defined(__MINGW32__)
02158             // Disable the SDL hack for Win32 to handle Num/Scroll/Caps
02159             SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(0);
02160 #endif
02161 
02162             UnhookWindowsHookEx(exthook_winhook);
02163             exthook_winhook = NULL;
02164         }
02165 
02166         exthook_enabled = enable;
02167     }
02168 #endif
02169 }
02170 
02171 void GFX_ReleaseMouse(void) {
02172     if (sdl.mouse.locked)
02173         GFX_CaptureMouse();
02174 }
02175 
02176 void GFX_CaptureMouse(void) {
02177     GFX_CaptureMouse(!sdl.mouse.locked);
02178 }
02179 
02180 void GFX_CaptureMouse(bool capture) {
02181     sdl.mouse.locked=capture;
02182     if (sdl.mouse.locked) {
02183 #if defined(C_SDL2)
02184         SDL_SetRelativeMouseMode(SDL_TRUE);
02185 #else
02186         SDL_WM_GrabInput(SDL_GRAB_ON);
02187 #endif
02188         if (enable_hook_special_keys) DoExtendedKeyboardHook(true);
02189         SDL_ShowCursor(SDL_DISABLE);
02190     } else {
02191         DoExtendedKeyboardHook(false);
02192 #if defined(C_SDL2)
02193         SDL_SetRelativeMouseMode(SDL_FALSE);
02194 #else
02195         SDL_WM_GrabInput(SDL_GRAB_OFF);
02196 #endif
02197         if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
02198     }
02199         mouselocked=sdl.mouse.locked;
02200 
02201 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
02202         if (sdl.mouse.locked) {
02203             void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
02204             void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
02205 
02206             GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
02207             GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
02208         }
02209 #endif
02210 
02211     /* keep the menu updated (it might not exist yet) */
02212     if (mainMenu.item_exists("mapper_capmouse"))
02213         mainMenu.get_item("mapper_capmouse").check(sdl.mouse.locked).refresh_item(mainMenu);
02214 }
02215 
02216 void GFX_UpdateSDLCaptureState(void) {
02217     if (sdl.mouse.locked) {
02218 #if defined(C_SDL2)
02219         SDL_SetRelativeMouseMode(SDL_TRUE);
02220 #else
02221         SDL_WM_GrabInput(SDL_GRAB_ON);
02222 #endif
02223         if (enable_hook_special_keys) DoExtendedKeyboardHook(true);
02224         SDL_ShowCursor(SDL_DISABLE);
02225     } else {
02226         DoExtendedKeyboardHook(false);
02227 #if defined(C_SDL2)
02228         SDL_SetRelativeMouseMode(SDL_FALSE);
02229 #else
02230         SDL_WM_GrabInput(SDL_GRAB_OFF);
02231 #endif
02232         if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
02233     }
02234     CPU_Reset_AutoAdjust();
02235     GFX_SetTitle(-1,-1,-1,false);
02236 }
02237 
02238 #if WIN32
02239 void CaptureMouseNotifyWin32(bool lck)
02240 {
02241     switch (sdl.mouse.autolock_feedback)
02242     {
02243     case AUTOLOCK_FEEDBACK_NONE: break;
02244     case AUTOLOCK_FEEDBACK_BEEP:
02245     {
02246         const auto lo = 1000;
02247         const auto hi = 2000;
02248         const auto t1 = 50;
02249         const auto t2 = 25;
02250         const auto f1 = lck ? hi : lo;
02251         const auto f2 = lck ? lo : hi;
02252         const auto tt = lck ? t1 : t2;
02253         Beep(f1, tt);
02254         Beep(f2, tt);
02255     }
02256     break;
02257     case AUTOLOCK_FEEDBACK_FLASH:
02258     {
02259 # if !defined(HX_DOS)
02260         const auto cnt = lck ? 4 : 2;
02261         const auto tim = lck ? 80 : 40;
02262         const auto wnd = GetHWND();
02263         if (wnd != nullptr)
02264         {
02265             FLASHWINFO fi;
02266             fi.cbSize    = sizeof(FLASHWINFO);
02267             fi.hwnd      = wnd;
02268             fi.dwFlags   = FLASHW_CAPTION;
02269             fi.uCount    = cnt;
02270             fi.dwTimeout = tim;
02271             FlashWindowEx(&fi);
02272         }
02273 # endif
02274         break;
02275     }
02276     default: ;
02277     }
02278 }
02279 #endif
02280 
02281 void CaptureMouseNotify()
02282 {
02283     CaptureMouseNotify(sdl.mouse.locked);
02284 }
02285 
02286 void CaptureMouseNotify(bool capture)
02287 {
02288 #if WIN32
02289     CaptureMouseNotifyWin32(capture);
02290 #else
02291     (void)capture;
02292     // TODO
02293 #endif
02294 }
02295 
02296 static void CaptureMouse(bool pressed) {
02297     if (!pressed)
02298         return;
02299 
02300     CaptureMouseNotify();
02301     GFX_CaptureMouse();
02302 }
02303 
02304 #if defined (WIN32)
02305 STICKYKEYS stick_keys = {sizeof(STICKYKEYS), 0};
02306 void sticky_keys(bool restore){
02307     static bool inited = false;
02308     if (!inited){
02309         inited = true;
02310         SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0);
02311     } 
02312     if (restore) {
02313         SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0);
02314         return;
02315     }
02316     //Get current sticky keys layout:
02317     STICKYKEYS s = {sizeof(STICKYKEYS), 0};
02318     SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0);
02319     if ( !(s.dwFlags & SKF_STICKYKEYSON)) { //Not on already
02320         s.dwFlags &= ~SKF_HOTKEYACTIVE;
02321         SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0);
02322     }
02323 }
02324 #else
02325 #define sticky_keys(a)
02326 #endif
02327 
02328 void GetDesktopResolution(int* width, int* height)
02329 {
02330 #ifdef WIN32
02331     RECT rDdesk;
02332     auto hDesk = GetDesktopWindow();
02333     GetWindowRect(hDesk, &rDdesk);
02334     *width = rDdesk.right - rDdesk.left;
02335     *height = rDdesk.bottom - rDdesk.top;
02336 #elif defined(LINUX)
02337     void Linux_GetDesktopResolution(int *width,int *height);
02338     Linux_GetDesktopResolution(width,height); /* this is MESSY but there's too much namespace collision going on here */
02339 #else
02340     *width = 1024; // guess
02341     *height = 768;
02342 #endif
02343 }
02344 
02345 void res_init(void) {
02346     Section * sec = control->GetSection("sdl");
02347     Section_prop * section=static_cast<Section_prop *>(sec);
02348     sdl.desktop.full.fixed=false;
02349     const char* fullresolution=section->Get_string("fullresolution");
02350     sdl.desktop.full.width  = 0; sdl.desktop.full.height = 0;
02351     if(fullresolution && *fullresolution) {
02352         char res[100];
02353         safe_strncpy( res, fullresolution, sizeof( res ));
02354         fullresolution = lowcase (res);//so x and X are allowed
02355         if (strcmp(fullresolution,"original")) {
02356             sdl.desktop.full.fixed = true;
02357             if (strcmp(fullresolution,"desktop")) { //desktop = 0x0
02358                 char* height = const_cast<char*>(strchr(fullresolution,'x'));
02359                 if(height && * height) {
02360                     *height = 0;
02361                     sdl.desktop.full.height = atoi(height+1);
02362                     sdl.desktop.full.width  = atoi(res);
02363                 }
02364             }
02365         }
02366     }
02367 
02368     sdl.desktop.window.width  = 0;
02369     sdl.desktop.window.height = 0;
02370     const char* windowresolution=section->Get_string("windowresolution");
02371     if(windowresolution && *windowresolution) {
02372         //if(sdl.desktop.type==SCREEN_SURFACE) return;
02373         char res[100];
02374         safe_strncpy( res,windowresolution, sizeof( res ));
02375         windowresolution = lowcase (res);//so x and X are allowed
02376         if(strcmp(windowresolution,"original")) {
02377             char* height = const_cast<char*>(strchr(windowresolution,'x'));
02378             if(height && *height) {
02379                 *height = 0;
02380                 sdl.desktop.window.height = (Bit16u)atoi(height+1);
02381                 sdl.desktop.window.width  = (Bit16u)atoi(res);
02382             }
02383         }
02384     }
02385     sdl.desktop.doublebuf=section->Get_bool("fulldouble");
02386 
02387     int width = 1024;
02388     int height = 768;
02389     
02390     // fullresolution == desktop -> get/set desktop size
02391     auto sdlSection = control->GetSection("sdl");
02392     auto sdlSectionProp = static_cast<Section_prop*>(sdlSection);
02393     auto fullRes = sdlSectionProp->Get_string("fullresolution");
02394     if (!strcmp(fullRes, "desktop")) GetDesktopResolution(&width, &height);
02395     
02396     if (!sdl.desktop.full.width) {
02397         sdl.desktop.full.width=width;
02398     }
02399     if (!sdl.desktop.full.height) {
02400         sdl.desktop.full.height=height;
02401     }
02402     if(sdl.desktop.type==SCREEN_SURFACE && !sdl.desktop.fullscreen) return;
02403     else {
02404         GFX_Stop();
02405         if (sdl.draw.callback)
02406             (sdl.draw.callback)( GFX_CallBackReset );
02407         GFX_Start();
02408     }
02409 }
02410 
02411 void res_input(bool type, const char * res) {
02412     Section* sec = control->GetSection("sdl");
02413     char win_res[11];
02414     if(sec) {
02415         strcpy(win_res,res);
02416         if(type) {
02417             std::string tmp("windowresolution="); tmp.append(win_res);
02418             sec->HandleInputline(tmp);
02419         } else {
02420             std::string tmp("fullresolution="); tmp.append(win_res);
02421             sec->HandleInputline(tmp);
02422         }
02423 
02424         res_init();
02425     }
02426 }
02427 
02428 void change_output(int output) {
02429     GFX_Stop();
02430     Section * sec = control->GetSection("sdl");
02431     Section_prop * section=static_cast<Section_prop *>(sec);
02432     sdl.overscan_width=(unsigned int)section->Get_int("overscan");
02433     UpdateOverscanMenu();
02434 
02435     switch (output) {
02436     case 0:
02437         OUTPUT_SURFACE_Select();
02438         break;
02439     case 1:
02440         OUTPUT_SURFACE_Select();
02441         break;
02442     case 2: /* do nothing */
02443         break;
02444     case 3:
02445 #if C_OPENGL
02446         change_output(2);
02447         OUTPUT_OPENGL_Select();
02448         sdl_opengl.bilinear = true;
02449 #endif
02450         break;
02451     case 4:
02452 #if C_OPENGL
02453         change_output(2);
02454         OUTPUT_OPENGL_Select();
02455         sdl_opengl.bilinear = false; //NB
02456 #endif
02457         break;
02458 #if C_DIRECT3D
02459     case 5:
02460         OUTPUT_DIRECT3D_Select();
02461         break;
02462 #endif
02463     case 6:
02464         break;
02465     case 7:
02466         // do not set want_type
02467         break;
02468     case 8:
02469 #if C_DIRECT3D
02470         if (sdl.desktop.want_type == SCREEN_DIRECT3D) 
02471             OUTPUT_DIRECT3D_Select();
02472 #endif
02473         break;
02474 
02475     default:
02476         LOG_MSG("SDL:Unsupported output device %d, switching back to surface",output);
02477         OUTPUT_SURFACE_Select();
02478         break;
02479     }
02480 
02481     const char* windowresolution=section->Get_string("windowresolution");
02482     if (windowresolution && *windowresolution) 
02483     {
02484         char res[100];
02485         safe_strncpy( res,windowresolution, sizeof( res ));
02486         windowresolution = lowcase (res);//so x and X are allowed
02487         if (strcmp(windowresolution,"original")) 
02488         {
02489             if (output == 0) 
02490             {
02491                 std::string tmp("windowresolution=original");
02492                 sec->HandleInputline(tmp);
02493             }
02494         }
02495     }
02496     res_init();
02497 
02498     if (sdl.draw.callback)
02499         (sdl.draw.callback)( GFX_CallBackReset );
02500 
02501     GFX_SetTitle((Bit32s)CPU_CycleMax,-1,-1,false);
02502     GFX_LogSDLState();
02503 
02504     UpdateWindowDimensions();
02505 }
02506 
02507 #if !defined(HX_DOS) && defined(SDL_DOSBOX_X_SPECIAL)
02508 extern "C" void SDL_hax_SetFSWindowPosition(int x,int y,int w,int h);
02509 #endif
02510 
02511 void GFX_SwitchFullScreen(void)
02512 {
02513 #if !defined(HX_DOS)
02514     if (sdl.desktop.prevent_fullscreen)
02515         return;
02516 
02517     if (!sdl.desktop.fullscreen) {/*is GOING fullscreen*/
02518         UpdateWindowDimensions();
02519 
02520         if (screen_size_info.screen_dimensions_pixels.width != 0 && screen_size_info.screen_dimensions_pixels.height != 0) {
02521             if (sdl.desktop.full.width_auto)
02522                 sdl.desktop.full.width = (unsigned int)screen_size_info.screen_dimensions_pixels.width;
02523             if (sdl.desktop.full.height_auto)
02524                 sdl.desktop.full.height = (unsigned int)screen_size_info.screen_dimensions_pixels.height;
02525 
02526 #if !defined(C_SDL2) && defined(MACOSX)
02527             /* Mac OS X has this annoying problem with their API where the System Preferences app, display settings panel
02528                allows setting 720p and 1080i/1080p modes on monitors who's native resolution is less than 1920x1080 but
02529                supports 1920x1080.
02530 
02531                This is a problem with HDTV sets made in the late 2000s early 2010s when "HD" apparently meant LCD displays
02532                with 1368x768 resolution that will nonetheless accept 1080i (and later, 1080p) and downscale on display.
02533 
02534                The problem is that with these monitors, Mac OS X's display API will NOT list 1920x1080 as one of the
02535                display modes even when the freaking desktop is obviously set to 1920x1080 on that monitor!
02536 
02537                If the screen reporting code says 1920x1080 and we try to go fullscreen, SDL1 will intervene and say
02538                "hey wait there's no 1920x1080 in the mode list" and then fail the call.
02539 
02540                So to work around this, we have to go check SDL's mode list before using the screen size returned by the
02541                API.
02542 
02543                Oh and for extra fun, Mac OS X is one of those modern systems where "setting the video mode" apparently
02544                means NOT setting the video hardware mode but just changing how the GPU scales the display... EXCEPT when
02545                talking to these older displays where if the user set it to 1080i/1080p, our request to set 1368x768
02546                actually DOES change the video mode. This is just wonderful considering the old HDTV set I bought back in
02547                2008 takes 1-2 seconds to adjust to the mode change on it's HDMI input.
02548 
02549                Frankly I wish I knew how to fix the SDL1 Quartz code to use whatever abracadabra magic code is required
02550                to enumerate these extra HDTV modes so this hack isn't necessary, except that experience says this hack is
02551                always necesary because it might not always work.
02552 
02553                This magic hidden super secret API bullshit is annoying. You're worse than Microsoft with this stuff, Apple --J.C. */
02554             {
02555                 SDL_Rect **ls = SDLCALL SDL_ListModes(NULL, SDL_FULLSCREEN);
02556                 if (ls != NULL) {
02557                     unsigned int maxwidth = 0,maxheight = 0;
02558 
02559                     for (size_t i=0;ls[i] != NULL;i++) {
02560                         unsigned int w = ls[i]->w;
02561                         unsigned int h = ls[i]->h;
02562 
02563                         if (maxwidth < w || maxheight < h) {
02564                             maxwidth = w;
02565                             maxheight = h;
02566                         }
02567                     }
02568 
02569                     if (maxwidth != 0 && maxheight != 0) {
02570                         LOG_MSG("OS X: Actual maximum screen resolution is %d x %d\n",maxwidth,maxheight);
02571 
02572                         if (sdl.desktop.full.width_auto) {
02573                             if (sdl.desktop.full.width > maxwidth)
02574                                 sdl.desktop.full.width = maxwidth;
02575                         }
02576                         if (sdl.desktop.full.height_auto) {
02577                             if (sdl.desktop.full.height > maxheight)
02578                                 sdl.desktop.full.height = maxheight;
02579                         }
02580                     }
02581                 }
02582             }
02583 #endif
02584 
02585 #if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
02586             SDL_hax_SetFSWindowPosition(
02587                 (int)screen_size_info.screen_position_pixels.x, (int)screen_size_info.screen_position_pixels.y,
02588                 (int)screen_size_info.screen_dimensions_pixels.width, (int)screen_size_info.screen_dimensions_pixels.height);
02589 #endif
02590         }
02591         else {
02592 #if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
02593             SDL_hax_SetFSWindowPosition(0,0,0,0);
02594 #endif
02595         }
02596     }
02597 
02598     menu.resizeusing = true;
02599 
02600     sdl.desktop.fullscreen = !sdl.desktop.fullscreen;
02601 
02602     auto full = sdl.desktop.fullscreen;
02603 
02604     // if we're going fullscreen and current scaler exceeds screen size,
02605     // cancel the fullscreen change -> fixes scaler crashes
02606     // TODO this will need further changes to accomodate different outputs (e.g. stretched)
02607     if (full)
02608     {
02609         int width, height;
02610         GetDesktopResolution(&width, &height);
02611         auto width1 = sdl.draw.width;
02612         auto height1 = sdl.draw.height;
02613         if ((unsigned int)width < width1 || (unsigned int)height < height1) {
02614             sdl.desktop.fullscreen = false;
02615             LOG_MSG("WARNING: full screen canceled, surface size (%ix%i) exceeds screen size (%ix%i).",
02616                 width1, height1, width, height);
02617             return;
02618         }
02619     }
02620 
02621     LOG_MSG("INFO: switched to %s mode", full ? "full screen" : "window");
02622 
02623 #if !defined(C_SDL2)
02624     // (re-)assign menu to window
02625     void DOSBox_SetSysMenu(void);
02626     DOSBox_SetSysMenu();
02627 #endif
02628 
02629     // ensure mouse capture when fullscreen || (re-)capture if user said so when windowed
02630     auto locked = sdl.mouse.locked;
02631     if ((full && !locked) || (!full && locked)) GFX_CaptureMouse();
02632 
02633     // disable/enable sticky keys for fullscreen/desktop
02634 #if defined (WIN32)     
02635     sticky_keys(!full);
02636 #endif
02637 
02638     GFX_ResetScreen();
02639 
02640     // set vsync to host
02641     // NOTE why forcing ???
02642 #ifdef WIN32
02643     if (menu.startup) // NOTE should be always true I suppose ???
02644     {
02645         auto vsync = static_cast<Section_prop *>(control->GetSection("vsync"));
02646         if (vsync)
02647         {
02648             auto vsyncMode = vsync->Get_string("vsyncmode");
02649             if (!strcmp(vsyncMode, "host")) SetVal("vsync", "vsyncmode", "host");
02650         }
02651     }
02652 #endif
02653 #endif
02654 }
02655 
02656 static void SwitchFullScreen(bool pressed) {
02657     if (!pressed)
02658         return;
02659 
02660     GFX_LosingFocus();
02661     if (sdl.desktop.lazy_fullscreen) {
02662         LOG_MSG("GFX LF: fullscreen switching not supported");
02663     } else {
02664         GFX_SwitchFullScreen();
02665     }
02666 }
02667 
02668 void GFX_SwitchLazyFullscreen(bool lazy) {
02669     sdl.desktop.lazy_fullscreen=lazy;
02670     sdl.desktop.lazy_fullscreen_req=false;
02671 }
02672 
02673 void GFX_SwitchFullscreenNoReset(void) {
02674     sdl.desktop.fullscreen=!sdl.desktop.fullscreen;
02675 }
02676 
02677 bool GFX_LazyFullscreenRequested(void) {
02678     if (sdl.desktop.lazy_fullscreen) return sdl.desktop.lazy_fullscreen_req;
02679     return false;
02680 }
02681 
02682 bool GFX_GetPreventFullscreen(void) {
02683     return sdl.desktop.prevent_fullscreen;
02684 }
02685 
02686 #if defined(WIN32) && !defined(C_SDL2)
02687 extern "C" unsigned char SDL1_hax_RemoveMinimize;
02688 #endif
02689 
02690 void GFX_PreventFullscreen(bool lockout) {
02691     if (sdl.desktop.prevent_fullscreen != lockout) {
02692         sdl.desktop.prevent_fullscreen = lockout;
02693 #if defined(WIN32) && !defined(C_SDL2)
02694         void DOSBox_SetSysMenu(void);
02695         int Reflect_Menu(void);
02696 
02697         SDL1_hax_RemoveMinimize = lockout ? 1 : 0;
02698 
02699         DOSBox_SetSysMenu();
02700         Reflect_Menu();
02701 #endif
02702     }
02703 }
02704 
02705 void GFX_RestoreMode(void) {
02706     if (sdl.draw.width == 0 || sdl.draw.height == 0)
02707         return;
02708 
02709     GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
02710     GFX_UpdateSDLCaptureState();
02711     GFX_ResetScreen();
02712 }
02713 
02714 #if !defined(C_SDL2)
02715 static bool GFX_GetSurfacePtrLock = false;
02716 
02717 unsigned char *GFX_GetSurfacePtr(size_t *pitch, unsigned int x, unsigned int y) {
02718     if (sdl.surface->pixels == NULL) {
02719         if (!GFX_GetSurfacePtrLock) {
02720             if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface))
02721                 return NULL;
02722 
02723             GFX_GetSurfacePtrLock = true;
02724         }
02725     }
02726 
02727     *pitch = sdl.surface->pitch;
02728     if (sdl.surface->pixels != NULL) {
02729         unsigned char *p = (unsigned char*)(sdl.surface->pixels);
02730         p += y * sdl.surface->pitch;
02731         p += x * ((unsigned int)sdl.surface->format->BitsPerPixel >> 3U);
02732         return p;
02733     }
02734 
02735     return NULL;
02736 }
02737 
02738 void GFX_ReleaseSurfacePtr(void) {
02739     if (GFX_GetSurfacePtrLock) {
02740         if (SDL_MUSTLOCK(sdl.surface))
02741             SDL_UnlockSurface(sdl.surface);
02742  
02743         GFX_GetSurfacePtrLock = false;
02744     }
02745 }
02746 #endif
02747 
02748 bool GFX_StartUpdate(Bit8u* &pixels,Bitu &pitch) 
02749 {
02750     if (!sdl.active || sdl.updating)
02751         return false;
02752 
02753     switch (sdl.desktop.type) 
02754     {
02755         case SCREEN_SURFACE:
02756             return OUTPUT_SURFACE_StartUpdate(pixels, pitch);
02757 
02758 #if C_OPENGL
02759         case SCREEN_OPENGL:
02760             return OUTPUT_OPENGL_StartUpdate(pixels, pitch);
02761 #endif
02762 
02763 #if C_DIRECT3D
02764         case SCREEN_DIRECT3D:
02765             return OUTPUT_DIRECT3D_StartUpdate(pixels, pitch);
02766 #endif
02767 
02768         default:
02769             break;
02770     }
02771 
02772     return false;
02773 }
02774 
02775 void GFX_OpenGLRedrawScreen(void) {
02776 #if C_OPENGL
02777     if (OpenGL_using()) {
02778         if (sdl_opengl.clear_countdown > 0) {
02779             sdl_opengl.clear_countdown--;
02780             glClearColor (0.0, 0.0, 0.0, 1.0);
02781             glClear(GL_COLOR_BUFFER_BIT);
02782         }
02783 
02784         if (sdl_opengl.pixel_buffer_object) {
02785             glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
02786             glBindTexture(GL_TEXTURE_2D, sdl_opengl.texture);
02787             glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
02788             glCallList(sdl_opengl.displaylist);
02789         } else {
02790             glBindTexture(GL_TEXTURE_2D, sdl_opengl.texture);
02791             glCallList(sdl_opengl.displaylist);
02792         }
02793     }
02794 #endif
02795 }
02796 
02797 void GFX_EndUpdate(const Bit16u *changedLines) {
02798 #if C_EMSCRIPTEN
02799     emscripten_sleep_with_yield(0);
02800 #endif
02801 
02802     /* don't present our output if 3Dfx is in OpenGL mode */
02803     if (sdl.desktop.prevent_fullscreen)
02804         return;
02805 
02806 #if C_DIRECT3D
02807     // we may have to do forced update in D3D case
02808     if (d3d && d3d->getForceUpdate());
02809     else
02810 #endif
02811     if (!sdl.updating)
02812         return;
02813 
02814     sdl.updating = false;
02815     switch (sdl.desktop.type) 
02816     {
02817         case SCREEN_SURFACE:
02818             OUTPUT_SURFACE_EndUpdate(changedLines);
02819             break;
02820 
02821 #if C_OPENGL
02822         case SCREEN_OPENGL:
02823             OUTPUT_OPENGL_EndUpdate(changedLines);
02824             break;
02825 #endif
02826 
02827 #if C_DIRECT3D
02828         case SCREEN_DIRECT3D:
02829             OUTPUT_DIRECT3D_EndUpdate(changedLines);
02830             break;
02831 #endif
02832 
02833         default:
02834             break;
02835     }
02836 
02837     if (changedLines != NULL) 
02838     {
02839         sdl.must_redraw_all = false;
02840 
02841 #if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
02842         sdl.surface->flags &= ~((unsigned int)SDL_HAX_NOREFRESH);
02843 #endif
02844 
02845         if (changedLines != NULL && sdl.deferred_resize) 
02846         {
02847             sdl.deferred_resize = false;
02848 #if !defined(C_SDL2)
02849             void GFX_RedrawScreen(Bit32u nWidth, Bit32u nHeight);
02850             GFX_RedrawScreen(sdl.draw.width, sdl.draw.height);
02851 #endif
02852         }
02853         else if (sdl.gfx_force_redraw_count > 0) 
02854         {
02855             void RENDER_CallBack( GFX_CallBackFunctions_t function );
02856             RENDER_CallBack(GFX_CallBackRedraw);
02857             sdl.gfx_force_redraw_count--;
02858         }
02859     }
02860 }
02861 
02862 void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) {
02863     (void)start;
02864     (void)count;
02865     (void)entries;
02866 #if !defined(C_SDL2)
02867     /* I should probably not change the GFX_PalEntry :) */
02868     if (sdl.surface->flags & SDL_HWPALETTE) {
02869         if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,(int)start,(int)count)) {
02870             E_Exit("SDL:Can't set palette");
02871         }
02872     } else {
02873         if (!SDL_SetPalette(sdl.surface,SDL_LOGPAL,(SDL_Color *)entries,(int)start,(int)count)) {
02874             E_Exit("SDL:Can't set palette");
02875         }
02876     }
02877 #endif
02878 }
02879 
02880 Bitu GFX_GetRGB(Bit8u red, Bit8u green, Bit8u blue) {
02881     switch (sdl.desktop.type) {
02882         case SCREEN_SURFACE:
02883             return SDL_MapRGB(sdl.surface->format, red, green, blue);
02884 
02885 #if C_OPENGL
02886         case SCREEN_OPENGL:
02887 # if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
02888             //USE BGRA
02889             return (((unsigned long)blue << 24ul) | ((unsigned long)green << 16ul) | ((unsigned long)red <<  8ul)) | (255ul <<  0ul);
02890 # else
02891             //USE ARGB
02892             return (((unsigned long)blue <<  0ul) | ((unsigned long)green <<  8ul) | ((unsigned long)red << 16ul)) | (255ul << 24ul);
02893 # endif
02894 #endif
02895 
02896 #if C_DIRECT3D
02897         case SCREEN_DIRECT3D:
02898             return SDL_MapRGB(sdl.surface->format, red, green, blue);
02899 #endif
02900 
02901         default:
02902             break;
02903     }
02904     return 0;
02905 }
02906 
02907 void GFX_Stop() {
02908     if (sdl.updating)
02909         GFX_EndUpdate( 0 );
02910     sdl.active=false;
02911 }
02912 
02913 void GFX_Start() {
02914     sdl.active=true;
02915 }
02916 
02917 static void GUI_ShutDown(Section * /*sec*/) {
02918     GFX_Stop();
02919     if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop );
02920     if (sdl.mouse.locked) GFX_CaptureMouse();
02921     if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
02922     switch (sdl.desktop.type)
02923     {
02924         case SCREEN_SURFACE:
02925             OUTPUT_SURFACE_Shutdown();
02926             break;
02927 
02928 #if C_OPENGL
02929         case SCREEN_OPENGL:
02930             OUTPUT_OPENGL_Shutdown();
02931             break;
02932 #endif
02933 
02934 #if C_DIRECT3D
02935         case SCREEN_DIRECT3D:
02936             OUTPUT_DIRECT3D_Shutdown();
02937             break;
02938 #endif
02939 
02940         default:
02941                 break;
02942     }
02943 }
02944 
02945 static void SetPriority(PRIORITY_LEVELS level) {
02946 
02947 #if C_SET_PRIORITY
02948 // Do nothing if priorties are not the same and not root, else the highest
02949 // priority can not be set as users can only lower priority (not restore it)
02950 
02951     if((sdl.priority.focus != sdl.priority.nofocus ) &&
02952         (getuid()!=0) ) return;
02953 
02954 #endif
02955     switch (level) {
02956 #ifdef WIN32
02957     case PRIORITY_LEVEL_PAUSE:  // if DOSBox is paused, assume idle priority
02958     case PRIORITY_LEVEL_LOWEST:
02959         SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS);
02960         break;
02961     case PRIORITY_LEVEL_LOWER:
02962         SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS);
02963         break;
02964     case PRIORITY_LEVEL_NORMAL:
02965         SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS);
02966         break;
02967     case PRIORITY_LEVEL_HIGHER:
02968         SetPriorityClass(GetCurrentProcess(),ABOVE_NORMAL_PRIORITY_CLASS);
02969         break;
02970     case PRIORITY_LEVEL_HIGHEST:
02971         SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
02972         break;
02973 #elif C_SET_PRIORITY
02974 /* Linux use group as dosbox has mulitple threads under linux */
02975     case PRIORITY_LEVEL_PAUSE:  // if DOSBox is paused, assume idle priority
02976     case PRIORITY_LEVEL_LOWEST:
02977         setpriority (PRIO_PGRP, 0,PRIO_MAX);
02978         break;
02979     case PRIORITY_LEVEL_LOWER:
02980         setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/3));
02981         break;
02982     case PRIORITY_LEVEL_NORMAL:
02983         setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/2));
02984         break;
02985     case PRIORITY_LEVEL_HIGHER:
02986         setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/5) );
02987         break;
02988     case PRIORITY_LEVEL_HIGHEST:
02989         setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/4) );
02990         break;
02991 #endif
02992     default:
02993         break;
02994     }
02995 }
02996 
02997 extern Bit8u int10_font_14[256 * 14];
02998 static void OutputString(Bitu x,Bitu y,const char * text,Bit32u color,Bit32u color2,SDL_Surface * output_surface) {
02999     Bit32u * draw=(Bit32u*)(((Bit8u *)output_surface->pixels)+((y)*output_surface->pitch))+x;
03000     while (*text) {
03001         Bit8u * font=&int10_font_14[(*text)*14];
03002         Bitu i,j;
03003         Bit32u * draw_line=draw;
03004         for (i=0;i<14;i++) {
03005             Bit8u map=*font++;
03006             for (j=0;j<8;j++) {
03007                 if (map & 0x80) *((Bit32u*)(draw_line+j))=color; else *((Bit32u*)(draw_line+j))=color2;
03008                 map<<=1;
03009             }
03010             draw_line+=output_surface->pitch/4;
03011         }
03012         text++;
03013         draw+=8;
03014     }
03015 }
03016 
03017 void ResetSystem(bool pressed) {
03018     if (!pressed) return;
03019 
03020     throw int(3);
03021 }
03022 
03023 ZIPFile savestate_zip;
03024 
03025 void GUI_EXP_LoadState(bool pressed) {
03026     if (!pressed) return;
03027 
03028     LOG_MSG("Loading state... (experimental)");
03029 
03030     if (savestate_zip.open("exsavest.zip",O_RDONLY) < 0) {
03031         LOG_MSG("Unable to open save state");
03032         return;
03033     }
03034 
03035     DispatchVMEvent(VM_EVENT_LOAD_STATE);
03036 
03037     savestate_zip.close();
03038 }
03039 
03040 void GUI_EXP_SaveState(bool pressed) {
03041     if (!pressed) return;
03042 
03043     LOG_MSG("Saving state... (experimental)");
03044 
03045     if (savestate_zip.open("exsavest.zip",O_RDWR|O_CREAT|O_TRUNC) < 0) {
03046         LOG_MSG("Unable to open save state for writing");
03047         return;
03048     }
03049 
03050     DispatchVMEvent(VM_EVENT_SAVE_STATE);
03051 
03052     savestate_zip.writeZIPFooter();
03053     savestate_zip.close();
03054 }
03055 
03056 bool has_GUI_StartUp = false;
03057 
03058 static void GUI_StartUp() {
03059     DOSBoxMenu::item *item;
03060 
03061     if (has_GUI_StartUp) return;
03062     has_GUI_StartUp = true;
03063 
03064     LOG(LOG_GUI,LOG_DEBUG)("Starting GUI");
03065 
03066 #if defined(C_SDL2)
03067     LOG(LOG_GUI,LOG_DEBUG)("This version compiled against SDL 2.x");
03068 #else
03069     LOG(LOG_GUI,LOG_DEBUG)("This version compiled against SDL 1.x");
03070 #endif
03071 
03072 #if defined(C_SDL2)
03073     /* while we're here, SDL 2.0.5 has some issues with Linux/X11, encourage the user to update SDL2. */
03074     {
03075         SDL_version v;
03076         SDL_GetVersion(&v);
03077         LOG(LOG_GUI,LOG_DEBUG)("SDL2 version %u.%u.%u",v.major,v.minor,v.patch);
03078 
03079 # if defined(LINUX)
03080         /* Linux/X11 2.0.5 has window positioning issues i.e. with XFCE */
03081         if (v.major == 2 && v.minor == 0 && v.patch == 5)
03082             LOG_MSG("WARNING: Your SDL2 library is known to have some issues with Linux/X11, please update your SDL2 library");
03083 # endif
03084     }
03085 #endif
03086 
03087     AddExitFunction(AddExitFunctionFuncPair(GUI_ShutDown));
03088     GUI_LoadFonts();
03089 
03090     sdl.active=false;
03091     sdl.updating=false;
03092 #if defined(C_SDL2)
03093     sdl.update_window=true;
03094 #endif
03095 
03096     GFX_SetIcon();
03097 
03098     sdl.desktop.lazy_fullscreen=false;
03099     sdl.desktop.lazy_fullscreen_req=false;
03100     sdl.desktop.prevent_fullscreen=false;
03101 
03102     Section_prop * section=static_cast<Section_prop *>(control->GetSection("sdl"));
03103     assert(section != NULL);
03104 
03105     sdl.desktop.fullscreen=section->Get_bool("fullscreen");
03106     sdl.wait_on_error=section->Get_bool("waitonerror");
03107 
03108     Prop_multival* p=section->Get_multival("priority");
03109     std::string focus = p->GetSection()->Get_string("active");
03110     std::string notfocus = p->GetSection()->Get_string("inactive");
03111 
03112     if      (focus == "lowest")  { sdl.priority.focus = PRIORITY_LEVEL_LOWEST;  }
03113     else if (focus == "lower")   { sdl.priority.focus = PRIORITY_LEVEL_LOWER;   }
03114     else if (focus == "normal")  { sdl.priority.focus = PRIORITY_LEVEL_NORMAL;  }
03115     else if (focus == "higher")  { sdl.priority.focus = PRIORITY_LEVEL_HIGHER;  }
03116     else if (focus == "highest") { sdl.priority.focus = PRIORITY_LEVEL_HIGHEST; }
03117 
03118     if      (notfocus == "lowest")  { sdl.priority.nofocus=PRIORITY_LEVEL_LOWEST;  }
03119     else if (notfocus == "lower")   { sdl.priority.nofocus=PRIORITY_LEVEL_LOWER;   }
03120     else if (notfocus == "normal")  { sdl.priority.nofocus=PRIORITY_LEVEL_NORMAL;  }
03121     else if (notfocus == "higher")  { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHER;  }
03122     else if (notfocus == "highest") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHEST; }
03123     else if (notfocus == "pause")   {
03124         /* we only check for pause here, because it makes no sense
03125          * for DOSBox to be paused while it has focus
03126          */
03127         sdl.priority.nofocus=PRIORITY_LEVEL_PAUSE;
03128     }
03129 
03130     SetPriority(sdl.priority.focus); //Assume focus on startup
03131     sdl.mouse.locked=false;
03132     mouselocked=false; //Global for mapper
03133     sdl.mouse.requestlock=false;
03134     sdl.desktop.full.fixed=false;
03135     const char* fullresolution=section->Get_string("fullresolution");
03136     sdl.desktop.full.width  = 0;
03137     sdl.desktop.full.height = 0;
03138     if(fullresolution && *fullresolution) {
03139         char res[100];
03140         safe_strncpy(res, fullresolution, sizeof(res));
03141         fullresolution = lowcase (res);//so x and X are allowed
03142         if (strcmp(fullresolution,"original")) {
03143             sdl.desktop.full.fixed = true;
03144             if (strcmp(fullresolution,"desktop")) { //desktop = 0x0
03145                 char* height = const_cast<char*>(strchr(fullresolution,'x'));
03146                 if (height && * height) {
03147                     *height = 0;
03148                     sdl.desktop.full.height = (Bit16u)atoi(height+1);
03149                     sdl.desktop.full.width  = (Bit16u)atoi(res);
03150                 }
03151             }
03152         }
03153     }
03154 
03155     sdl.desktop.window.width  = 0;
03156     sdl.desktop.window.height = 0;
03157     const char* windowresolution=section->Get_string("windowresolution");
03158     if(windowresolution && *windowresolution) {
03159         char res[100];
03160         safe_strncpy(res, windowresolution, sizeof(res));
03161         windowresolution = lowcase (res);//so x and X are allowed
03162         if(strcmp(windowresolution,"original")) {
03163             char* height = const_cast<char*>(strchr(windowresolution,'x'));
03164             if(height && *height) {
03165                 *height = 0;
03166                 sdl.desktop.window.height = (Bit16u)atoi(height+1);
03167                 sdl.desktop.window.width  = (Bit16u)atoi(res);
03168             }
03169         }
03170     }
03171     sdl.desktop.doublebuf=section->Get_bool("fulldouble");
03172 #if defined(C_SDL2)
03173     {
03174         SDL_DisplayMode dm;
03175         if (SDL_GetDesktopDisplayMode(0/*FIXME display index*/,&dm) == 0) {
03176             if (sdl.desktop.full.width == 0) {
03177                 sdl.desktop.full.width_auto = true;
03178                 sdl.desktop.full.width = dm.w;
03179             }
03180             if (sdl.desktop.full.height == 0) {
03181                 sdl.desktop.full.height_auto = true;
03182                 sdl.desktop.full.height = dm.h;
03183             }
03184             LOG_MSG("SDL2 reports desktop display mode %u x %u",dm.w,dm.h);
03185         }
03186         else {
03187             LOG_MSG("SDL2 unable to determine desktop display mode, error %s",SDL_GetError());
03188         }
03189     }
03190 #endif
03191 #if !defined(C_SDL2)
03192   #if SDL_VERSION_ATLEAST(1, 2, 10)
03193   #ifdef WIN32
03194     /* NTS: This should not print any warning whatsoever because Windows builds by default will use
03195      *      the Windows API to disable DPI scaling of the main window, unless the user modifies the
03196      *      setting through dosbox.conf or the command line. */
03197     /* NTS: Mac OS X has high DPI scaling too, though Apple is wise to enable it by default only for
03198      *      Macbooks with "Retina" displays. On Mac OS X, unless otherwise wanted by the user, it is
03199      *      wise to let Mac OS X scale up the DOSBox-X window by 2x so that the DOS prompt is not
03200      *      a teeny tiny window on the screen. */
03201     const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo();
03202     if (vidinfo) {
03203         int sdl_w = vidinfo->current_w;
03204         int sdl_h = vidinfo->current_h;
03205         int win_w = GetSystemMetrics(SM_CXSCREEN);
03206         int win_h = GetSystemMetrics(SM_CYSCREEN);
03207         if (sdl_w != win_w && sdl_h != win_h) 
03208             LOG_MSG("Windows dpi/blurry apps scaling detected! The screen might be too large or not show properly,\n"
03209                 "please see the DOSBox Manual/README for details.\n");
03210         }
03211   #else
03212     if (!sdl.desktop.full.width || !sdl.desktop.full.height){
03213         //Can only be done on the very first call! Not restartable.
03214         //On windows don't use it as SDL returns the values without taking in account the dpi scaling
03215         const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo();
03216         if (vidinfo) {
03217             if (sdl.desktop.full.width == 0) {
03218                 sdl.desktop.full.width_auto = true;
03219                 sdl.desktop.full.width = vidinfo->current_w;
03220             }
03221             if (sdl.desktop.full.height == 0) {
03222                 sdl.desktop.full.height_auto = true;
03223                 sdl.desktop.full.height = vidinfo->current_h;
03224             }
03225 
03226             LOG_MSG("SDL1 auto-detected desktop as %u x %u",
03227                 (unsigned int)sdl.desktop.full.width,
03228                 (unsigned int)sdl.desktop.full.height);
03229         }
03230     }
03231   #endif
03232   #endif
03233 #endif
03234 
03235     int width=1024;
03236     int height=768;
03237     if (!sdl.desktop.full.width) {
03238         sdl.desktop.full.width_auto = true;
03239         sdl.desktop.full.width=width;
03240     }
03241     if (!sdl.desktop.full.height) {
03242         sdl.desktop.full.height_auto = true;
03243         sdl.desktop.full.height=height;
03244     }
03245     sdl.mouse.autoenable=section->Get_bool("autolock");
03246     if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE);
03247     sdl.mouse.autolock=false;
03248 
03249     const std::string feedback = section->Get_string("autolock_feedback");
03250     if (feedback == "none")
03251         sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_NONE;
03252     else if (feedback == "beep")
03253         sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_BEEP;
03254     else if (feedback == "flash")
03255         sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_FLASH;
03256 
03257     Prop_multival* p3 = section->Get_multival("sensitivity");
03258     sdl.mouse.xsensitivity = p3->GetSection()->Get_int("xsens");
03259     sdl.mouse.ysensitivity = p3->GetSection()->Get_int("ysens");
03260     std::string output=section->Get_string("output");
03261 
03262     const std::string emulation = section->Get_string("mouse_emulation");
03263     if (emulation == "always")
03264         sdl.mouse.emulation = MOUSE_EMULATION_ALWAYS;
03265     else if (emulation == "locked")
03266         sdl.mouse.emulation = MOUSE_EMULATION_LOCKED;
03267     else if (emulation == "integration")
03268         sdl.mouse.emulation = MOUSE_EMULATION_INTEGRATION;
03269     else if (emulation == "never")
03270         sdl.mouse.emulation = MOUSE_EMULATION_NEVER;
03271 
03272     /* Setup Mouse correctly if fullscreen */
03273     if(sdl.desktop.fullscreen) GFX_CaptureMouse();
03274 
03275 #if C_XBRZ
03276     // initialize xBRZ parameters and check output type for compatibility
03277     xBRZ_Initialize();
03278 
03279     if (sdl_xbrz.enable) {
03280         // xBRZ requirements
03281         if ((output != "surface") && (output != "direct3d") && (output != "opengl") && (output != "openglhq") && (output != "openglnb"))
03282             output = "surface";
03283     }
03284 #endif
03285 
03286     // output type selection
03287     // "overlay" was removed, pre-map to Direct3D or OpenGL or surface
03288     if (output == "overlay") 
03289     {
03290 #if C_DIRECT3D
03291         output = "direct3d";
03292 #elif C_OPENGL
03293         output = "opengl";
03294 #else
03295         output = "surface";
03296 #endif
03297     }
03298 
03299     if (output == "surface") 
03300     {
03301         OUTPUT_SURFACE_Select();
03302     } 
03303     else if (output == "ddraw") 
03304     {
03305         OUTPUT_SURFACE_Select();
03306 #if C_OPENGL
03307     } 
03308     else if (output == "opengl" || output == "openglhq") 
03309     {
03310         OUTPUT_OPENGL_Select();
03311         sdl_opengl.bilinear = true;
03312     }
03313     else if (output == "openglnb") 
03314     {
03315         OUTPUT_OPENGL_Select();
03316         sdl_opengl.bilinear = false;
03317 #endif
03318 #if C_DIRECT3D
03319     } 
03320     else if (output == "direct3d") 
03321     {
03322         OUTPUT_DIRECT3D_Select();
03323 #if LOG_D3D
03324         LOG_MSG("SDL:Direct3D activated");
03325 #endif
03326 #endif
03327     } 
03328     else 
03329     {
03330         LOG_MSG("SDL:Unsupported output device %s, switching back to surface",output.c_str());
03331         OUTPUT_SURFACE_Select(); // should not reach there anymore
03332     }
03333 
03334     sdl.overscan_width=(unsigned int)section->Get_int("overscan");
03335 //  sdl.overscan_color=section->Get_int("overscancolor");
03336 
03337 #if defined(C_SDL2)
03338     /* Initialize screen for first time */
03339     GFX_SetResizeable(true);
03340     if (!GFX_SetSDLSurfaceWindow(640,400))
03341         E_Exit("Could not initialize video: %s",SDL_GetError());
03342     sdl.surface = SDL_GetWindowSurface(sdl.window);
03343 //    SDL_Rect splash_rect=GFX_GetSDLSurfaceSubwindowDims(640,400);
03344     sdl.desktop.pixelFormat = SDL_GetWindowPixelFormat(sdl.window);
03345     LOG_MSG("SDL:Current window pixel format: %s", SDL_GetPixelFormatName(sdl.desktop.pixelFormat));
03346     sdl.desktop.bpp=8*SDL_BYTESPERPIXEL(sdl.desktop.pixelFormat);
03347     if (SDL_BITSPERPIXEL(sdl.desktop.pixelFormat) == 24)
03348         LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!");
03349 #else
03350     /* Initialize screen for first time */
03351     sdl.surface=SDL_SetVideoMode(640,400,0,SDL_RESIZABLE);
03352     if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError());
03353     sdl.deferred_resize = false;
03354     sdl.must_redraw_all = true;
03355     sdl.desktop.bpp=sdl.surface->format->BitsPerPixel;
03356     if (sdl.desktop.bpp==24)
03357         LOG_MSG("SDL:You are running in 24 bpp mode, this will slow down things!");
03358 #endif
03359 
03360     GFX_LogSDLState();
03361     GFX_Stop();
03362 
03363 #if defined(C_SDL2)
03364     SDL_SetWindowTitle(sdl.window,"DOSBox");
03365 #else
03366     SDL_WM_SetCaption("DOSBox",VERSION);
03367 #endif
03368 
03369     /* Please leave the Splash screen stuff in working order in DOSBox. We spend a lot of time making DOSBox. */
03370     //ShowSplashScreen();   /* I will keep the splash screen alive. But now, the BIOS will do it --J.C. */
03371 
03372     /* Get some Event handlers */
03373 #if defined(__WIN32__) && !defined(C_SDL2)
03374     MAPPER_AddHandler(ToggleMenu,MK_return,MMOD1|MMOD2,"togglemenu","ToggleMenu");
03375 #endif // WIN32
03376     MAPPER_AddHandler(ResetSystem, MK_r, MMODHOST, "reset", "Reset", &item); /* Host+R (Host+CTRL+R acts funny on my Linux system) */
03377     item->set_text("Reset guest system");
03378 
03379 #if !defined(C_EMSCRIPTEN)//FIXME: Shutdown causes problems with Emscripten
03380     MAPPER_AddHandler(KillSwitch,MK_f9,MMOD1,"shutdown","ShutDown", &item); /* KEEP: Most DOSBox-X users may have muscle memory for this */
03381     item->set_text("Quit");
03382 #endif
03383 
03384     MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Cap Mouse", &item); /* KEEP: Most DOSBox-X users may have muscle memory for this */
03385     item->set_text("Capture mouse");
03386 
03387     MAPPER_AddHandler(SwitchFullScreen,MK_f,MMODHOST,"fullscr","Fullscreen", &item);
03388     item->set_text("Toggle fullscreen");
03389 
03390     void PasteClipboard(bool bPressed); // emendelson from dbDOS adds MMOD2 to this for Ctrl-Alt-F5 for PasteClipboard
03391     MAPPER_AddHandler(PasteClipboard, MK_nothing, 0, "paste", "Paste Clipboard"); //end emendelson
03392 #if C_DEBUG
03393     /* Pause binds with activate-debugger */
03394     MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD1, "pause", "Pause");
03395 #else
03396     MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause");
03397 #endif
03398 
03399 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
03400     pause_menu_item_tag = mainMenu.get_item("mapper_pause").get_master_id() + DOSBoxMenu::nsMenuMinimumID;
03401 #endif
03402 
03403     MAPPER_AddHandler(&GUI_Run, MK_nothing, 0, "gui", "ShowGUI", &item);
03404     item->set_text("Configuration GUI");
03405 
03406     MAPPER_AddHandler(&GUI_ResetResize, MK_nothing, 0, "resetsize", "ResetSize", &item);
03407     item->set_text("Reset window size");
03408 
03409     /* EXPERIMENTAL!!!! */
03410     MAPPER_AddHandler(&GUI_EXP_SaveState, MK_f1, MMODHOST, "exp_savestate", "EX:SvState", &item);
03411     item->set_text("Save State (EXPERIMENTAL)");
03412 
03413     /* EXPERIMENTAL!!!! */
03414     MAPPER_AddHandler(&GUI_EXP_LoadState, MK_f2, MMODHOST, "exp_loadstate", "EX:LdState", &item);
03415     item->set_text("Load State (EXPERIMENTAL)");
03416 
03417     UpdateWindowDimensions();
03418 }
03419 
03420 void Mouse_AutoLock(bool enable) {
03421     if (sdl.mouse.autolock == enable)
03422         return;
03423 
03424     sdl.mouse.autolock=enable;
03425     if (sdl.mouse.autoenable) sdl.mouse.requestlock=enable;
03426     else {
03427         SDL_ShowCursor(enable?SDL_DISABLE:SDL_ENABLE);
03428         sdl.mouse.requestlock=false;
03429     }
03430 }
03431 
03432 bool Mouse_IsLocked()
03433 {
03434     return sdl.mouse.locked;
03435 }
03436 
03437 static void RedrawScreen(Bit32u nWidth, Bit32u nHeight) {
03438     (void)nWidth;//UNUSED
03439     (void)nHeight;//UNUSED
03440 //  int width;
03441 //  int height;
03442 #ifdef __WIN32__
03443 //   width=sdl.clip.w; 
03444 //   height=sdl.clip.h;
03445 #else
03446 //  width=sdl.draw.width; 
03447 //  height=sdl.draw.height;
03448 #endif
03449     void RENDER_CallBack( GFX_CallBackFunctions_t function );
03450 #if 0
03451     while (sdl.desktop.fullscreen) {
03452         int temp_size;
03453         temp_size=render.scale.size;
03454         if(!sdl.desktop.fullscreen) { render.scale.size=temp_size; RENDER_CallBack( GFX_CallBackReset); return; }
03455     }
03456 #endif
03457 #ifdef WIN32
03458     if(menu.resizeusing) {
03459         RENDER_CallBack( GFX_CallBackReset);
03460         return;
03461     }
03462 #endif
03463 #if 0 /* FIXME: This code misbehaves when doublescan=false on Linux/X11 */
03464     if((Bitu)nWidth == (Bitu)width && (Bitu)nHeight == (Bitu)height) {
03465         RENDER_CallBack( GFX_CallBackReset);
03466         return;
03467     }
03468     Section_prop * section=static_cast<Section_prop *>(control->GetSection("sdl")); 
03469     if ((!strcmp(section->Get_string("windowresolution"),"original") || (!strcmp(section->Get_string("windowresolution"),"desktop"))) && (render.src.dblw && render.src.dblh)) {
03470         switch (render.scale.op) {
03471             case scalerOpNormal:
03472                 if(!render.scale.hardware) {
03473                     if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) {
03474                         if (render.scale.size <= 4 && render.scale.size >=1) ++render.scale.size; break;
03475                     } else {
03476                         if (render.scale.size <= 5 && render.scale.size >= 2) --render.scale.size; break;
03477                     }
03478                 } else {
03479                     if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) {
03480                         if (render.scale.size == 1) { render.scale.size=4; break; }
03481                         if (render.scale.size == 4) { render.scale.size=6; break; }
03482                         if (render.scale.size == 6) { render.scale.size=8; break; }
03483                         if (render.scale.size == 8) { render.scale.size=10; break; }
03484                     }
03485                     if((Bitu)nWidth<(Bitu)width || (Bitu)nHeight<(Bitu)height) {
03486                         if (render.scale.size == 10) { render.scale.size=8; break; }
03487                         if (render.scale.size == 8) { render.scale.size=6; break; }
03488                         if (render.scale.size == 6) { render.scale.size=4; break; }
03489                         if (render.scale.size == 4) { render.scale.size=1; break; }
03490                     }
03491                 }
03492                 break;
03493             case scalerOpAdvMame:
03494             case scalerOpHQ:
03495             case scalerOpAdvInterp:
03496             case scalerOpTV:
03497             case scalerOpRGB:
03498             case scalerOpScan:
03499                 if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) { if (render.scale.size == 2) ++render.scale.size; }
03500                 if((Bitu)nWidth<(Bitu)width || (Bitu)nHeight<(Bitu)height) { if (render.scale.size == 3) --render.scale.size; }
03501                 break;
03502             case scalerOpSaI:
03503             case scalerOpSuperSaI:
03504             case scalerOpSuperEagle:
03505             default: // other scalers
03506                 break;
03507         }
03508     }
03509 #endif
03510     RENDER_CallBack( GFX_CallBackReset);
03511 }
03512 
03513 void GFX_RedrawScreen(Bit32u nWidth, Bit32u nHeight) {
03514     RedrawScreen(nWidth, nHeight);
03515 }
03516 
03517 bool GFX_MustActOnResize() {
03518     if (!GFX_IsFullscreen())
03519         return false;
03520 
03521     return true;
03522 }
03523 
03524 #if defined(C_SDL2)
03525 void GFX_HandleVideoResize(int width, int height) {
03526     /* Maybe a screen rotation has just occurred, so we simply resize.
03527        There may be a different cause for a forced resized, though.    */
03528     if (sdl.desktop.full.display_res && IsFullscreen()) {
03529         /* Note: We should not use GFX_ObtainDisplayDimensions
03530            (SDL_GetDisplayBounds) on Android after a screen rotation:
03531            The older values from application startup are returned. */
03532         sdl.desktop.full.width = width;
03533         sdl.desktop.full.height = height;
03534     }
03535 
03536     /* assume the resize comes from user preference UNLESS the window
03537      * is fullscreen or maximized */
03538     if (!menu.maxwindow && !sdl.desktop.fullscreen && !sdl.init_ignore && NonUserResizeCounter == 0 && !window_was_maximized) {
03539         UpdateWindowDimensions();
03540         UpdateWindowDimensions((unsigned int)width, (unsigned int)height);
03541 
03542         /* if the dimensions actually changed from our surface dimensions, then
03543            assume it's the user's input. Linux/X11 is good at doing this anyway,
03544            but the Windows SDL 1.x support will return us a resize event for the
03545            window size change resulting from SDL mode set. */
03546         if (width != sdl.surface->w || height != sdl.surface->h) {
03547             userResizeWindowWidth = (unsigned int)width;
03548             userResizeWindowHeight = (unsigned int)height;
03549         }
03550     }
03551     else {
03552         UpdateWindowDimensions();
03553     }
03554 
03555     /* TODO: Only if FULLSCREEN_DESKTOP */
03556     if (screen_size_info.screen_dimensions_pixels.width != 0 && screen_size_info.screen_dimensions_pixels.height != 0) {
03557         sdl.desktop.full.width = (Bit16u)screen_size_info.screen_dimensions_pixels.width;
03558         sdl.desktop.full.height = (Bit16u)screen_size_info.screen_dimensions_pixels.height;
03559     }
03560     else {
03561         SDL_DisplayMode dm;
03562         if (SDL_GetDesktopDisplayMode(0/*FIXME display index*/,&dm) == 0) {
03563             sdl.desktop.full.width = dm.w;
03564             sdl.desktop.full.height = dm.h;
03565             LOG_MSG("SDL2 reports desktop display mode %u x %u",dm.w,dm.h);
03566         }
03567         else {
03568             LOG_MSG("SDL2 unable to determine desktop display mode, error %s",SDL_GetError());
03569         }
03570     }
03571 
03572     window_was_maximized = menu.maxwindow;
03573     if (NonUserResizeCounter > 0)
03574         NonUserResizeCounter--;
03575 
03576     /* Even if the new window's dimensions are actually the desired ones
03577      * we may still need to re-obtain a new window surface or do
03578      * a different thing. So we basically call GFX_SetSize, but without
03579      * touching the window itself (or else we may end in an infinite loop).
03580      *
03581      * Furthermore, if the new dimensions are *not* the desired ones, we
03582      * don't fight it. Rather than attempting to resize it back, we simply
03583      * keep the window as-is and disable screen updates. This is done
03584      * in SDL_SetSDLWindowSurface by setting sdl.update_display_contents
03585      * to false.
03586      */
03587     sdl.update_window = false;
03588     GFX_ResetScreen();
03589     sdl.update_window = true;
03590 }
03591 #else
03592 static void HandleVideoResize(void * event) {
03593     if(sdl.desktop.fullscreen) return;
03594 
03595     /* don't act on resize events if we made the window non-resizeable.
03596      * especially if 3Dfx voodoo emulation is active. */
03597     if (!(sdl.surface->flags & SDL_RESIZABLE)) return;
03598 
03599     /* don't act if 3Dfx OpenGL emulation is active */
03600     if (GFX_GetPreventFullscreen()) return;
03601 
03602     SDL_ResizeEvent* ResizeEvent = (SDL_ResizeEvent*)event;
03603 
03604     /* assume the resize comes from user preference UNLESS the window
03605      * is fullscreen or maximized */
03606     if (!menu.maxwindow && !sdl.desktop.fullscreen && !sdl.init_ignore && NonUserResizeCounter == 0 && !window_was_maximized) {
03607         UpdateWindowDimensions();
03608         UpdateWindowDimensions((unsigned int)ResizeEvent->w, (unsigned int)ResizeEvent->h);
03609 
03610         /* if the dimensions actually changed from our surface dimensions, then
03611            assume it's the user's input. Linux/X11 is good at doing this anyway,
03612            but the Windows SDL 1.x support will return us a resize event for the
03613            window size change resulting from SDL mode set. */
03614         if (ResizeEvent->w != sdl.surface->w || ResizeEvent->h != sdl.surface->h) {
03615             userResizeWindowWidth = (unsigned int)ResizeEvent->w;
03616             userResizeWindowHeight = (unsigned int)ResizeEvent->h;
03617         }
03618     }
03619     else {
03620         UpdateWindowDimensions();
03621     }
03622 
03623     window_was_maximized = menu.maxwindow;
03624     if (NonUserResizeCounter > 0)
03625         NonUserResizeCounter--;
03626 
03627     if (sdl.updating && !GFX_MustActOnResize()) {
03628         /* act on resize when updating is complete */
03629         sdl.deferred_resize = true;
03630     }
03631     else {
03632         sdl.deferred_resize = false;
03633         RedrawScreen((unsigned int)ResizeEvent->w, (unsigned int)ResizeEvent->h);
03634     }
03635 #ifdef WIN32
03636     menu.resizeusing=false;
03637 #endif
03638 }
03639 #endif
03640 
03641 extern unsigned int mouse_notify_mode;
03642 
03643 bool user_cursor_locked = false;
03644 MOUSE_EMULATION user_cursor_emulation = MOUSE_EMULATION_NEVER;
03645 int user_cursor_x = 0,user_cursor_y = 0;
03646 int user_cursor_sw = 640,user_cursor_sh = 480;
03647 
03648 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
03649 DOSBoxMenu::item_handle_t DOSBoxMenu::displaylist::itemFromPoint(DOSBoxMenu &menu,int x,int y) {
03650     for (auto &id : disp_list) {
03651         DOSBoxMenu::item &item = menu.get_item(id);
03652         if (x >= item.screenBox.x && y >= item.screenBox.y) {
03653             int sx = x - item.screenBox.x;
03654             int sy = y - item.screenBox.y;
03655             int adj = (this != &menu.display_list && item.get_type() == DOSBoxMenu::submenu_type_id) ? 2 : 0;
03656             if (sx < (item.screenBox.w+adj) && sy < item.screenBox.h)
03657                 return id;
03658         }
03659     }
03660 
03661     return unassigned_item_handle;
03662 }
03663 
03664 void DOSBoxMenu::item::updateScreenFromItem(DOSBoxMenu &menu) {
03665     (void)menu;//UNUSED
03666     if (!OpenGL_using()) {
03667         SDL_Rect uprect = screenBox;
03668 
03669         SDL_rect_cliptoscreen(uprect);
03670 
03671 #if defined(C_SDL2)
03672         SDL_UpdateWindowSurfaceRects(sdl.window, &uprect, 1);
03673 #else
03674         SDL_UpdateRects( sdl.surface, 1, &uprect );
03675 #endif
03676     }
03677 }
03678 
03679 void DOSBoxMenu::item::updateScreenFromPopup(DOSBoxMenu &menu) {
03680     (void)menu;//UNUSED
03681     if (!OpenGL_using()) {
03682         SDL_Rect uprect = popupBox;
03683 
03684         uprect.w += DOSBoxMenu::dropshadowX;
03685         uprect.h += DOSBoxMenu::dropshadowY;
03686         SDL_rect_cliptoscreen(uprect);
03687 
03688 #if defined(C_SDL2)
03689         SDL_UpdateWindowSurfaceRects(sdl.window, &uprect, 1);
03690 #else
03691         SDL_UpdateRects( sdl.surface, 1, &uprect );
03692 #endif
03693     }
03694 }
03695 
03696 void DOSBoxMenu::item::drawBackground(DOSBoxMenu &menu) {
03697     (void)menu;//UNUSED
03698     Bitu bordercolor = GFX_GetRGB(31, 31, 31);
03699     Bitu bgcolor = GFX_GetRGB(63, 63, 63);
03700 
03701     if (popupBox.w <= 1 || popupBox.h <= 1)
03702         return;
03703 
03704     MenuDrawRect(popupBox.x, popupBox.y, popupBox.w, popupBox.h, bgcolor);
03705 
03706     if (borderTop)
03707         MenuDrawRect(popupBox.x, popupBox.y, popupBox.w, 1, bordercolor);
03708 
03709     MenuDrawRect(popupBox.x, popupBox.y + popupBox.h - 1, popupBox.w, 1, bordercolor);
03710 
03711     MenuDrawRect(popupBox.x, popupBox.y, 1, popupBox.h, bordercolor);
03712     MenuDrawRect(popupBox.x + popupBox.w - 1, popupBox.y, 1, popupBox.h, bordercolor);
03713 
03714     if (type == DOSBoxMenu::submenu_type_id) {
03715         MenuShadeRect((int)popupBox.x + (int)popupBox.w, (int)popupBox.y + (int)DOSBoxMenu::dropshadowY,
03716                       (int)DOSBoxMenu::dropshadowX, (int)popupBox.h);
03717         MenuShadeRect((int)popupBox.x + (int)DOSBoxMenu::dropshadowX, (int)popupBox.y + (int)popupBox.h,
03718                       (int)popupBox.w - (int)DOSBoxMenu::dropshadowX, (int)DOSBoxMenu::dropshadowY);
03719     }
03720 }
03721 #endif
03722 
03723 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
03724 void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id) {
03725     (void)menu;//UNUSED
03726     if (mainMenu.menuUserHoverAt != item_id) {
03727         if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
03728             auto &item = mainMenu.get_item(mainMenu.menuUserHoverAt);
03729             item.setHover(mainMenu,false);
03730             if (item.checkResetRedraw()) {
03731                 item.drawMenuItem(mainMenu);
03732                 item.updateScreenFromItem(mainMenu);
03733             }
03734         }
03735 
03736         mainMenu.menuUserHoverAt = item_id;
03737 
03738         if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
03739             auto &item = mainMenu.get_item(mainMenu.menuUserHoverAt);
03740             item.setHover(mainMenu,true);
03741             if (item.checkResetRedraw()) {
03742                 item.drawMenuItem(mainMenu);
03743                 item.updateScreenFromItem(mainMenu);
03744             }
03745         }
03746 
03747         if (OpenGL_using())
03748             mainMenu.setRedraw();
03749     }
03750 }
03751 
03752 void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id) {
03753     (void)menu;//UNUSED
03754     if (mainMenu.menuUserAttentionAt != item_id) {
03755         if (mainMenu.menuUserAttentionAt != DOSBoxMenu::unassigned_item_handle) {
03756             auto &item = mainMenu.get_item(mainMenu.menuUserAttentionAt);
03757             item.setHilight(mainMenu,false);
03758             if (item.checkResetRedraw()) {
03759                 item.drawMenuItem(mainMenu);
03760                 item.updateScreenFromItem(mainMenu);
03761             }
03762         }
03763 
03764         mainMenu.menuUserAttentionAt = item_id;
03765 
03766         if (mainMenu.menuUserAttentionAt != DOSBoxMenu::unassigned_item_handle) {
03767             auto &item = mainMenu.get_item(mainMenu.menuUserAttentionAt);
03768             item.setHilight(mainMenu,true);
03769             if (item.checkResetRedraw()) {
03770                 item.drawMenuItem(mainMenu);
03771                 item.updateScreenFromItem(mainMenu);
03772             }
03773         }
03774 
03775         if (OpenGL_using())
03776             mainMenu.setRedraw();
03777     }
03778 }
03779 #endif
03780 
03781 uint8_t Mouse_GetButtonState(void);
03782 
03783 bool GFX_CursorInOrNearScreen(int wx,int wy) {
03784     int minx = sdl.clip.x - (sdl.clip.w / 10);
03785     int miny = sdl.clip.y - (sdl.clip.h / 10);
03786     int maxx = sdl.clip.x + sdl.clip.w + (sdl.clip.w / 10);
03787     int maxy = sdl.clip.y + sdl.clip.h + (sdl.clip.h / 10);
03788 
03789     return  (wx >= minx && wx < maxx) && (wy >= miny && wy < maxy);
03790 }
03791 
03792 static void HandleMouseMotion(SDL_MouseMotionEvent * motion) {
03793     bool inputToScreen = false;
03794 
03795     /* limit mouse input to whenever the cursor is on the screen, or near the edge of the screen. */
03796     if (is_paused)
03797         inputToScreen = false;
03798     else if (sdl.mouse.locked || Mouse_GetButtonState() != 0)
03799         inputToScreen = true;
03800     else
03801         inputToScreen = GFX_CursorInOrNearScreen(motion->x,motion->y);
03802 
03803 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
03804     if (GFX_GetPreventFullscreen()) {
03805         /* do NOT draw SDL menu in OpenGL mode when 3Dfx emulation is using OpenGL */
03806     }
03807     else if (!sdl.mouse.locked && !sdl.desktop.fullscreen && mainMenu.isVisible() && motion->y < mainMenu.menuBox.h && Mouse_GetButtonState() == 0) {
03808         GFX_SDLMenuTrackHover(mainMenu,mainMenu.display_list.itemFromPoint(mainMenu,motion->x,motion->y));
03809         SDL_ShowCursor(SDL_ENABLE);
03810 
03811         if (OpenGL_using() && mainMenu.needsRedraw()) {
03812 #if C_OPENGL
03813             sdl_opengl.menudraw_countdown = 2; // two GL buffers
03814             GFX_OpenGLRedrawScreen();
03815             GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
03816 # if defined(C_SDL2)
03817             SDL_GL_SwapWindow(sdl.window);
03818 # else
03819             SDL_GL_SwapBuffers();
03820 # endif
03821 #endif
03822         }
03823  
03824         return;
03825     }
03826     else {
03827         GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
03828 
03829         if (OpenGL_using() && mainMenu.needsRedraw()) {
03830 #if C_OPENGL
03831             sdl_opengl.menudraw_countdown = 2; // two GL buffers
03832             GFX_OpenGLRedrawScreen();
03833             GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
03834 # if defined(C_SDL2)
03835             SDL_GL_SwapWindow(sdl.window);
03836 # else
03837             SDL_GL_SwapBuffers();
03838 # endif
03839 #endif
03840         }
03841     }
03842 #endif
03843 
03844     if (!inputToScreen) {
03845 #if defined(C_SDL2)
03846         if (!sdl.mouse.locked)
03847 #else
03848         /* SDL1 has some sort of weird mouse warping bug in fullscreen mode no matter whether the mouse is captured or not (Windows, Linux/X11) */
03849         if (!sdl.mouse.locked && !sdl.desktop.fullscreen)
03850 #endif
03851             SDL_ShowCursor(SDL_ENABLE);
03852  
03853         return;
03854     }
03855 
03856     user_cursor_x      = motion->x - sdl.clip.x;
03857     user_cursor_y      = motion->y - sdl.clip.y;
03858     user_cursor_locked = sdl.mouse.locked;
03859     user_cursor_emulation = sdl.mouse.emulation;
03860     user_cursor_sw     = sdl.clip.w;
03861     user_cursor_sh     = sdl.clip.h;
03862 
03863     auto xrel = static_cast<float>(motion->xrel) * sdl.mouse.xsensitivity / 100.0f;
03864     auto yrel = static_cast<float>(motion->yrel) * sdl.mouse.ysensitivity / 100.0f;
03865     auto x    = static_cast<float>(motion->x - sdl.clip.x) / (sdl.clip.w - 1) * sdl.mouse.xsensitivity / 100.0f;
03866     auto y    = static_cast<float>(motion->y - sdl.clip.y) / (sdl.clip.h - 1) * sdl.mouse.ysensitivity / 100.0f;
03867     auto emu  = sdl.mouse.locked;
03868 
03869     const auto inside =
03870         motion->x >= sdl.clip.x && motion->x < sdl.clip.x + sdl.clip.w &&
03871         motion->y >= sdl.clip.y && motion->y < sdl.clip.y + sdl.clip.h;
03872 
03873     if (mouse_notify_mode != 0)
03874     {
03875         /* for mouse integration driver */
03876         if (!sdl.mouse.locked)
03877             xrel = yrel = x = y = 0.0f;
03878 
03879         emu               = sdl.mouse.locked;
03880         const auto isdown = Mouse_GetButtonState() != 0;
03881 
03882 #if defined(C_SDL2)
03883         if (!sdl.mouse.locked)
03884 #else
03885         /* SDL1 has some sort of weird mouse warping bug in fullscreen mode no matter whether the mouse is captured or not (Windows, Linux/X11) */
03886         if (!sdl.mouse.locked && !sdl.desktop.fullscreen)
03887 #endif
03888             SDL_ShowCursor((isdown || inside) ? SDL_DISABLE : SDL_ENABLE);
03889     }
03890     else if (!user_cursor_locked)
03891     {
03892         extern bool MOUSE_HasInterruptSub();
03893         extern bool MOUSE_IsBeingPolled();
03894         extern bool MOUSE_IsHidden();
03895         /* Show only when DOS app is not using mouse */
03896 
03897 #if defined(C_SDL2)
03898         if (!sdl.mouse.locked)
03899 #else
03900         /* SDL1 has some sort of weird mouse warping bug in fullscreen mode no matter whether the mouse is captured or not (Windows, Linux/X11) */
03901         if (!sdl.mouse.locked && !sdl.desktop.fullscreen)
03902 #endif
03903             SDL_ShowCursor(((!inside) || ((MOUSE_IsHidden()) && !(MOUSE_IsBeingPolled() || MOUSE_HasInterruptSub()))) ? SDL_ENABLE : SDL_DISABLE);
03904     }
03905     Mouse_CursorMoved(xrel, yrel, x, y, emu);
03906 }
03907 
03908 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
03909 void MenuFullScreenRedraw(void) {
03910 #if defined(C_SDL2)
03911     SDL_UpdateWindowSurface(sdl.window);
03912 #else
03913     SDL_Flip(sdl.surface);
03914 #endif
03915 }
03916 
03917 static struct {
03918     unsigned char*      bmp = NULL;
03919     unsigned int        stride = 0,height = 0;
03920 } menuSavedScreen;
03921 
03922 void MenuSaveScreen(void) {
03923     if (!OpenGL_using()) {
03924         if (menuSavedScreen.bmp == NULL) {
03925             menuSavedScreen.height = (unsigned int)sdl.surface->h;
03926             menuSavedScreen.stride = (unsigned int)sdl.surface->pitch;
03927             menuSavedScreen.bmp = new unsigned char[menuSavedScreen.height * menuSavedScreen.stride];
03928         }
03929 
03930         if (SDL_MUSTLOCK(sdl.surface))
03931             SDL_LockSurface(sdl.surface);
03932 
03933         memcpy(menuSavedScreen.bmp, sdl.surface->pixels, menuSavedScreen.height * menuSavedScreen.stride);
03934 
03935         if (SDL_MUSTLOCK(sdl.surface))
03936             SDL_UnlockSurface(sdl.surface);
03937     }
03938 }
03939 
03940 void MenuRestoreScreen(void) {
03941     if (!OpenGL_using()) {
03942         if (menuSavedScreen.bmp == NULL)
03943             return;
03944 
03945         if (SDL_MUSTLOCK(sdl.surface))
03946             SDL_LockSurface(sdl.surface);
03947 
03948         memcpy(sdl.surface->pixels, menuSavedScreen.bmp, menuSavedScreen.height * menuSavedScreen.stride);
03949 
03950         if (SDL_MUSTLOCK(sdl.surface))
03951             SDL_UnlockSurface(sdl.surface);
03952     }
03953 }
03954 
03955 void MenuFreeScreen(void) {
03956     if (menuSavedScreen.bmp == NULL)
03957         return;
03958 
03959     delete[] menuSavedScreen.bmp;
03960     menuSavedScreen.bmp = NULL;
03961 }
03962 #endif
03963 
03964 static void HandleMouseButton(SDL_MouseButtonEvent * button) {
03965     bool inputToScreen = false;
03966     bool inMenu = false;
03967 
03968 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
03969     if (GFX_GetPreventFullscreen()) {
03970         /* do NOT draw SDL menu in OpenGL mode when 3Dfx emulation is using OpenGL */
03971         mainMenu.menuUserHoverAt = DOSBoxMenu::unassigned_item_handle;
03972     }
03973     else if (!sdl.mouse.locked && !sdl.desktop.fullscreen && mainMenu.isVisible() && button->y < mainMenu.menuBox.h) {
03974         GFX_SDLMenuTrackHover(mainMenu,mainMenu.display_list.itemFromPoint(mainMenu,button->x,button->y));
03975         inMenu = true;
03976     }
03977     else {
03978         GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
03979     }
03980 
03981     if (button->button == SDL_BUTTON_LEFT) {
03982         if (button->state == SDL_PRESSED) {
03983             GFX_SDLMenuTrackHilight(mainMenu,mainMenu.menuUserHoverAt);
03984             if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
03985                 std::vector<DOSBoxMenu::item_handle_t> popup_stack;
03986                 DOSBoxMenu::item_handle_t choice_item;
03987                 DOSBoxMenu::item_handle_t psel_item;
03988                 DOSBoxMenu::item_handle_t sel_item;
03989                 bool button_holding=true;
03990                 bool redrawAll=false;
03991                 bool resized=false;
03992                 bool runloop=true;
03993                 SDL_Rect uprect;
03994                 SDL_Event event;
03995 
03996                 psel_item = DOSBoxMenu::unassigned_item_handle;
03997                 choice_item = mainMenu.menuUserHoverAt = mainMenu.menuUserAttentionAt;
03998 
03999                 popup_stack.push_back(mainMenu.menuUserAttentionAt);
04000 
04001 #if C_DIRECT3D
04002         if (sdl.desktop.want_type == SCREEN_DIRECT3D) {
04003             /* In output=direct3d mode, SDL still has a surface but this code ignores SDL
04004              * and draws directly to a Direct3D9 backbuffer which is presented to the window
04005              * client area. However, GDI output to the window still works, and this code
04006              * uses the SDL surface still. Therefore, for menus to draw correctly atop the
04007              * Direct3D output, this code copies the Direct3D backbuffer to the SDL surface
04008              * first.
04009              *
04010              * WARNING: This happens to work with Windows (even Windows 10 build 18xx as of
04011              * 2018/05/21) because Windows appears to permit mixing Direct3D and GDI rendering
04012              * to the window.
04013              *
04014              * Someday, if Microsoft should break that ability, this code will need to be
04015              * revised to send screen "updates" to the Direct3D backbuffer first, then
04016              * Present to the window client area. */
04017             if (d3d) d3d->UpdateRectToSDLSurface(0, 0, sdl.surface->w, sdl.surface->h);
04018         }
04019 #endif
04020 
04021                 if (OpenGL_using()) {
04022 #if C_OPENGL
04023                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,false);
04024                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,false);
04025 
04026                     /* show the menu */
04027                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,true);
04028                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,true);
04029 
04030                     glClearColor (0.0, 0.0, 0.0, 1.0);
04031                     glClear(GL_COLOR_BUFFER_BIT);
04032 
04033                     GFX_OpenGLRedrawScreen();
04034 
04035                     /* give the menu bar a drop shadow */
04036                     MenuShadeRect(
04037                             (int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX,
04038                             (int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h,
04039                             (int)mainMenu.menuBox.w,
04040                             (int)DOSBoxMenu::dropshadowY - 1/*menubar border*/);
04041 
04042                     mainMenu.setRedraw();                  
04043                     GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
04044 
04045                     for (auto i=popup_stack.begin();i!=popup_stack.end();i++) {
04046                         if (mainMenu.get_item(*i).get_type() == DOSBoxMenu::submenu_type_id) {
04047                             mainMenu.get_item(*i).drawBackground(mainMenu);
04048                             mainMenu.get_item(*i).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
04049                         }
04050                     }
04051 
04052 # if defined(C_SDL2)
04053                     SDL_GL_SwapWindow(sdl.window);
04054 # else
04055                     SDL_GL_SwapBuffers();
04056 # endif
04057 #endif
04058                 }
04059                 else {
04060                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,false);
04061                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,false);
04062                     mainMenu.get_item(mainMenu.menuUserAttentionAt).drawMenuItem(mainMenu);
04063                     MenuSaveScreen();
04064 
04065                     /* give the menu bar a drop shadow */
04066                     MenuShadeRect(
04067                             (int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX,
04068                             (int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h,
04069                             (int)mainMenu.menuBox.w,
04070                             (int)DOSBoxMenu::dropshadowY - 1/*menubar border*/);
04071 
04072                     uprect.x = 0;
04073                     uprect.y = mainMenu.menuBox.y + mainMenu.menuBox.h;
04074                     uprect.w = mainMenu.menuBox.w;
04075                     uprect.h = DOSBoxMenu::dropshadowY;
04076 #if defined(C_SDL2)
04077                     SDL_UpdateWindowSurfaceRects(sdl.window, &uprect, 1);
04078 #else
04079                     SDL_UpdateRects( sdl.surface, 1, &uprect );
04080 #endif
04081 
04082                     /* show the menu */
04083                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,true);
04084                     mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,true);
04085                     mainMenu.get_item(mainMenu.menuUserAttentionAt).drawBackground(mainMenu);
04086                     mainMenu.get_item(mainMenu.menuUserAttentionAt).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
04087                     mainMenu.get_item(mainMenu.menuUserAttentionAt).updateScreenFromPopup(mainMenu);
04088                 }
04089 
04090                 /* hack */
04091                 mainMenu.menuUserAttentionAt = DOSBoxMenu::unassigned_item_handle;
04092 
04093                 /* fall into another loop to process the menu */
04094                 while (runloop) {
04095 #if C_EMSCRIPTEN
04096                     emscripten_sleep_with_yield(0);
04097                     if (!SDL_PollEvent(&event)) continue;
04098 #else
04099                     if (!SDL_WaitEvent(&event)) break;
04100 #endif
04101 
04102 #if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
04103                     switch (event.type) {
04104                         case SDL_FINGERDOWN:
04105                             if (touchscreen_finger_lock == no_finger_id &&
04106                                 touchscreen_touch_lock == no_touch_id) {
04107                                 touchscreen_finger_lock = event.tfinger.fingerId;
04108                                 touchscreen_touch_lock = event.tfinger.touchId;
04109                                 Sint32 x,y;
04110 
04111                                 x = (Sint32)(event.tfinger.x * currentWindowWidth);
04112                                 y = (Sint32)(event.tfinger.y * currentWindowHeight);
04113 
04114                                 memset(&event.button,0,sizeof(event.button));
04115                                 event.type = SDL_MOUSEBUTTONDOWN;
04116                                 event.button.x = x;
04117                                 event.button.y = y;
04118                             }
04119                             else {
04120                                 event.type = -1;
04121                             }
04122                             break;
04123                         case SDL_FINGERUP:
04124                             if (touchscreen_finger_lock == event.tfinger.fingerId &&
04125                                 touchscreen_touch_lock == event.tfinger.touchId) {
04126                                 touchscreen_finger_lock = no_finger_id;
04127                                 touchscreen_touch_lock = no_touch_id;
04128                                 Sint32 x,y;
04129 
04130                                 x = (Sint32)(event.tfinger.x * currentWindowWidth);
04131                                 y = (Sint32)(event.tfinger.y * currentWindowHeight);
04132                                 
04133                                 memset(&event.button,0,sizeof(event.button));
04134                                 event.type = SDL_MOUSEBUTTONUP;
04135                                 event.button.x = x;
04136                                 event.button.y = y;
04137                             }
04138                             else {
04139                                 event.type = -1;
04140                             }
04141                             break;
04142                         case SDL_FINGERMOTION:
04143                             if (touchscreen_finger_lock == event.tfinger.fingerId &&
04144                                 touchscreen_touch_lock == event.tfinger.touchId) {
04145                                 Sint32 x,y;
04146 
04147                                 x = (Sint32)(event.tfinger.x * currentWindowWidth);
04148                                 y = (Sint32)(event.tfinger.y * currentWindowHeight);
04149 
04150                                 memset(&event.button,0,sizeof(event.button));
04151                                 event.type = SDL_MOUSEMOTION;
04152                                 event.button.x = x;
04153                                 event.button.y = y;
04154                             }
04155                             else if (touchscreen_finger_lock != no_finger_id ||
04156                                      touchscreen_touch_lock != no_touch_id) {
04157                                 event.type = -1;
04158                             }
04159                             break;
04160                     }
04161 #endif
04162 
04163                     switch (event.type) {
04164                         case SDL_QUIT:
04165                             throw(0);
04166                             break;
04167                         case SDL_KEYUP:
04168                             if (event.key.keysym.sym == SDLK_ESCAPE) {
04169                                 choice_item = DOSBoxMenu::unassigned_item_handle;
04170                                 runloop = false;
04171                             }
04172                             break;
04173 #if defined(C_SDL2)
04174                         case SDL_WINDOWEVENT:
04175                             switch (event.window.event) {
04176                                 case SDL_WINDOWEVENT_RESIZED:
04177                                     GFX_HandleVideoResize(event.window.data1, event.window.data2);
04178                                     runloop = false;
04179                                     resized = true;
04180                                     break;
04181                                 default:
04182                                     break;
04183                             }
04184 #endif
04185 #if !defined(C_SDL2)
04186                         case SDL_VIDEORESIZE:
04187                             UpdateWindowDimensions(); // FIXME: Use SDL window dimensions, except that on Windows, SDL won't tell us our actual dimensions
04188                             HandleVideoResize(&event.resize);
04189 
04190                             runloop = false;
04191                             resized = true;
04192                             break;
04193 #endif
04194                         case SDL_MOUSEBUTTONDOWN:
04195                             button_holding=true;
04196                             choice_item = mainMenu.menuUserHoverAt;
04197                             if (choice_item != DOSBoxMenu::unassigned_item_handle) {
04198                                 DOSBoxMenu::item &item = mainMenu.get_item(choice_item);
04199                                 item.setHilight(mainMenu,true);
04200                                 item.drawMenuItem(mainMenu);
04201                                 if (OpenGL_using())
04202                                     redrawAll = true;
04203                                 else
04204                                     item.updateScreenFromItem(mainMenu);
04205                             }
04206                             else {
04207                                 /* clicking on nothing should dismiss */
04208                                 runloop = false;
04209                             }
04210                             break;
04211                         case SDL_MOUSEBUTTONUP:
04212                             button_holding=false;
04213                             choice_item = mainMenu.menuUserHoverAt;
04214                             if (choice_item != DOSBoxMenu::unassigned_item_handle) {
04215                                 if (choice_item == psel_item) { /* clicking something twice should dismiss */
04216                                     runloop = false;
04217                                 }
04218                                 else {
04219                                     DOSBoxMenu::item &item = mainMenu.get_item(choice_item);
04220                                     if (item.get_type() == DOSBoxMenu::item_type_id && item.is_enabled())
04221                                         runloop = false;
04222                                 }
04223 
04224                                 psel_item = choice_item;
04225                             }
04226                             else {
04227                                 /* not selecting anything counts as a reason to exit */
04228                                 runloop = false;
04229                             }
04230                             break;
04231                         case SDL_MOUSEMOTION:
04232                             {
04233                                 bool noRedrawNew = false,noRedrawOld = false;
04234 
04235                                 sel_item = DOSBoxMenu::unassigned_item_handle;
04236 
04237                                 auto search = popup_stack.end();
04238                                 if (search != popup_stack.begin()) {
04239                                     do {
04240                                         search--;
04241 
04242                                         sel_item = mainMenu.get_item(*search).display_list.itemFromPoint(mainMenu,event.button.x,event.button.y);
04243                                         if (sel_item != DOSBoxMenu::unassigned_item_handle) {
04244                                             assert(search != popup_stack.end());
04245                                             search++;
04246                                             break;
04247                                         }
04248                                     } while (search != popup_stack.begin());
04249                                 }
04250 
04251                                 if (sel_item == DOSBoxMenu::unassigned_item_handle)
04252                                     sel_item = mainMenu.display_list.itemFromPoint(mainMenu,event.button.x,event.button.y);
04253 
04254                                 /* at this point:
04255                                  *  sel_item = item under cursor, or unassigned if no item
04256                                  *  search = iterator just past the item's level (to remove items if changing) */
04257 
04258                                 if (mainMenu.menuUserHoverAt != sel_item) {
04259                                     if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
04260                                         mainMenu.get_item(mainMenu.menuUserHoverAt).setHover(mainMenu,false);
04261                                         if (mainMenu.get_item(mainMenu.menuUserHoverAt).get_type() == DOSBoxMenu::item_type_id)
04262                                             mainMenu.get_item(mainMenu.menuUserHoverAt).setHilight(mainMenu,false);
04263                                         else if (mainMenu.get_item(mainMenu.menuUserHoverAt).get_type() == DOSBoxMenu::submenu_type_id) {
04264                                             if (mainMenu.get_item(mainMenu.menuUserHoverAt).isHilight()) {
04265                                                 noRedrawOld = true;
04266                                             }
04267                                         }
04268                                     }
04269 
04270                                     if (sel_item != DOSBoxMenu::unassigned_item_handle) {
04271                                         if (mainMenu.get_item(sel_item).get_type() == DOSBoxMenu::submenu_type_id) {
04272                                             if (!mainMenu.get_item(sel_item).isHilight()) {
04273                                                 /* use a copy of the iterator to scan forward and un-hilight the menu items.
04274                                                  * then use the original iterator to erase from the vector. */
04275                                                 for (auto ss=search;ss != popup_stack.end();ss++) {
04276                                                     for (auto &id : mainMenu.get_item(*ss).display_list.get_disp_list())
04277                                                         mainMenu.get_item(id).setHilight(mainMenu,false).setHover(mainMenu,false);
04278 
04279                                                     mainMenu.get_item(*ss).setHilight(mainMenu,false).setHover(mainMenu,false);
04280                                                 }
04281 
04282                                                 popup_stack.erase(search,popup_stack.end());
04283                                                 mainMenu.get_item(sel_item).setHilight(mainMenu,true).setHover(mainMenu,true);
04284                                                 popup_stack.push_back(sel_item);
04285                                                 redrawAll = true;
04286                                             }
04287                                             else {
04288                                                 /* no change in item state, don't bother redrawing */
04289                                                 noRedrawNew = true;
04290                                             }
04291                                         }
04292                                         else {
04293                                             /* use a copy of the iterator to scan forward and un-hilight the menu items.
04294                                              * then use the original iterator to erase from the vector. */
04295                                             for (auto ss=search;ss != popup_stack.end();ss++) {
04296                                                 for (auto &id : mainMenu.get_item(*ss).display_list.get_disp_list())
04297                                                     mainMenu.get_item(id).setHilight(mainMenu,false).setHover(mainMenu,false);
04298 
04299                                                 mainMenu.get_item(*ss).setHilight(mainMenu,false).setHover(mainMenu,false);
04300                                                 redrawAll = true;
04301                                             }
04302 
04303                                             popup_stack.erase(search,popup_stack.end());
04304                                         }
04305 
04306                                         if (OpenGL_using())
04307                                             redrawAll = true;
04308 
04309                                         mainMenu.get_item(sel_item).setHover(mainMenu,true);
04310                                         if (mainMenu.get_item(sel_item).get_type() == DOSBoxMenu::item_type_id && button_holding)
04311                                             mainMenu.get_item(sel_item).setHilight(mainMenu,true);
04312                                     }
04313                                     else {
04314                                         if (OpenGL_using())
04315                                             redrawAll = true;
04316                                     }
04317 
04318                                     if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle && !OpenGL_using() && !redrawAll && !noRedrawOld) {
04319                                         if (mainMenu.get_item(mainMenu.menuUserHoverAt).checkResetRedraw()) {
04320                                             mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu);
04321                                             mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu);
04322                                         }
04323                                     }
04324 
04325                                     mainMenu.menuUserHoverAt = sel_item;
04326 
04327                                     if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle && !OpenGL_using() && !redrawAll && !noRedrawNew) {
04328                                         if (mainMenu.get_item(mainMenu.menuUserHoverAt).checkResetRedraw()) {
04329                                             mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu);
04330                                             mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu);
04331                                         }
04332                                     }
04333                                 }
04334                             }
04335                             break;
04336                     }
04337 
04338                     if (redrawAll) {
04339                         redrawAll = false;
04340 
04341 #if 0/*DEBUG*/
04342                         LOG_MSG("Redraw %u",(unsigned int)SDL_GetTicks());
04343 #endif
04344 
04345                         if (OpenGL_using()) {
04346 #if C_OPENGL
04347                             glClearColor (0.0, 0.0, 0.0, 1.0);
04348                             glClear(GL_COLOR_BUFFER_BIT);
04349 
04350                             GFX_OpenGLRedrawScreen();
04351   
04352                             mainMenu.setRedraw();                  
04353                             GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
04354 #endif
04355                         }
04356                         else {
04357                             MenuRestoreScreen();
04358                             mainMenu.display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
04359                         }
04360 
04361                         /* give the menu bar a drop shadow */
04362                         MenuShadeRect(
04363                                 (int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX,
04364                                 (int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h,
04365                                 (int)mainMenu.menuBox.w,
04366                                 (int)DOSBoxMenu::dropshadowY - 1/*menubar border*/);
04367 
04368                         for (auto i=popup_stack.begin();i!=popup_stack.end();i++) {
04369                             if (mainMenu.get_item(*i).get_type() == DOSBoxMenu::submenu_type_id) {
04370                                 mainMenu.get_item(*i).drawBackground(mainMenu);
04371                                 mainMenu.get_item(*i).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
04372                             }
04373                         }
04374 
04375 #if C_OPENGL
04376                         if (OpenGL_using()) {
04377 # if defined(C_SDL2)
04378                             SDL_GL_SwapWindow(sdl.window);
04379 # else
04380                             SDL_GL_SwapBuffers();
04381 # endif
04382                         }
04383                         else
04384 #endif
04385                             MenuFullScreenRedraw();
04386                     }
04387                 }
04388 
04389 #if defined(C_SDL2)
04390                 /* force touchscreen mapping to let go */
04391                 touchscreen_finger_lock = no_finger_id;
04392                 touchscreen_touch_lock = no_touch_id;
04393 #endif
04394 
04395                 /* then return */
04396                 GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
04397                 GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
04398                 if (!resized) {
04399                     MenuRestoreScreen();
04400                     if (!OpenGL_using())
04401                         MenuFullScreenRedraw();
04402                 }
04403                 MenuFreeScreen();
04404 
04405                 while (!popup_stack.empty()) {
04406                     DOSBoxMenu::item &item = mainMenu.get_item(popup_stack.back());
04407 
04408                     for (auto &id : item.display_list.get_disp_list()) {
04409                         mainMenu.get_item(id).setHilight(mainMenu,false);
04410                         mainMenu.get_item(id).setHover(mainMenu,false);
04411                     }
04412 
04413                     item.setHilight(mainMenu,false);
04414                     item.setHover(mainMenu,false);
04415                     popup_stack.pop_back();
04416                 }
04417 
04418                 if (OpenGL_using()) {
04419 #if C_OPENGL
04420                     glClearColor (0.0, 0.0, 0.0, 1.0);
04421                     glClear(GL_COLOR_BUFFER_BIT);
04422         
04423                     GFX_OpenGLRedrawScreen();
04424 
04425                     mainMenu.setRedraw();
04426                     GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
04427 
04428 # if defined(C_SDL2)
04429                     SDL_GL_SwapWindow(sdl.window);
04430 # else
04431                     SDL_GL_SwapBuffers();
04432 # endif
04433 
04434                     sdl_opengl.clear_countdown = 2;
04435                     sdl_opengl.menudraw_countdown = 2; // two GL buffers
04436 #endif
04437                 }
04438 
04439                 /* action! */
04440                 if (!resized && choice_item != DOSBoxMenu::unassigned_item_handle) {
04441                     DOSBoxMenu::item &item = mainMenu.get_item(choice_item);
04442 
04443                     if (item.get_type() == DOSBoxMenu::item_type_id && item.is_enabled())
04444                         mainMenu.dispatchItemCommand(item);
04445                 }
04446 
04447                 return;
04448             }
04449         }
04450         else {
04451             GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
04452         }
04453     }
04454 #endif
04455 
04456     /* limit mouse input to whenever the cursor is on the screen, or near the edge of the screen. */
04457     if (is_paused)
04458         inputToScreen = false;
04459     else if (sdl.mouse.locked)
04460         inputToScreen = true;
04461     else if (!inMenu)
04462         inputToScreen = GFX_CursorInOrNearScreen(button->x, button->y);
04463 
04464     switch (button->state) {
04465     case SDL_PRESSED:
04466         if (inMenu || !inputToScreen) return;
04467         if (sdl.mouse.requestlock && !sdl.mouse.locked && mouse_notify_mode == 0) {
04468             CaptureMouseNotify();
04469             GFX_CaptureMouse();
04470             // Don't pass click to mouse handler
04471             break;
04472         }
04473         if (!sdl.mouse.autoenable && sdl.mouse.autolock && mouse_notify_mode == 0 && button->button == SDL_BUTTON_MIDDLE) {
04474             GFX_CaptureMouse();
04475             break;
04476         }
04477         switch (button->button) {
04478         case SDL_BUTTON_LEFT:
04479             Mouse_ButtonPressed(0);
04480             break;
04481         case SDL_BUTTON_RIGHT:
04482             Mouse_ButtonPressed(1);
04483             break;
04484         case SDL_BUTTON_MIDDLE:
04485             Mouse_ButtonPressed(2);
04486             break;
04487 #if !defined(C_SDL2)
04488         case SDL_BUTTON_WHEELUP: /* Ick, really SDL? */
04489             Mouse_ButtonPressed(100-1);
04490             break;
04491         case SDL_BUTTON_WHEELDOWN: /* Ick, really SDL? */
04492             Mouse_ButtonPressed(100+1);
04493             break;
04494 #endif
04495         }
04496         break;
04497     case SDL_RELEASED:
04498         switch (button->button) {
04499         case SDL_BUTTON_LEFT:
04500             Mouse_ButtonReleased(0);
04501             break;
04502         case SDL_BUTTON_RIGHT:
04503             Mouse_ButtonReleased(1);
04504             break;
04505         case SDL_BUTTON_MIDDLE:
04506             Mouse_ButtonReleased(2);
04507             break;
04508 #if !defined(C_SDL2)
04509         case SDL_BUTTON_WHEELUP: /* Ick, really SDL? */
04510             Mouse_ButtonReleased(100-1);
04511             break;
04512         case SDL_BUTTON_WHEELDOWN: /* Ick, really SDL? */
04513             Mouse_ButtonReleased(100+1);
04514             break;
04515 #endif
04516         }
04517         break;
04518     }
04519 }
04520 
04521 void GFX_LosingFocus(void) {
04522     sdl.laltstate=SDL_KEYUP;
04523     sdl.raltstate=SDL_KEYUP;
04524     MAPPER_LosingFocus();
04525     DoExtendedKeyboardHook(false);
04526 }
04527 
04528 #if !defined(C_SDL2)
04529 static bool PasteClipboardNext(); // added emendelson from dbDOS
04530 #endif
04531 
04532 bool GFX_IsFullscreen(void) {
04533     return sdl.desktop.fullscreen;
04534 }
04535 
04536 void* GetSetSDLValue(int isget, std::string target, void* setval) {
04537     if (target == "wait_on_error") {
04538         if (isget) return (void*) sdl.wait_on_error;
04539         else sdl.wait_on_error = setval;
04540     }
04541     else if (target == "opengl.bilinear") {
04542 #if C_OPENGL
04543         if (isget) return (void*) sdl_opengl.bilinear;
04544         else sdl_opengl.bilinear = setval;
04545 #else
04546         if (isget) return (void*) 0;
04547 #endif
04548 /*
04549     } else if (target == "draw.callback") {
04550         if (isget) return (void*) sdl.draw.callback;
04551         else sdl.draw.callback = *static_cast<GFX_CallBack_t*>(setval);
04552     } else if (target == "desktop.full.width") {
04553         if (isget) return (void*) sdl.desktop.full.width;
04554         else sdl.desktop.full.width = *static_cast<Bit16u*>(setval);
04555     } else if (target == "desktop.full.height") {
04556         if (isget) return (void*) sdl.desktop.full.height;
04557         else sdl.desktop.full.height = *static_cast<Bit16u*>(setval);
04558     } else if (target == "desktop.full.fixed") {
04559         if (isget) return (void*) sdl.desktop.full.fixed;
04560         else sdl.desktop.full.fixed = setval;
04561     } else if (target == "desktop.window.width") {
04562         if (isget) return (void*) sdl.desktop.window.width;
04563         else sdl.desktop.window.width = *static_cast<Bit16u*>(setval);
04564     } else if (target == "desktop.window.height") {
04565         if (isget) return (void*) sdl.desktop.window.height;
04566         else sdl.desktop.window.height = *static_cast<Bit16u*>(setval);
04567 */
04568     } else if (target == "desktop.fullscreen") {
04569         if (isget) return (void*) sdl.desktop.fullscreen;
04570         else sdl.desktop.fullscreen = setval;
04571     } else if (target == "desktop.doublebuf") {
04572         if (isget) return (void*) sdl.desktop.doublebuf;
04573         else sdl.desktop.doublebuf = setval;
04574 /*
04575     } else if (target == "desktop.type") {
04576         if (isget) return (void*) sdl.desktop.type;
04577         else sdl.desktop.type = *static_cast<SCREEN_TYPES*>(setval);
04578 */
04579     } else if (target == "desktop.want_type") {
04580         if (isget) return (void*) sdl.desktop.want_type;
04581         else sdl.desktop.want_type = *static_cast<SCREEN_TYPES*>(setval);
04582 /*
04583     } else if (target == "surface") {
04584         if (isget) return (void*) sdl.surface;
04585         else sdl.surface = static_cast<SDL_Surface*>(setval);
04586     } else if (target == "overlay") {
04587         if (isget) return (void*) sdl.overlay;
04588         else sdl.overlay = static_cast<SDL_Overlay*>(setval);
04589 */
04590     } else if (target == "mouse.autoenable") {
04591         if (isget) return (void*) sdl.mouse.autoenable;
04592         else sdl.mouse.autoenable = setval;
04593 /*
04594     } else if (target == "overscan_width") {
04595         if (isget) return (void*) sdl.overscan_width;
04596         else sdl.overscan_width = *static_cast<Bitu*>(setval);
04597 */
04598 #if defined (WIN32)
04599     } else if (target == "using_windib") {
04600         if (isget) return (void*) sdl.using_windib;
04601         else sdl.using_windib = setval;
04602 #endif
04603     }
04604 
04605     return NULL;
04606 }
04607 
04608 #if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
04609 static void FingerToFakeMouseMotion(SDL_TouchFingerEvent * finger) {
04610     SDL_MouseMotionEvent fake;
04611 
04612     memset(&fake,0,sizeof(fake));
04613     /* NTS: Windows versions of SDL2 do normalize the coordinates */
04614     fake.x = (Sint32)(finger->x * currentWindowWidth);
04615     fake.y = (Sint32)(finger->y * currentWindowHeight);
04616     fake.xrel = (Sint32)finger->dx;
04617     fake.yrel = (Sint32)finger->dy;
04618     HandleMouseMotion(&fake);
04619 
04620     if (finger->type == SDL_FINGERDOWN || finger->type == SDL_FINGERUP) {
04621         SDL_MouseButtonEvent fakeb;
04622 
04623         memset(&fakeb,0,sizeof(fakeb));
04624 
04625         fakeb.state = (finger->type == SDL_FINGERDOWN) ? SDL_PRESSED : SDL_RELEASED;
04626         fakeb.button = SDL_BUTTON_LEFT;
04627         fakeb.x = fake.x;
04628         fakeb.y = fake.y;
04629         HandleMouseButton(&fakeb);
04630     }
04631 }
04632 
04633 static void HandleTouchscreenFinger(SDL_TouchFingerEvent * finger) {
04634     /* Now that SDL2 can tell my mouse from my laptop touchscreen, let's
04635      * map tap events to the left mouse button. Now I can use my laptop
04636      * touchscreen with Windows 3.11 again! --J.C. */
04637     /* Now let's handle The Finger (har har) */
04638 
04639     /* NTS: This code is written to map ONLY one finger to the mouse.
04640      *      If multiple fingers are touching the screen, this code will
04641      *      only respond to the first finger that touched the screen. */
04642 
04643     if (finger->type == SDL_FINGERDOWN) {
04644         if (touchscreen_finger_lock == no_finger_id &&
04645             touchscreen_touch_lock == no_touch_id) {
04646             touchscreen_finger_lock = finger->fingerId;
04647             touchscreen_touch_lock = finger->touchId;
04648             FingerToFakeMouseMotion(finger);
04649         }
04650     }
04651     else if (finger->type == SDL_FINGERUP) {
04652         if (touchscreen_finger_lock == finger->fingerId &&
04653             touchscreen_touch_lock == finger->touchId) {
04654             touchscreen_finger_lock = no_finger_id;
04655             touchscreen_touch_lock = no_touch_id;
04656             FingerToFakeMouseMotion(finger);
04657         }
04658     }
04659     else if (finger->type == SDL_FINGERMOTION) {
04660         if (touchscreen_finger_lock == finger->fingerId &&
04661             touchscreen_touch_lock == finger->touchId) {
04662             FingerToFakeMouseMotion(finger);
04663         }
04664     }
04665 }
04666 #endif
04667 
04668 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
04669 void MSG_WM_COMMAND_handle(SDL_SysWMmsg &Message);
04670 #endif
04671 
04672 struct mouse_pos
04673 {
04674     long x = 0;
04675     long y = 0;
04676 } mouse_pos;
04677 
04678 bool mouse_inside = false;
04679 
04680 void GFX_EventsMouseProcess(const long x, const long y, const long rx, const long ry)
04681 {
04682     const auto x1 = sdl.clip.x;
04683     const auto x2 = x1 + sdl.clip.w - 1;
04684     const auto y1 = sdl.clip.y;
04685     const auto y2 = y1 + sdl.clip.h - 1;
04686     const auto in = x >= x1 && x <= x2 && y >= y1 && y <= y2;
04687 
04688     if (mouse_inside && !in)
04689     {
04690         const auto x3 = max((int)x1, min((int)x2, (int)x));
04691         const auto y3 = max((int)y1, min((int)y2, (int)y));
04692         SDL_Event  evt;
04693         evt.type         = SDL_MOUSEMOTION;
04694         evt.motion.state = 0;
04695         evt.motion.which = 0;
04696         evt.motion.x     = x3;
04697         evt.motion.y     = y3;
04698         evt.motion.xrel  = (Sint16)((rx >= 0) ? min(rx, 32767l) : max(rx, -32768l));
04699         evt.motion.yrel  = (Sint16)((ry >= 0) ? min(ry, 32767l) : max(ry, -32768l));
04700         SDL_PushEvent(&evt);
04701     }
04702 
04703     mouse_inside = in;
04704 }
04705 
04706 #if defined(WIN32)
04707 void GFX_EventsMouseWin32()
04708 {
04709     /* Compute relative mouse movement */
04710 
04711     POINT point;
04712 
04713     if (!GetCursorPos(&point))
04714         return;
04715 
04716     const auto hwnd = GetSurfaceHWND();
04717 
04718     if (hwnd == nullptr || !ScreenToClient(hwnd, &point))
04719         return;
04720 
04721     const auto x  = point.x;
04722     const auto y  = point.y;
04723     const auto rx = x - mouse_pos.x;
04724     const auto ry = y - mouse_pos.y;
04725 
04726     mouse_pos.x = x;
04727     mouse_pos.y = y;
04728 
04729     /* Let the method do the heavy uplifting */
04730     GFX_EventsMouseProcess(x, y, rx, ry);
04731 }
04732 #endif
04733 
04740 void GFX_EventsMouse()
04741 {
04742     if (sdl.desktop.fullscreen || sdl.mouse.locked)
04743         return;
04744 
04745 #if WIN32
04746     GFX_EventsMouseWin32();
04747 #else
04748     // TODO
04749 #endif
04750 }
04751 
04752 /* DOSBox SVN revision 4176:4177: For Linux/X11, Xorg 1.20.1
04753  * will make spurious focus gain and loss events when locking the mouse in windowed mode.
04754  *
04755  * This has not been tested with DOSBox-X yet becaus I do not run Xorg 1.20.1, yet */
04756 #if defined(LINUX)
04757 #define SDL_XORG_FIX 1
04758 #else
04759 #define SDL_XORG_FIX 0
04760 #endif
04761 /* end patch fragment */
04762 
04763 bool gfx_in_mapper = false;
04764 
04765 #if defined(MACOSX)
04766 #define DB_POLLSKIP 3
04767 #else
04768 //Not used yet, see comment below
04769 #define DB_POLLSKIP 1
04770 #endif
04771 
04772 void GFX_Events() {
04773     CheckMapperKeyboardLayout();
04774 #if defined(C_SDL2) /* SDL 2.x---------------------------------- */
04775     //Don't poll too often. This can be heavy on the OS, especially Macs.
04776     //In idle mode 3000-4000 polls are done per second without this check.
04777     //Macs, with this code,  max 250 polls per second. (non-macs unused default max 500)
04778     //Currently not implemented for all platforms, given the ALT-TAB stuff for WIN32.
04779 #if defined (MACOSX)
04780     static int last_check = 0;
04781     int current_check = GetTicks();
04782     if (current_check - last_check <=  DB_POLLSKIP) return;
04783     last_check = current_check;
04784 #endif
04785 
04786     SDL_Event event;
04787 #if defined (REDUCE_JOYSTICK_POLLING)
04788     static int poll_delay = 0;
04789     int time = GetTicks();
04790     if (time - poll_delay > 20) {
04791         poll_delay = time;
04792         if (sdl.num_joysticks > 0)
04793         {
04794             SDL_JoystickUpdate();
04795             MAPPER_UpdateJoysticks();
04796         }
04797     }
04798 #endif
04799 
04800     GFX_EventsMouse();
04801 
04802 #if C_EMSCRIPTEN
04803     emscripten_sleep_with_yield(0);
04804 #endif
04805 
04806     while (SDL_PollEvent(&event)) {
04807         switch (event.type) {
04808         case SDL_WINDOWEVENT:
04809             switch (event.window.event) {
04810             case SDL_WINDOWEVENT_RESTORED:
04811                 GFX_ResetScreen();
04812                 continue;
04813             case SDL_WINDOWEVENT_RESIZED:
04814                 GFX_HandleVideoResize(event.window.data1, event.window.data2);
04815                 continue;
04816             case SDL_WINDOWEVENT_EXPOSED:
04817                 if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw );
04818                 continue;
04819             case SDL_WINDOWEVENT_LEAVE:
04820 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
04821                 void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
04822                 void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
04823 
04824                 GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
04825                 GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
04826 
04827                 GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
04828 #endif
04829                 break;
04830             case SDL_WINDOWEVENT_FOCUS_GAINED:
04831                 if (IsFullscreen() && !sdl.mouse.locked)
04832                     GFX_CaptureMouse();
04833                 SetPriority(sdl.priority.focus);
04834                 CPU_Disable_SkipAutoAdjust();
04835                 break;
04836             case SDL_WINDOWEVENT_FOCUS_LOST:
04837                 if (sdl.mouse.locked) {
04838                     CaptureMouseNotify();
04839                     GFX_CaptureMouse();
04840                 }
04841 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
04842                 void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
04843                 void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
04844 
04845                 GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
04846                 GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
04847 
04848                 GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
04849 #endif
04850                 SetPriority(sdl.priority.nofocus);
04851                 GFX_LosingFocus();
04852                 CPU_Enable_SkipAutoAdjust();
04853                 break;
04854             default:
04855                 ;
04856             }
04857 
04858             /* Non-focus priority is set to pause; check to see if we've lost window or input focus
04859              * i.e. has the window been minimised or made inactive?
04860              */
04861             if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) {
04862                 if ((event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (event.window.event == SDL_WINDOWEVENT_MINIMIZED)) {
04863                     /* Window has lost focus, pause the emulator.
04864                      * This is similar to what PauseDOSBox() does, but the exit criteria is different.
04865                      * Instead of waiting for the user to hit Alt-Break, we wait for the window to
04866                      * regain window or input focus.
04867                      */
04868                     bool paused = true;
04869                     SDL_Event ev;
04870 
04871                     GFX_SetTitle(-1,-1,-1,true);
04872                     KEYBOARD_ClrBuffer();
04873 //                  SDL_Delay(500);
04874 //                  while (SDL_PollEvent(&ev)) {
04875                     // flush event queue.
04876 //                  }
04877 
04878                     while (paused) {
04879 #if C_EMSCRIPTEN
04880                         emscripten_sleep_with_yield(0);
04881                         SDL_PollEvent(&ev);
04882 #else
04883                         // WaitEvent waits for an event rather than polling, so CPU usage drops to zero
04884                         SDL_WaitEvent(&ev);
04885 #endif
04886 
04887                         switch (ev.type) {
04888                         case SDL_QUIT:
04889                             throw(0);
04890                             break; // a bit redundant at linux at least as the active events gets before the quit event.
04891                         case SDL_WINDOWEVENT:     // wait until we get window focus back
04892                             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)) {
04893                                 // We've got focus back, so unpause and break out of the loop
04894                                 if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) {
04895                                     paused = false;
04896                                     GFX_SetTitle(-1,-1,-1,false);
04897                                 }
04898 
04899                                 /* Now poke a "release ALT" command into the keyboard buffer
04900                                  * we have to do this, otherwise ALT will 'stick' and cause
04901                                  * problems with the app running in the DOSBox.
04902                                  */
04903                                 KEYBOARD_AddKey(KBD_leftalt, false);
04904                                 KEYBOARD_AddKey(KBD_rightalt, false);
04905                                 if (ev.window.event == SDL_WINDOWEVENT_RESTORED) {
04906                                     // We may need to re-create a texture and more
04907                                     GFX_ResetScreen();
04908                                 }
04909                             }
04910                             break;
04911                         }
04912                     }
04913                 }
04914             }
04915             break;
04916         case SDL_MOUSEMOTION:
04917 #if defined(C_SDL2)
04918             if (touchscreen_finger_lock == no_finger_id &&
04919                 touchscreen_touch_lock == no_touch_id &&
04920                 event.motion.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
04921                 HandleMouseMotion(&event.motion);
04922             }
04923 #else
04924             HandleMouseMotion(&event.motion);
04925 #endif
04926             break;
04927         case SDL_MOUSEBUTTONDOWN:
04928         case SDL_MOUSEBUTTONUP:
04929 #if defined(C_SDL2)
04930             if (touchscreen_finger_lock == no_finger_id &&
04931                 touchscreen_touch_lock == no_touch_id &&
04932                 event.button.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
04933                 HandleMouseButton(&event.button);
04934             }
04935 #else
04936             HandleMouseButton(&event.button);
04937 #endif
04938             break;
04939 #if !defined(IGNORE_TOUCHSCREEN)
04940         case SDL_FINGERDOWN:
04941         case SDL_FINGERUP:
04942         case SDL_FINGERMOTION:
04943             HandleTouchscreenFinger(&event.tfinger);
04944             break;
04945 #endif
04946         case SDL_QUIT:
04947             throw(0);
04948             break;
04949 #if defined (MACOSX)
04950         case SDL_KEYDOWN:
04951         case SDL_KEYUP:
04952             /* On macs CMD-Q is the default key to close an application */
04953             if (event.key.keysym.sym == SDLK_q &&
04954                     (event.key.keysym.mod == KMOD_RGUI ||
04955                      event.key.keysym.mod == KMOD_LGUI)
04956                ) {
04957                 KillSwitch(true);
04958                 break;
04959             }
04960 #endif
04961         default:
04962             gfx_in_mapper = true;
04963             void MAPPER_CheckEvent(SDL_Event * event);
04964             MAPPER_CheckEvent(&event);
04965             gfx_in_mapper = false;
04966         }
04967     }
04968 #else /* SDL 1.x---------------------------------- */
04969     SDL_Event event;
04970 #if defined (REDUCE_JOYSTICK_POLLING)
04971     static uint32_t poll_delay=0;
04972     uint32_t time=GetTicks();
04973     if ((int32_t)(time-poll_delay)>20) {
04974         poll_delay=time;
04975         if (sdl.num_joysticks>0)
04976         {
04977             SDL_JoystickUpdate();
04978             MAPPER_UpdateJoysticks();
04979         }
04980     }
04981 #endif
04982 
04983     GFX_EventsMouse();
04984 
04985 #if C_EMSCRIPTEN
04986     emscripten_sleep_with_yield(0);
04987 #endif
04988 
04989     while (SDL_PollEvent(&event)) {
04990         /* DOSBox SVN revision 4176:4177: For Linux/X11, Xorg 1.20.1
04991          * will make spurious focus gain and loss events when locking the mouse in windowed mode.
04992          *
04993          * This has not been tested with DOSBox-X yet becaus I do not run Xorg 1.20.1, yet */
04994 #if SDL_XORG_FIX
04995         // Special code for broken SDL with Xorg 1.20.1, where pairs of inputfocus gain and loss events are generated
04996         // when locking the mouse in windowed mode.
04997         if (event.type == SDL_ACTIVEEVENT && event.active.state == SDL_APPINPUTFOCUS && event.active.gain == 0) {
04998             SDL_Event test; //Check if the next event would undo this one.
04999             if (SDL_PeepEvents(&test,1,SDL_PEEKEVENT,SDL_ACTIVEEVENTMASK) == 1 && test.active.state == SDL_APPINPUTFOCUS && test.active.gain == 1) {
05000                 // Skip both events.
05001                 SDL_PeepEvents(&test,1,SDL_GETEVENT,SDL_ACTIVEEVENTMASK);
05002                 continue;
05003             }
05004         }
05005 #endif
05006         /* end patch fragment */
05007 
05008         switch (event.type) {
05009 #ifdef __WIN32__
05010         case SDL_SYSWMEVENT : {
05011             switch( event.syswm.msg->msg ) {
05012 #if !defined(HX_DOS)
05013                 case WM_COMMAND:
05014                     MSG_WM_COMMAND_handle(/*&*/(*event.syswm.msg));
05015                     break;
05016 #endif
05017                 case WM_SYSCOMMAND:
05018                     switch (event.syswm.msg->wParam) {
05019                         case 0xF032: // FIXME: What is this?
05020                         case SC_MAXIMIZE:
05021                             userResizeWindowWidth = 0;
05022                             userResizeWindowHeight = 0;
05023                             menu.maxwindow = true;
05024                             break;
05025                         case 0xF122: // FIXME: What is this?
05026                         case SC_RESTORE:
05027                             if (sdl.desktop.fullscreen)
05028                                 GFX_SwitchFullScreen();
05029                             menu.maxwindow = false;
05030                             UpdateWindowDimensions();
05031                             RENDER_Reset();
05032                             if (OpenGL_using()) {
05033                                 UpdateWindowDimensions();
05034                                 RENDER_Reset();
05035                             }
05036                             break;
05037                         case ID_WIN_SYSMENU_RESTOREMENU:
05038                             /* prevent removing the menu in 3Dfx mode */
05039                             if (!GFX_GetPreventFullscreen()) {
05040                                 DOSBox_SetMenu();
05041                                 mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
05042                             }
05043                             break;
05044                         case ID_WIN_SYSMENU_TOGGLEMENU:
05045                             /* prevent removing the menu in 3Dfx mode */
05046                             if (!GFX_GetPreventFullscreen())
05047                             {
05048                                 if (menu.toggle) DOSBox_NoMenu(); else DOSBox_SetMenu();
05049                                 mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
05050                             }
05051                             break;
05052 #if !defined(HX_DOS)
05053                         case ID_WIN_SYSMENU_MAPPER:
05054                             extern void MAPPER_Run(bool pressed);
05055                             MAPPER_Run(false);
05056                             break;
05057                         case ID_WIN_SYSMENU_CFG_GUI:
05058                             extern void GUI_Run(bool pressed);
05059                             GUI_Run(false);
05060                             break;
05061                         case ID_WIN_SYSMENU_PAUSE:
05062                             extern void PauseDOSBox(bool pressed);
05063                             PauseDOSBox(true);
05064                             break;
05065 #endif
05066                     }
05067                 default:
05068                     break;
05069             }
05070         }
05071 #endif
05072         case SDL_ACTIVEEVENT:
05073                 if (event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) {
05074                     if (event.active.gain) {
05075 #ifdef WIN32
05076                         if (!sdl.desktop.fullscreen) sdl.focus_ticks = GetTicks();
05077 #endif
05078                         if (sdl.desktop.fullscreen && !sdl.mouse.locked)
05079                             GFX_CaptureMouse();
05080                         SetPriority(sdl.priority.focus);
05081                         CPU_Disable_SkipAutoAdjust();
05082                         BIOS_SynchronizeNumLock();
05083                         BIOS_SynchronizeCapsLock();
05084                         BIOS_SynchronizeScrollLock();
05085                     } else {
05086                         if (sdl.mouse.locked)
05087                         {
05088                             CaptureMouseNotify();
05089                             GFX_CaptureMouse();
05090                         }
05091 
05092 #if defined(WIN32)
05093                         if (sdl.desktop.fullscreen)
05094                             GFX_ForceFullscreenExit();
05095 #endif
05096 
05097 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
05098                         void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
05099                         void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
05100 
05101                         GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
05102                         GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
05103 #endif
05104 
05105                         SetPriority(sdl.priority.nofocus);
05106                         GFX_LosingFocus();
05107                         CPU_Enable_SkipAutoAdjust();
05108                     }
05109                 }
05110 
05111 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
05112                 if (event.active.state & SDL_APPMOUSEFOCUS) {
05113                     if (!(event.active.gain & SDL_APPMOUSEFOCUS)) {
05114                         /* losing focus or moving the mouse outside the window should un-hilight the currently selected menu item */
05115                         void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
05116                         void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
05117 
05118                         GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
05119                         GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
05120 
05121                     }
05122                 }
05123 
05124                 GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
05125 #endif
05126 
05127                 /* Non-focus priority is set to pause; check to see if we've lost window or input focus
05128                  * i.e. has the window been minimised or made inactive?
05129                  */
05130                 if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) {
05131                     if ((event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) && (!event.active.gain)) {
05132                     /* Window has lost focus, pause the emulator.
05133                      * This is similar to what PauseDOSBox() does, but the exit criteria is different.
05134                      * Instead of waiting for the user to hit Alt-Break, we wait for the window to
05135                      * regain window or input focus.
05136                      */
05137                     bool paused = true;
05138                     SDL_Event ev;
05139 
05140                     GFX_SetTitle(-1,-1,-1,true);
05141                     KEYBOARD_ClrBuffer();
05142 //                  SDL_Delay(500);
05143 //                  while (SDL_PollEvent(&ev)) {
05144                         // flush event queue.
05145 //                  }
05146 
05147                     while (paused) {
05148 #if C_EMSCRIPTEN
05149                         emscripten_sleep_with_yield(0);
05150                         SDL_PollEvent(&ev);
05151 #else
05152                         // WaitEvent waits for an event rather than polling, so CPU usage drops to zero
05153                         SDL_WaitEvent(&ev);
05154 #endif
05155 
05156                         switch (ev.type) {
05157                         case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event.
05158                         case SDL_ACTIVEEVENT:     // wait until we get window focus back
05159                             if (ev.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) {
05160                                 // We've got focus back, so unpause and break out of the loop
05161                                 if (ev.active.gain) {
05162                                     paused = false;
05163                                     GFX_SetTitle(-1,-1,-1,false);
05164                                 }
05165 
05166                                 /* Now poke a "release ALT" command into the keyboard buffer
05167                                  * we have to do this, otherwise ALT will 'stick' and cause
05168                                  * problems with the app running in the DOSBox.
05169                                  */
05170                                 KEYBOARD_AddKey(KBD_leftalt, false);
05171                                 KEYBOARD_AddKey(KBD_rightalt, false);
05172                             }
05173                             break;
05174                         }
05175                     }
05176                 }
05177             }
05178             break;
05179         case SDL_MOUSEMOTION:
05180             HandleMouseMotion(&event.motion);
05181             break;
05182         case SDL_MOUSEBUTTONDOWN:
05183         case SDL_MOUSEBUTTONUP:
05184             HandleMouseButton(&event.button);
05185             break;
05186         case SDL_VIDEORESIZE:
05187             UpdateWindowDimensions(); // FIXME: Use SDL window dimensions, except that on Windows, SDL won't tell us our actual dimensions
05188             HandleVideoResize(&event.resize);
05189             break;
05190         case SDL_QUIT:
05191             throw(0);
05192             break;
05193         case SDL_VIDEOEXPOSE:
05194             if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw );
05195             break;
05196 #ifdef WIN32
05197         case SDL_KEYDOWN:
05198         case SDL_KEYUP:
05199             // ignore event alt+tab
05200             if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type;
05201             if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type;
05202             if (((event.key.keysym.sym==SDLK_TAB)) &&
05203                 ((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) { MAPPER_LosingFocus(); break; }
05204             // This can happen as well.
05205             if (((event.key.keysym.sym == SDLK_TAB )) && (event.key.keysym.mod & KMOD_ALT)) break;
05206             // ignore tab events that arrive just after regaining focus. (likely the result of alt-tab)
05207             if ((event.key.keysym.sym == SDLK_TAB) && (GetTicks() - sdl.focus_ticks < 2)) break;
05208 #endif
05209 #if defined (MACOSX)            
05210         case SDL_KEYDOWN:
05211         case SDL_KEYUP:
05212             /* On macs CMD-Q is the default key to close an application */
05213             if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
05214                 KillSwitch(true);
05215                 break;
05216             } 
05217 #endif
05218         default:
05219             void MAPPER_CheckEvent(SDL_Event * event);
05220             MAPPER_CheckEvent(&event);
05221         }
05222     }
05223     // start emendelson from dbDOS
05224     // Disabled multiple characters per dispatch b/c occasionally
05225     // keystrokes get lost in the spew. (Prob b/c of DI usage on Win32, sadly..)
05226     // while (PasteClipboardNext());
05227     // Doesn't really matter though, it's fast enough as it is...
05228     static Bitu iPasteTicker = 0;
05229     if ((iPasteTicker++ % 20) == 0) // emendelson: was %2, %20 is good for WP51
05230         PasteClipboardNext();   // end added emendelson from dbDOS
05231 #endif
05232 }
05233 
05234 // added emendelson from dbDos
05235 #if defined(WIN32) && !defined(C_SDL2) && !defined(__MINGW32__)
05236 #include <cassert>
05237 
05238 // Ripped from SDL's SDL_dx5events.c, since there's no API to access it...
05239 #define DIRECTINPUT_VERSION 0x0800
05240 #include <dinput.h>
05241 #ifndef DIK_PAUSE
05242 #define DIK_PAUSE   0xC5
05243 #endif
05244 #ifndef DIK_OEM_102
05245 #define DIK_OEM_102 0x56    /* < > | on UK/Germany keyboards */
05246 #endif
05247 static SDLKey aryScanCodeToSDLKey[0xFF];
05248 static bool   bScanCodeMapInited = false;
05249 static void PasteInitMapSCToSDLKey()
05250 {
05251     /* Map the DIK scancodes to SDL keysyms */
05252     for (int i = 0; i<SDL_arraysize(aryScanCodeToSDLKey); ++i)
05253         aryScanCodeToSDLKey[i] = SDLK_UNKNOWN;
05254 
05255     /* Defined DIK_* constants */
05256     aryScanCodeToSDLKey[DIK_ESCAPE] = SDLK_ESCAPE;
05257     aryScanCodeToSDLKey[DIK_1] = SDLK_1;
05258     aryScanCodeToSDLKey[DIK_2] = SDLK_2;
05259     aryScanCodeToSDLKey[DIK_3] = SDLK_3;
05260     aryScanCodeToSDLKey[DIK_4] = SDLK_4;
05261     aryScanCodeToSDLKey[DIK_5] = SDLK_5;
05262     aryScanCodeToSDLKey[DIK_6] = SDLK_6;
05263     aryScanCodeToSDLKey[DIK_7] = SDLK_7;
05264     aryScanCodeToSDLKey[DIK_8] = SDLK_8;
05265     aryScanCodeToSDLKey[DIK_9] = SDLK_9;
05266     aryScanCodeToSDLKey[DIK_0] = SDLK_0;
05267     aryScanCodeToSDLKey[DIK_MINUS] = SDLK_MINUS;
05268     aryScanCodeToSDLKey[DIK_EQUALS] = SDLK_EQUALS;
05269     aryScanCodeToSDLKey[DIK_BACK] = SDLK_BACKSPACE;
05270     aryScanCodeToSDLKey[DIK_TAB] = SDLK_TAB;
05271     aryScanCodeToSDLKey[DIK_Q] = SDLK_q;
05272     aryScanCodeToSDLKey[DIK_W] = SDLK_w;
05273     aryScanCodeToSDLKey[DIK_E] = SDLK_e;
05274     aryScanCodeToSDLKey[DIK_R] = SDLK_r;
05275     aryScanCodeToSDLKey[DIK_T] = SDLK_t;
05276     aryScanCodeToSDLKey[DIK_Y] = SDLK_y;
05277     aryScanCodeToSDLKey[DIK_U] = SDLK_u;
05278     aryScanCodeToSDLKey[DIK_I] = SDLK_i;
05279     aryScanCodeToSDLKey[DIK_O] = SDLK_o;
05280     aryScanCodeToSDLKey[DIK_P] = SDLK_p;
05281     aryScanCodeToSDLKey[DIK_LBRACKET] = SDLK_LEFTBRACKET;
05282     aryScanCodeToSDLKey[DIK_RBRACKET] = SDLK_RIGHTBRACKET;
05283     aryScanCodeToSDLKey[DIK_RETURN] = SDLK_RETURN;
05284     aryScanCodeToSDLKey[DIK_LCONTROL] = SDLK_LCTRL;
05285     aryScanCodeToSDLKey[DIK_A] = SDLK_a;
05286     aryScanCodeToSDLKey[DIK_S] = SDLK_s;
05287     aryScanCodeToSDLKey[DIK_D] = SDLK_d;
05288     aryScanCodeToSDLKey[DIK_F] = SDLK_f;
05289     aryScanCodeToSDLKey[DIK_G] = SDLK_g;
05290     aryScanCodeToSDLKey[DIK_H] = SDLK_h;
05291     aryScanCodeToSDLKey[DIK_J] = SDLK_j;
05292     aryScanCodeToSDLKey[DIK_K] = SDLK_k;
05293     aryScanCodeToSDLKey[DIK_L] = SDLK_l;
05294     aryScanCodeToSDLKey[DIK_SEMICOLON] = SDLK_SEMICOLON;
05295     aryScanCodeToSDLKey[DIK_APOSTROPHE] = SDLK_QUOTE;
05296     aryScanCodeToSDLKey[DIK_GRAVE] = SDLK_BACKQUOTE;
05297     aryScanCodeToSDLKey[DIK_LSHIFT] = SDLK_LSHIFT;
05298     aryScanCodeToSDLKey[DIK_BACKSLASH] = SDLK_BACKSLASH;
05299     aryScanCodeToSDLKey[DIK_OEM_102] = SDLK_LESS;
05300     aryScanCodeToSDLKey[DIK_Z] = SDLK_z;
05301     aryScanCodeToSDLKey[DIK_X] = SDLK_x;
05302     aryScanCodeToSDLKey[DIK_C] = SDLK_c;
05303     aryScanCodeToSDLKey[DIK_V] = SDLK_v;
05304     aryScanCodeToSDLKey[DIK_B] = SDLK_b;
05305     aryScanCodeToSDLKey[DIK_N] = SDLK_n;
05306     aryScanCodeToSDLKey[DIK_M] = SDLK_m;
05307     aryScanCodeToSDLKey[DIK_COMMA] = SDLK_COMMA;
05308     aryScanCodeToSDLKey[DIK_PERIOD] = SDLK_PERIOD;
05309     aryScanCodeToSDLKey[DIK_SLASH] = SDLK_SLASH;
05310     aryScanCodeToSDLKey[DIK_RSHIFT] = SDLK_RSHIFT;
05311     aryScanCodeToSDLKey[DIK_MULTIPLY] = SDLK_KP_MULTIPLY;
05312     aryScanCodeToSDLKey[DIK_LMENU] = SDLK_LALT;
05313     aryScanCodeToSDLKey[DIK_SPACE] = SDLK_SPACE;
05314     aryScanCodeToSDLKey[DIK_CAPITAL] = SDLK_CAPSLOCK;
05315     aryScanCodeToSDLKey[DIK_F1] = SDLK_F1;
05316     aryScanCodeToSDLKey[DIK_F2] = SDLK_F2;
05317     aryScanCodeToSDLKey[DIK_F3] = SDLK_F3;
05318     aryScanCodeToSDLKey[DIK_F4] = SDLK_F4;
05319     aryScanCodeToSDLKey[DIK_F5] = SDLK_F5;
05320     aryScanCodeToSDLKey[DIK_F6] = SDLK_F6;
05321     aryScanCodeToSDLKey[DIK_F7] = SDLK_F7;
05322     aryScanCodeToSDLKey[DIK_F8] = SDLK_F8;
05323     aryScanCodeToSDLKey[DIK_F9] = SDLK_F9;
05324     aryScanCodeToSDLKey[DIK_F10] = SDLK_F10;
05325     aryScanCodeToSDLKey[DIK_NUMLOCK] = SDLK_NUMLOCK;
05326     aryScanCodeToSDLKey[DIK_SCROLL] = SDLK_SCROLLOCK;
05327     aryScanCodeToSDLKey[DIK_NUMPAD7] = SDLK_KP7;
05328     aryScanCodeToSDLKey[DIK_NUMPAD8] = SDLK_KP8;
05329     aryScanCodeToSDLKey[DIK_NUMPAD9] = SDLK_KP9;
05330     aryScanCodeToSDLKey[DIK_SUBTRACT] = SDLK_KP_MINUS;
05331     aryScanCodeToSDLKey[DIK_NUMPAD4] = SDLK_KP4;
05332     aryScanCodeToSDLKey[DIK_NUMPAD5] = SDLK_KP5;
05333     aryScanCodeToSDLKey[DIK_NUMPAD6] = SDLK_KP6;
05334     aryScanCodeToSDLKey[DIK_ADD] = SDLK_KP_PLUS;
05335     aryScanCodeToSDLKey[DIK_NUMPAD1] = SDLK_KP1;
05336     aryScanCodeToSDLKey[DIK_NUMPAD2] = SDLK_KP2;
05337     aryScanCodeToSDLKey[DIK_NUMPAD3] = SDLK_KP3;
05338     aryScanCodeToSDLKey[DIK_NUMPAD0] = SDLK_KP0;
05339     aryScanCodeToSDLKey[DIK_DECIMAL] = SDLK_KP_PERIOD;
05340     aryScanCodeToSDLKey[DIK_F11] = SDLK_F11;
05341     aryScanCodeToSDLKey[DIK_F12] = SDLK_F12;
05342 
05343     aryScanCodeToSDLKey[DIK_F13] = SDLK_F13;
05344     aryScanCodeToSDLKey[DIK_F14] = SDLK_F14;
05345     aryScanCodeToSDLKey[DIK_F15] = SDLK_F15;
05346 
05347     aryScanCodeToSDLKey[DIK_NUMPADEQUALS] = SDLK_KP_EQUALS;
05348     aryScanCodeToSDLKey[DIK_NUMPADENTER] = SDLK_KP_ENTER;
05349     aryScanCodeToSDLKey[DIK_RCONTROL] = SDLK_RCTRL;
05350     aryScanCodeToSDLKey[DIK_DIVIDE] = SDLK_KP_DIVIDE;
05351     aryScanCodeToSDLKey[DIK_SYSRQ] = SDLK_PRINT;
05352     aryScanCodeToSDLKey[DIK_RMENU] = SDLK_RALT;
05353     aryScanCodeToSDLKey[DIK_PAUSE] = SDLK_PAUSE;
05354     aryScanCodeToSDLKey[DIK_HOME] = SDLK_HOME;
05355     aryScanCodeToSDLKey[DIK_UP] = SDLK_UP;
05356     aryScanCodeToSDLKey[DIK_PRIOR] = SDLK_PAGEUP;
05357     aryScanCodeToSDLKey[DIK_LEFT] = SDLK_LEFT;
05358     aryScanCodeToSDLKey[DIK_RIGHT] = SDLK_RIGHT;
05359     aryScanCodeToSDLKey[DIK_END] = SDLK_END;
05360     aryScanCodeToSDLKey[DIK_DOWN] = SDLK_DOWN;
05361     aryScanCodeToSDLKey[DIK_NEXT] = SDLK_PAGEDOWN;
05362     aryScanCodeToSDLKey[DIK_INSERT] = SDLK_INSERT;
05363     aryScanCodeToSDLKey[DIK_DELETE] = SDLK_DELETE;
05364     aryScanCodeToSDLKey[DIK_LWIN] = SDLK_LMETA;
05365     aryScanCodeToSDLKey[DIK_RWIN] = SDLK_RMETA;
05366     aryScanCodeToSDLKey[DIK_APPS] = SDLK_MENU;
05367 
05368     bScanCodeMapInited = true;
05369 }
05370 
05371 static std::string strPasteBuffer;
05372 // Just in case, to keep us from entering an unexpected KB state
05373 const  size_t      kPasteMinBufExtra = 4;
05375 static void GenKBStroke(const UINT uiScanCode, const bool bDepressed, const SDLMod keymods)
05376 {
05377     const SDLKey sdlkey = aryScanCodeToSDLKey[uiScanCode];
05378     if (sdlkey == SDLK_UNKNOWN)
05379         return;
05380 
05381     SDL_Event evntKeyStroke = { 0 };
05382     evntKeyStroke.type = bDepressed ? SDL_KEYDOWN : SDL_KEYUP;
05383     evntKeyStroke.key.keysym.scancode = (unsigned char)LOBYTE(uiScanCode);
05384     evntKeyStroke.key.keysym.sym = sdlkey;
05385     evntKeyStroke.key.keysym.mod = keymods;
05386     evntKeyStroke.key.keysym.unicode = 0;
05387     evntKeyStroke.key.state = bDepressed ? SDL_PRESSED : SDL_RELEASED;
05388     SDL_PushEvent(&evntKeyStroke);
05389 }
05390 
05391 static bool PasteClipboardNext()
05392 {
05393     if (strPasteBuffer.length() == 0)
05394         return false;
05395 
05396     if (!bScanCodeMapInited)
05397         PasteInitMapSCToSDLKey();
05398 
05399     const char cKey = strPasteBuffer[0];
05400     SHORT shVirKey = VkKeyScan(cKey); // If it fails then MapVirtK will also fail, so no bail yet
05401     UINT uiScanCode = MapVirtualKey(LOBYTE(shVirKey), MAPVK_VK_TO_VSC);
05402     if (uiScanCode)
05403     {
05404         const bool   bModShift = ((shVirKey & 0x0100) != 0);
05405         const bool   bModCntrl = ((shVirKey & 0x0200) != 0);
05406         const bool   bModAlt = ((shVirKey & 0x0400) != 0);
05407         const SDLMod sdlmModsOn = SDL_GetModState();
05408         const bool   bModShiftOn = ((sdlmModsOn & (KMOD_LSHIFT | KMOD_RSHIFT)) > 0);
05409         const bool   bModCntrlOn = ((sdlmModsOn & (KMOD_LCTRL | KMOD_RCTRL)) > 0);
05410         const bool   bModAltOn = ((sdlmModsOn & (KMOD_LALT | KMOD_RALT)) > 0);
05411         const UINT   uiScanCodeShift = MapVirtualKey(VK_SHIFT, MAPVK_VK_TO_VSC);
05412         const UINT   uiScanCodeCntrl = MapVirtualKey(VK_CONTROL, MAPVK_VK_TO_VSC);
05413         const UINT   uiScanCodeAlt = MapVirtualKey(VK_MENU, MAPVK_VK_TO_VSC);
05414         const SDLMod sdlmMods = (SDLMod)((sdlmModsOn & ~(KMOD_LSHIFT | KMOD_RSHIFT |
05415             KMOD_LCTRL | KMOD_RCTRL |
05416             KMOD_LALT | KMOD_RALT)) |
05417             (bModShiftOn ? KMOD_LSHIFT : 0) |
05418             (bModCntrlOn ? KMOD_LCTRL : 0) |
05419             (bModAltOn ? KMOD_LALT : 0));
05420 
05423         // Could be made more efficient, but would require tracking of more state,
05424         // so let's forgot that for now...
05425         size_t sStrokesRequired = 2; // At least the key & up/down
05426         if (bModShift != bModShiftOn) sStrokesRequired += 2; // To press/release Shift
05427         if (bModCntrl != bModCntrlOn) sStrokesRequired += 2; // To press/release Control
05428         if (bModAlt != bModAltOn) sStrokesRequired += 2; // To press/release Alt
05433         if (KEYBOARD_BufferSpaceAvail() < (sStrokesRequired + kPasteMinBufExtra))
05434             return false;
05435 
05436         if (bModShift != bModShiftOn) GenKBStroke(uiScanCodeShift, !bModShiftOn, sdlmMods);
05437         if (bModCntrl != bModCntrlOn) GenKBStroke(uiScanCodeCntrl, !bModCntrlOn, sdlmMods);
05438         if (bModAlt != bModAltOn) GenKBStroke(uiScanCodeAlt, !bModAltOn, sdlmMods);
05439         GenKBStroke(uiScanCode, true, sdlmMods);
05440         GenKBStroke(uiScanCode, false, sdlmMods);
05441         if (bModShift != bModShiftOn) GenKBStroke(uiScanCodeShift, bModShiftOn, sdlmMods);
05442         if (bModCntrl != bModCntrlOn) GenKBStroke(uiScanCodeCntrl, bModCntrlOn, sdlmMods);
05443         if (bModAlt != bModAltOn) GenKBStroke(uiScanCodeAlt, bModAltOn, sdlmMods);
05444         //putchar(cKey); // For debugging dropped strokes
05445     }
05446 
05447     // Pop head. Could be made more efficient, but this is neater.
05448     strPasteBuffer = strPasteBuffer.substr(1, strPasteBuffer.length()); // technically -1, but it clamps by itself anyways...
05449     return true;
05450 }
05451 
05452 void PasteClipboard(bool bPressed)
05453 {
05454     if (!bPressed) return;
05455     SDL_SysWMinfo wmiInfo;
05456     SDL_VERSION(&wmiInfo.version);
05457 
05458     if (SDL_GetWMInfo(&wmiInfo) != 1) return;
05459     if (!::OpenClipboard(wmiInfo.window)) return;
05460     if (!::IsClipboardFormatAvailable(CF_TEXT)) return;
05461 
05462     HANDLE hContents = ::GetClipboardData(CF_TEXT);
05463     if (!hContents) return;
05464 
05465     const char* szClipboard = (const char*)::GlobalLock(hContents);
05466     if (szClipboard)
05467     {
05468         // Create a copy of the string, and filter out Linefeed characters (ASCII '10')
05469         size_t sClipboardLen = strlen(szClipboard);
05470         char* szFilteredText = reinterpret_cast<char*>(alloca(sClipboardLen + 1));
05471         char* szFilterNextChar = szFilteredText;
05472         for (size_t i = 0; i < sClipboardLen; ++i)
05473             if (szClipboard[i] != 0x0A) // Skip linefeeds
05474             {
05475                 *szFilterNextChar = szClipboard[i];
05476                 ++szFilterNextChar;
05477             }
05478         *szFilterNextChar = '\0'; // Cap it.
05479 
05480         strPasteBuffer.append(szFilteredText);
05481         ::GlobalUnlock(hContents);
05482     }
05483 
05484     ::CloseClipboard();
05485 }
05487 #else // end emendelson from dbDOS
05488 void PasteClipboard(bool bPressed) {
05489     (void)bPressed;//UNUSED
05490     // stub
05491 }
05492 
05493 # if !defined(C_SDL2)
05494 bool PasteClipboardNext() {
05495     // stub
05496     return false;
05497 }
05498 # endif
05499 #endif
05500 
05501 
05502 #if defined (WIN32)
05503 static BOOL WINAPI ConsoleEventHandler(DWORD event) {
05504     switch (event) {
05505     case CTRL_SHUTDOWN_EVENT:
05506     case CTRL_LOGOFF_EVENT:
05507     case CTRL_CLOSE_EVENT:
05508     case CTRL_BREAK_EVENT:
05509         raise(SIGTERM);
05510         return TRUE;
05511     case CTRL_C_EVENT:
05512     default: //pass to the next handler
05513         return FALSE;
05514     }
05515 }
05516 #endif
05517 
05518 void Null_Init(Section *sec);
05519 
05520 void SDL_OnSectionPropChange(Section *x) {
05521     (void)x;//UNUSED
05522     Section_prop * section = static_cast<Section_prop *>(control->GetSection("sdl"));
05523 
05524     {
05525         bool cfg_want_menu = section->Get_bool("showmenu");
05526 
05527         /* -- -- decide whether to set menu */
05528         if (menu_gui && !control->opt_nomenu && cfg_want_menu)
05529             DOSBox_SetMenu();
05530         else
05531             DOSBox_NoMenu();
05532     }
05533 }
05534 
05535 void SDL_SetupConfigSection() {
05536     Section_prop * sdl_sec=control->AddSection_prop("sdl",&Null_Init);
05537 
05538     Prop_bool* Pbool;
05539     Prop_string* Pstring;
05540     Prop_int* Pint;
05541     Prop_multival* Pmulti;
05542 
05543     Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false);
05544     Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)");
05545      
05546     Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false);
05547     Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox.");
05548 
05549     //Pbool = sdl_sec->Add_bool("sdlresize",Property::Changeable::Always,false);
05550     //Pbool->Set_help("Makes window resizable (depends on scalers)");
05551 
05552     Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"desktop");
05553     Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n"
05554                       "  Using your monitor's native resolution with aspect=true might give the best results.\n"
05555               "  If you end up with small window on a large screen, try an output different from surface.");
05556 
05557     Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original");
05558     Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n"
05559                       "  (output=surface does not!)");
05560 
05561     const char* outputs[] = {
05562         "surface", "overlay",
05563 #if C_OPENGL
05564         "opengl", "openglnb", "openglhq",
05565 #endif
05566         "ddraw",
05567 #if C_DIRECT3D
05568         "direct3d",
05569 #endif
05570         0 };
05571 #ifdef __WIN32__
05572 # if defined(HX_DOS)
05573         Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "surface"); /* HX DOS should stick to surface */
05574 # elif defined(__MINGW32__) && !(C_DIRECT3D) && !defined(C_SDL2)
05575         /* NTS: OpenGL output never seems to work in VirtualBox under Windows XP */
05576         Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, isVirtualBox ? "surface" : "opengl"); /* MinGW builds do not yet have Direct3D */
05577 # else
05578         Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "direct3d");
05579 # endif
05580 #elif defined(MACOSX) && defined(C_OPENGL) && !defined(C_SDL2)
05581         /* NTS: Lately, especially on Macbooks with Retina displays, OpenGL gives better performance
05582                 than the CG bitmap-based "surface" output.
05583 
05584                 On my dev system, "top" shows a 20% CPU load doing 1:1 CG Bitmap output to the screen
05585                 on a High DPI setup, while ignoring High DPI and rendering through Cocoa at 2x size
05586                 pegs the CPU core DOSBox-X is running on at 100% and emulation stutters.
05587 
05588                 So for the best experience, default to OpenGL.
05589 
05590                 Note that "surface" yields good performance with SDL2 and OS X because SDL2 doesn't
05591                 use the CGBitmap system, it uses OpenGL or Metal underneath automatically. */
05592         Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "opengl");
05593 #else
05594         Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "surface");
05595 #endif
05596     Pstring->Set_help("What video system to use for output.");
05597     Pstring->Set_values(outputs);
05598 
05599     Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always, false);
05600     Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)");
05601 
05602     const char* feeds[] = { "none", "beep", "flash", nullptr};
05603     Pstring = sdl_sec->Add_string("autolock_feedback", Property::Changeable::Always, feeds[1]);
05604     Pstring->Set_help("Autolock status feedback type, i.e. visual, auditive, none.");
05605     Pstring->Set_values(feeds);
05606 
05607     Pmulti = sdl_sec->Add_multi("sensitivity",Property::Changeable::Always, ",");
05608     Pmulti->Set_help("Mouse sensitivity. The optional second parameter specifies vertical sensitivity (e.g. 100,-50).");
05609     Pmulti->SetValue("100");
05610     Pint = Pmulti->GetSection()->Add_int("xsens",Property::Changeable::Always,100);
05611     Pint->SetMinMax(-1000,1000);
05612     Pint = Pmulti->GetSection()->Add_int("ysens",Property::Changeable::Always,100);
05613     Pint->SetMinMax(-1000,1000);
05614 
05615     const char * emulation[] = {"integration", "locked", "always", "never", nullptr};
05616     Pstring  = sdl_sec->Add_string("mouse_emulation", Property::Changeable::Always, emulation[1]);
05617     Pstring->Set_help(
05618         "When is mouse emulated ?\n"
05619         "integration: when not locked\n"
05620         "locked:      when locked\n"
05621         "always:      every time\n"
05622         "never:       at no time\n"
05623         "If disabled, the mouse position in DOSBox-X is exactly where the host OS reports it.\n"
05624         "When using a high DPI mouse, the emulation of mouse movement can noticeably reduce the\n"
05625         "sensitiveness of your device, i.e. the mouse is slower but more precise.");
05626     Pstring->Set_values(emulation);
05627 
05628     Pbool = sdl_sec->Add_bool("waitonerror",Property::Changeable::Always, true);
05629     Pbool->Set_help("Wait before closing the console if dosbox has an error.");
05630 
05631     Pmulti = sdl_sec->Add_multi("priority", Property::Changeable::Always, ",");
05632     Pmulti->SetValue("higher,normal",/*init*/true);
05633     Pmulti->Set_help("Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized.\n"
05634                      "  pause is only valid for the second entry.");
05635 
05636     const char* actt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0};
05637     Pstring = Pmulti->GetSection()->Add_string("active",Property::Changeable::Always,"higher");
05638     Pstring->Set_values(actt);
05639 
05640     const char* inactt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0};
05641     Pstring = Pmulti->GetSection()->Add_string("inactive",Property::Changeable::Always,"normal");
05642     Pstring->Set_values(inactt);
05643 
05644     Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE);
05645     Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the default value.");
05646 
05647 #if C_DIRECT3D && C_D3DSHADERS
05648     Pmulti = sdl_sec->Add_multi("pixelshader",Property::Changeable::Always," ");
05649     Pmulti->SetValue("none",/*init*/true);
05650     Pmulti->Set_help("Pixelshader program (effect file must be in Shaders subdirectory). If 'forced' is appended,\n"
05651         "then the shader will be used even if the result might not be desired.");
05652 
05653     Pstring = Pmulti->GetSection()->Add_string("type",Property::Changeable::Always,"none");
05654     Pstring = Pmulti->GetSection()->Add_string("force",Property::Changeable::Always,"");
05655 #endif
05656 
05657     Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,false);
05658     Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems.");
05659 
05660     Pint = sdl_sec->Add_int("overscan",Property::Changeable::Always, 0);
05661     Pint->SetMinMax(0,10);
05662     Pint->Set_help("Width of overscan border (0 to 10). (works only if output=surface)");
05663 
05664     Pstring = sdl_sec->Add_string("titlebar", Property::Changeable::Always, "");
05665     Pstring->Set_help("Change the string displayed in the DOSBox title bar.");
05666 
05667     Pbool = sdl_sec->Add_bool("showmenu", Property::Changeable::Always, true);
05668     Pbool->Set_help("Whether to show the menu bar (if supported). Default true.");
05669 
05670 //  Pint = sdl_sec->Add_int("overscancolor",Property::Changeable::Always, 0);
05671 //  Pint->SetMinMax(0,1000);
05672 //  Pint->Set_help("Value of overscan color.");
05673 }
05674 
05675 static void show_warning(char const * const message) {
05676     bool textonly = true;
05677 #ifdef WIN32
05678     textonly = false;
05679     if ( !sdl.inited && SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE) < 0 ) textonly = true;
05680     sdl.inited = true;
05681 #endif
05682     LOG_MSG( "Warning: %s", message);
05683     if(textonly) return;
05684 #if defined(C_SDL2)
05685     if (!sdl.window)
05686         if (!GFX_SetSDLSurfaceWindow(640,400)) return;
05687     sdl.surface = SDL_GetWindowSurface(sdl.window);
05688 #else
05689     if(!sdl.surface) sdl.surface = SDL_SetVideoMode(640,400,0,SDL_RESIZABLE);
05690     sdl.deferred_resize = false;
05691     sdl.must_redraw_all = true;
05692 #endif
05693     if(!sdl.surface) return;
05694 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
05695     Bit32u rmask = 0xff000000;
05696     Bit32u gmask = 0x00ff0000;
05697     Bit32u bmask = 0x0000ff00;
05698 #else
05699     Bit32u rmask = 0x000000ff;
05700     Bit32u gmask = 0x0000ff00;                    
05701     Bit32u bmask = 0x00ff0000;
05702 #endif
05703     SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0);
05704     if (!splash_surf) return;
05705 
05706     int x = 120,y = 20;
05707     std::string m(message),m2;
05708     std::string::size_type a,b,c,d;
05709    
05710     while(m.size()) { //Max 50 characters. break on space before or on a newline
05711         c = m.find('\n');
05712         d = m.rfind(' ',50);
05713         if(c>d) a=b=d; else a=b=c;
05714         if( a != std::string::npos) b++; 
05715         m2 = m.substr(0,a); m.erase(0,b);
05716         OutputString((unsigned int)x,(unsigned int)y,m2.c_str(),0xffffffffu,0,splash_surf);
05717         y += 20;
05718     }
05719    
05720     SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
05721 #if defined(C_SDL2)
05722     SDL_UpdateWindowSurface(sdl.window);
05723 #else
05724     SDL_Flip(sdl.surface);
05725 #endif
05726     SDL_Delay(12000);
05727 }
05728    
05729 static void launcheditor(std::string edit) {
05730     std::string path,file;
05731     Cross::CreatePlatformConfigDir(path);
05732     Cross::GetPlatformConfigName(file);
05733     path += file;
05734     FILE* f = fopen(path.c_str(),"r");
05735     if(!f && !control->PrintConfig(path.c_str())) {
05736         printf("tried creating %s. but failed.\n",path.c_str());
05737         exit(1);
05738     }
05739     if(f) fclose(f);
05740 
05741     execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
05742 
05743     //if you get here the launching failed!
05744     printf("can't find editor(s) specified at the command line.\n");
05745     exit(1);
05746 }
05747 #if C_DEBUG
05748 extern void DEBUG_ShutDown(Section * /*sec*/);
05749 #endif
05750 
05751 static void launchcaptures(std::string const& edit) {
05752     std::string path,file;
05753     struct stat cstat;
05754     Section* t = control->GetSection("dosbox");
05755     if(t) file = t->GetPropValue("captures");
05756     if(!t || file == NO_SUCH_PROPERTY) {
05757         printf("Config system messed up.\n");
05758         exit(1);
05759     }
05760     path = ".";
05761     path += CROSS_FILESPLIT;
05762     path += file;
05763 
05764     stat(path.c_str(),&cstat);
05765     if(cstat.st_mode & S_IFDIR) {
05766         execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
05767         //if you get here the launching failed!
05768         printf("can't find filemanager %s\n",edit.c_str());
05769         exit(1);
05770     } else {
05771         path = "";
05772         Cross::CreatePlatformConfigDir(path);
05773         path += file;
05774         Cross::CreateDir(path);
05775         stat(path.c_str(),&cstat);
05776         if((cstat.st_mode & S_IFDIR) == 0) {
05777             printf("%s doesn't exist or isn't a directory.\n",path.c_str());
05778             exit(1);
05779         }
05780         execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
05781         //if you get here the launching failed!
05782         printf("can't find filemanager %s\n",edit.c_str());
05783         exit(1);
05784     }
05785 }
05786 
05787 static void launchsaves(std::string const& edit) {
05788     std::string path,file;
05789     struct stat cstat;
05790     file="SAVE";
05791     path = ".";
05792     path += CROSS_FILESPLIT;
05793     path += file;
05794     stat(path.c_str(),&cstat);
05795     if(cstat.st_mode & S_IFDIR) {
05796         execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
05797         //if you get here the launching failed!
05798         printf("can't find filemanager %s\n",edit.c_str());
05799         exit(1);
05800     } else {
05801         path = "";
05802         Cross::CreatePlatformConfigDir(path);
05803         path += file;
05804         Cross::CreateDir(path);
05805         stat(path.c_str(),&cstat);
05806         if((cstat.st_mode & S_IFDIR) == 0) {
05807             printf("%s doesn't exists or isn't a directory.\n",path.c_str());
05808             exit(1);
05809         }
05810         execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
05811         //if you get here the launching failed!
05812         printf("can't find filemanager %s\n",edit.c_str());
05813         exit(1);
05814     }
05815 }
05816 
05817 static void printconfiglocation() {
05818     std::string path,file;
05819     Cross::CreatePlatformConfigDir(path);
05820     Cross::GetPlatformConfigName(file);
05821     path += file;
05822      
05823     FILE* f = fopen(path.c_str(),"r");
05824     if(!f && !control->PrintConfig(path.c_str())) {
05825         printf("tried creating %s. but failed",path.c_str());
05826         exit(1);
05827     }
05828     if(f) fclose(f);
05829     printf("%s\n",path.c_str());
05830     exit(0);
05831 }
05832 
05833 static void eraseconfigfile() {
05834     FILE* f = fopen("dosbox.conf","r");
05835     if(f) {
05836         fclose(f);
05837         show_warning("Warning: dosbox.conf exists in current working directory.\nThis will override the configuration file at runtime.\n");
05838     }
05839     std::string path,file;
05840     Cross::GetPlatformConfigDir(path);
05841     Cross::GetPlatformConfigName(file);
05842     path += file;
05843     f = fopen(path.c_str(),"r");
05844     if(!f) exit(0);
05845     fclose(f);
05846     unlink(path.c_str());
05847     exit(0);
05848 }
05849 
05850 static void erasemapperfile() {
05851     FILE* g = fopen("dosbox.conf","r");
05852     if(g) {
05853         fclose(g);
05854         show_warning("Warning: dosbox.conf exists in current working directory.\nKeymapping might not be properly reset.\n"
05855                      "Please reset configuration as well and delete the dosbox.conf.\n");
05856     }
05857 
05858     std::string path,file=MAPPERFILE;
05859     Cross::GetPlatformConfigDir(path);
05860     path += file;
05861     FILE* f = fopen(path.c_str(),"r");
05862     if(!f) exit(0);
05863     fclose(f);
05864     unlink(path.c_str());
05865     exit(0);
05866 }
05867 
05868 void SetNumLock(void) {
05869 #ifdef WIN32
05870     if (control->opt_disable_numlock_check)
05871         return;
05872 
05873     // Simulate a key press
05874     keybd_event(VK_NUMLOCK,0x45,KEYEVENTF_EXTENDEDKEY | 0,0);
05875 
05876     // Simulate a key release
05877     keybd_event(VK_NUMLOCK,0x45,KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,0);
05878 #endif
05879 }
05880 
05881 void CheckNumLockState(void) {
05882 #ifdef WIN32
05883     BYTE keyState[256];
05884 
05885     GetKeyboardState((LPBYTE)(&keyState));
05886     if (keyState[VK_NUMLOCK] & 1) {
05887         startup_state_numlock = true;
05888     }
05889 #endif
05890 }
05891 
05892 void CheckCapsLockState(void) {
05893 #ifdef WIN32
05894     BYTE keyState[256];
05895 
05896     GetKeyboardState((LPBYTE)(&keyState));
05897     if (keyState[VK_CAPITAL] & 1) {
05898         startup_state_capslock = true;
05899     }
05900 #endif
05901 }
05902 
05903 void CheckScrollLockState(void) {
05904 #ifdef WIN32
05905     BYTE keyState[256];
05906 
05907     GetKeyboardState((LPBYTE)(&keyState));
05908     if (keyState[VK_SCROLL] & 1) {
05909         startup_state_scrlock = true;
05910     }
05911 #endif
05912 }
05913 
05914 extern bool log_keyboard_scan_codes;
05915 
05916 bool showconsole_init = false;
05917 
05918 #if C_DEBUG
05919 bool DEBUG_IsDebuggerConsoleVisible(void);
05920 #endif
05921 
05922 void DOSBox_ShowConsole() {
05923 #if defined(WIN32) && !defined(HX_DOS)
05924     CONSOLE_SCREEN_BUFFER_INFO csbi;
05925     COORD crd;
05926     HWND hwnd;
05927 
05928 #if C_DEBUG
05929     /* if the debugger has already taken the console, do nothing */
05930     if (DEBUG_IsDebuggerConsoleVisible())
05931         return;
05932 #endif
05933 
05934     /* if WE have already opened the console, do nothing */
05935     if (showconsole_init)
05936         return;
05937 
05938     /* Microsoft Windows: Allocate a console and begin spewing to it.
05939        DOSBox is compiled on Windows platforms as a Win32 application, and therefore, no console. */
05940     /* FIXME: What about "file handles" 0/1/2 emulated by C library, for use with _open/_close/_lseek/etc? */
05941     AllocConsole();
05942 
05943     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
05944     crd = csbi.dwSize;
05945     crd.X = 130;
05946     SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), crd);
05947 
05948     hwnd = GetConsoleWindow();
05949     ShowWindow(hwnd, SW_MAXIMIZE);
05950 
05951     freopen("CONIN$", "r", stdin);
05952     freopen("CONOUT$", "w", stdout);
05953     freopen("CONOUT$", "w", stderr);
05954 
05955     showconsole_init = true;
05956 #endif
05957 }
05958 
05959 void DOSBox_ConsolePauseWait() {
05960     char c;
05961 
05962     printf("Hit ENTER to continue\n");
05963     do {
05964         if (fread(&c, 1, 1, stdin) != 1) break;
05965     } while (!(c == 13 || c == 10)); /* wait for Enter key */
05966 }
05967 
05968 bool DOSBOX_parse_argv() {
05969     std::string optname,tmp;
05970 
05971     assert(control != NULL);
05972     assert(control->cmdline != NULL);
05973 
05974     control->cmdline->BeginOpt(true/*eat argv*/);
05975     while (control->cmdline->GetOpt(optname)) {
05976         std::transform(optname.begin(), optname.end(), optname.begin(), ::tolower);
05977 
05978         if (optname == "version") {
05979             DOSBox_ShowConsole();
05980 
05981             fprintf(stderr,"\nDOSBox version %s %s, copyright 2002-2019 DOSBox Team.\n\n",VERSION,SDL_STRING);
05982             fprintf(stderr,"DOSBox is written by the DOSBox Team (See AUTHORS file))\n");
05983             fprintf(stderr,"DOSBox comes with ABSOLUTELY NO WARRANTY.  This is free software,\n");
05984             fprintf(stderr,"and you are welcome to redistribute it under certain conditions;\n");
05985             fprintf(stderr,"please read the COPYING file thoroughly before doing so.\n\n");
05986 
05987 #if defined(WIN32)
05988             DOSBox_ConsolePauseWait();
05989 #endif
05990 
05991             return 0;
05992         }
05993         else if (optname == "h" || optname == "help") {
05994             DOSBox_ShowConsole();
05995 
05996             fprintf(stderr,"\ndosbox [options]\n");
05997             fprintf(stderr,"\nDOSBox version %s %s, copyright 2002-2019 DOSBox Team.\n\n",VERSION,SDL_STRING);
05998             fprintf(stderr,"  -h     -help                            Show this help\n");
05999             fprintf(stderr,"  -editconf                               Launch editor\n");
06000             fprintf(stderr,"  -opencaptures <param>                   Launch captures\n");
06001             fprintf(stderr,"  -opensaves <param>                      Launch saves\n");
06002             fprintf(stderr,"  -eraseconf                              Erase config file\n");
06003             fprintf(stderr,"  -resetconf                              Erase config file\n");
06004             fprintf(stderr,"  -printconf                              Print config file location\n");
06005             fprintf(stderr,"  -erasemapper                            Erase mapper file\n");
06006             fprintf(stderr,"  -resetmapper                            Erase mapper file\n");
06007             fprintf(stderr,"  -console                                Show console (win32)\n");
06008             fprintf(stderr,"  -noconsole                              Don't show console (debug+win32 only)\n");
06009             fprintf(stderr,"  -nogui                                  Don't show gui (win32 only)\n");
06010             fprintf(stderr,"  -nomenu                                 Don't show menu (win32 only)\n");
06011             fprintf(stderr,"  -userconf                               Create user level config file\n");
06012             fprintf(stderr,"  -conf <param>                           Use config file <param>\n");
06013             fprintf(stderr,"  -startui -startgui                      Start DOSBox-X with UI\n");
06014             fprintf(stderr,"  -startmapper                            Start DOSBox-X with mapper\n");
06015             fprintf(stderr,"  -showcycles                             Show cycles count\n");
06016             fprintf(stderr,"  -showrt                                 Show emulation speed relative to realtime\n");
06017             fprintf(stderr,"  -fullscreen                             Start in fullscreen\n");
06018             fprintf(stderr,"  -savedir <path>                         Save path\n");
06019             fprintf(stderr,"  -disable-numlock-check                  Disable numlock check (win32 only)\n");
06020             fprintf(stderr,"  -date-host-forced                       Force synchronization of date with host\n");
06021             fprintf(stderr,"  -debug                                  Set all logging levels to debug\n");
06022             fprintf(stderr,"  -early-debug                            Log early initialization messages in DOSBox (implies -console)\n");
06023             fprintf(stderr,"  -keydbg                                 Log all SDL key events (debugging)\n");
06024             fprintf(stderr,"  -lang <message file>                    Use specific message file instead of language= setting\n");
06025             fprintf(stderr,"  -nodpiaware                             Ignore (don't signal) Windows DPI awareness\n");
06026             fprintf(stderr,"  -securemode                             Enable secure mode\n");
06027             fprintf(stderr,"  -noautoexec                             Don't execute AUTOEXEC.BAT config section\n");
06028             fprintf(stderr,"  -exit                                   Exit after executing AUTOEXEC.BAT\n");
06029             fprintf(stderr,"  -c <command string>                     Execute this command in addition to AUTOEXEC.BAT.\n");
06030             fprintf(stderr,"                                          Make sure to surround the command in quotes to cover spaces.\n");
06031             fprintf(stderr,"  -break-start                            Break into debugger at startup\n");
06032             fprintf(stderr,"  -time-limit <n>                         Kill the emulator after 'n' seconds\n");
06033             fprintf(stderr,"  -fastbioslogo                           Fast BIOS logo (skip 1-second pause)\n");
06034             fprintf(stderr,"  -log-con                                Log CON output to a log file\n");
06035 
06036 #if defined(WIN32)
06037             DOSBox_ConsolePauseWait();
06038 #endif
06039 
06040             return 0;
06041         }
06042         else if (optname == "c") {
06043             if (!control->cmdline->NextOptArgv(tmp)) return false;
06044             control->opt_c.push_back(tmp);
06045         }
06046         else if (optname == "alt-vga") {
06047             control->opt_alt_vga_render = true;
06048         }
06049         else if (optname == "log-con") {
06050             control->opt_log_con = true;
06051         }
06052         else if (optname == "time-limit") {
06053             if (!control->cmdline->NextOptArgv(tmp)) return false;
06054             control->opt_time_limit = atof(tmp.c_str());
06055         }
06056         else if (optname == "break-start") {
06057             control->opt_break_start = true;
06058         }
06059         else if (optname == "exit") {
06060             control->opt_exit = true;
06061         }
06062         else if (optname == "noautoexec") {
06063             control->opt_noautoexec = true;
06064         }
06065         else if (optname == "securemode") {
06066             control->opt_securemode = true;
06067         }
06068         else if (optname == "nodpiaware") {
06069             control->opt_disable_dpi_awareness = true;
06070         }
06071         else if (optname == "keydbg") {
06072             log_keyboard_scan_codes = true;
06073         }
06074         else if (optname == "date-host-forced" || optname == "date_host_forced") {
06075             control->opt_date_host_forced = true;
06076         }
06077         else if (optname == "showrt") {
06078             control->opt_showrt = true;
06079         }
06080         else if (optname == "showcycles") {
06081             control->opt_showcycles = true;
06082         }
06083         else if (optname == "startmapper") {
06084             control->opt_startmapper = true;
06085         }
06086         else if (optname == "fullscreen") {
06087             control->opt_fullscreen = true;
06088         }
06089         else if (optname == "startui" || optname == "startgui") {
06090             control->opt_startui = true;
06091         }
06092         else if (optname == "disable-numlock-check" || optname == "disable_numlock_check") {
06093             /* mainline DOSBox expects -disable_numlock_check so we support that here too */
06094             control->opt_disable_numlock_check = true;
06095         }
06096         else if (optname == "savedir") {
06097             if (!control->cmdline->NextOptArgv(custom_savedir)) return false;
06098         }
06099         else if (optname == "userconf") {
06100             control->opt_userconf = true;
06101         }
06102         else if (optname == "lang") {
06103             if (!control->cmdline->NextOptArgv(control->opt_lang)) return false;
06104         }
06105         else if (optname == "fastbioslogo") {
06106             control->opt_fastbioslogo = true;
06107         }
06108         else if (optname == "conf") {
06109             if (!control->cmdline->NextOptArgv(tmp)) return false;
06110             control->config_file_list.push_back(tmp);
06111         }
06112         else if (optname == "editconf") {
06113             if (!control->cmdline->NextOptArgv(control->opt_editconf)) return false;
06114         }
06115         else if (optname == "opencaptures") {
06116             if (!control->cmdline->NextOptArgv(control->opt_opencaptures)) return false;
06117         }
06118         else if (optname == "opensaves") {
06119             if (!control->cmdline->NextOptArgv(control->opt_opensaves)) return false;
06120         }
06121         else if (optname == "eraseconf") {
06122             control->opt_eraseconf = true;
06123         }
06124         else if (optname == "resetconf") {
06125             control->opt_resetconf = true;
06126         }
06127         else if (optname == "printconf") {
06128             control->opt_printconf = true;
06129         }
06130         else if (optname == "erasemapper") {
06131             control->opt_erasemapper = true;
06132         }
06133         else if (optname == "resetmapper") {
06134             control->opt_resetmapper = true;
06135         }
06136         else if (optname == "noconsole") {
06137             control->opt_noconsole = true;
06138             control->opt_console = false;
06139         }
06140         else if (optname == "console") {
06141             control->opt_noconsole = false;
06142             control->opt_console = true;
06143         }
06144         else if (optname == "nomenu") {
06145             control->opt_nomenu = true;
06146         }
06147         else if (optname == "nogui") {
06148             control->opt_nogui = true;
06149         }
06150         else if (optname == "debug") {
06151             control->opt_debug = true;
06152         }
06153         else if (optname == "early-debug") {
06154             control->opt_earlydebug = true;
06155             control->opt_console = true;
06156         }
06157         else {
06158             printf("WARNING: Unknown option %s (first parsing stage)\n",optname.c_str());
06159         }
06160     }
06161 
06162     /* now that the above loop has eaten all the options from the command
06163      * line, scan the command line for batch files to run.
06164      * https://github.com/joncampbell123/dosbox-x/issues/369 */
06165     control->cmdline->BeginOpt(/*don't eat*/false);
06166     while (!control->cmdline->CurrentArgvEnd()) {
06167         control->cmdline->GetCurrentArgv(tmp);
06168 
06169         {
06170             struct stat st;
06171             const char *ext = strrchr(tmp.c_str(),'.');
06172             if (ext != NULL) { /* if it looks like a file... with an extension */
06173                 if (stat(tmp.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
06174                     if (!strcasecmp(ext,".bat") || !strcasecmp(ext,".exe") || !strcasecmp(ext,".com")) { /* .BAT files given on the command line trigger automounting C: to run it */
06175                         control->auto_bat_additional.push_back(tmp);
06176                         control->cmdline->EatCurrentArgv();
06177                         continue;
06178                     }
06179                 }
06180             }
06181         }
06182 
06183         control->cmdline->NextArgv();
06184     }
06185 
06186     return true;
06187 }
06188 
06189 void MSG_Init();
06190 void DOSBOX_RealInit();
06191 void DOSBOX_InitTickLoop();
06192 void TIMER_ShutdownTickHandlers();
06193 void DOSBOX_SetupConfigSections(void);
06194 void PAGING_Init();
06195 void IO_Init();
06196 void Init_VGABIOS();
06197 void Init_AddressLimitAndGateMask();
06198 void Init_RAM();
06199 void Init_MemHandles();
06200 void Init_MemoryAccessArray();
06201 void Init_PCJR_CartridgeROM();
06202 void Init_PS2_Port_92h();
06203 void Init_A20_Gate();
06204 void HARDWARE_Init();
06205 void CAPTURE_Init();
06206 void ROMBIOS_Init();
06207 void CALLBACK_Init();
06208 void Init_DMA();
06209 void Init_PIC();
06210 void PCIBUS_Init();
06211 void PROGRAMS_Init();
06212 void RENDER_Init();
06213 void TIMER_Init();
06214 void CMOS_Init();
06215 void VGA_Init();
06216 void CPU_Init();
06217 void ISAPNP_Cfg_Init();
06218 #if C_FPU
06219 void FPU_Init();
06220 #endif
06221 void KEYBOARD_Init();
06222 void VOODOO_Init();
06223 void MIXER_Init();
06224 void MIDI_Init();
06225 
06226 /* Init all the sections */
06227 void MPU401_Init();
06228 #if C_DEBUG
06229 void DEBUG_Init();
06230 #endif
06231 void SBLASTER_Init();
06232 void GUS_Init();
06233 void INNOVA_Init();
06234 void PCSPEAKER_Init();
06235 void TANDYSOUND_Init();
06236 void DISNEY_Init();
06237 void PS1SOUND_Init();
06238 void BIOS_Init();
06239 void INT10_Init();
06240 void JOYSTICK_Init();
06241 void SERIAL_Init();
06242 #if C_PRINTER
06243 void PRINTER_Init();
06244 #endif
06245 void PARALLEL_Init();
06246 void DONGLE_Init();
06247 void DOS_Init();
06248 void XMS_Init();
06249 void EMS_Init();
06250 void MOUSE_Init();
06251 void DOS_KeyboardLayout_Init();
06252 void MSCDEX_Init();
06253 void DRIVES_Init();
06254 void IPX_Init();
06255 void IDE_Init();
06256 void NE2K_Init();
06257 void FDC_Primary_Init();
06258 void AUTOEXEC_Init();
06259 
06260 #if defined(WIN32)
06261 // NTS: I intend to add code that not only indicates High DPI awareness but also queries the monitor DPI
06262 //      and then factor the DPI into DOSBox's scaler and UI decisions.
06263 void Windows_DPI_Awareness_Init() {
06264     // if the user says not to from the command line, or disables it from dosbox.conf, then don't enable DPI awareness.
06265     if (!dpi_aware_enable || control->opt_disable_dpi_awareness)
06266         return;
06267 
06268     /* log it */
06269     LOG(LOG_MISC,LOG_DEBUG)("Win32: I will announce High DPI awareness to Windows to eliminate upscaling");
06270 
06271     // turn off DPI scaling so DOSBox-X doesn't look so blurry on Windows 8 & Windows 10.
06272     // use GetProcAddress and LoadLibrary so that these functions are not hard dependencies that prevent us from
06273     // running under Windows 7 or XP.
06274     HRESULT (WINAPI *__SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS) = NULL; // windows 8.1
06275     BOOL (WINAPI *__SetProcessDPIAware)(void) = NULL; // vista/7/8/10
06276     HMODULE __user32;
06277     HMODULE __shcore;
06278 
06279     __user32 = GetModuleHandle("USER32.DLL");
06280     __shcore = GetModuleHandle("SHCORE.DLL");
06281 
06282     if (__user32)
06283         __SetProcessDPIAware = (BOOL(WINAPI *)(void))GetProcAddress(__user32, "SetProcessDPIAware");
06284     if (__shcore)
06285         __SetProcessDpiAwareness = (HRESULT (WINAPI *)(PROCESS_DPI_AWARENESS))GetProcAddress(__shcore, "SetProcessDpiAwareness");
06286 
06287     if (__SetProcessDpiAwareness) {
06288         LOG(LOG_MISC,LOG_DEBUG)("SHCORE.DLL exports SetProcessDpiAwareness function, calling it to signal we are DPI aware.");
06289         if (__SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) != S_OK)
06290             LOG(LOG_MISC,LOG_DEBUG)("SetProcessDpiAwareness failed");
06291     }
06292     if (__SetProcessDPIAware) {
06293         LOG(LOG_MISC,LOG_DEBUG)("USER32.DLL exports SetProcessDPIAware function, calling it to signal we are DPI aware.");
06294         __SetProcessDPIAware();
06295     }
06296 }
06297 #endif
06298 
06299 bool VM_Boot_DOSBox_Kernel() {
06300     if (!dos_kernel_disabled) {
06301         RemoveEMSPageFrame();
06302         RemoveUMBBlock();
06303         DisableINT33();
06304         DOS_GetMemory_unmap();
06305         VFILE_Shutdown();
06306         PROGRAMS_Shutdown();
06307         DOS_UninstallMisc();
06308         SBLASTER_DOS_Shutdown();
06309         GUS_DOS_Shutdown();
06310         EMS_DoShutDown();
06311         XMS_DoShutDown();
06312         DOS_DoShutDown();
06313 
06314         DispatchVMEvent(VM_EVENT_DOS_SURPRISE_REBOOT); // <- apparently we rebooted without any notification (such as jmp'ing to FFFF:0000)
06315 
06316         dos_kernel_disabled = true;
06317 
06318 #if defined(WIN32) && !defined(C_SDL2)
06319         int Reflect_Menu(void);
06320         Reflect_Menu();
06321 #endif
06322     }
06323 
06324     if (dos_kernel_disabled) {
06325         /* in case of reboot */
06326         Init_MemHandles();
06327 
06328         DispatchVMEvent(VM_EVENT_DOS_BOOT); // <- just starting the DOS kernel now
06329 
06330         /* DOS kernel init */
06331         dos_kernel_disabled = false; // FIXME: DOS_Init should install VM callback handler to set this
06332         void DOS_Startup(Section* sec);
06333         DOS_Startup(NULL);
06334 
06335 #if defined(WIN32) && !defined(C_SDL2)
06336         int Reflect_Menu(void);
06337         Reflect_Menu();
06338 #endif
06339 
06340         void update_pc98_function_row(unsigned char setting,bool force_redraw=false);
06341 
06342         void DRIVES_Startup(Section *s);
06343         DRIVES_Startup(NULL);
06344 
06345         /* NEC's function key row seems to be deeply embedded in the CON driver. Am I wrong? */
06346         if (IS_PC98_ARCH) update_pc98_function_row(1);
06347 
06348         DispatchVMEvent(VM_EVENT_DOS_INIT_KERNEL_READY); // <- kernel is ready
06349 
06350         /* keyboard mapping, at this point in CONFIG.SYS parsing, right? */
06351         void DOS_KeyboardLayout_Startup(Section* sec);
06352         DOS_KeyboardLayout_Startup(NULL);
06353 
06354         /* Most MS-DOS installations have a DEVICE=C:\HIMEM.SYS somewhere near the top of their CONFIG.SYS */
06355         void XMS_Startup(Section *sec);
06356         XMS_Startup(NULL);
06357 
06358         /* And then after that, usually a DEVICE=C:\EMM386.EXE just after HIMEM.SYS */
06359         void EMS_Startup(Section* sec);
06360         EMS_Startup(NULL);
06361 
06362         DispatchVMEvent(VM_EVENT_DOS_INIT_CONFIG_SYS_DONE); // <- we just finished executing CONFIG.SYS
06363         SHELL_Init(); // <- NTS: this will change CPU instruction pointer!
06364         DispatchVMEvent(VM_EVENT_DOS_INIT_SHELL_READY); // <- we just finished loading the shell (COMMAND.COM)
06365 
06366         /* it's time to init parsing AUTOEXEC.BAT */
06367         void AUTOEXEC_Startup(Section *sec);
06368         AUTOEXEC_Startup(NULL);
06369 
06370         /* Most MS-DOS installations run MSCDEX.EXE from somewhere in AUTOEXEC.BAT. We do the same here, in a fashion. */
06371         /* TODO: Can we make this an OPTION if the user doesn't want to make MSCDEX.EXE resident? */
06372         /* 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:\
06373          *       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? */
06374         void MSCDEX_Startup(Section* sec);
06375         MSCDEX_Startup(NULL);
06376 
06377         /* Some installations load the MOUSE.COM driver from AUTOEXEC.BAT as well */
06378         /* TODO: Can we make this an option? Can we add a fake MOUSE.COM to the Z:\ drive as well? */
06379         void MOUSE_Startup(Section *sec);
06380         MOUSE_Startup(NULL);
06381 
06382         DispatchVMEvent(VM_EVENT_DOS_INIT_AUTOEXEC_BAT_DONE); // <- we just finished executing AUTOEXEC.BAT
06383         DispatchVMEvent(VM_EVENT_DOS_INIT_AT_PROMPT); // <- now, we're at the DOS prompt
06384         SHELL_Run();
06385     }
06386 
06387     return true;
06388 }
06389 
06390 bool VM_PowerOn() {
06391     if (!guest_machine_power_on) {
06392         // powering on means power on event, followed by reset assert, then reset deassert
06393         guest_machine_power_on = true;
06394         DispatchVMEvent(VM_EVENT_POWERON);
06395         DispatchVMEvent(VM_EVENT_RESET);
06396         DispatchVMEvent(VM_EVENT_RESET_END);
06397     }
06398 
06399     return true;
06400 }
06401 
06402 #if !defined(C_EMSCRIPTEN)
06403 void update_capture_fmt_menu(void);
06404 #endif
06405 
06406 bool capture_fmt_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem);
06407 
06408 void update_pc98_clock_pit_menu(void) {
06409     Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06410 
06411     int pc98rate = dosbox_section->Get_int("pc-98 timer master frequency");
06412     if (pc98rate > 6) pc98rate /= 2;
06413     if (pc98rate == 0) pc98rate = 5; /* Pick the most likely to work with DOS games (FIXME: This is a GUESS!! Is this correct?) */
06414     else if (pc98rate < 5) pc98rate = 4;
06415     else pc98rate = 5;
06416 
06417     mainMenu.get_item("dos_pc98_pit_4mhz").check(pc98rate == 4).refresh_item(mainMenu);
06418     mainMenu.get_item("dos_pc98_pit_5mhz").check(pc98rate == 5).refresh_item(mainMenu);
06419 }
06420 
06421 bool dos_pc98_clock_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06422     (void)menu;//UNUSED
06423     (void)menuitem;//UNUSED
06424     void TIMER_OnPowerOn(Section*);
06425     void TIMER_OnEnterPC98_Phase2_UpdateBDA(void);
06426 
06427     const char *ts = menuitem->get_name().c_str();
06428     if (!strncmp(ts,"dos_pc98_pit_",13))
06429         ts += 13;
06430     else
06431         return true;
06432 
06433     std::string tmp = "pc-98 timer master frequency=";
06434 
06435     {
06436         char tmp1[64];
06437         sprintf(tmp1,"%u",atoi(ts));
06438         tmp += tmp1;
06439     }
06440 
06441     Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06442     dosbox_section->HandleInputline(tmp.c_str());
06443 
06444     TIMER_OnPowerOn(NULL);
06445     TIMER_OnEnterPC98_Phase2_UpdateBDA();
06446 
06447     update_pc98_clock_pit_menu();
06448     return true;
06449 }
06450 
06451 void SetScaleForced(bool forced);
06452 void OutputSettingMenuUpdate(void);
06453 
06454 bool scaler_forced_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06455     (void)menu;//UNUSED
06456     (void)menuitem;//UNUSED
06457     SetScaleForced(!render.scale.forced);
06458     return true;
06459 }
06460 
06461 void MENU_swapstereo(bool enabled);
06462 bool MENU_get_swapstereo(void);
06463 
06464 bool mixer_swapstereo_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06465     (void)menu;//UNUSED
06466     (void)menuitem;//UNUSED
06467     MENU_swapstereo(!MENU_get_swapstereo());
06468     return true;
06469 }
06470 
06471 void MENU_mute(bool enabled);
06472 bool MENU_get_mute(void);
06473 
06474 bool mixer_mute_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06475     (void)menu;//UNUSED
06476     (void)menuitem;//UNUSED
06477     MENU_mute(!MENU_get_mute());
06478     return true;
06479 }
06480 
06481 bool dos_mouse_enable_int33_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06482     (void)menu;//UNUSED
06483     (void)menuitem;//UNUSED
06484     extern bool Mouse_Drv;
06485     Mouse_Drv = !Mouse_Drv;
06486     mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu);
06487     return true;
06488 }
06489 
06490 bool dos_mouse_y_axis_reverse_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06491     (void)menu;//UNUSED
06492     (void)menuitem;//UNUSED
06493     extern bool Mouse_Vertical;
06494     Mouse_Vertical = !Mouse_Vertical;
06495     mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu);
06496     return true;
06497 }
06498 
06499 bool dos_mouse_sensitivity_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06500     (void)menu;//UNUSED
06501     (void)menuitem;//UNUSED
06502     GUI_Shortcut(2);
06503     return true;
06504 }
06505 
06506 extern bool                         gdc_5mhz_mode_initial;
06507 
06508 bool vid_pc98_5mhz_gdc_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06509     (void)menu;//UNUSED
06510     (void)menuitem;//UNUSED
06511     if (IS_PC98_ARCH) {
06512         void gdc_5mhz_mode_update_vars(void);
06513         extern bool gdc_5mhz_mode;
06514         extern bool gdc_clock_1;
06515         extern bool gdc_clock_2;
06516 
06517         gdc_5mhz_mode = !gdc_5mhz_mode;
06518         gdc_5mhz_mode_update_vars();
06519 
06520         // this is the user's command to change GDC setting, so it should appear
06521         // as if the initial setting in the dip switches
06522         gdc_5mhz_mode_initial = gdc_5mhz_mode;
06523 
06524         gdc_clock_1 = gdc_5mhz_mode;
06525         gdc_clock_2 = gdc_5mhz_mode;
06526 
06527         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06528         if (gdc_5mhz_mode)
06529             dosbox_section->HandleInputline("pc-98 start gdc at 5mhz=1");
06530         else
06531             dosbox_section->HandleInputline("pc-98 start gdc at 5mhz=0");
06532 
06533         mainMenu.get_item("pc98_5mhz_gdc").check(gdc_5mhz_mode).refresh_item(mainMenu);
06534     }
06535 
06536     return true;
06537 }
06538 
06539 bool vid_pc98_200scanline_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06540     (void)menu;//UNUSED
06541     (void)menuitem;//UNUSED
06542     if (IS_PC98_ARCH) {
06543         extern bool pc98_allow_scanline_effect;
06544 
06545         pc98_allow_scanline_effect = !pc98_allow_scanline_effect;
06546 
06547         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06548         if (pc98_allow_scanline_effect)
06549             dosbox_section->HandleInputline("pc-98 allow scanline effect=1");
06550         else
06551             dosbox_section->HandleInputline("pc-98 allow scanline effect=0");
06552 
06553         mainMenu.get_item("pc98_allow_200scanline").check(pc98_allow_scanline_effect).refresh_item(mainMenu);
06554     }
06555 
06556     return true;
06557 }
06558 
06559 bool vid_pc98_4parts_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06560     (void)menu;//UNUSED
06561     (void)menuitem;//UNUSED
06562     if (IS_PC98_ARCH) {
06563         extern bool pc98_allow_4_display_partitions;
06564         void updateGDCpartitions4(bool enable);
06565 
06566         updateGDCpartitions4(!pc98_allow_4_display_partitions);
06567 
06568         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06569         if (pc98_allow_4_display_partitions)
06570             dosbox_section->HandleInputline("pc-98 allow 4 display partition graphics=1");
06571         else
06572             dosbox_section->HandleInputline("pc-98 allow 4 display partition graphics=0");
06573 
06574         mainMenu.get_item("pc98_allow_4partitions").check(pc98_allow_4_display_partitions).refresh_item(mainMenu);
06575     }
06576 
06577     return true;
06578 }
06579 
06580 bool vid_pc98_enable_188user_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06581     (void)menu;//UNUSED
06582     (void)menuitem;//UNUSED
06583     void gdc_egc_enable_update_vars(void);
06584 //    extern bool enable_pc98_egc;
06585 //    extern bool enable_pc98_grcg;
06586 //    extern bool enable_pc98_16color;
06587     extern bool enable_pc98_188usermod;
06588 
06589     if(IS_PC98_ARCH) {
06590         enable_pc98_188usermod = !enable_pc98_188usermod;
06591         gdc_egc_enable_update_vars();
06592 
06593         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06594         if (enable_pc98_188usermod)
06595             dosbox_section->HandleInputline("pc-98 enable 188 user cg=1");
06596         else
06597             dosbox_section->HandleInputline("pc-98 enable 188 user cg=0");
06598 
06599         mainMenu.get_item("pc98_enable_188user").check(enable_pc98_188usermod).refresh_item(mainMenu);
06600     }
06601     
06602     return true;
06603 }
06604 
06605 bool vid_pc98_enable_egc_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06606     (void)menu;//UNUSED
06607     (void)menuitem;//UNUSED
06608     void gdc_egc_enable_update_vars(void);
06609     extern bool enable_pc98_egc;
06610     extern bool enable_pc98_grcg;
06611     extern bool enable_pc98_16color;
06612 //    extern bool enable_pc98_188usermod;
06613 
06614     if(IS_PC98_ARCH) {
06615         enable_pc98_egc = !enable_pc98_egc;
06616         gdc_egc_enable_update_vars();
06617 
06618         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06619         if (enable_pc98_egc) {
06620             dosbox_section->HandleInputline("pc-98 enable egc=1");
06621 
06622             if(!enable_pc98_grcg) { //Also enable GRCG if GRCG is disabled when enabling EGC
06623                 enable_pc98_grcg = !enable_pc98_grcg;
06624                 mem_writeb(0x54C,(enable_pc98_grcg ? 0x02 : 0x00) | (enable_pc98_16color ? 0x04 : 0x00));   
06625                 dosbox_section->HandleInputline("pc-98 enable grcg=1");
06626             }
06627         }
06628         else
06629             dosbox_section->HandleInputline("pc-98 enable egc=0");
06630 
06631         mainMenu.get_item("pc98_enable_egc").check(enable_pc98_egc).refresh_item(mainMenu);
06632         mainMenu.get_item("pc98_enable_grcg").check(enable_pc98_grcg).refresh_item(mainMenu);
06633     }
06634     
06635     return true;
06636 }
06637 
06638 bool vid_pc98_enable_grcg_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06639     (void)menu;//UNUSED
06640     (void)menuitem;//UNUSED
06641     extern bool enable_pc98_grcg;
06642     extern bool enable_pc98_egc;
06643     void gdc_grcg_enable_update_vars(void);
06644 
06645     if(IS_PC98_ARCH) {
06646         enable_pc98_grcg = !enable_pc98_grcg;
06647         gdc_grcg_enable_update_vars();
06648 
06649         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06650         if (enable_pc98_grcg)
06651             dosbox_section->HandleInputline("pc-98 enable grcg=1");
06652         else
06653             dosbox_section->HandleInputline("pc-98 enable grcg=0");
06654 
06655         if ((!enable_pc98_grcg) && enable_pc98_egc) { // Also disable EGC if switching off GRCG
06656             void gdc_egc_enable_update_vars(void);
06657             enable_pc98_egc = !enable_pc98_egc;
06658             gdc_egc_enable_update_vars();   
06659             dosbox_section->HandleInputline("pc-98 enable egc=0");
06660         }
06661 
06662         mainMenu.get_item("pc98_enable_egc").check(enable_pc98_egc).refresh_item(mainMenu);
06663         mainMenu.get_item("pc98_enable_grcg").check(enable_pc98_grcg).refresh_item(mainMenu);
06664     }
06665 
06666     return true;
06667 }
06668 
06669 bool vid_pc98_enable_analog_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06670     (void)menu;//UNUSED
06671     (void)menuitem;//UNUSED
06672     //NOTE: I thought that even later PC-9801s and some PC-9821s could use EGC features in digital 8-colors mode? 
06673     extern bool enable_pc98_16color;
06674     void gdc_16color_enable_update_vars(void);
06675 
06676     if(IS_PC98_ARCH) {
06677         enable_pc98_16color = !enable_pc98_16color;
06678         gdc_16color_enable_update_vars();
06679 
06680         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06681         if (enable_pc98_16color)
06682             dosbox_section->HandleInputline("pc-98 enable 16-color=1");
06683         else
06684             dosbox_section->HandleInputline("pc-98 enable 16-color=0");
06685 
06686         mainMenu.get_item("pc98_enable_analog").check(enable_pc98_16color).refresh_item(mainMenu);
06687     }
06688 
06689     return true;
06690 }
06691 
06692 bool vid_pc98_enable_analog256_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06693     (void)menu;//UNUSED
06694     (void)menuitem;//UNUSED
06695     //NOTE: I thought that even later PC-9801s and some PC-9821s could use EGC features in digital 8-colors mode? 
06696     extern bool enable_pc98_256color;
06697     void gdc_16color_enable_update_vars(void);
06698 
06699     if(IS_PC98_ARCH) {
06700         enable_pc98_256color = !enable_pc98_256color;
06701         gdc_16color_enable_update_vars();
06702 
06703         Section_prop * dosbox_section = static_cast<Section_prop *>(control->GetSection("dosbox"));
06704         if (enable_pc98_256color)
06705             dosbox_section->HandleInputline("pc-98 enable 256-color=1");
06706         else
06707             dosbox_section->HandleInputline("pc-98 enable 256-color=0");
06708 
06709         mainMenu.get_item("pc98_enable_analog256").check(enable_pc98_256color).refresh_item(mainMenu);
06710     }
06711 
06712     return true;
06713 }
06714 
06715 bool vid_pc98_cleartext_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06716     (void)menu;//UNUSED
06717     (void)menuitem;//UNUSED
06718     void pc98_clear_text(void);
06719     if (IS_PC98_ARCH) pc98_clear_text();
06720     return true;
06721 }
06722 
06723 bool vid_pc98_graphics_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06724     (void)menu;//UNUSED
06725     (void)menuitem;//UNUSED
06726     void pc98_clear_graphics(void);
06727     if (IS_PC98_ARCH) pc98_clear_graphics();
06728     return true;
06729 }
06730 
06731 bool overscan_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06732     (void)menu;//UNUSED
06733     int f = atoi(menuitem->get_text().c_str()); /* Off becomes 0 */
06734     char tmp[64];
06735 
06736     sprintf(tmp,"%u",f);
06737     SetVal("sdl", "overscan", tmp);
06738     change_output(7);
06739     return true;
06740 }
06741 
06742 void UpdateOverscanMenu(void) {
06743     for (size_t i=0;i <= 10;i++) {
06744         char tmp[64];
06745         sprintf(tmp,"overscan_%zu",i);
06746         mainMenu.get_item(tmp).check(sdl.overscan_width == i).refresh_item(mainMenu);
06747     }
06748 }
06749 
06750 bool vsync_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06751     (void)menu;//UNUSED
06752     (void)menuitem;//UNUSED
06753 #if !defined(C_SDL2)
06754     const char *val = menuitem->get_name().c_str();
06755     if (!strncmp(val,"vsync_",6))
06756         val += 6;
06757     else
06758         return true;
06759 
06760     SetVal("vsync", "vsyncmode", val);
06761 
06762     void change_output(int output);
06763     change_output(8);
06764 
06765     VGA_Vsync VGA_Vsync_Decode(const char *vsyncmodestr);
06766     void VGA_VsyncUpdateMode(VGA_Vsync vsyncmode);
06767     VGA_VsyncUpdateMode(VGA_Vsync_Decode(val));
06768 #endif
06769     return true;
06770 }
06771 
06772 bool vsync_set_syncrate_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06773     (void)menu;//UNUSED
06774     (void)menuitem;//UNUSED
06775 #if !defined(C_SDL2)
06776     GUI_Shortcut(17);
06777 #endif
06778     return true;
06779 }
06780 
06781 bool output_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06782     (void)menu;//UNUSED
06783 
06784     const char *what = menuitem->get_name().c_str();
06785 
06786     if (!strncmp(what,"output_",7))
06787         what += 7;
06788     else
06789         return true;
06790 
06791     if (!strcmp(what,"surface")) {
06792         if (sdl.desktop.want_type == SCREEN_SURFACE) return true;
06793         change_output(0);
06794     }
06795     else if (!strcmp(what,"opengl")) {
06796 #if C_OPENGL
06797         if (sdl.desktop.want_type == SCREEN_OPENGL && sdl_opengl.bilinear) return true;
06798         change_output(3);
06799 #endif
06800     }
06801     else if (!strcmp(what,"openglnb")) {
06802 #if C_OPENGL
06803         if (sdl.desktop.want_type == SCREEN_OPENGL && !sdl_opengl.bilinear) return true;
06804         change_output(4);
06805 #endif
06806     }
06807     else if (!strcmp(what,"direct3d")) {
06808 #if C_DIRECT3D
06809         if (sdl.desktop.want_type == SCREEN_DIRECT3D) return true;
06810         change_output(5);
06811 #endif
06812     }
06813 
06814     SetVal("sdl", "output", what);
06815     OutputSettingMenuUpdate();
06816     return true;
06817 }
06818 
06819 bool MENU_SetBool(std::string secname, std::string value);
06820 
06821 bool vga_9widetext_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06822     (void)menu;//UNUSED
06823     (void)menuitem;//UNUSED
06824     MENU_SetBool("render", "char9");
06825     return true;
06826 }
06827 
06828 bool doublescan_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06829     (void)menu;//UNUSED
06830     (void)menuitem;//UNUSED
06831     MENU_SetBool("render", "doublescan");
06832     return true;
06833 }
06834 
06835 bool scaler_set_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06836     (void)menu;//UNUSED
06837 
06838     const char *scaler = menuitem->get_name().c_str();
06839     if (!strncmp(scaler,"scaler_set_",11))
06840         scaler += 11;
06841     else
06842         abort();
06843 
06844     auto value = std::string(scaler) + (render.scale.forced ? " forced" : "");
06845     SetVal("render", "scaler", value);
06846 
06847     void RENDER_UpdateFromScalerSetting(void);
06848     RENDER_UpdateFromScalerSetting();
06849 
06850     void RENDER_UpdateScalerMenu(void);
06851     RENDER_UpdateScalerMenu();
06852 
06853     void RENDER_CallBack( GFX_CallBackFunctions_t function );
06854     RENDER_CallBack(GFX_CallBackReset);
06855 
06856     return true;
06857 }
06858 
06859 void CALLBACK_Idle(void);
06860 
06861 bool pausewithinterrupts_enable = false;
06862 
06863 void PauseWithInterruptsEnabled(Bitu /*val*/) {
06864     /* we can ONLY do this when the CPU is either in real mode or v86 mode.
06865      * doing this from protected mode will only crash the game.
06866      * also require that interrupts are enabled before pausing. */
06867     if (cpu.pmode) {
06868         if (!(reg_flags & FLAG_VM)) {
06869             PIC_AddEvent(PauseWithInterruptsEnabled,0.001);
06870             return;
06871         }
06872     }
06873 
06874     if (!(reg_flags & FLAG_IF)) {
06875         PIC_AddEvent(PauseWithInterruptsEnabled,0.001);
06876         return;
06877     }
06878 
06879     while (pausewithinterrupts_enable) CALLBACK_Idle();
06880 }
06881 
06882 void PauseWithInterrupts_mapper_shortcut(bool pressed) {
06883     if (!pressed) return;
06884 
06885     if (!pausewithinterrupts_enable) {
06886         pausewithinterrupts_enable = true;
06887         PIC_AddEvent(PauseWithInterruptsEnabled,0.001);
06888     }
06889     else {
06890         pausewithinterrupts_enable = false;
06891     }
06892 
06893     mainMenu.get_item("mapper_pauseints").check(pausewithinterrupts_enable).refresh_item(mainMenu);
06894 }
06895 
06896 bool video_frameskip_common_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06897     (void)menu;//UNUSED
06898 
06899     int f = atoi(menuitem->get_text().c_str()); /* Off becomes 0 */
06900     char tmp[64];
06901 
06902     sprintf(tmp,"%u",f);
06903     SetVal("render", "frameskip", tmp);
06904     return true;
06905 }
06906 
06907 bool show_console_menu_callback(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
06908 #if !defined(C_EMSCRIPTEN)
06909     (void)menu;//UNUSED
06910     (void)menuitem;//UNUSED
06911     DOSBox_ShowConsole();
06912     mainMenu.get_item("show_console").check(true).refresh_item(mainMenu);
06913 #endif
06914     return true;
06915 }
06916 
06917 bool wait_on_error_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) {
06918 #if !defined(C_EMSCRIPTEN)
06919     (void)menu;//UNUSED
06920     (void)menuitem;//UNUSED
06921     sdl.wait_on_error = !sdl.wait_on_error;
06922     mainMenu.get_item("wait_on_error").check(sdl.wait_on_error).refresh_item(mainMenu);
06923 #endif
06924     return true;
06925 }
06926 
06927 bool autolock_mouse_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) {
06928     (void)menu;//UNUSED
06929     (void)menuitem;//UNUSED
06930     sdl.mouse.autoenable = !sdl.mouse.autoenable;
06931     mainMenu.get_item("auto_lock_mouse").check(sdl.mouse.autoenable).refresh_item(mainMenu);
06932     return true;
06933 }
06934 
06935 bool doublebuf_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) {
06936     (void)menu;//UNUSED
06937     (void)menuitem;//UNUSED
06938     SetVal("sdl", "fulldouble", (GetSetSDLValue(1, "desktop.doublebuf", 0)) ? "false" : "true"); res_init();
06939     mainMenu.get_item("doublebuf").check(!!GetSetSDLValue(1, "desktop.doublebuf", 0)).refresh_item(mainMenu);
06940     return true;
06941 }
06942 
06943 #if defined(LINUX)
06944 bool x11_on_top = false;
06945 #endif
06946 
06947 #if defined(MACOSX) && !defined(C_SDL2)
06948 bool macosx_on_top = false;
06949 #endif
06950 
06951 bool is_always_on_top(void) {
06952 #if defined(_WIN32) && !defined(C_SDL2)
06953     DWORD dwExStyle = ::GetWindowLong(GetHWND(), GWL_EXSTYLE);
06954     return !!(dwExStyle & WS_EX_TOPMOST);
06955 #elif defined(MACOSX) && !defined(C_SDL2)
06956     return macosx_on_top;
06957 #elif defined(LINUX)
06958     return x11_on_top;
06959 #else
06960     return false;
06961 #endif
06962 }
06963 
06964 #if defined(_WIN32) && !defined(C_SDL2)
06965 extern "C" void sdl1_hax_set_topmost(unsigned char topmost);
06966 #endif
06967 #if defined(MACOSX) && !defined(C_SDL2)
06968 extern "C" void sdl1_hax_set_topmost(unsigned char topmost);
06969 #endif
06970 #if defined(MACOSX) && !defined(C_SDL2)
06971 extern "C" void sdl1_hax_macosx_highdpi_set_enable(const bool enable);
06972 #endif
06973 
06974 void toggle_always_on_top(void) {
06975     bool cur = is_always_on_top();
06976 #if defined(_WIN32) && !defined(C_SDL2)
06977     sdl1_hax_set_topmost(!cur);
06978 #elif defined(MACOSX) && !defined(C_SDL2)
06979     sdl1_hax_set_topmost(macosx_on_top = (!cur));
06980 #elif defined(LINUX)
06981     void LinuxX11_OnTop(bool f);
06982     LinuxX11_OnTop(x11_on_top = (!cur));
06983 #else
06984     (void)cur;
06985 #endif
06986 }
06987 
06988 void BlankDisplay(void);
06989 
06990 bool refreshtest_menu_callback(DOSBoxMenu * const xmenu, DOSBoxMenu::item * const menuitem) {
06991     (void)menuitem;
06992     (void)xmenu;
06993 
06994     BlankDisplay();
06995 
06996 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
06997     mainMenu.setRedraw();
06998     GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
06999 #endif
07000 
07001     return true;
07002 }
07003 
07004 bool showdetails_menu_callback(DOSBoxMenu * const xmenu, DOSBoxMenu::item * const menuitem) {
07005     (void)xmenu;//UNUSED
07006     (void)menuitem;//UNUSED
07007     menu.showrt = !(menu.hidecycles = !menu.hidecycles);
07008     GFX_SetTitle((Bit32s)CPU_CycleMax, -1, -1, false);
07009     mainMenu.get_item("showdetails").check(!menu.hidecycles).refresh_item(mainMenu);
07010     return true;
07011 }
07012 
07013 bool alwaysontop_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) {
07014     (void)menu;//UNUSED
07015     (void)menuitem;//UNUSED
07016     toggle_always_on_top();
07017     mainMenu.get_item("alwaysontop").check(is_always_on_top()).refresh_item(mainMenu);
07018     return true;
07019 }
07020 
07021 bool highdpienable_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) {
07022     void RENDER_CallBack( GFX_CallBackFunctions_t function );
07023 
07024     (void)menu;//UNUSED
07025     (void)menuitem;//UNUSED
07026 
07027 #if defined(MACOSX) && !defined(C_SDL2)
07028     dpi_aware_enable = !dpi_aware_enable;
07029     if (!control->opt_disable_dpi_awareness) {
07030         sdl1_hax_macosx_highdpi_set_enable(dpi_aware_enable);
07031         RENDER_CallBack(GFX_CallBackReset);
07032     }
07033 #endif
07034 
07035     mainMenu.get_item("highdpienable").check(dpi_aware_enable).refresh_item(mainMenu);
07036     return true;
07037 }
07038 
07039 bool sendkey_preset_menu_callback(DOSBoxMenu * const menu, DOSBoxMenu::item * const menuitem) {
07040     (void)menu;//UNUSED
07041     if (menuitem->get_name() == "sendkey_ctrlesc") {
07042         KEYBOARD_AddKey(KBD_leftctrl, true);
07043         KEYBOARD_AddKey(KBD_esc, true);
07044         KEYBOARD_AddKey(KBD_leftctrl, false);
07045         KEYBOARD_AddKey(KBD_esc, false);
07046     }
07047     else if (menuitem->get_name() == "sendkey_alttab") {
07048         KEYBOARD_AddKey(KBD_leftalt, true);
07049         KEYBOARD_AddKey(KBD_tab, true);
07050         KEYBOARD_AddKey(KBD_leftalt, false);
07051         KEYBOARD_AddKey(KBD_tab, false);
07052     }
07053     else if (menuitem->get_name() == "sendkey_winlogo") {
07054         KEYBOARD_AddKey(KBD_lwindows, true);
07055         KEYBOARD_AddKey(KBD_lwindows, false);
07056     }
07057     else if (menuitem->get_name() == "sendkey_winmenu") {
07058         KEYBOARD_AddKey(KBD_rwinmenu, true);
07059         KEYBOARD_AddKey(KBD_rwinmenu, false);
07060     }
07061     else if (menuitem->get_name() == "sendkey_cad") {
07062         KEYBOARD_AddKey(KBD_leftctrl, true);
07063         KEYBOARD_AddKey(KBD_leftalt, true);
07064         KEYBOARD_AddKey(KBD_delete, true);
07065         KEYBOARD_AddKey(KBD_leftctrl, false);
07066         KEYBOARD_AddKey(KBD_leftalt, false);
07067         KEYBOARD_AddKey(KBD_delete, false);
07068     }
07069 
07070     return true;
07071 }
07072 
07073 void SetCyclesCount_mapper_shortcut_RunInternal(void) {
07074     void MAPPER_ReleaseAllKeys(void);
07075     MAPPER_ReleaseAllKeys();
07076 
07077     GFX_LosingFocus();
07078 
07079     GUI_Shortcut(16);
07080 
07081     void MAPPER_ReleaseAllKeys(void);
07082     MAPPER_ReleaseAllKeys();
07083 
07084     GFX_LosingFocus();
07085 }
07086 
07087 void SetCyclesCount_mapper_shortcut_RunEvent(Bitu /*val*/) {
07088     KEYBOARD_ClrBuffer();   //Clear buffer
07089     GFX_LosingFocus();      //Release any keys pressed (buffer gets filled again).
07090     SetCyclesCount_mapper_shortcut_RunInternal();
07091 }
07092 
07093 void SetCyclesCount_mapper_shortcut(bool pressed) {
07094     if (!pressed) return;
07095     PIC_AddEvent(SetCyclesCount_mapper_shortcut_RunEvent, 0.0001f); //In case mapper deletes the key object that ran it
07096 }
07097 
07098 void AspectRatio_mapper_shortcut(bool pressed) {
07099     if (!pressed) return;
07100 
07101     if (!GFX_GetPreventFullscreen()) {
07102         SetVal("render", "aspect", render.aspect ? "false" : "true");
07103     }
07104 }
07105 
07106 void HideMenu_mapper_shortcut(bool pressed) {
07107     if (!pressed) return;
07108 
07109     void ToggleMenu(bool pressed);
07110     ToggleMenu(true);
07111 
07112     mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
07113 }
07114 
07115 void OutputSettingMenuUpdate(void) {
07116     mainMenu.get_item("output_surface").check(sdl.desktop.want_type == SCREEN_SURFACE).refresh_item(mainMenu);
07117 #if C_DIRECT3D
07118     mainMenu.get_item("output_direct3d").check(sdl.desktop.want_type == SCREEN_DIRECT3D).refresh_item(mainMenu);
07119 #endif
07120 #if C_OPENGL
07121     mainMenu.get_item("output_opengl").check(sdl.desktop.want_type == SCREEN_OPENGL && sdl_opengl.bilinear).refresh_item(mainMenu);
07122     mainMenu.get_item("output_openglnb").check(sdl.desktop.want_type == SCREEN_OPENGL && !sdl_opengl.bilinear).refresh_item(mainMenu);
07123 #endif
07124 }
07125 
07126 bool custom_bios = false;
07127 
07128 // OK why isn't this being set for Linux??
07129 #ifndef SDL_MAIN_NOEXCEPT
07130 #define SDL_MAIN_NOEXCEPT
07131 #endif
07132 
07133 //extern void UI_Init(void);
07134 int main(int argc, char* argv[]) SDL_MAIN_NOEXCEPT {
07135     CommandLine com_line(argc,argv);
07136     Config myconf(&com_line);
07137 
07138 #if defined(WIN32) && !defined(HX_DOS)
07139     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
07140 #endif
07141 
07142 #if 0 /* VGA_Draw_2 self test: dot clock */
07143     {
07144         const double start_time = 0;
07145         signed long long count = 0;
07146         VGA_Draw_2 t;
07147 
07148         fprintf(stderr,"VGA_Draw_2: 1000Hz\n");
07149 
07150         t.dotclock.set_rate(1000,start_time);/*hz*/
07151         for (int i=0;i < 10000;i++) {
07152             t.dotclock.update(start_time + i);
07153 
07154             if (labs(t.dotclock.ticks - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
07155                 fprintf(stderr,"* TEST FAILURE:\n");
07156                 fprintf(stderr,"   ticks=%lld ticks_prev=%lld\n",(signed long long)i,(signed long long)t.dotclock.ticks);
07157                 return 1;
07158             }
07159         }
07160 
07161         t.dotclock.reset(start_time);
07162         assert(t.dotclock.base == start_time);
07163         assert(t.dotclock.ticks_prev == 0);
07164         assert(t.dotclock.ticks == 0);
07165 
07166         fprintf(stderr,"VGA_Draw_2: 1000Hz incremental\n");
07167 
07168         count = 0;
07169         t.dotclock.set_rate(1000,start_time);/*hz*/
07170         for (int i=0;i < 10000;i++) {
07171             t.dotclock.update(start_time + i);
07172             count += t.dotclock.delta_get();
07173             assert(t.dotclock.ticks == t.dotclock.ticks_prev);
07174 
07175             if (labs(count - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
07176                 fprintf(stderr,"* TEST FAILURE:\n");
07177                 fprintf(stderr,"   count=%lld ticks=%lld ticks_prev=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks);
07178                 return 1;
07179             }
07180 
07181             signed long long rc = t.dotclock.ticks2pic(count);
07182             if (labs(rc - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
07183                 fprintf(stderr,"* TEST FAILURE:\n");
07184                 fprintf(stderr,"   count=%lld ticks=%lld ticks_prev=%lld rc=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks,rc);
07185                 return 1;
07186             }
07187         }
07188 
07189         fprintf(stderr,"VGA_Draw_2: 1000Hz inc then 100Hz inc\n");
07190 
07191         count = 0;
07192         t.dotclock.set_rate(100,start_time + 1000);/*hz, rate change*/
07193         for (int i=0;i < 10000;i++) {
07194             t.dotclock.update(start_time + 1000 + (i * 10));/*1ms * 10 = 10ms = 100Hz */
07195             count += t.dotclock.delta_get();
07196             assert(t.dotclock.ticks == t.dotclock.ticks_prev);
07197 
07198             if (labs(count - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
07199                 fprintf(stderr,"* TEST FAILURE:\n");
07200                 fprintf(stderr,"   count=%lld ticks=%lld ticks_prev=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks);
07201                 return 1;
07202             }
07203 
07204             signed long long rc = t.dotclock.ticks2pic(count);
07205             if (labs(rc - ((signed long long)(i * 10) + 1000 + start_time)) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
07206                 fprintf(stderr,"* TEST FAILURE:\n");
07207                 fprintf(stderr,"   count=%lld ticks=%lld ticks_prev=%lld rc=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks,rc);
07208                 return 1;
07209             }
07210         }
07211 
07212         return 0;
07213     }
07214 #endif
07215 
07216 #if C_EMSCRIPTEN
07217     control->opt_debug = true;
07218     control->opt_earlydebug = true;
07219 #endif
07220 
07221     bitop::self_test();
07222     ptrop::self_test();
07223 
07224     // initialize output libraries
07225     OUTPUT_SURFACE_Initialize();
07226 #if C_OPENGL
07227     OUTPUT_OPENGL_Initialize();
07228 #endif
07229 #if C_DIRECT3D
07230     OUTPUT_DIRECT3D_Initialize();
07231 #endif
07232 
07233     // initialize some defaults in SDL structure here
07234     sdl.srcAspect.x = 4; sdl.srcAspect.y = 3; 
07235     sdl.srcAspect.xToY = (double)sdl.srcAspect.x / sdl.srcAspect.y;
07236     sdl.srcAspect.yToX = (double)sdl.srcAspect.y / sdl.srcAspect.x;
07237 
07238     control=&myconf;
07239 #if defined(WIN32) && !defined(HX_DOS)
07240     /* Microsoft's IME does not play nice with DOSBox */
07241     ImmDisableIME((DWORD)(-1));
07242 #endif
07243 
07244 #if defined(MACOSX)
07245     /* The resource system of DOSBox-X relies on being able to locate the Resources subdirectory
07246        within the DOSBox-X .app bundle. To do this, we have to first know where our own executable
07247        is, which Mac OS X helpfully puts int argv[0] for us */
07248     /* NTS: Experimental testing shows that when we are run from the desktop (double-clicking on
07249             the .app bundle from the Finder) the current working directory is / (fs root). */
07250     extern std::string MacOSXEXEPath;
07251     extern std::string MacOSXResPath;
07252     MacOSXEXEPath = argv[0];
07253 
07254     /* The path should be something like /blah/blah/dosbox-x.app/Contents/MacOS/DosBox */
07255     /* If that's true, then we can move one level up the tree and look for */
07256     /* /blah/blah/dosbox-x.app/Contents/Resources */
07257     {
07258         const char *ref = argv[0];
07259         const char *s = strrchr(ref,'/');
07260         if (s != NULL) {
07261             if (s > ref) s--;
07262             while (s > ref && *s != '/') s--;
07263             if (!strncasecmp(s,"/MacOS/",7)) {
07264                 MacOSXResPath = std::string(ref,(size_t)(s-ref)) + "/Resources";
07265             }
07266         }
07267     }
07268 
07269     /* If we were launched by the Finder, the current working directory will usually be
07270        the root of the filesystem (/) which is useless. If we see that, change instead
07271        to the user's home directory */
07272     {
07273         char *home = getenv("HOME");
07274         char cwd[512];
07275 
07276         cwd[0]=0;
07277         getcwd(cwd,sizeof(cwd)-1);
07278 
07279         if (!strcmp(cwd,"/")) {
07280             /* Only the Finder would do that.
07281                Even if the user somehow did this from the Terminal app, it's still
07282                worth changing to the home directory because certain directories
07283                including / are locked readonly even for sudo in Mac OS X */
07284             /* NTS: HOME is usually an absolute path */
07285             if (home != NULL) chdir(home);
07286         }
07287     }
07288 #endif
07289 
07290     {
07291         std::string tmp,config_path,config_combined;
07292 
07293         /* -- parse command line arguments */
07294         if (!DOSBOX_parse_argv()) return 1;
07295 
07296         if (control->opt_time_limit > 0)
07297             time_limit_ms = (Bitu)(control->opt_time_limit * 1000);
07298 
07299         if (control->opt_console)
07300             DOSBox_ShowConsole();
07301 
07302         /* -- Handle some command line options */
07303         if (control->opt_eraseconf || control->opt_resetconf)
07304             eraseconfigfile();
07305         if (control->opt_printconf)
07306             printconfiglocation();
07307         if (control->opt_erasemapper || control->opt_resetmapper)
07308             erasemapperfile();
07309 
07310         /* -- Early logging init, in case these details are needed to debug problems at this level */
07311         /*    If --early-debug was given this opens up logging to STDERR until Log::Init() */
07312         LOG::EarlyInit();
07313 
07314 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
07315         {
07316             DISPLAY_DEVICE dd;
07317             unsigned int i = 0;
07318 
07319             do {
07320                 memset(&dd, 0, sizeof(dd));
07321                 dd.cb = sizeof(dd);
07322                 if (!EnumDisplayDevices(NULL, i, &dd, 0)) break;
07323                 LOG_MSG("Win32 EnumDisplayDevices #%d: name=%s string=%s", i, dd.DeviceName, dd.DeviceString);
07324                 i++;
07325 
07326                 if (strstr(dd.DeviceString, "VirtualBox") != NULL)
07327                     isVirtualBox = true;
07328             } while (1);
07329         }
07330 #endif
07331 
07332         /* -- Init the configuration system and add default values */
07333         CheckNumLockState();
07334         CheckCapsLockState();
07335         CheckScrollLockState();
07336 
07337         /* -- setup the config sections for config parsing */
07338         LOG::SetupConfigSection();
07339         SDL_SetupConfigSection();
07340         DOSBOX_SetupConfigSections();
07341 
07342         /* -- Parse configuration files */
07343         Cross::GetPlatformConfigDir(config_path);
07344 
07345         /* -- -- first the user config file */
07346         if (control->opt_userconf) {
07347             tmp.clear();
07348             Cross::GetPlatformConfigDir(config_path);
07349             Cross::GetPlatformConfigName(tmp);
07350             config_combined = config_path + tmp;
07351 
07352             LOG(LOG_MISC,LOG_DEBUG)("Loading config file according to -userconf from %s",config_combined.c_str());
07353             control->ParseConfigFile(config_combined.c_str());
07354             if (!control->configfiles.size()) {
07355                 //Try to create the userlevel configfile.
07356                 tmp.clear();
07357                 Cross::CreatePlatformConfigDir(config_path);
07358                 Cross::GetPlatformConfigName(tmp);
07359                 config_combined = config_path + tmp;
07360 
07361                 LOG(LOG_MISC,LOG_DEBUG)("Attempting to write config file according to -userconf, to %s",config_combined.c_str());
07362                 if (control->PrintConfig(config_combined.c_str())) {
07363                     LOG(LOG_MISC,LOG_NORMAL)("Generating default configuration. Writing it to %s",config_combined.c_str());
07364                     //Load them as well. Makes relative paths much easier
07365                     control->ParseConfigFile(config_combined.c_str());
07366                 }
07367             }
07368         }
07369 
07370         /* -- -- second the -conf switches from the command line */
07371         for (size_t si=0;si < control->config_file_list.size();si++) {
07372             std::string &cfg = control->config_file_list[si];
07373             if (!control->ParseConfigFile(cfg.c_str())) {
07374                 // try to load it from the user directory
07375                 control->ParseConfigFile((config_path + cfg).c_str());
07376                 if (!control->ParseConfigFile((config_path + cfg).c_str())) {
07377                 LOG_MSG("CONFIG: Can't open specified config file: %s",cfg.c_str());
07378                 }
07379             }
07380         }
07381 
07382         /* -- -- if none found, use dosbox.conf */
07383         if (!control->configfiles.size()) control->ParseConfigFile("dosbox.conf");
07384 
07385         /* -- -- if none found, use userlevel conf */
07386         if (!control->configfiles.size()) {
07387             tmp.clear();
07388             Cross::GetPlatformConfigName(tmp);
07389             control->ParseConfigFile((config_path + tmp).c_str());
07390         }
07391 
07392 #if (ENVIRON_LINKED)
07393         /* -- parse environment block (why?) */
07394         control->ParseEnv(environ);
07395 #endif
07396 
07397         /* -- initialize logging first, so that higher level inits can report problems to the log file */
07398         LOG::Init();
07399 
07400 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
07401         {
07402             DISPLAY_DEVICE dd;
07403             unsigned int i = 0;
07404 
07405             do {
07406                 memset(&dd, 0, sizeof(dd));
07407                 dd.cb = sizeof(dd);
07408                 if (!EnumDisplayDevices(NULL, i, &dd, 0)) break;
07409                 LOG_MSG("Win32 EnumDisplayDevices #%d: name=%s string=%s", i, dd.DeviceName, dd.DeviceString);
07410                 i++;
07411             } while (1);
07412         }
07413 
07414         if (isVirtualBox) LOG_MSG("Win32 VirtualBox graphics adapter detected");
07415 #endif
07416 
07417         /* -- Welcome to DOSBox-X! */
07418         LOG_MSG("DOSBox-X version %s",VERSION);
07419         LOG(LOG_MISC,LOG_NORMAL)("Copyright 2002-2019 enhanced branch by The Great Codeholio, forked from the main project by the DOSBox Team, published under GNU GPL.");
07420 
07421 #if defined(MACOSX)
07422         LOG_MSG("Mac OS X EXE path: %s",MacOSXEXEPath.c_str());
07423         LOG_MSG("Mac OS X Resource path: %s",MacOSXResPath.c_str());
07424 #endif
07425 
07426         /* -- [debug] setup console */
07427 #if C_DEBUG
07428 # if defined(WIN32) && !defined(HX_DOS)
07429         /* Can't disable the console with debugger enabled */
07430         if (control->opt_noconsole) {
07431             LOG(LOG_MISC,LOG_DEBUG)("-noconsole: hiding Win32 console window");
07432             ShowWindow(GetConsoleWindow(), SW_HIDE);
07433             DestroyWindow(GetConsoleWindow());
07434         }
07435 # endif
07436 #endif
07437 
07438 #if defined(WIN32)
07439         /* -- Windows: set console control handler */
07440         SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleEventHandler,TRUE);
07441 #endif
07442 
07443 #if !defined(C_SDL2)
07444         {
07445             int id, major, minor;
07446 
07447             DOSBox_CheckOS(id, major, minor);
07448             if (id == 1) menu.compatible=true;
07449 
07450             /* use all variables to shut up the compiler about unused vars */
07451             LOG(LOG_MISC,LOG_DEBUG)("DOSBox_CheckOS results: id=%u major=%u minor=%u",id,major,minor);
07452         }
07453 #endif
07454 
07455         /* -- SDL init hackery */
07456 #if SDL_VERSION_ATLEAST(1, 2, 14)
07457         /* hack: On debian/ubuntu with older libsdl version as they have done this themselves, but then differently.
07458          * with this variable they will work correctly. I've only tested the 1.2.14 behaviour against the windows version of libsdl */
07459         putenv(const_cast<char*>("SDL_DISABLE_LOCK_KEYS=1"));
07460         LOG(LOG_GUI,LOG_DEBUG)("SDL 1.2.14 hack: SDL_DISABLE_LOCK_KEYS=1");
07461 #endif
07462 
07463 #ifdef WIN32
07464         /* hack: Encourage SDL to use windib if not otherwise specified */
07465         if (getenv("SDL_VIDEODRIVER") == NULL) {
07466 #if defined(C_SDL2)
07467             LOG(LOG_GUI, LOG_DEBUG)("Win32 hack: setting SDL_VIDEODRIVER=windows because environ variable is not set");
07468             putenv("SDL_VIDEODRIVER=windows");
07469 #else
07470             LOG(LOG_GUI,LOG_DEBUG)("Win32 hack: setting SDL_VIDEODRIVER=windib because environ variable is not set");
07471             putenv("SDL_VIDEODRIVER=windib");
07472 #endif
07473             sdl.using_windib=true;
07474         }
07475 #endif
07476 
07477 #if defined(WIN32) && defined(C_SDL2)
07478         /* HACK: WASAPI output on Windows 10 isn't working... */
07479         if (getenv("SDL_AUDIODRIVER") == NULL) {
07480             LOG(LOG_GUI, LOG_DEBUG)("Win32: using directsound audio driver");
07481             putenv("SDL_AUDIODRIVER=directsound");
07482         }
07483 #endif
07484 
07485         sdl.init_ignore = true;
07486 
07487     {
07488         Section_prop *section = static_cast<Section_prop *>(control->GetSection("dosbox"));
07489         assert(section != NULL);
07490 
07491         // boot-time option whether or not to report ourself as "DPI aware" to Windows so the
07492         // DWM doesn't upscale our window for backwards compat.
07493         dpi_aware_enable = section->Get_bool("dpi aware");
07494     }
07495 
07496 #ifdef WIN32
07497         /* Windows Vista/7/8/10 DPI awareness. If we don't tell Windows we're high DPI aware, the DWM will
07498          * upscale our window to emulate a 96 DPI display which on high res screen will make our UI look blurry.
07499          * But we obey the user if they don't want us to do that. */
07500         Windows_DPI_Awareness_Init();
07501 #endif
07502 #if defined(MACOSX) && !defined(C_SDL2)
07503     /* Our SDL1 in-tree library has a High DPI awareness function for Mac OS X now */
07504         if (!control->opt_disable_dpi_awareness)
07505             sdl1_hax_macosx_highdpi_set_enable(dpi_aware_enable);
07506 #endif
07507 
07508 #ifdef MACOSX
07509         osx_detect_nstouchbar();/*assigns to has_touch_bar_support*/
07510         if (has_touch_bar_support) {
07511             LOG_MSG("Mac OS X: NSTouchBar support detected in system");
07512             osx_init_touchbar();
07513         }
07514 
07515         extern void osx_init_dock_menu(void);
07516         osx_init_dock_menu();
07517 
07518         void qz_set_match_monitor_cb(void);
07519         qz_set_match_monitor_cb();
07520 #endif
07521 
07522         /* -- SDL init */
07523         if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) >= 0)
07524             sdl.inited = true;
07525         else
07526             E_Exit("Can't init SDL %s",SDL_GetError());
07527 
07528         /* -- -- decide whether to show menu in GUI */
07529         if (control->opt_nogui || menu.compatible)
07530             menu.gui=false;
07531 
07532         /* -- -- helpful advice */
07533         LOG(LOG_GUI,LOG_NORMAL)("Press Ctrl-F10 to capture/release mouse, Alt-F10 for configuration.");
07534 
07535         /* -- -- other steps to prepare SDL window/output */
07536         SDL_Prepare();
07537 
07538         /* -- NOW it is safe to send change events to SDL */
07539         {
07540             Section_prop *sdl_sec = static_cast<Section_prop*>(control->GetSection("sdl"));
07541             sdl_sec->onpropchange.push_back(&SDL_OnSectionPropChange);
07542         }
07543 
07544         /* -- -- Keyboard layout detection and setup */
07545         KeyboardLayoutDetect();
07546         SetMapperKeyboardLayout(host_keyboard_layout);
07547 
07548         /* -- -- Initialise Joystick and CD-ROM seperately. This way we can warn when it fails instead of exiting the application */
07549         LOG(LOG_MISC,LOG_DEBUG)("Initializing SDL joystick subsystem...");
07550         if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0) {
07551             sdl.num_joysticks = (Bitu)SDL_NumJoysticks();
07552             LOG(LOG_MISC,LOG_DEBUG)("SDL reports %u joysticks",(unsigned int)sdl.num_joysticks);
07553         }
07554         else {
07555             LOG(LOG_GUI,LOG_WARN)("Failed to init joystick support");
07556             sdl.num_joysticks = 0;
07557         }
07558 
07559 #if !defined(C_SDL2)
07560         if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0) {
07561             LOG(LOG_GUI,LOG_WARN)("Failed to init CD-ROM support");
07562         }
07563 #endif
07564 
07565         /* must redraw after modeset */
07566         sdl.must_redraw_all = true;
07567         sdl.deferred_resize = false;
07568 
07569         /* assume L+R ALT keys are up */
07570         sdl.laltstate = SDL_KEYUP;
07571         sdl.raltstate = SDL_KEYUP;
07572 
07573 #if defined(WIN32) && !defined(C_SDL2)
07574 # if SDL_VERSION_ATLEAST(1, 2, 10)
07575         sdl.using_windib=true;
07576 # else
07577         sdl.using_windib=false;
07578 # endif
07579 
07580         if (getenv("SDL_VIDEODRIVER")==NULL) {
07581             putenv("SDL_VIDEODRIVER=windib");
07582             if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) E_Exit("Can't init SDL Video %s",SDL_GetError());
07583             sdl.using_windib=true;
07584         } else {
07585             char* sdl_videodrv = getenv("SDL_VIDEODRIVER");
07586 
07587             LOG(LOG_MISC,LOG_DEBUG)("Win32: SDL_VIDEODRIVER is '%s', so I will obey it",sdl_videodrv);
07588             if (strcmp(sdl_videodrv,"directx")==0) sdl.using_windib = false;
07589             else if (strcmp(sdl_videodrv,"windib")==0) sdl.using_windib = true;
07590         }
07591 #endif
07592 
07593         /* GUI init */
07594         GUI_StartUp();
07595 
07596         /* FIXME: We need a more general "init list", outside of the section-based design,
07597          *        that we then execute serially here. */
07598         /* TODO: Each section currently uses "AddDestroyFunction" per section. We need to
07599          *       change over that code to a global destroy callback list instead. */
07600         /* TODO: Get rid of "init" and "destroy" callback lists per section. */
07601         /* TODO: Add a global (within the Config object) init and destroy callback list.
07602          *       On each call, init functions are added to the end of the list, and
07603          *       destroy functions added to the beginning of the list. That way, init
07604          *       is lowest level to highest, destroy is highest level to lowest. */
07605         /* TODO: Config object should also have a "reset" callback list. On system
07606          *       reset each device would be notified so that it can emulate hardware
07607          *       reset (the RESET line on ISA/PCI bus), lowest level to highest. */
07608         /* TODO: Each "init" function should do the work of getting the section object,
07609          *       whatever section it wants to read, instead of us doing the work. When
07610          *       that's complete, the call to init should be without parameters (void).
07611          *       The hope is that the init functions can read whatever sections it wants,
07612          *       both newer DOSBox-X sections and existing DOSBox (mainline) compatible
07613          *       sections. */
07614 
07615         /* The order is important here:
07616          * Init functions are called low-level first to high level last,
07617          * because some init functions rely on others. */
07618 
07619 #if !defined(C_SDL2)
07620 # if defined(WIN32)
07621         Reflect_Menu();
07622 # endif
07623 #endif
07624 
07625         if (control->opt_startui)
07626             GUI_Run(false);
07627         if (control->opt_editconf.length() != 0)
07628             launcheditor(control->opt_editconf);
07629         if (control->opt_opencaptures.length() != 0)
07630             launchcaptures(control->opt_opencaptures);
07631         if (control->opt_opensaves.length() != 0)
07632             launchsaves(control->opt_opensaves);
07633 
07634         {
07635             /* Some extra SDL Functions */
07636             Section_prop *sdl_sec = static_cast<Section_prop*>(control->GetSection("sdl"));
07637 
07638             if (control->opt_fullscreen || sdl_sec->Get_bool("fullscreen")) {
07639                 LOG(LOG_MISC,LOG_DEBUG)("Going fullscreen immediately, during startup");
07640 
07641 #if !defined(C_SDL2)
07642                 void DOSBox_SetSysMenu(void);
07643                 DOSBox_SetSysMenu();
07644 #endif
07645                 //only switch if not already in fullscreen
07646                 if (!sdl.desktop.fullscreen) GFX_SwitchFullScreen();
07647             }
07648         }
07649 
07650         /* stock top-level menu items */
07651         {
07652             DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"MainMenu");
07653             item.set_text("Main");
07654             {
07655                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"MainSendKey");
07656                 item.set_text("Send Key");
07657             }
07658         }
07659         {
07660             DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CpuMenu");
07661             item.set_text("CPU");
07662             {
07663                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CpuCoreMenu");
07664                 item.set_text("CPU core");
07665             }
07666             {
07667                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CpuTypeMenu");
07668                 item.set_text("CPU type");
07669             }
07670         }
07671         {
07672             DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoMenu");
07673             item.set_text("Video");
07674             {
07675                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoFrameskipMenu");
07676                 item.set_text("Frameskip");
07677         
07678                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"frameskip_0").set_text("Off").
07679                     set_callback_function(video_frameskip_common_menu_callback);
07680 
07681                 for (unsigned int f=1;f <= 10;f++) {
07682                     char tmp1[64],tmp2[64];
07683 
07684                     sprintf(tmp1,"frameskip_%u",f);
07685                     sprintf(tmp2,"%u frame",f);
07686 
07687                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,tmp1).set_text(tmp2).
07688                         set_callback_function(video_frameskip_common_menu_callback);
07689                 }
07690             }
07691             {
07692                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoScalerMenu");
07693                 item.set_text("Scaler");
07694 
07695                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"scaler_forced").set_text("Force scaler").
07696                     set_callback_function(scaler_forced_menu_callback);
07697 
07698                 for (size_t i=0;scaler_menu_opts[i][0] != NULL;i++) {
07699                     const std::string name = std::string("scaler_set_") + scaler_menu_opts[i][0];
07700 
07701                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,name).set_text(scaler_menu_opts[i][1]).
07702                         set_callback_function(scaler_set_menu_callback);
07703                 }
07704             }
07705             {
07706                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoCompatMenu");
07707                 item.set_text("Compatibility");
07708 
07709                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vga_9widetext").set_text("Allow 9-pixel wide text mode").
07710                     set_callback_function(vga_9widetext_menu_callback);
07711                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"doublescan").set_text("Doublescan").
07712                     set_callback_function(doublescan_menu_callback);
07713             }
07714             {
07715                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoOutputMenu");
07716                 item.set_text("Output");
07717 
07718                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_surface").set_text("Surface").
07719                     set_callback_function(output_menu_callback);
07720                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_direct3d").set_text("Direct3D").
07721                     set_callback_function(output_menu_callback);
07722                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_opengl").set_text("OpenGL").
07723                     set_callback_function(output_menu_callback);
07724                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"output_openglnb").set_text("OpenGL NB").
07725                     set_callback_function(output_menu_callback);
07726             }
07727             {
07728                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoVsyncMenu");
07729                 item.set_text("V-Sync");
07730 
07731                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_on").set_text("On").
07732                     set_callback_function(vsync_menu_callback);
07733                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_force").set_text("Force").
07734                     set_callback_function(vsync_menu_callback);
07735                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_host").set_text("Host").
07736                     set_callback_function(vsync_menu_callback);
07737                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_off").set_text("Off").
07738                     set_callback_function(vsync_menu_callback);
07739                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"vsync_set_syncrate").set_text("Set syncrate").
07740                     set_callback_function(vsync_set_syncrate_menu_callback);
07741             }
07742             {
07743                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoOverscanMenu");
07744                 item.set_text("Overscan");
07745 
07746                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"overscan_0").set_text("Off").
07747                     set_callback_function(overscan_menu_callback);
07748 
07749                 for (size_t i=1;i <= 10;i++) {
07750                     char tmp1[64],tmp2[64];
07751 
07752                     sprintf(tmp1,"overscan_%zu",i);
07753                     sprintf(tmp2,"%zu",i);
07754                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,tmp1).set_text(tmp2).
07755                         set_callback_function(overscan_menu_callback);
07756                 }
07757             }
07758             {
07759                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoPC98Menu");
07760                 item.set_text("PC-98");
07761 
07762                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_5mhz_gdc").set_text("5MHz GDC clock").
07763                     set_callback_function(vid_pc98_5mhz_gdc_menu_callback);
07764                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_allow_200scanline").set_text("Allow 200-line scanline effect").
07765                     set_callback_function(vid_pc98_200scanline_menu_callback);
07766                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_allow_4partitions").set_text("Allow 4 display partitions in graphics layer").
07767                     set_callback_function(vid_pc98_4parts_menu_callback);
07768                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_egc").set_text("Enable EGC").
07769                     set_callback_function(vid_pc98_enable_egc_menu_callback);
07770                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_grcg").set_text("Enable GRCG").
07771                     set_callback_function(vid_pc98_enable_grcg_menu_callback);
07772                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_analog").set_text("Enable analog display").
07773                     set_callback_function(vid_pc98_enable_analog_menu_callback);
07774                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_analog256").set_text("Enable analog 256-color display").
07775                     set_callback_function(vid_pc98_enable_analog256_menu_callback);
07776                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_enable_188user").set_text("Enable 188+ user CG cells").
07777                     set_callback_function(vid_pc98_enable_188user_menu_callback);
07778                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_clear_text").set_text("Clear text layer").
07779                     set_callback_function(vid_pc98_cleartext_menu_callback);
07780                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"pc98_clear_graphics").set_text("Clear graphics layer").
07781                     set_callback_function(vid_pc98_graphics_menu_callback);
07782             }
07783             {
07784                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"VideoDebugMenu");
07785                 item.set_text("Debug");
07786             }
07787         }
07788         {
07789             DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"SoundMenu");
07790             item.set_text("Sound");
07791 
07792             {
07793                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"mixer_swapstereo").set_text("Swap stereo").
07794                     set_callback_function(mixer_swapstereo_menu_callback);
07795                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"mixer_mute").set_text("Mute").
07796                     set_callback_function(mixer_mute_menu_callback);
07797             }
07798         }
07799         {
07800             DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"DOSMenu");
07801             item.set_text("DOS");
07802 
07803             {
07804                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"DOSMouseMenu");
07805                 item.set_text("Mouse");
07806 
07807                 {
07808                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_mouse_enable_int33").set_text("Internal Emulation").
07809                         set_callback_function(dos_mouse_enable_int33_menu_callback);
07810                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_mouse_y_axis_reverse").set_text("Y-axis Reverse").
07811                         set_callback_function(dos_mouse_y_axis_reverse_menu_callback);
07812                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_mouse_sensitivity").set_text("Sensitivity").
07813                         set_callback_function(dos_mouse_sensitivity_menu_callback);
07814                 }
07815             }
07816 
07817             {
07818                 DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"DOSPC98Menu");
07819                 item.set_text("PC-98 PIT master clock");
07820 
07821                 {
07822                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_pc98_pit_4mhz").set_text("4MHz/8MHz").
07823                         set_callback_function(dos_pc98_clock_menu_callback);
07824                     mainMenu.alloc_item(DOSBoxMenu::item_type_id,"dos_pc98_pit_5mhz").set_text("5MHz/10MHz").
07825                         set_callback_function(dos_pc98_clock_menu_callback);
07826                 }
07827             }
07828         }
07829 #if !defined(C_EMSCRIPTEN)
07830         {
07831             DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CaptureMenu");
07832             item.set_text("Capture");
07833         }
07834 # if (C_SSHOT)
07835         {
07836             DOSBoxMenu::item &item = mainMenu.alloc_item(DOSBoxMenu::submenu_type_id,"CaptureFormatMenu");
07837             item.set_text("Capture format");
07838 
07839             {
07840                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"capture_fmt_avi_zmbv").set_text("AVI + ZMBV").
07841                     set_callback_function(capture_fmt_menu_callback);
07842 #  if (C_AVCODEC)
07843                 mainMenu.alloc_item(DOSBoxMenu::item_type_id,"capture_fmt_mpegts_h264").set_text("MPEG-TS + H.264").
07844                     set_callback_function(capture_fmt_menu_callback);
07845 #  endif
07846             }
07847         }
07848 # endif
07849 #endif
07850 
07851         /* Start up main machine */
07852 
07853         // Shows menu bar (window)
07854         menu.startup = true;
07855         menu.showrt = control->opt_showrt;
07856         menu.hidecycles = (control->opt_showcycles ? false : true);
07857 
07858 #if defined(WIN32) && !defined(C_SDL2)
07859         {
07860             Section_prop *sec = static_cast<Section_prop *>(control->GetSection("dosbox"));
07861             enable_hook_special_keys = sec->Get_bool("keyboard hook");
07862         }
07863 #endif
07864 
07865         MSG_Init();
07866         MAPPER_StartUp();
07867         DOSBOX_InitTickLoop();
07868         DOSBOX_RealInit();
07869 
07870         /* at this point: If the machine type is PC-98, and the mapper keyboard layout was "Japanese",
07871          * then change the mapper layout to "Japanese PC-98" */
07872         if (host_keyboard_layout == DKM_JPN && IS_PC98_ARCH)
07873             SetMapperKeyboardLayout(DKM_JPN_PC98);
07874 
07875         /* more */
07876         {
07877             DOSBoxMenu::item *item;
07878 
07879             MAPPER_AddHandler(&SetCyclesCount_mapper_shortcut, MK_nothing, 0, "editcycles", "EditCycles", &item);
07880             item->set_text("Edit cycles");
07881 
07882             MAPPER_AddHandler(&HideMenu_mapper_shortcut, MK_escape, MMODHOST, "togmenu", "TogMenu", &item);
07883             item->set_text("Hide/show menu bar");
07884 
07885             MAPPER_AddHandler(&PauseWithInterrupts_mapper_shortcut, MK_nothing, 0, "pauseints", "PauseInts", &item);
07886             item->set_text("Pause with interrupts enabled");
07887         }
07888 
07889         {
07890             DOSBoxMenu::item *item;
07891 
07892             MAPPER_AddHandler(&AspectRatio_mapper_shortcut, MK_nothing, 0, "aspratio", "AspRatio", &item);
07893             item->set_text("Fit to aspect ratio");
07894         }
07895 
07896         RENDER_Init();
07897         CAPTURE_Init();
07898         IO_Init();
07899         HARDWARE_Init();
07900         Init_AddressLimitAndGateMask(); /* <- need to init address mask so Init_RAM knows the maximum amount of RAM possible */
07901         Init_MemoryAccessArray(); /* <- NTS: In DOSBox-X this is the "cache" of devices that responded to memory access */
07902         Init_A20_Gate(); // FIXME: Should be handled by motherboard!
07903         Init_PS2_Port_92h(); // FIXME: Should be handled by motherboard!
07904         Init_RAM();
07905         Init_DMA();
07906         Init_PIC();
07907         TIMER_Init();
07908         PCIBUS_Init();
07909         PAGING_Init(); /* <- NTS: At this time, must come before memory init because paging is so well integrated into emulation code */
07910         CMOS_Init();
07911         ROMBIOS_Init();
07912         CALLBACK_Init(); /* <- NTS: This relies on ROM BIOS allocation and it must happen AFTER ROMBIOS init */
07913 #if C_DEBUG
07914         DEBUG_Init(); /* <- NTS: Relies on callback system */
07915 #endif
07916         Init_VGABIOS();
07917         VOODOO_Init();
07918         PROGRAMS_Init(); /* <- NTS: Does not init programs, it inits the callback used later when creating the .COM programs on drive Z: */
07919         PCSPEAKER_Init();
07920         TANDYSOUND_Init();
07921         MPU401_Init();
07922         MIXER_Init();
07923         MIDI_Init();
07924         CPU_Init();
07925 #if C_FPU
07926         FPU_Init();
07927 #endif
07928         VGA_Init();
07929         ISAPNP_Cfg_Init();
07930         FDC_Primary_Init();
07931         KEYBOARD_Init();
07932         SBLASTER_Init();
07933         JOYSTICK_Init();
07934         PS1SOUND_Init();
07935         DISNEY_Init();
07936         GUS_Init();
07937         IDE_Init();
07938         INNOVA_Init();
07939         BIOS_Init();
07940         INT10_Init();
07941         SERIAL_Init();
07942         DONGLE_Init();
07943 #if C_PRINTER
07944         PRINTER_Init();
07945 #endif
07946         PARALLEL_Init();
07947 #if C_NE2000
07948         NE2K_Init();
07949 #endif
07950 
07951 #if defined(WIN32) && !defined(C_SDL2)
07952         Reflect_Menu();
07953 #endif
07954 
07955         /* If PCjr emulation, map cartridge ROM */
07956         if (machine == MCH_PCJR)
07957             Init_PCJR_CartridgeROM();
07958 
07959         /* let's assume motherboards are sane on boot because A20 gate is ENABLED on first boot */
07960         MEM_A20_Enable(true);
07961 
07962         /* OS init now */
07963         DOS_Init();
07964         DRIVES_Init();
07965         DOS_KeyboardLayout_Init();
07966         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
07967         XMS_Init();
07968         EMS_Init();
07969         AUTOEXEC_Init();
07970 #if C_IPX
07971         IPX_Init();
07972 #endif
07973         MSCDEX_Init();
07974 
07975         /* Init memhandle system. This part is used by DOSBox's XMS/EMS emulation to associate handles
07976          * per page. FIXME: I would like to push this down to the point that it's never called until
07977          * XMS/EMS emulation needs it. I would also like the code to free the mhandle array immediately
07978          * upon booting into a guest OS, since memory handles no longer have meaning in the guest OS
07979          * memory layout. */
07980         Init_MemHandles();
07981 
07982         /* finally, the mapper */
07983         MAPPER_Init();
07984 
07985         /* stop at this point, and show the mapper, if instructed */
07986         if (control->opt_startmapper) {
07987             LOG(LOG_MISC,LOG_DEBUG)("Running mapper interface, during startup, as instructed");
07988             MAPPER_RunInternal();
07989         }
07990 
07991         /* more */
07992 #if !defined(C_EMSCRIPTEN)
07993         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"show_console").set_text("Show console").set_callback_function(show_console_menu_callback);
07994 #endif
07995         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);
07996         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);
07997         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_ctrlesc").set_text("Ctrl+Esc").set_callback_function(sendkey_preset_menu_callback);
07998         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_alttab").set_text("Alt+Tab").set_callback_function(sendkey_preset_menu_callback);
07999         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_winlogo").set_text("Logo key").set_callback_function(sendkey_preset_menu_callback);
08000         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_winmenu").set_text("Menu key").set_callback_function(sendkey_preset_menu_callback);
08001         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"sendkey_cad").set_text("Ctrl+Alt+Del").set_callback_function(sendkey_preset_menu_callback);
08002         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));
08003         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"alwaysontop").set_text("Always on top").set_callback_function(alwaysontop_menu_callback).check(is_always_on_top());
08004         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"showdetails").set_text("Show details").set_callback_function(showdetails_menu_callback).check(!menu.hidecycles && !menu.showrt);
08005         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"highdpienable").set_text("High DPI enable").set_callback_function(highdpienable_menu_callback).check(dpi_aware_enable);
08006 
08007         mainMenu.get_item("mapper_blankrefreshtest").set_text("Refresh test (blank display)").set_callback_function(refreshtest_menu_callback).refresh_item(mainMenu);
08008 
08009         bool MENU_get_swapstereo(void);
08010         mainMenu.get_item("mixer_swapstereo").check(MENU_get_swapstereo()).refresh_item(mainMenu);
08011 
08012         bool MENU_get_mute(void);
08013         mainMenu.get_item("mixer_mute").check(MENU_get_mute()).refresh_item(mainMenu);
08014 
08015         mainMenu.get_item("scaler_forced").check(render.scale.forced);
08016 
08017         mainMenu.get_item("vga_9widetext").enable(!IS_PC98_ARCH);
08018         mainMenu.get_item("doublescan").enable(!IS_PC98_ARCH);
08019 
08020         mainMenu.get_item("pc98_5mhz_gdc").enable(IS_PC98_ARCH);
08021         mainMenu.get_item("pc98_allow_200scanline").enable(IS_PC98_ARCH);
08022         mainMenu.get_item("pc98_allow_4partitions").enable(IS_PC98_ARCH);
08023         mainMenu.get_item("pc98_enable_egc").enable(IS_PC98_ARCH);
08024         mainMenu.get_item("pc98_enable_grcg").enable(IS_PC98_ARCH);
08025         mainMenu.get_item("pc98_enable_analog").enable(IS_PC98_ARCH);
08026         mainMenu.get_item("pc98_enable_analog256").enable(IS_PC98_ARCH);
08027         mainMenu.get_item("pc98_enable_188user").enable(IS_PC98_ARCH);
08028         mainMenu.get_item("pc98_clear_text").enable(IS_PC98_ARCH);
08029         mainMenu.get_item("pc98_clear_graphics").enable(IS_PC98_ARCH);
08030         mainMenu.get_item("dos_pc98_pit_4mhz").enable(IS_PC98_ARCH);
08031         mainMenu.get_item("dos_pc98_pit_5mhz").enable(IS_PC98_ARCH);
08032 
08033         extern bool Mouse_Vertical;
08034         extern bool Mouse_Drv;
08035 
08036         mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu);
08037         mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu);
08038 #if !defined(C_EMSCRIPTEN)
08039         mainMenu.get_item("show_console").check(showconsole_init).refresh_item(mainMenu);
08040 #endif
08041 
08042         OutputSettingMenuUpdate();
08043         update_pc98_clock_pit_menu();
08044 #if !defined(C_EMSCRIPTEN)
08045         update_capture_fmt_menu();
08046 #endif
08047 
08048         /* The machine just "powered on", and then reset finished */
08049         if (!VM_PowerOn()) E_Exit("VM failed to power on");
08050 
08051         /* go! */
08052         sdl.init_ignore = false;
08053         UpdateWindowDimensions();
08054         userResizeWindowWidth = 0;
08055         userResizeWindowHeight = 0;
08056 
08057         UpdateOverscanMenu();
08058 
08059         void GUI_ResetResize(bool pressed);
08060         GUI_ResetResize(true);
08061 
08062         void ConstructMenu(void);
08063         ConstructMenu();
08064 
08065 #if 0
08066         mainMenu.dump_log_debug(); /*DEBUG*/
08067 #endif
08068         mainMenu.rebuild();
08069 
08070 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
08071         mainMenu.screenWidth = (unsigned int)sdl.surface->w;
08072         mainMenu.screenHeight = (unsigned int)sdl.surface->h;
08073         mainMenu.updateRect();
08074 #endif
08075 #if defined(WIN32) && !defined(HX_DOS)
08076         /* Windows 7 taskbar extension support */
08077         {
08078             HRESULT hr;
08079 
08080             hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_SERVER, IID_ITaskbarList3, (LPVOID*)(&winTaskbarList));
08081             if (hr == S_OK) {
08082                 LOG_MSG("Windows: IID_ITaskbarList3 is available");
08083 
08084 #if !defined(C_SDL2)
08085                 THUMBBUTTON buttons[8];
08086                 int buttoni = 0;
08087 
08088                 {
08089                     THUMBBUTTON &b = buttons[buttoni++];
08090                     memset(&b, 0, sizeof(b));
08091                     b.iId = ID_WIN_SYSMENU_MAPPER;
08092                     b.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAPPER));
08093                     b.dwMask = THB_TOOLTIP | THB_FLAGS | THB_ICON;
08094                     b.dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
08095                     wcscpy(b.szTip, L"Mapper");
08096                 }
08097 
08098                 {
08099                     THUMBBUTTON &b = buttons[buttoni++];
08100                     memset(&b, 0, sizeof(b));
08101                     b.iId = ID_WIN_SYSMENU_CFG_GUI;
08102                     b.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_CFG_GUI));
08103                     b.dwMask = THB_TOOLTIP | THB_FLAGS | THB_ICON;
08104                     b.dwFlags = THBF_ENABLED | THBF_DISMISSONCLICK;
08105                     wcscpy(b.szTip, L"Configuration GUI");
08106                 }
08107 
08108                 {
08109                     THUMBBUTTON &b = buttons[buttoni++];
08110                     memset(&b, 0, sizeof(b));
08111                     b.iId = 1;
08112                     b.dwMask = THB_FLAGS;
08113                     b.dwFlags = THBF_DISABLED | THBF_NONINTERACTIVE | THBF_NOBACKGROUND;
08114                 }
08115 
08116                 {
08117                     THUMBBUTTON &b = buttons[buttoni++];
08118                     memset(&b, 0, sizeof(b));
08119                     b.iId = ID_WIN_SYSMENU_PAUSE;
08120                     b.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_PAUSE));
08121                     b.dwMask = THB_TOOLTIP | THB_FLAGS | THB_ICON;
08122                     b.dwFlags = THBF_ENABLED;
08123                     wcscpy(b.szTip, L"Pause");
08124                 }
08125 
08126                 winTaskbarList->ThumbBarAddButtons(GetHWND(), buttoni, buttons);
08127 #endif
08128             }
08129         }
08130 #endif
08131         {
08132             Section_prop *section = static_cast<Section_prop *>(control->GetSection("SDL"));
08133             assert(section != NULL);
08134 
08135             bool cfg_want_menu = section->Get_bool("showmenu");
08136 
08137             /* -- -- decide whether to set menu */
08138             if (menu_gui && !control->opt_nomenu && cfg_want_menu)
08139                 DOSBox_SetMenu();
08140             else
08141                 DOSBox_NoMenu();
08142         }
08143 
08144 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
08145         int Reflect_Menu(void);
08146         Reflect_Menu();
08147 #endif
08148 
08149         bool reboot_dos;
08150         bool run_machine;
08151         bool wait_debugger;
08152         bool reboot_machine;
08153         bool dos_kernel_shutdown;
08154 
08155 fresh_boot:
08156         reboot_dos = false;
08157         run_machine = false;
08158         wait_debugger = false;
08159         reboot_machine = false;
08160         dos_kernel_shutdown = false;
08161         guest_msdos_mcb_chain = (Bit16u)(~0u);
08162 
08163         /* NTS: CPU reset handler, and BIOS init, has the instruction pointer poised to run through BIOS initialization,
08164          *      which will then "boot" into the DOSBox kernel, and then the shell, by calling VM_Boot_DOSBox_Kernel() */
08165         /* FIXME: throwing int() is a stupid and nondescriptive way to signal shutdown/reset. */
08166         try {
08167 #if C_DEBUG
08168             if (control->opt_break_start) DEBUG_EnableDebugger();
08169 #endif
08170             DOSBOX_RunMachine();
08171         } catch (int x) {
08172             if (x == 2) { /* booting a guest OS. "boot" has already done the work to load the image and setup CPU registers */
08173                 LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to boot guest OS");
08174 
08175                 run_machine = true; /* make note. don't run the whole shebang from an exception handler! */
08176                 dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
08177             }
08178             else if (x == 3) { /* reboot the system */
08179                 LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot the system");
08180 
08181                 reboot_machine = true;
08182                 dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
08183             }
08184             else if (x == 5) { /* go to PC-98 mode */
08185                 E_Exit("Obsolete int signal");
08186             }
08187             else if (x == 6) { /* reboot DOS kernel */
08188                 LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot DOS kernel");
08189 
08190                 reboot_dos = true;
08191                 dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
08192             }
08193             else if (x == 7) { /* DOS kernel corruption error (need to restart the DOS kernel) */
08194                 LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot DOS kernel");
08195 
08196                 reboot_dos = true;
08197                 wait_debugger = true;
08198                 dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
08199             }
08200             else if (x == 8) { /* Booting to a BIOS, shutting down DOSBox BIOS */
08201                 LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to boot into BIOS image");
08202 
08203                 reboot_machine = true;
08204                 dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
08205             }
08206             else {
08207                 LOG(LOG_MISC,LOG_DEBUG)("Emulation threw DOSBox kill switch signal");
08208 
08209                 // kill switch (see instances of throw(0) and throw(1) elsewhere in DOSBox)
08210                 run_machine = false;
08211                 dos_kernel_shutdown = false;
08212             }
08213         }
08214         catch (...) {
08215             throw;
08216         }
08217 
08218 #if defined(WIN32) && !defined(C_SDL2)
08219         int Reflect_Menu(void);
08220         Reflect_Menu();
08221 #endif
08222 
08223         if (dos_kernel_shutdown) {
08224             /* NTS: we take different paths depending on whether we're just shutting down DOS
08225              *      or doing a hard reboot. */
08226 
08227             if (wait_debugger) {
08228 #if C_DEBUG
08229                 Bitu DEBUG_EnableDebugger(void);
08230                 void DEBUG_WaitNoExecute(void);
08231 
08232                 LOG_MSG("Starting debugger.");
08233                 DEBUG_EnableDebugger();
08234                 DEBUG_WaitNoExecute();
08235 #endif
08236             }
08237 
08238             /* new code: fire event */
08239             if (reboot_machine)
08240                 DispatchVMEvent(VM_EVENT_DOS_EXIT_REBOOT_BEGIN);
08241             else
08242                 DispatchVMEvent(VM_EVENT_DOS_EXIT_BEGIN);
08243 
08244             /* older shutdown code */
08245             RemoveEMSPageFrame();
08246 
08247             /* remove UMB block */
08248             if (!keep_umb_on_boot) RemoveUMBBlock();
08249 
08250             /* disable INT 33h mouse services. it can interfere with guest OS paging and control of the mouse */
08251             DisableINT33();
08252 
08253             /* unmap the DOSBox kernel private segment. if the user told us not to,
08254              * but the segment exists below 640KB, then we must, because the guest OS
08255              * will trample it and assume control of that region of RAM. */
08256             if (!keep_private_area_on_boot || reboot_machine)
08257                 DOS_GetMemory_unmap();
08258             else if (DOS_PRIVATE_SEGMENT < 0xA000)
08259                 DOS_GetMemory_unmap();
08260 
08261             /* revector some dos-allocated interrupts */
08262             if (!reboot_machine) {
08263                 real_writed(0,0x01*4,(Bit32u)BIOS_DEFAULT_HANDLER_LOCATION);
08264                 real_writed(0,0x03*4,(Bit32u)BIOS_DEFAULT_HANDLER_LOCATION);
08265             }
08266 
08267             /* shutdown DOSBox's virtual drive Z */
08268             VFILE_Shutdown();
08269 
08270             /* shutdown the programs */
08271             PROGRAMS_Shutdown();        /* FIXME: Is this safe? Or will this cause use-after-free bug? */
08272 
08273             /* remove environment variables for some components */
08274             DOS_UninstallMisc();
08275             SBLASTER_DOS_Shutdown();
08276             GUS_DOS_Shutdown();
08277             /* disable Expanded Memory. EMM is a DOS API, not a BIOS API */
08278             EMS_DoShutDown();
08279             /* and XMS, also a DOS API */
08280             XMS_DoShutDown();
08281             /* and the DOS API in general */
08282             DOS_DoShutDown();
08283 
08284             /* mem handles too */
08285             ShutDownMemHandles(NULL);
08286 
08287             /* set the "disable DOS kernel" flag so other parts of this program
08288              * do not attempt to manipulate now-defunct parts of the kernel
08289              * such as the environment block */
08290             dos_kernel_disabled = true;
08291 
08292             /* new code: fire event */
08293             if (reboot_machine)
08294                 DispatchVMEvent(VM_EVENT_DOS_EXIT_REBOOT_KERNEL);
08295             else
08296                 DispatchVMEvent(VM_EVENT_DOS_EXIT_KERNEL);
08297 
08298 #if defined(WIN32) && !defined(C_SDL2)
08299             int Reflect_Menu(void);
08300             Reflect_Menu();
08301 #endif
08302         }
08303 
08304 #if defined(WIN32) && !defined(C_SDL2)
08305         int Reflect_Menu(void);
08306         Reflect_Menu();
08307 #endif
08308 
08309         if (run_machine) {
08310             bool disable_a20 = static_cast<Section_prop *>(control->GetSection("dosbox"))->Get_bool("turn off a20 gate on boot");
08311 
08312             /* if instructed, turn off A20 at boot */
08313             if (disable_a20) MEM_A20_Enable(false);
08314 
08315             /* PC-98: hide the cursor */
08316             if (IS_PC98_ARCH) {
08317                 void PC98_show_cursor(bool show);
08318                 PC98_show_cursor(false);
08319             }
08320 
08321             /* new code: fire event */
08322             DispatchVMEvent(VM_EVENT_GUEST_OS_BOOT);
08323 
08324             LOG_MSG("Alright: DOS kernel shutdown, booting a guest OS\n");
08325             LOG_MSG("  CS:IP=%04x:%04x SS:SP=%04x:%04x AX=%04x BX=%04x CX=%04x DX=%04x\n",
08326                 SegValue(cs),reg_ip,
08327                 SegValue(ss),reg_sp,
08328                 reg_ax,reg_bx,reg_cx,reg_dx);
08329 
08330 #if C_DEBUG
08331             if (boot_debug_break) {
08332                 boot_debug_break = false;
08333 
08334                 Bitu DEBUG_EnableDebugger(void);
08335                 DEBUG_EnableDebugger();
08336             }
08337 #endif
08338 
08339             /* run again */
08340             goto fresh_boot;
08341         }
08342 
08343 #if defined(WIN32) && !defined(C_SDL2)
08344         int Reflect_Menu(void);
08345         Reflect_Menu();
08346 #endif
08347 
08348         if (reboot_machine) {
08349             LOG_MSG("Rebooting the system\n");
08350 
08351             boothax = BOOTHAX_NONE;
08352             guest_msdos_LoL = 0;
08353             guest_msdos_mcb_chain = 0;
08354 
08355             void CPU_Snap_Back_Forget();
08356             /* Shutdown everything. For shutdown to work properly we must force CPU to real mode */
08357             CPU_Snap_Back_To_Real_Mode();
08358             CPU_Snap_Back_Forget();
08359 
08360             /* new code: fire event */
08361             DispatchVMEvent(VM_EVENT_RESET);
08362 
08363             extern bool custom_bios;
08364             if (custom_bios) {
08365                 /* need to relocate BIOS allocations */
08366                 void ROMBIOS_InitForCustomBIOS(void);
08367                 ROMBIOS_InitForCustomBIOS();
08368 
08369                 void CALLBACK_Init();
08370                 CALLBACK_Init();
08371 
08372 #if C_DEBUG
08373                 void DEBUG_ReinitCallback(void);
08374                 DEBUG_ReinitCallback();
08375 #endif
08376             }
08377 
08378             DispatchVMEvent(VM_EVENT_RESET_END);
08379 
08380             /* HACK: EGA/VGA modes will need VGA BIOS mapped in, ready to go */
08381             if (IS_EGAVGA_ARCH) {
08382                 void INT10_Startup(Section *sec);
08383                 INT10_Startup(NULL);
08384             }
08385 
08386 #if C_DEBUG
08387             if (boot_debug_break) {
08388                 boot_debug_break = false;
08389 
08390                 Bitu DEBUG_EnableDebugger(void);
08391                 DEBUG_EnableDebugger();
08392             }
08393 #endif
08394 
08395             /* run again */
08396             goto fresh_boot;
08397         }
08398         else if (reboot_dos) { /* typically (at this time) to enter/exit PC-98 mode */
08399             LOG_MSG("Rebooting DOS\n");
08400 
08401             void CPU_Snap_Back_Forget();
08402             /* Shutdown everything. For shutdown to work properly we must force CPU to real mode */
08403             CPU_Snap_Back_To_Real_Mode();
08404             CPU_Snap_Back_Forget();
08405 
08406             /* all hardware devices need to know to reregister themselves PC-98 style */
08407 
08408             /* begin booting DOS again. */
08409             void BIOS_Enter_Boot_Phase(void);
08410             BIOS_Enter_Boot_Phase();
08411 
08412             /* run again */
08413             goto fresh_boot;
08414         }
08415 
08416 #if defined(WIN32) && !defined(C_SDL2)
08417         int Reflect_Menu(void);
08418         Reflect_Menu();
08419 #endif
08420 
08421         /* and then shutdown */
08422         GFX_ShutDown();
08423 
08424         void CPU_Snap_Back_Forget();
08425         /* Shutdown everything. For shutdown to work properly we must force CPU to real mode */
08426         CPU_Snap_Back_To_Real_Mode();
08427         CPU_Snap_Back_Forget();
08428 
08429         /* NTS: The "control" object destructor is called here because the "myconf" object leaves scope.
08430          * The destructor calls all section destroy functions here. After this point, all sections have
08431          * freed resources. */
08432     }
08433 
08434     void CALLBACK_Dump(void);
08435     CALLBACK_Dump();
08436 
08437     /* GUI font registry shutdown */
08438 #if !defined(C_SDL2)
08439     GUI::Font::registry_freeall();
08440 #endif
08441     DOS_ShutdownDrives();
08442     DOS_ShutdownFiles();
08443     DOS_ShutdownDevices();
08444     CALLBACK_Shutdown();
08445 #if C_DYNAMIC_X86
08446     CPU_Core_Dyn_X86_Shutdown();
08447 #endif
08448     FreeBIOSDiskList();
08449     MAPPER_Shutdown();
08450     VFILE_Shutdown();
08451     PROGRAMS_Shutdown();
08452     TIMER_ShutdownTickHandlers();
08453 #if C_DEBUG
08454     DEBUG_ShutDown(NULL);
08455 #endif
08456 
08457     sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt
08458 
08459     //Force visible mouse to end user. Somehow this sometimes doesn't happen
08460 #if defined(C_SDL2)
08461     SDL_SetRelativeMouseMode(SDL_FALSE);
08462 #else
08463     SDL_WM_GrabInput(SDL_GRAB_OFF);
08464 #endif
08465     SDL_ShowCursor(SDL_ENABLE);
08466 
08467     /* Exit functions */
08468     while (!exitfunctions.empty()) {
08469         Function_wrapper &ent = exitfunctions.front();
08470 
08471         LOG(LOG_MISC,LOG_DEBUG)("Calling exit function (%p) '%s'",(void*)((uintptr_t)ent.function),ent.name.c_str());
08472         ent.function(NULL);
08473         exitfunctions.pop_front();
08474     }
08475 
08476     LOG::Exit();
08477 
08478 #if defined(WIN32) && !defined(C_SDL2)
08479 # if !defined(HX_DOS)
08480     ShowWindow(GetHWND(), SW_HIDE);
08481     SDL1_hax_SetMenu(NULL);/* detach menu from window, or else Windows will destroy the menu out from under the C++ class */
08482 # endif
08483 #endif
08484 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
08485     void sdl_hax_macosx_setmenu(void *nsMenu);
08486     sdl_hax_macosx_setmenu(NULL);
08487 #endif
08488 
08489     SDL_Quit();//Let's hope sdl will quit as well when it catches an exception
08490 
08491 #if defined(WIN32) && !defined(HX_DOS)
08492     if (winTaskbarList != NULL) {
08493         winTaskbarList->Release();
08494         winTaskbarList = NULL;
08495     }
08496 #endif
08497 
08498     mainMenu.unbuild();
08499     mainMenu.clear_all_menu_items();
08500 
08501     return 0;
08502 }
08503 
08504 void GFX_GetSizeAndPos(int &x,int &y,int &width, int &height, bool &fullscreen) {
08505     x = sdl.clip.x;
08506     y = sdl.clip.y;
08507     width = sdl.clip.w; // draw.width
08508     height = sdl.clip.h; // draw.height
08509     fullscreen = sdl.desktop.fullscreen;
08510 }
08511 
08512 void GFX_GetSize(int &width, int &height, bool &fullscreen) {
08513     width = sdl.clip.w; // draw.width
08514     height = sdl.clip.h; // draw.height
08515     fullscreen = sdl.desktop.fullscreen;
08516 }
08517 
08518 void GFX_ShutDown(void) {
08519     LOG(LOG_MISC,LOG_DEBUG)("Shutting down GFX renderer");
08520     GFX_Stop();
08521     if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop );
08522     if (sdl.mouse.locked) GFX_CaptureMouse();
08523     if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
08524 }
08525 
08526 bool OpenGL_using(void) {
08527 #if C_OPENGL
08528     return (sdl.desktop.want_type==SCREEN_OPENGL?true:false);
08529 #else
08530     return false;
08531 #endif
08532 }
08533 
08534 bool Get_Custom_SaveDir(std::string& savedir) {
08535     (void)savedir;//UNUSED
08536     if (custom_savedir.length() != 0)
08537         return true;
08538 
08539     return false;
08540 }
08541 
08542 void GUI_ResetResize(bool pressed) {
08543     void RENDER_CallBack( GFX_CallBackFunctions_t function );
08544 
08545     if (!pressed) return;
08546     userResizeWindowWidth = 0;
08547     userResizeWindowHeight = 0;
08548 
08549     if (GFX_GetPreventFullscreen())
08550         return;
08551 
08552     if (sdl.updating && !GFX_MustActOnResize()) {
08553         /* act on resize when updating is complete */
08554         sdl.deferred_resize = true;
08555     }
08556     else {
08557         sdl.deferred_resize = false;
08558         RENDER_CallBack(GFX_CallBackReset);
08559     }
08560 }
08561 
08562 bool MOUSE_IsLocked()
08563 {
08564     return sdl.mouse.locked;
08565 }
08566 
08567 #if defined(C_SDL2) && defined(C_OPENGL)/*HACK*/
08568 void SDL_GL_SwapBuffers(void) {
08569     SDL_GL_SwapWindow(sdl.window);
08570 }
08571 #endif
08572