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