mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-09 20:01:19 +08:00
9546 lines
364 KiB
C++
9546 lines
364 KiB
C++
/** \mainpage DOSBox-X emulation
|
|
*
|
|
* \section i Introduction
|
|
*
|
|
* \section f Features
|
|
*
|
|
* \li Complete and accurate x86/DOS emulation
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2002-2021 The DOSBox Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
# ifndef WIN32_LEAN_AND_MEAN
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef OS2
|
|
# define INCL_DOS
|
|
# define INCL_WIN
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
#ifndef S_ISDIR
|
|
#define S_ISDIR(m) (((m)&S_IFMT)==S_IFDIR)
|
|
#endif
|
|
#endif
|
|
|
|
int socknum=-1;
|
|
int posx = -1;
|
|
int posy = -1;
|
|
int initgl = 0;
|
|
int transparency=0;
|
|
int selsrow = -1, selscol = -1;
|
|
int selerow = -1, selecol = -1;
|
|
int middleunlock = 1;
|
|
bool rtl = false;
|
|
bool selmark = false;
|
|
extern bool testerr;
|
|
extern bool blinking;
|
|
extern bool dpi_aware_enable;
|
|
extern bool log_int21;
|
|
extern bool log_fileio;
|
|
extern bool ticksLocked;
|
|
extern bool isJPkeyboard;
|
|
extern bool enable_autosave;
|
|
extern bool noremark_save_state;
|
|
extern bool use_quick_reboot, skipdraw;
|
|
extern bool force_load_state, force_conversion;
|
|
extern bool pc98_force_ibm_layout, gbk, chinasea;
|
|
extern bool inshell, enable_config_as_shell_commands;
|
|
extern bool showdbcs, switchttf, ttfswitch, switch_output_from_ttf;
|
|
bool checkmenuwidth = false;
|
|
bool dos_kernel_disabled = true;
|
|
bool winrun=false, use_save_file=false;
|
|
bool maximize = false, tooutttf = false;
|
|
bool usesystemcursor = false, enableime = false;
|
|
bool mountfro[26], mountiro[26];
|
|
bool OpenGL_using(void), Direct3D_using(void);
|
|
void DOSBox_SetSysMenu(void), GFX_OpenGLRedrawScreen(void), InitFontHandle(void), DOSV_FillScreen(void), SetWindowTransparency(int trans);
|
|
void MenuBrowseProgramFile(void), OutputSettingMenuUpdate(void), aspect_ratio_menu(void), update_pc98_clock_pit_menu(void), AllocCallback1(void), AllocCallback2(void), ToggleMenu(bool pressed);
|
|
int Reflect_Menu(void);
|
|
|
|
#ifndef _GNU_SOURCE
|
|
# define _GNU_SOURCE
|
|
#endif
|
|
|
|
// Tell macOS to shut up about deprecated OpenGL calls
|
|
#ifndef GL_SILENCE_DEPRECATION
|
|
#define GL_SILENCE_DEPRECATION
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <sys/types.h>
|
|
#include <algorithm> // std::transform
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <gtest/gtest.h>
|
|
#ifdef WIN32
|
|
# include <signal.h>
|
|
# include <process.h>
|
|
# if !defined(__MINGW32__) /* MinGW does not have these headers */
|
|
# include <shcore.h>
|
|
# include <shellscalingapi.h>
|
|
# endif
|
|
#endif
|
|
|
|
#include "control.h"
|
|
#include "dosbox.h"
|
|
#include "menudef.h"
|
|
#include "pic.h"
|
|
#include "timer.h"
|
|
#include "setup.h"
|
|
#include "bios.h"
|
|
#include "callback.h"
|
|
#include "support.h"
|
|
#include "debug.h"
|
|
#include "ide.h"
|
|
#include "bitop.h"
|
|
#include "ptrop.h"
|
|
#include "mapper.h"
|
|
#include "sdlmain.h"
|
|
#include "zipfile.h"
|
|
#include "glidedef.h"
|
|
#include "bios_disk.h"
|
|
#include "inout.h"
|
|
#include "jfont.h"
|
|
#include "render.h"
|
|
#include "../dos/cdrom.h"
|
|
#include "../dos/drives.h"
|
|
#include "../ints/int10.h"
|
|
#if !defined(HX_DOS)
|
|
#if !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)
|
|
#include "whereami.c"
|
|
#endif
|
|
#include "../libs/tinyfiledialogs/tinyfiledialogs.h"
|
|
#endif
|
|
#if C_DEBUG
|
|
#include "display2.cpp"
|
|
#endif
|
|
|
|
#if defined(LINUX) && defined(HAVE_ALSA)
|
|
# include <alsa/asoundlib.h>
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
# include <shobjidl.h>
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
#include "resource.h"
|
|
#if !defined(HX_DOS)
|
|
BOOL CALLBACK EnumDispProc(HMONITOR hMon, HDC dcMon, RECT* pRcMon, LPARAM lParam) {
|
|
xyp* xy = reinterpret_cast<xyp*>(lParam);
|
|
curscreen++;
|
|
if (sdl.displayNumber==curscreen) monrect=*pRcMon;
|
|
return TRUE;
|
|
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(WIN32) && defined(__MINGW32__) /* MinGW does not have IID_ITaskbarList3 */
|
|
/* MinGW now contains this, the older MinGW for HX-DOS does not.
|
|
* Keep things simple and just #define around it like this */
|
|
static const GUID __my_CLSID_TaskbarList ={ 0x56FDF344,0xFD6D,0x11d0,{0x95,0x8A,0x00,0x60,0x97,0xC9,0xA0,0x90}};
|
|
# define CLSID_TaskbarList __my_CLSID_TaskbarList
|
|
|
|
static const GUID __my_IID_ITaskbarList3 = { 0xEA1AFB91ul,0x9E28u,0x4B86u,0x90u,0xE9u,0x9Eu,0x9Fu,0x8Au,0x5Eu,0xEFu,0xAFu };
|
|
# define IID_ITaskbarList3 __my_IID_ITaskbarList3
|
|
#endif
|
|
|
|
#if defined(WIN32) && defined(__MINGW32__) /* MinGW does not have this */
|
|
typedef enum PROCESS_DPI_AWARENESS {
|
|
PROCESS_DPI_UNAWARE = 0,
|
|
PROCESS_SYSTEM_DPI_AWARE = 1,
|
|
PROCESS_PER_MONITOR_DPI_AWARE = 2
|
|
} PROCESS_DPI_AWARENESS;
|
|
#endif
|
|
|
|
#if C_EMSCRIPTEN
|
|
# include <emscripten.h>
|
|
#endif
|
|
|
|
#include "../src/libs/gui_tk/gui_tk.h"
|
|
|
|
#ifdef __WIN32__
|
|
# include "callback.h"
|
|
# include "dos_inc.h"
|
|
# include <malloc.h>
|
|
# include "Commdlg.h"
|
|
# include "windows.h"
|
|
# include "Shellapi.h"
|
|
# include <cstring>
|
|
# include <fstream>
|
|
# if defined(__MINGW32__) && !defined(HX_DOS)
|
|
# include <imm.h> // input method editor
|
|
# endif
|
|
#endif // WIN32
|
|
|
|
#include <sstream>
|
|
|
|
#include "mapper.h"
|
|
#include "vga.h"
|
|
#include "keyboard.h"
|
|
#include "cpu.h"
|
|
#include "fpu.h"
|
|
#include "cross.h"
|
|
#include "keymap.h"
|
|
#include "voodoo.h"
|
|
#if C_OPENGL
|
|
#include "../hardware/voodoo_types.h"
|
|
#include "../hardware/voodoo_data.h"
|
|
#include "../hardware/voodoo_opengl.h"
|
|
#endif
|
|
|
|
#if defined(MACOSX) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
extern "C" void sdl1_hax_macosx_highdpi_set_enable(const bool enable);
|
|
#endif
|
|
|
|
#if !defined(C_SDL2) && !defined(RISCOS)
|
|
# include "SDL_version.h"
|
|
# ifndef SDL_DOSBOX_X_SPECIAL
|
|
# warning It is STRONGLY RECOMMENDED to compile the DOSBox-X code using the SDL 1.x library provided in this source repository.
|
|
#if !defined(__FreeBSD__)
|
|
# error You can ignore this by commenting out this error, but you will encounter problems if you use the unmodified SDL 1.x library.
|
|
#endif
|
|
# endif
|
|
#endif
|
|
|
|
#include "sdlmain.h"
|
|
#include "build_timestamp.h"
|
|
|
|
#if C_OPENGL
|
|
namespace gl2 {
|
|
extern PFNGLATTACHSHADERPROC glAttachShader;
|
|
extern PFNGLCOMPILESHADERPROC glCompileShader;
|
|
extern PFNGLCREATEPROGRAMPROC glCreateProgram;
|
|
extern PFNGLCREATESHADERPROC glCreateShader;
|
|
extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
|
|
extern PFNGLDELETESHADERPROC glDeleteShader;
|
|
extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
|
|
extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
|
|
extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
|
|
extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
|
|
extern PFNGLGETSHADERIVPROC glGetShaderiv;
|
|
extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
|
|
extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
|
|
extern PFNGLLINKPROGRAMPROC glLinkProgram;
|
|
extern PFNGLSHADERSOURCEPROC_NP glShaderSource;
|
|
extern PFNGLUNIFORM2FPROC glUniform2f;
|
|
extern PFNGLUNIFORM1IPROC glUniform1i;
|
|
extern PFNGLUSEPROGRAMPROC glUseProgram;
|
|
extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
|
|
}
|
|
|
|
#define glAttachShader gl2::glAttachShader
|
|
#define glCompileShader gl2::glCompileShader
|
|
#define glCreateProgram gl2::glCreateProgram
|
|
#define glCreateShader gl2::glCreateShader
|
|
#define glDeleteProgram gl2::glDeleteProgram
|
|
#define glDeleteShader gl2::glDeleteShader
|
|
#define glEnableVertexAttribArray gl2::glEnableVertexAttribArray
|
|
#define glGetAttribLocation gl2::glGetAttribLocation
|
|
#define glGetProgramiv gl2::glGetProgramiv
|
|
#define glGetProgramInfoLog gl2::glGetProgramInfoLog
|
|
#define glGetShaderiv gl2::glGetShaderiv
|
|
#define glGetShaderInfoLog gl2::glGetShaderInfoLog
|
|
#define glGetUniformLocation gl2::glGetUniformLocation
|
|
#define glLinkProgram gl2::glLinkProgram
|
|
#define glShaderSource gl2::glShaderSource
|
|
#define glUniform2f gl2::glUniform2f
|
|
#define glUniform1i gl2::glUniform1i
|
|
#define glUseProgram gl2::glUseProgram
|
|
#define glVertexAttribPointer gl2::glVertexAttribPointer
|
|
#endif
|
|
|
|
#ifdef MACOSX
|
|
#include <CoreGraphics/CoreGraphics.h>
|
|
extern bool has_touch_bar_support;
|
|
bool macosx_detect_nstouchbar(void);
|
|
void macosx_init_touchbar(void);
|
|
void macosx_GetWindowDPI(ScreenSizeInfo &info);
|
|
int macosx_yesno(const char *title, const char *message);
|
|
int macosx_yesnocancel(const char *title, const char *message);
|
|
std::string macosx_prompt_folder(const char *default_folder);
|
|
#endif
|
|
|
|
#if C_DIRECT3D
|
|
void d3d_init(void);
|
|
#endif
|
|
void ShutDownMemHandles(Section * sec), GFX_ReleaseMouse();
|
|
void resetFontSize(), increaseFontSize(), decreaseFontSize();
|
|
void GetMaxWidthHeight(unsigned int *pmaxWidth, unsigned int *pmaxHeight);
|
|
void MAPPER_CheckEvent(SDL_Event * event), MAPPER_CheckKeyboardLayout(), MAPPER_ReleaseAllKeys();
|
|
bool isDBCSCP(), InitCodePage();
|
|
int GetNumScreen();
|
|
|
|
SDL_Block sdl;
|
|
Bitu frames = 0;
|
|
unsigned int page=0;
|
|
unsigned int hostkeyalt=0;
|
|
unsigned int sendkeymap=0;
|
|
std::string configfile = "";
|
|
std::string strPasteBuffer = "";
|
|
ScreenSizeInfo screen_size_info;
|
|
void FormFeed(bool pressed), PrintText(bool pressed);
|
|
void DOSBOX_UnlockSpeed2(bool pressed), DEBUG_Enable_Handler(bool pressed);
|
|
int FileDirExistCP(const char *name), FileDirExistUTF8(std::string &localname, const char *name);
|
|
bool CodePageHostToGuestUTF16(char *d/*CROSS_LEN*/,const uint16_t *s/*CROSS_LEN*/);
|
|
|
|
#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && defined(C_SDL2)
|
|
static std::string ime_text = "";
|
|
extern bool CodePageHostToGuestUTF8(char *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/);
|
|
extern bool IME_GetEnable();
|
|
#endif
|
|
|
|
int NonUserResizeCounter = 0;
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
enum class CornerPreference {
|
|
Default = 0,
|
|
DoNotRound = 1,
|
|
Round = 2,
|
|
RoundSmall = 3,
|
|
};
|
|
bool UpdateWindows11RoundCorners(HWND hWnd, CornerPreference cornerPreference) {
|
|
typedef HRESULT(WINAPI *PFNSETWINDOWATTRIBUTE)(HWND hWnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
|
|
enum DWMWINDOWATTRIBUTE {
|
|
DWMWA_WINDOW_CORNER_PREFERENCE = 33
|
|
};
|
|
enum DWM_WINDOW_CORNER_PREFERENCE {
|
|
DWMWCP_DEFAULT = 0,
|
|
DWMWCP_DONOTROUND = 1,
|
|
DWMWCP_ROUND = 2,
|
|
DWMWCP_ROUNDSMALL = 3
|
|
};
|
|
HMODULE hDwmApi = ::LoadLibrary("dwmapi.dll");
|
|
if (hDwmApi) {
|
|
auto *pfnSetWindowAttribute = reinterpret_cast<PFNSETWINDOWATTRIBUTE>(GetProcAddress(hDwmApi, "DwmSetWindowAttribute"));
|
|
if (pfnSetWindowAttribute) {
|
|
auto preference = static_cast<DWM_WINDOW_CORNER_PREFERENCE>(cornerPreference);
|
|
HRESULT res = pfnSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(DWM_WINDOW_CORNER_PREFERENCE));
|
|
return res == S_OK;
|
|
}
|
|
::FreeLibrary(hDwmApi);
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
static BOOL WINAPI ConsoleEventHandler(DWORD event) {
|
|
switch (event) {
|
|
case CTRL_SHUTDOWN_EVENT:
|
|
case CTRL_LOGOFF_EVENT:
|
|
case CTRL_CLOSE_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
raise(SIGTERM);
|
|
return TRUE;
|
|
case CTRL_C_EVENT:
|
|
default: //pass to the next handler
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
extern int bootdrive, resolveopt;
|
|
extern int dos_clipboard_device_access;
|
|
extern int aspect_ratio_x, aspect_ratio_y;
|
|
extern bool sync_time, loadlang, addovl;
|
|
extern bool bootguest, bootfast, bootvm;
|
|
|
|
std::string dosboxpath="";
|
|
std::string GetDOSBoxXPath(bool withexe=false) {
|
|
std::string full;
|
|
#if defined(HX_DOS) || defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
|
char exepath[MAX_PATH];
|
|
GetModuleFileName(NULL, exepath, sizeof(exepath));
|
|
full=std::string(exepath);
|
|
#else
|
|
int length = wai_getExecutablePath(NULL, 0, NULL);
|
|
char *exepath = (char*)malloc(length + 1);
|
|
wai_getExecutablePath(exepath, length, NULL);
|
|
exepath[length] = 0;
|
|
full=std::string(exepath);
|
|
free(exepath);
|
|
#endif
|
|
if (withexe)
|
|
dosboxpath=full;
|
|
else {
|
|
size_t found=full.find_last_of("/\\");
|
|
if (found!=std::string::npos)
|
|
dosboxpath=full.substr(0, found+1);
|
|
else
|
|
dosboxpath="";
|
|
}
|
|
return dosboxpath;
|
|
}
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2)
|
|
bool isVirtualBox = false; /* OpenGL never works with Windows XP inside VirtualBox */
|
|
#endif
|
|
|
|
bool OpenGL_using(void);
|
|
|
|
#if defined(WIN32) && !defined(S_ISREG)
|
|
# define S_ISREG(x) ((x & S_IFREG) == S_IFREG)
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
void UpdateOverscanMenu(void);
|
|
|
|
const char *DKM_to_string(const unsigned int dkm) {
|
|
switch (dkm) {
|
|
case DKM_US: return "us";
|
|
case DKM_DEU: return "ger";
|
|
case DKM_JPN_PC98: return "jpn_pc98";
|
|
case DKM_JPN: return "jpn";
|
|
default: break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
const char *DKM_to_descriptive_string(const unsigned int dkm) {
|
|
switch (dkm) {
|
|
case DKM_US: return "US English";
|
|
case DKM_DEU: return "German";
|
|
case DKM_JPN_PC98: return "Japanese (PC-98)";
|
|
case DKM_JPN: return "Japanese";
|
|
default: break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
ITaskbarList3 *winTaskbarList = NULL;
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
void WindowsTaskbarUpdatePreviewRegion(void) {
|
|
if (winTaskbarList != NULL) {
|
|
/* Windows 7/8/10: Tell the taskbar which part of our window contains the DOS screen */
|
|
RECT r;
|
|
|
|
r.top = sdl.clip.y;
|
|
r.left = sdl.clip.x;
|
|
r.right = sdl.clip.x + sdl.clip.w;
|
|
r.bottom = sdl.clip.y + sdl.clip.h;
|
|
|
|
/* NTS: The MSDN documentation is misleading. Apparently, despite 30+ years of Windows SDK
|
|
behavior where the "client area" is the area below the menu bar and inside the frame,
|
|
ITaskbarList3's idea of the "client area" is the the area inside the frame INCLUDING
|
|
the menu bar. Why? */
|
|
if (GetMenu(GetHWND()) != NULL) {
|
|
MENUBARINFO mb;
|
|
int rh;
|
|
|
|
memset(&mb, 0, sizeof(mb));
|
|
mb.cbSize = sizeof(mb);
|
|
|
|
GetMenuBarInfo(GetHWND(), OBJID_MENU, 0, &mb); // returns absolute screen coordinates, apparently.
|
|
rh = mb.rcBar.bottom + 1 - mb.rcBar.top; // menu screen space is top <= y <= bottom, inclusive.
|
|
|
|
r.top += rh;
|
|
r.bottom += rh;
|
|
}
|
|
|
|
if (winTaskbarList->SetThumbnailClip(GetHWND(), &r) != S_OK)
|
|
LOG_MSG("WARNING: ITaskbarList3::SetThumbnailClip() failed");
|
|
}
|
|
}
|
|
|
|
void WindowsTaskbarResetPreviewRegion(void) {
|
|
if (winTaskbarList != NULL) {
|
|
/* Windows 7/8/10: Tell the taskbar which part of our window contains the client area (not including the menu bar) */
|
|
RECT r;
|
|
|
|
GetClientRect(GetHWND(), &r);
|
|
|
|
/* NTS: The MSDN documentation is misleading. Apparently, despite 30+ years of Windows SDK
|
|
behavior where the "client area" is the area below the menu bar and inside the frame,
|
|
ITaskbarList3's idea of the "client area" is the the area inside the frame INCLUDING
|
|
the menu bar. Why? */
|
|
if (GetMenu(GetHWND()) != NULL) {
|
|
MENUBARINFO mb;
|
|
int rh;
|
|
|
|
memset(&mb, 0, sizeof(mb));
|
|
mb.cbSize = sizeof(mb);
|
|
|
|
GetMenuBarInfo(GetHWND(), OBJID_MENU, 0, &mb); // returns absolute screen coordinates, apparently.
|
|
rh = mb.rcBar.bottom + 1 - mb.rcBar.top; // menu screen space is top <= y <= bottom, inclusive.
|
|
|
|
r.top += rh;
|
|
r.bottom += rh;
|
|
}
|
|
|
|
if (winTaskbarList->SetThumbnailClip(GetHWND(), &r) != S_OK)
|
|
LOG_MSG("WARNING: ITaskbarList3::SetThumbnailClip() failed");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
unsigned int mapper_keyboard_layout = DKM_US;
|
|
unsigned int host_keyboard_layout = DKM_US;
|
|
|
|
void KeyboardLayoutDetect(void) {
|
|
unsigned int nlayout = DKM_US;
|
|
|
|
#if defined(LINUX)
|
|
unsigned int Linux_GetKeyboardLayout(void);
|
|
nlayout = Linux_GetKeyboardLayout();
|
|
|
|
/* BUGFIX: The xkbmap for 'jp' in Linux/X11 has a problem that maps both
|
|
* Ro and Yen to backslash, which in SDL's default state makes
|
|
* it impossible to map them properly in the mapper. */
|
|
if (nlayout == DKM_JPN) {
|
|
LOG_MSG("Engaging Linux/X11 fix for jp xkbmap in order to handle Ro/Yen keys");
|
|
|
|
void Linux_JPXKBFix(void);
|
|
Linux_JPXKBFix();
|
|
}
|
|
#elif defined(WIN32)
|
|
WORD lid = LOWORD(GetKeyboardLayout(0));
|
|
|
|
LOG_MSG("Windows keyboard layout ID is 0x%04x", lid);
|
|
|
|
switch (lid) {
|
|
case 0x0407: nlayout = DKM_DEU; break;
|
|
case 0x0409: nlayout = DKM_US; break;
|
|
case 0x0411: nlayout = DKM_JPN; break;
|
|
default: break;
|
|
}
|
|
#endif
|
|
|
|
host_keyboard_layout = nlayout;
|
|
|
|
LOG_MSG("Host keyboard layout is now %s (%s)",
|
|
DKM_to_string(host_keyboard_layout),
|
|
DKM_to_descriptive_string(host_keyboard_layout));
|
|
|
|
if (IS_PC98_ARCH) {
|
|
Section_prop * pc98_section = static_cast<Section_prop *>(control->GetSection("pc98"));
|
|
const char *layoutstr = pc98_section->Get_string("pc-98 force ibm keyboard layout");
|
|
if (!strcasecmp(layoutstr, "auto")) {
|
|
pc98_force_ibm_layout = host_keyboard_layout == DKM_US;
|
|
mainMenu.get_item("pc98_use_uskb").check(pc98_force_ibm_layout).refresh_item(mainMenu);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetMapperKeyboardLayout(const unsigned int dkm) {
|
|
/* TODO: Make mapper re-initialize layout. If the mapper interface is visible, redraw it. */
|
|
mapper_keyboard_layout = dkm;
|
|
|
|
LOG_MSG("Mapper keyboard layout is now %s (%s)",
|
|
DKM_to_string(mapper_keyboard_layout),
|
|
DKM_to_descriptive_string(mapper_keyboard_layout));
|
|
}
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
extern "C" unsigned char SDL1_hax_hasLayoutChanged(void);
|
|
extern "C" void SDL1_hax_ackLayoutChanged(void);
|
|
#endif
|
|
|
|
void CheckMapperKeyboardLayout(void) {
|
|
#if defined(WIN32) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
if (SDL1_hax_hasLayoutChanged()) {
|
|
SDL1_hax_ackLayoutChanged();
|
|
LOG_MSG("Keyboard layout changed");
|
|
KeyboardLayoutDetect();
|
|
|
|
if (host_keyboard_layout == DKM_JPN && IS_PC98_ARCH)
|
|
SetMapperKeyboardLayout(DKM_JPN_PC98);
|
|
else
|
|
SetMapperKeyboardLayout(host_keyboard_layout);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* yksoft1 says that older MinGW headers lack this value --Jonathan C. */
|
|
#ifndef MAPVK_VK_TO_VSC
|
|
#define MAPVK_VK_TO_VSC 0
|
|
#endif
|
|
|
|
bool boot_debug_break = false;
|
|
|
|
bool window_was_maximized = false;
|
|
|
|
/* this flag is needed in order to know if we're AT the shell,
|
|
or if we're in a program running under the shell. */
|
|
bool dos_shell_running_program = false;
|
|
|
|
Bitu userResizeWindowWidth = 0, userResizeWindowHeight = 0;
|
|
Bitu currentWindowWidth = 640, currentWindowHeight = 480;
|
|
|
|
bool setSizeButNotResize() {
|
|
return (userResizeWindowWidth > 0 && userResizeWindowHeight > 0);
|
|
}
|
|
|
|
Bitu time_limit_ms = 0;
|
|
|
|
extern bool keep_umb_on_boot;
|
|
extern bool keep_private_area_on_boot;
|
|
bool guest_machine_power_on = false;
|
|
|
|
std::string custom_savedir;
|
|
|
|
void SHELL_Run();
|
|
void DisableINT33();
|
|
void EMS_DoShutDown();
|
|
void XMS_DoShutDown();
|
|
void DOS_DoShutDown();
|
|
void GUS_DOS_Shutdown();
|
|
void SBLASTER_DOS_Shutdown();
|
|
void DOS_ShutdownDevices(void);
|
|
void RemoveEMSPageFrame(void);
|
|
void RemoveUMBBlock();
|
|
void DOS_GetMemory_unmap();
|
|
void VFILE_Shutdown(void);
|
|
void PROGRAMS_Shutdown(void);
|
|
void DOS_UninstallMisc(void);
|
|
void CALLBACK_Shutdown(void);
|
|
void DOS_ShutdownDrives();
|
|
void VFILE_Shutdown(void);
|
|
void DOS_ShutdownFiles();
|
|
void FreeBIOSDiskList();
|
|
void GFX_ShutDown(void);
|
|
void MAPPER_Shutdown();
|
|
void SHELL_Init(void);
|
|
void CopyClipboard(int all);
|
|
void CopyAllClipboard(bool bPressed);
|
|
void PasteClipboard(bool bPressed);
|
|
void PasteClipStop(bool bPressed);
|
|
void QuickEdit(bool bPressed);
|
|
void ClipKeySelect(int sym);
|
|
bool isModifierApplied(void);
|
|
bool PasteClipboardNext(void);
|
|
|
|
#if C_DYNAMIC_X86
|
|
void CPU_Core_Dyn_X86_Shutdown(void);
|
|
#endif
|
|
|
|
void UpdateWindowMaximized(bool flag) {
|
|
menu.maxwindow = flag;
|
|
}
|
|
|
|
void UpdateWindowDimensions(Bitu width, Bitu height)
|
|
{
|
|
currentWindowWidth = width;
|
|
currentWindowHeight = height;
|
|
}
|
|
|
|
void PrintScreenSizeInfo(void) {
|
|
#if 1
|
|
const char *method = "?";
|
|
|
|
switch (screen_size_info.method) {
|
|
case METHOD_NONE: method = "None"; break;
|
|
case METHOD_X11: method = "X11"; break;
|
|
case METHOD_XRANDR: method = "XRandR"; break;
|
|
case METHOD_WIN98BASE: method = "Win98base"; break;
|
|
case METHOD_COREGRAPHICS:method = "CoreGraphics";break;
|
|
default: break;
|
|
}
|
|
|
|
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)",
|
|
method,
|
|
|
|
screen_size_info.screen_dimensions_pixels.width,
|
|
screen_size_info.screen_dimensions_pixels.height,
|
|
|
|
screen_size_info.screen_position_pixels.x,
|
|
screen_size_info.screen_position_pixels.y,
|
|
|
|
screen_size_info.screen_dimensions_mm.width,
|
|
screen_size_info.screen_dimensions_mm.height,
|
|
|
|
screen_size_info.screen_dimensions_mm.width / 25.4,
|
|
screen_size_info.screen_dimensions_mm.height / 25.4,
|
|
|
|
screen_size_info.screen_dpi.width,
|
|
screen_size_info.screen_dpi.height);
|
|
#endif
|
|
}
|
|
|
|
#if defined(WIN32)
|
|
void Windows_GetWindowDPI(ScreenSizeInfo &info) {
|
|
info.clear();
|
|
|
|
# if !defined(HX_DOS)
|
|
HMONITOR mon;
|
|
HWND hwnd;
|
|
|
|
info.method = METHOD_WIN98BASE;
|
|
|
|
hwnd = GetHWND();
|
|
if (hwnd == NULL) return;
|
|
|
|
mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
|
if (mon == NULL) mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
|
|
if (mon == NULL) return;
|
|
|
|
MONITORINFO mi;
|
|
memset(&mi,0,sizeof(mi));
|
|
mi.cbSize = sizeof(mi);
|
|
if (!GetMonitorInfo(mon,&mi)) return;
|
|
|
|
info.screen_position_pixels.x = mi.rcMonitor.left;
|
|
info.screen_position_pixels.y = mi.rcMonitor.top;
|
|
|
|
info.screen_dimensions_pixels.width = mi.rcMonitor.right - mi.rcMonitor.left;
|
|
info.screen_dimensions_pixels.height = mi.rcMonitor.bottom - mi.rcMonitor.top;
|
|
|
|
/* Windows 10 build 1607 and later offer a "Get DPI of window" function */
|
|
{
|
|
HMODULE __user32;
|
|
|
|
__user32 = GetModuleHandle("USER32.DLL");
|
|
if (__user32) {
|
|
UINT (WINAPI *__GetDpiForWindow)(HWND) = NULL;
|
|
|
|
__GetDpiForWindow = (UINT (WINAPI *)(HWND))GetProcAddress(__user32,"GetDpiForWindow");
|
|
if (__GetDpiForWindow) {
|
|
UINT dpi = __GetDpiForWindow(hwnd);
|
|
|
|
if (dpi != 0) {
|
|
info.screen_dpi.width = dpi;
|
|
info.screen_dpi.height = dpi;
|
|
|
|
info.screen_dimensions_mm.width = (25.4 * screen_size_info.screen_dimensions_pixels.width) / dpi;
|
|
info.screen_dimensions_mm.height = (25.4 * screen_size_info.screen_dimensions_pixels.height) / dpi;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
void UpdateWindowDimensions(void)
|
|
{
|
|
#if defined(C_SDL2)
|
|
int w = 640,h = 480;
|
|
SDL_GetWindowSize(sdl.window, &w, &h);
|
|
UpdateWindowDimensions(w,h);
|
|
|
|
Uint32 fl = SDL_GetWindowFlags(sdl.window);
|
|
UpdateWindowMaximized((fl & SDL_WINDOW_MAXIMIZED) != 0);
|
|
#endif
|
|
#if defined(MACOSX)
|
|
macosx_GetWindowDPI(/*&*/screen_size_info);
|
|
#endif
|
|
#if defined(WIN32)
|
|
// When maximized, SDL won't actually tell us our new dimensions, so get it ourselves.
|
|
// FIXME: Instead of GetHWND() we need to track our own handle or add something to SDL 1.x
|
|
// to provide the handle!
|
|
RECT r = { 0 };
|
|
|
|
GetClientRect(GetHWND(), &r);
|
|
UpdateWindowDimensions(r.right, r.bottom);
|
|
UpdateWindowMaximized(IsZoomed(GetHWND()));
|
|
Windows_GetWindowDPI(/*&*/screen_size_info);
|
|
#endif
|
|
#if defined(LINUX)
|
|
void UpdateWindowDimensions_Linux(void);
|
|
UpdateWindowDimensions_Linux();
|
|
void Linux_GetWindowDPI(ScreenSizeInfo &info);
|
|
Linux_GetWindowDPI(/*&*/screen_size_info);
|
|
#endif
|
|
PrintScreenSizeInfo();
|
|
}
|
|
|
|
#define MAPPERFILE "mapper-dosbox-x.map"
|
|
#define MAPPERFILE_SDL1 "mapper-dosbox-x.sdl1.map"
|
|
#define MAPPERFILE_SDL2 "mapper-dosbox-x.sdl2.map"
|
|
|
|
void GUI_ResetResize(bool);
|
|
void GUI_LoadFonts();
|
|
void GUI_Run(bool);
|
|
|
|
const char* titlebar = NULL;
|
|
extern const char* RunningProgram;
|
|
extern bool CPU_CycleAutoAdjust;
|
|
extern cpu_cycles_count_t CPU_CyclePercUsed;
|
|
#if !(ENVIRON_INCLUDED)
|
|
extern char** environ;
|
|
#endif
|
|
|
|
double rtdelta = 0;
|
|
bool emu_paused = false;
|
|
bool mouselocked = false; //Global variable for mapper
|
|
bool fullscreen_switch = true;
|
|
bool startup_state_numlock = false; // Global for keyboard initialisation
|
|
bool startup_state_capslock = false; // Global for keyboard initialisation
|
|
bool startup_state_scrlock = false; // Global for keyboard initialisation
|
|
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, mbutton=3;
|
|
bool wheel_guest = false, clipboard_dosapi = true, clipboard_biospaste =
|
|
#if defined (WIN32) && (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR))
|
|
false;
|
|
#else
|
|
true;
|
|
#endif
|
|
const char *modifier;
|
|
|
|
#ifdef WIN32
|
|
# define STDOUT_FILE TEXT("stdout.txt")
|
|
# define STDERR_FILE TEXT("stderr.txt")
|
|
# define DEFAULT_CONFIG_FILE "/dosbox-x.conf"
|
|
#elif defined(MACOSX)
|
|
# define DEFAULT_CONFIG_FILE "/Library/Preferences/DOSBox Preferences"
|
|
#elif defined(HAIKU)
|
|
#define DEFAULT_CONFIG_FILE "~/config/settings/dosbox-x/dosbox-x.conf"
|
|
#else /*linux freebsd*/
|
|
# define DEFAULT_CONFIG_FILE "/.dosboxrc"
|
|
#endif
|
|
|
|
#if C_SET_PRIORITY
|
|
# include <sys/resource.h>
|
|
# define PRIO_TOTAL (PRIO_MAX-PRIO_MIN)
|
|
#endif
|
|
|
|
#ifdef OS2
|
|
# include <os2.h>
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
HWND GetHWND(void) {
|
|
SDL_SysWMinfo wmi;
|
|
SDL_VERSION(&wmi.version);
|
|
# if defined(C_SDL2)
|
|
if (sdl.window == NULL)
|
|
return nullptr;
|
|
if (!SDL_GetWindowWMInfo(sdl.window, &wmi))
|
|
return nullptr;
|
|
return wmi.info.win.window;
|
|
#else
|
|
if(!SDL_GetWMInfo(&wmi)) {
|
|
return NULL;
|
|
}
|
|
return wmi.window;
|
|
#endif
|
|
}
|
|
|
|
HWND GetSurfaceHWND(void) {
|
|
# if defined(C_SDL2)
|
|
return GetHWND();
|
|
# else
|
|
SDL_SysWMinfo wmi;
|
|
SDL_VERSION(&wmi.version);
|
|
|
|
if (!SDL_GetWMInfo(&wmi)) {
|
|
return NULL;
|
|
}
|
|
return wmi.child_window;
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
void SDL_rect_cliptoscreen(SDL_Rect &r) {
|
|
if (r.x < 0) {
|
|
r.w += r.x;
|
|
r.x = 0;
|
|
}
|
|
if (r.y < 0) {
|
|
r.h += r.y;
|
|
r.y = 0;
|
|
}
|
|
if ((r.x+r.w) > sdl.surface->w)
|
|
r.w = sdl.surface->w - r.x;
|
|
if ((r.y+r.h) > sdl.surface->h)
|
|
r.h = sdl.surface->h - r.y;
|
|
/* NTS: Apparently r.w and r.h are unsigned, therefore no need to check if negative */
|
|
// if (r.w < 0) r.w = 0;
|
|
// if (r.h < 0) r.h = 0;
|
|
}
|
|
|
|
Bitu GUI_JoystickCount(void) {
|
|
return sdl.num_joysticks;
|
|
}
|
|
|
|
#if !defined(MACOSX)
|
|
/* TODO: should move to it's own file ================================================ */
|
|
static unsigned char logo[32*32*4]= {
|
|
#include "dosbox_logo.h"
|
|
};
|
|
#endif
|
|
|
|
#if !defined(MACOSX)
|
|
static void DOSBox_SetOriginalIcon(void) {
|
|
SDL_Surface *logos;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
logos = SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0xff000000,0x00ff0000,0x0000ff00,0);
|
|
#else
|
|
logos = SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0);
|
|
#endif
|
|
|
|
#if defined(C_SDL2)
|
|
SDL_SetWindowIcon(sdl.window, logos);
|
|
#else
|
|
SDL_WM_SetIcon(logos,NULL);
|
|
#endif
|
|
}
|
|
#endif
|
|
/* =================================================================================== */
|
|
|
|
#if defined (WIN32)
|
|
bool GFX_SDLUsingWinDIB(void) {
|
|
return sdl.using_windib;
|
|
}
|
|
#endif
|
|
|
|
void GFX_SetIcon(void)
|
|
{
|
|
#if !defined(MACOSX)
|
|
/* Set Icon (must be done before any sdl_setvideomode call) */
|
|
/* But don't set it on macOS, as we use a nicer external icon there. */
|
|
/* Made into a separate call, so it can be called again when we restart the graphics output on win32 */
|
|
if (menu_compatible) { DOSBox_SetOriginalIcon(); return; }
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2)
|
|
HICON hIcon1;
|
|
|
|
hIcon1 = (HICON) LoadImage( GetModuleHandle(NULL), MAKEINTRESOURCE(dosbox_ico), IMAGE_ICON,
|
|
16,16,LR_DEFAULTSIZE);
|
|
|
|
SendMessage(GetHWND(), WM_SETICON, ICON_SMALL, (LPARAM) hIcon1 );
|
|
#endif
|
|
}
|
|
|
|
#if C_DEBUG
|
|
bool IsDebuggerActive(void);
|
|
#endif
|
|
|
|
extern std::string dosbox_title, GetDefaultOutput();
|
|
|
|
void GFX_SetTitle(int32_t cycles, int frameskip, Bits timing, bool paused) {
|
|
(void)frameskip;//UNUSED
|
|
(void)timing;//UNUSED
|
|
// static Bits internal_frameskip=0;
|
|
static int32_t internal_cycles=0;
|
|
// static Bits internal_timing=0;
|
|
char title[250] = {0};
|
|
|
|
Section_prop *section = static_cast<Section_prop *>(control->GetSection("SDL"));
|
|
assert(section != NULL);
|
|
titlebar = section->Get_string("titlebar");
|
|
|
|
if (cycles != -1) internal_cycles = cycles;
|
|
// if (timing != -1) internal_timing = timing;
|
|
// if (frameskip != -1) internal_frameskip = frameskip;
|
|
|
|
bool showbasic = section->Get_bool("showbasic");
|
|
if (showbasic) {
|
|
sprintf(title,"%s%sDOSBox-X %s", dosbox_title.c_str(),dosbox_title.empty()?"":" - ", VERSION);
|
|
|
|
const char *what = RunningProgram;
|
|
if (what != NULL && *what != 0) {
|
|
char *p = title + strlen(title); // append to end of string
|
|
|
|
sprintf(p,": %s - ", what);
|
|
}
|
|
|
|
char *p = title + strlen(title); // append to end of string
|
|
if (CPU_CycleAutoAdjust && menu.hidecycles && !menu.showrt)
|
|
sprintf(p,"%d%%", (int)internal_cycles);
|
|
else
|
|
sprintf(p,"%d cycles/ms", (int)internal_cycles);
|
|
} else
|
|
sprintf(title,"%s%sDOSBox-X", dosbox_title.c_str(),dosbox_title.empty()?"":" - ");
|
|
|
|
if (!menu.hidecycles) {
|
|
char *p = title + strlen(title); // append to end of string
|
|
|
|
sprintf(p,", FPS %2d",(int)frames);
|
|
}
|
|
|
|
if (menu.showrt) {
|
|
char *p = title + strlen(title); // append to end of string
|
|
|
|
sprintf(p,", %2d%%/RT",(int)floor((rtdelta / 10) + 0.5));
|
|
}
|
|
|
|
if (titlebar != NULL && *titlebar != 0) {
|
|
char *p = title + strlen(title); // append to end of string
|
|
|
|
sprintf(p,": %s",titlebar);
|
|
}
|
|
|
|
if (sdl.mouse.locked) {
|
|
std::string get_mapper_shortcut(const char *name);
|
|
std::string key=get_mapper_shortcut("capmouse");
|
|
strcat(title, key.size()?(" ["+key+" releases mouse]").c_str():" [mouse locked]");
|
|
}
|
|
|
|
if (paused) strcat(title," PAUSED");
|
|
#if C_DEBUG
|
|
if (IsDebuggerActive()) strcat(title," DEBUGGER");
|
|
#endif
|
|
#if defined(C_SDL2)
|
|
SDL_SetWindowTitle(sdl.window,title);
|
|
#else
|
|
SDL_WM_SetCaption(title,VERSION);
|
|
#endif
|
|
}
|
|
|
|
bool warn_on_mem_write = false;
|
|
|
|
bool systemmessagebox(char const * aTitle, char const * aMessage, char const * aDialogType, char const * aIconType, int aDefaultButton) {
|
|
#if !defined(HX_DOS)
|
|
bool fs=sdl.desktop.fullscreen;
|
|
if (fs) GFX_SwitchFullScreen();
|
|
MAPPER_ReleaseAllKeys();
|
|
GFX_LosingFocus();
|
|
GFX_ReleaseMouse();
|
|
bool ret=tinyfd_messageBox(aTitle, aMessage, aDialogType, aIconType, aDefaultButton);
|
|
MAPPER_ReleaseAllKeys();
|
|
GFX_LosingFocus();
|
|
if (fs&&!sdl.desktop.fullscreen) GFX_SwitchFullScreen();
|
|
return ret;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool CheckQuit(void) {
|
|
#if !defined(HX_DOS)
|
|
Section_prop *section = static_cast<Section_prop *>(control->GetSection("dosbox"));
|
|
std::string warn = section->Get_string("quit warning");
|
|
bool quit = section->Get_bool("allow quit after warning");
|
|
if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
|
|
if (warn == "true") {
|
|
if (!quit) {
|
|
systemmessagebox("Quit DOSBox-X warning","Quitting from DOSBox-X with this is currently disabled.","ok", "warning", 1);
|
|
return false;
|
|
} else
|
|
return systemmessagebox("Quit DOSBox-X warning","This will quit from DOSBox-X.\nAre you sure?","yesno", "question", 1);
|
|
} else if (warn == "false")
|
|
return true;
|
|
if (dos_kernel_disabled&&strcmp(RunningProgram, "DOSBOX-X")) {
|
|
if (!quit) {
|
|
systemmessagebox("Quit DOSBox-X warning","You cannot quit DOSBox-X while running a guest system.","ok", "warning", 1);
|
|
return false;
|
|
} else
|
|
return systemmessagebox("Quit DOSBox-X warning","You are currently running a guest system.\nAre you sure to quit anyway now?","yesno", "question", 1);
|
|
}
|
|
if (warn == "autofile")
|
|
for (uint8_t handle = 0; handle < DOS_FILES; handle++) {
|
|
if (Files[handle] && (Files[handle]->GetName() == NULL || strcmp(Files[handle]->GetName(), "CON")) && (Files[handle]->GetInformation()&DeviceInfoFlags::Device) == 0) {
|
|
if (!quit) {
|
|
systemmessagebox("Quit DOSBox-X warning","You cannot quit DOSBox-X while one or more files are open.","ok", "warning", 1);
|
|
return false;
|
|
} else
|
|
return systemmessagebox("Quit DOSBox-X warning","It may be unsafe to quit from DOSBox-X right now\nbecause one or more files are currently open.\nAre you sure to quit anyway now?","yesno", "question", 1);
|
|
}
|
|
}
|
|
else if (RunningProgram&&strcmp(RunningProgram, "DOSBOX-X")&&strcmp(RunningProgram, "COMMAND")&&strcmp(RunningProgram, "4DOS")) {
|
|
if (!quit) {
|
|
systemmessagebox("Quit DOSBox-X warning","You cannot quit DOSBox-X while running a program or game.","ok", "warning", 1);
|
|
return false;
|
|
} else
|
|
return systemmessagebox("Quit DOSBox-X warning","You are currently running a program or game.\nAre you sure to quit anyway now?","yesno", "question", 1);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void NewInstanceEvent(bool pressed) {
|
|
if (!pressed) return;
|
|
#if defined(MACOSX)
|
|
pid_t p = fork();
|
|
if (p == 0) {
|
|
/* child process */
|
|
char *argv[8];
|
|
extern std::string MacOSXEXEPath;
|
|
{
|
|
int fd = open("/dev/null",O_RDWR);
|
|
dup2(fd,0);
|
|
dup2(fd,1);
|
|
dup2(fd,2);
|
|
close(fd);
|
|
}
|
|
chdir("/");
|
|
argv[0] = (char*)MacOSXEXEPath.c_str();
|
|
argv[1] = NULL;
|
|
execv(argv[0],argv);
|
|
fprintf(stderr,"Failed to exec to %s\n",argv[0]);
|
|
_exit(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CPU_Snap_Back_To_Real_Mode();
|
|
static void KillSwitch(bool pressed) {
|
|
if (!pressed) return;
|
|
if (!CheckQuit()) return;
|
|
if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
|
|
#if 0 /* Re-enable this hack IF DOSBox-X continues to have problems page-faulting on kill switch */
|
|
CPU_Snap_Back_To_Real_Mode(); /* TEMPORARY HACK. There are portions of DOSBox that write to memory as if still running DOS. */
|
|
/* ^ Without this hack, when running Windows NT 3.1 this Kill Switch effectively becomes the Instant Page Fault BSOD switch
|
|
* because the DOSBox-X code attempting to write to real mode memory causes a page fault (though hitting the kill switch a
|
|
* second time shuts DOSBox-X down properly). It's sort of the same issue behind the INT 33h emulation causing instant BSOD
|
|
* in Windows NT the instant you moved or clicked the mouse. The purpose of this hack is that, before any of that DOSBox-X
|
|
* 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
|
|
* shuts down properly. */
|
|
#endif
|
|
warn_on_mem_write = true;
|
|
throw 1;
|
|
}
|
|
|
|
void DoKillSwitch(void) {
|
|
KillSwitch(true);
|
|
}
|
|
|
|
void BlankDisplay(void) {
|
|
if (OpenGL_using()) {
|
|
LOG_MSG("FIXME: BlankDisplay() not implemented for OpenGL mode");
|
|
}
|
|
else {
|
|
SDL_FillRect(sdl.surface,0,0);
|
|
#if defined(C_SDL2)
|
|
SDL_UpdateWindowSurface(sdl.window);
|
|
#else
|
|
SDL_Flip(sdl.surface);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void GFX_SDL_Overscan(void) {
|
|
sdl.overscan_color=0;
|
|
if (sdl.overscan_width) {
|
|
Bitu border_color = GFX_GetRGB(vga.dac.rgb[vga.attr.overscan_color].red<<2,
|
|
vga.dac.rgb[vga.attr.overscan_color].green<<2, vga.dac.rgb[vga.attr.overscan_color].blue<<2);
|
|
if (border_color != sdl.overscan_color) {
|
|
sdl.overscan_color = border_color;
|
|
|
|
// Find four rectangles forming the border
|
|
SDL_Rect *rect = &sdl.updateRects[0];
|
|
rect->x = 0; rect->y = 0; rect->w = sdl.draw.width+(unsigned int)(2*sdl.clip.x); rect->h = (uint16_t)sdl.clip.y; // top
|
|
if ((Bitu)rect->h > (Bitu)sdl.overscan_width) { rect->y += (int)(rect->h-sdl.overscan_width); rect->h = (uint16_t)sdl.overscan_width; }
|
|
if ((Bitu)sdl.clip.x > (Bitu)sdl.overscan_width) { rect->x += (int)sdl.clip.x-(int)sdl.overscan_width; rect->w -= (uint16_t)(2*((int)sdl.clip.x-(int)sdl.overscan_width)); }
|
|
rect = &sdl.updateRects[1];
|
|
rect->x = 0; rect->y = sdl.clip.y; rect->w = (uint16_t)sdl.clip.x; rect->h = (uint16_t)sdl.draw.height; // left
|
|
if ((unsigned int)rect->w > (unsigned int)sdl.overscan_width) { rect->x += (int)rect->w-(int)sdl.overscan_width; rect->w = (uint16_t)sdl.overscan_width; }
|
|
rect = &sdl.updateRects[2];
|
|
rect->x = (int)sdl.clip.x+(int)sdl.draw.width; rect->y = sdl.clip.y; rect->w = (uint16_t)sdl.clip.x; rect->h = (uint16_t)sdl.draw.height; // right
|
|
if ((unsigned int)rect->w > (unsigned int)sdl.overscan_width) { rect->w = (uint16_t)sdl.overscan_width; }
|
|
rect = &sdl.updateRects[3];
|
|
rect->x = 0; rect->y = (int)sdl.clip.y+(int)sdl.draw.height; rect->w = sdl.draw.width+(unsigned int)(2*sdl.clip.x); rect->h = (uint16_t)sdl.clip.y; // bottom
|
|
if ((Bitu)rect->h > (Bitu)sdl.overscan_width) { rect->h = (uint16_t)sdl.overscan_width; }
|
|
if ((Bitu)sdl.clip.x > (Bitu)sdl.overscan_width) { rect->x += (int)sdl.clip.x-(int)sdl.overscan_width; rect->w -= (unsigned int)(2*((int)sdl.clip.x-(int)sdl.overscan_width)); }
|
|
|
|
if (sdl.surface->format->BitsPerPixel == 8) { // SDL_FillRect seems to have some issues with palettized hw surfaces
|
|
uint8_t* pixelptr = (uint8_t*)sdl.surface->pixels;
|
|
Bitu linepitch = sdl.surface->pitch;
|
|
for (Bitu i=0; i<4; i++) {
|
|
rect = &sdl.updateRects[i];
|
|
uint8_t* start = pixelptr + (unsigned int)rect->y*(unsigned int)linepitch + (unsigned int)rect->x;
|
|
for (Bitu j=0; j<(unsigned int)rect->h; j++) {
|
|
memset(start, vga.attr.overscan_color, rect->w);
|
|
start += linepitch;
|
|
}
|
|
}
|
|
} else {
|
|
for (Bitu i=0; i<4; i++)
|
|
SDL_FillRect(sdl.surface, &sdl.updateRects[i], (Uint32)border_color);
|
|
|
|
#if defined(C_SDL2)
|
|
SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
|
|
#else
|
|
SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DOSBox_Paused()
|
|
{
|
|
return emu_paused;
|
|
}
|
|
|
|
bool pause_on_vsync = false;
|
|
|
|
#if defined(C_SDL2)
|
|
static bool IsFullscreen() {
|
|
if (sdl.window == NULL) return false;
|
|
uint32_t windowFlags = SDL_GetWindowFlags(sdl.window);
|
|
if (windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) return true;
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool is_paused = false;
|
|
bool unpause_now = false;
|
|
bool pausewithinterrupts_enable = false;
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
|
|
int pause_menu_item_tag = -1;
|
|
#endif
|
|
|
|
void PushDummySDL(void) {
|
|
SDL_Event event;
|
|
|
|
memset(&event,0,sizeof(event));
|
|
event.type = SDL_KEYUP;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
static void HandleMouseMotion(SDL_MouseMotionEvent * motion);
|
|
static void HandleMouseButton(SDL_MouseButtonEvent * button, SDL_MouseMotionEvent * motion);
|
|
|
|
#if defined(C_SDL2)
|
|
# if !defined(IGNORE_TOUCHSCREEN)
|
|
static void HandleTouchscreenFinger(SDL_TouchFingerEvent * finger);
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(C_SDL2)
|
|
static const SDL_TouchID no_touch_id = (SDL_TouchID)(~0ULL);
|
|
static const SDL_FingerID no_finger_id = (SDL_FingerID)(~0ULL);
|
|
static SDL_FingerID touchscreen_finger_lock = no_finger_id;
|
|
static SDL_TouchID touchscreen_touch_lock = no_touch_id;
|
|
#endif
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
int menuwidth_atleast(int width) {
|
|
HMENU hMenu = mainMenu.getWinMenu();
|
|
int count = GetMenuItemCount(hMenu);
|
|
int tWidth = 0;
|
|
RECT r;
|
|
for(int idx = 0; idx < count; ++idx) {
|
|
if (GetMenuItemRect(GetHWND(), hMenu, idx, &r)) {
|
|
int res=MapWindowPoints(NULL, GetHWND(), (LPPOINT)&r, 2);
|
|
tWidth += r.right - r.left;
|
|
}
|
|
}
|
|
tWidth += GetSystemMetrics(SM_CXBORDER)*2+(TTF_using()?24:60);
|
|
unsigned int maxWidth = 0, maxHeight = 0;
|
|
GetMaxWidthHeight(&maxWidth, &maxHeight);
|
|
return tWidth>width && tWidth<=maxWidth ? tWidth : -1;
|
|
}
|
|
#endif
|
|
|
|
void HideMenu_mapper_shortcut(bool pressed) {
|
|
if (!pressed) return;
|
|
|
|
ToggleMenu(true);
|
|
|
|
mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
|
|
#if defined(USE_TTF) && DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
int last = 0;
|
|
while (TTF_using() && !sdl.desktop.fullscreen && menu_gui && menu.toggle && menuwidth_atleast(ttf.cols*ttf.width+ttf.offX*2+GetSystemMetrics(SM_CXBORDER)*2)>0 && ttf.pointsize>last) {
|
|
last = ttf.pointsize;
|
|
increaseFontSize();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CALLBACK_Idle(void);
|
|
|
|
void PauseWithInterruptsEnabled(Bitu /*val*/) {
|
|
/* we can ONLY do this when the CPU is either in real mode or v86 mode.
|
|
* doing this from protected mode will only crash the game.
|
|
* also require that interrupts are enabled before pausing. */
|
|
if (cpu.pmode) {
|
|
if (!(reg_flags & FLAG_VM)) {
|
|
PIC_AddEvent(PauseWithInterruptsEnabled,0.001);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!(reg_flags & FLAG_IF)) {
|
|
PIC_AddEvent(PauseWithInterruptsEnabled,0.001);
|
|
return;
|
|
}
|
|
|
|
while (pausewithinterrupts_enable) CALLBACK_Idle();
|
|
}
|
|
|
|
void PauseWithInterrupts_mapper_shortcut(bool pressed) {
|
|
if (!pressed) return;
|
|
|
|
if (!pausewithinterrupts_enable) {
|
|
pausewithinterrupts_enable = true;
|
|
PIC_AddEvent(PauseWithInterruptsEnabled,0.001);
|
|
}
|
|
else {
|
|
pausewithinterrupts_enable = false;
|
|
}
|
|
|
|
mainMenu.get_item("mapper_pauseints").check(pausewithinterrupts_enable).refresh_item(mainMenu);
|
|
}
|
|
|
|
void PauseDOSBoxLoop(Bitu /*unused*/) {
|
|
bool paused = true;
|
|
SDL_Event event;
|
|
|
|
/* reflect in the menu that we're paused now */
|
|
mainMenu.get_item("mapper_pause").check(true).refresh_item(mainMenu);
|
|
|
|
MAPPER_ReleaseAllKeys();
|
|
GFX_ReleaseMouse();
|
|
GFX_SetTitle(-1,-1,-1,true);
|
|
// KEYBOARD_ClrBuffer();
|
|
GFX_LosingFocus();
|
|
while (SDL_PollEvent(&event)); // flush event queue.
|
|
|
|
// reset pause conditions
|
|
pause_on_vsync = false;
|
|
|
|
// give mouse to win32 (ex. alt-tab)
|
|
#if defined(C_SDL2)
|
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
|
#else
|
|
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
|
#endif
|
|
|
|
is_paused = true;
|
|
#if defined(WIN32)
|
|
DOSBox_SetSysMenu();
|
|
#endif
|
|
|
|
while (paused) {
|
|
if (unpause_now) {
|
|
unpause_now = false;
|
|
break;
|
|
}
|
|
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
SDL_PollEvent(&event);
|
|
#else
|
|
SDL_WaitEvent(&event); // since we're not polling, cpu usage drops to 0.
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
UINT msg=0;
|
|
WPARAM wparam;
|
|
if (event.type==SDL_SYSWMEVENT) {
|
|
#if defined(C_SDL2)
|
|
msg=event.syswm.msg->msg.win.msg;
|
|
wparam=event.syswm.msg->msg.win.wParam;
|
|
#else
|
|
msg=event.syswm.msg->msg;
|
|
wparam=event.syswm.msg->wParam;
|
|
#endif
|
|
}
|
|
if (event.type==SDL_SYSWMEVENT && msg == WM_COMMAND && (LOWORD(wparam) == ID_WIN_SYSMENU_PAUSE || LOWORD(wparam) == (mainMenu.get_item("mapper_pause").get_master_id()+DOSBoxMenu::winMenuMinimumID))) {
|
|
paused=false;
|
|
GFX_SetTitle(-1,-1,-1,false);
|
|
break;
|
|
}
|
|
if (event.type == SDL_SYSWMEVENT && msg == WM_SYSCOMMAND && LOWORD(wparam) == ID_WIN_SYSMENU_PAUSE) {
|
|
paused = false;
|
|
GFX_SetTitle(-1, -1, -1, false);
|
|
break;
|
|
}
|
|
#endif
|
|
switch (event.type) {
|
|
|
|
case SDL_QUIT: KillSwitch(true); break;
|
|
case SDL_KEYDOWN: // Must use Pause/Break or escape Key to resume.
|
|
if(event.key.keysym.sym == SDLK_PAUSE || event.key.keysym.sym == SDLK_ESCAPE) {
|
|
|
|
paused = false;
|
|
GFX_SetTitle(-1,-1,-1,false);
|
|
break;
|
|
}
|
|
else if (event.key.keysym.sym == SDLK_SPACE) { /* spacebar = single frame step */
|
|
/* resume, but let the VGA code know to call us on vertical retrace */
|
|
paused = false;
|
|
pause_on_vsync = true;
|
|
GFX_SetTitle(-1,-1,-1,false);
|
|
break;
|
|
}
|
|
#if defined (MACOSX) && !defined(C_SDL2)
|
|
if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
|
|
/* On macs, all apps exit when pressing cmd-q */
|
|
KillSwitch(true);
|
|
break;
|
|
}
|
|
#endif
|
|
case SDL_MOUSEMOTION:
|
|
#if defined(C_SDL2)
|
|
if (touchscreen_finger_lock == no_finger_id &&
|
|
touchscreen_touch_lock == no_touch_id &&
|
|
event.motion.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
|
|
HandleMouseMotion(&event.motion);
|
|
}
|
|
#else
|
|
HandleMouseMotion(&event.motion);
|
|
#endif
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
#if defined(C_SDL2)
|
|
if (touchscreen_finger_lock == no_finger_id &&
|
|
touchscreen_touch_lock == no_touch_id &&
|
|
event.button.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
|
|
HandleMouseButton(&event.button,&event.motion);
|
|
}
|
|
#else
|
|
HandleMouseButton(&event.button,&event.motion);
|
|
#endif
|
|
break;
|
|
#if defined(C_SDL2)
|
|
# if !defined(IGNORE_TOUCHSCREEN)
|
|
case SDL_FINGERDOWN:
|
|
case SDL_FINGERUP:
|
|
case SDL_FINGERMOTION:
|
|
HandleTouchscreenFinger(&event.tfinger);
|
|
break;
|
|
# endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// restore mouse state
|
|
void GFX_UpdateSDLCaptureState();
|
|
GFX_UpdateSDLCaptureState();
|
|
|
|
MAPPER_ReleaseAllKeys();
|
|
|
|
// KEYBOARD_ClrBuffer();
|
|
GFX_LosingFocus();
|
|
|
|
// redraw screen (ex. fullscreen - pause - alt+tab x2 - unpause)
|
|
if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackReset );
|
|
|
|
/* reflect in the menu that we're paused now */
|
|
mainMenu.get_item("mapper_pause").check(false).refresh_item(mainMenu);
|
|
|
|
is_paused = false;
|
|
#if defined(WIN32)
|
|
DOSBox_SetSysMenu();
|
|
#endif
|
|
}
|
|
|
|
void PauseDOSBox(bool pressed) {
|
|
if (pressed) {
|
|
if (is_paused) {
|
|
unpause_now = true;
|
|
PushDummySDL();
|
|
}
|
|
else {
|
|
PIC_AddEvent(PauseDOSBoxLoop,0.001);
|
|
}
|
|
}
|
|
}
|
|
|
|
int GetDisplayNumber(void) {
|
|
return sdl.displayNumber;
|
|
}
|
|
|
|
void SetDisplayNumber(int display) {
|
|
sdl.displayNumber = display;
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
static bool SDL2_resize_enable = false;
|
|
|
|
SDL_Window* GFX_GetSDLWindow(void) {
|
|
return sdl.window;
|
|
}
|
|
|
|
SDL_Window* GFX_SetSDLWindowMode(uint16_t width, uint16_t height, SCREEN_TYPES screenType)
|
|
{
|
|
static SCREEN_TYPES lastType = SCREEN_SURFACE;
|
|
if (sdl.renderer) {
|
|
SDL_DestroyRenderer(sdl.renderer);
|
|
sdl.renderer=0;
|
|
}
|
|
if (sdl.texture.pixelFormat) {
|
|
SDL_FreeFormat(sdl.texture.pixelFormat);
|
|
sdl.texture.pixelFormat = 0;
|
|
}
|
|
if (sdl.texture.texture) {
|
|
SDL_DestroyTexture(sdl.texture.texture);
|
|
sdl.texture.texture=0;
|
|
}
|
|
sdl.window_desired_width = width;
|
|
sdl.window_desired_height = height;
|
|
int currWidth, currHeight;
|
|
if (sdl.window) {
|
|
//SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
|
|
if (!sdl.update_window) {
|
|
SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
|
|
sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
|
|
|
|
currentWindowWidth = currWidth;
|
|
currentWindowHeight = currHeight;
|
|
|
|
return sdl.window;
|
|
}
|
|
}
|
|
|
|
#if C_OPENGL
|
|
if (sdl_opengl.context) {
|
|
SDL_GL_DeleteContext(sdl_opengl.context);
|
|
sdl_opengl.context=0;
|
|
}
|
|
#endif
|
|
|
|
/* If we change screen type, recreate the window. Furthermore, if
|
|
* it is our very first time then we simply create a new window.
|
|
*/
|
|
if (!sdl.window
|
|
|| (lastType != screenType)
|
|
// || (currWidth != width) || (currHeight != height)
|
|
// || (glwindow != (0 != (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_OPENGL)))
|
|
// || (fullscreen && (0 == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN)))
|
|
// || (fullscreen != (SDL_WINDOW_FULLSCREEN == (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN)))
|
|
// || (fullscreen && ((width != currWidth) || (height != currHeight)))
|
|
) {
|
|
lastType = screenType;
|
|
if (sdl.window) {
|
|
SDL_DestroyWindow(sdl.window);
|
|
}
|
|
|
|
sdl.window = SDL_CreateWindow("",
|
|
SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber?sdl.displayNumber-1:0),
|
|
SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber?sdl.displayNumber-1:0),
|
|
width, height,
|
|
(GFX_IsFullscreen() ? (sdl.desktop.full.display_res ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0)
|
|
| ((screenType == SCREEN_OPENGL) ? SDL_WINDOW_OPENGL : 0) | (maximize && !TTF_using()? SDL_WINDOW_MAXIMIZED : 0)
|
|
| SDL_WINDOW_SHOWN | (SDL2_resize_enable ? SDL_WINDOW_RESIZABLE : 0)
|
|
| (dpi_aware_enable ? SDL_WINDOW_ALLOW_HIGHDPI : 0));
|
|
if (sdl.window) {
|
|
GFX_SetTitle(-1, -1, -1, false); //refresh title.
|
|
}
|
|
sdl.surface = SDL_GetWindowSurface(sdl.window);
|
|
SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
|
|
sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
|
|
|
|
currentWindowWidth = currWidth;
|
|
currentWindowHeight = currHeight;
|
|
|
|
#if C_OPENGL
|
|
if (screenType == SCREEN_OPENGL) {
|
|
sdl_opengl.context = SDL_GL_CreateContext(sdl.window);
|
|
if (sdl_opengl.context == NULL) LOG_MSG("WARNING: SDL2 unable to create GL context");
|
|
if (SDL_GL_MakeCurrent(sdl.window, sdl_opengl.context) != 0) LOG_MSG("WARNING: SDL2 unable to make current GL context");
|
|
}
|
|
#endif
|
|
|
|
return sdl.window;
|
|
}
|
|
/* Fullscreen mode switching has its limits, and is also problematic on
|
|
* some window managers. For now, the following may work up to some
|
|
* level. On X11, SDL_VIDEO_X11_LEGACY_FULLSCREEN=1 can also help,
|
|
* although it has its own issues.
|
|
* Suggestion: Use the desktop res if possible, with output=surface
|
|
* if one is not interested in scaling.
|
|
* On Android, desktop res is the only way.
|
|
*/
|
|
SDL_SetWindowResizable(sdl.window, SDL2_resize_enable ? SDL_TRUE : SDL_FALSE);
|
|
if (GFX_IsFullscreen()) {
|
|
SDL_DisplayMode displayMode;
|
|
SDL_GetWindowDisplayMode(sdl.window, &displayMode);
|
|
displayMode.w = width;
|
|
displayMode.h = height;
|
|
SDL_SetWindowDisplayMode(sdl.window, &displayMode);
|
|
|
|
SDL_SetWindowFullscreen(sdl.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
} else {
|
|
SDL_SetWindowFullscreen(sdl.window, 0);
|
|
|
|
SDL_SetWindowSize(sdl.window, width, height);
|
|
}
|
|
/* Maybe some requested fullscreen resolution is unsupported? */
|
|
SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
|
|
sdl.update_display_contents = ((width == currWidth) && (height == currHeight));
|
|
sdl.surface = SDL_GetWindowSurface(sdl.window);
|
|
|
|
currentWindowWidth = currWidth;
|
|
currentWindowHeight = currHeight;
|
|
|
|
#if C_OPENGL
|
|
if (screenType == SCREEN_OPENGL) {
|
|
sdl_opengl.context = SDL_GL_CreateContext(sdl.window);
|
|
if (sdl_opengl.context == NULL) LOG_MSG("WARNING: SDL2 unable to create GL context");
|
|
if (SDL_GL_MakeCurrent(sdl.window, sdl_opengl.context) != 0) LOG_MSG("WARNING: SDL2 unable to make current GL context");
|
|
}
|
|
#endif
|
|
|
|
return sdl.window;
|
|
}
|
|
|
|
void GFX_SetResizeable(bool enable) {
|
|
if (SDL2_resize_enable != enable) {
|
|
SDL2_resize_enable = enable;
|
|
|
|
if (sdl.window != NULL)
|
|
SDL_SetWindowResizable(sdl.window, SDL2_resize_enable ? SDL_TRUE : SDL_FALSE);
|
|
}
|
|
}
|
|
|
|
// Used for the mapper UI and more: Creates a fullscreen window with desktop res
|
|
// on Android, and a non-fullscreen window with the input dimensions otherwise.
|
|
SDL_Window * GFX_SetSDLSurfaceWindow(uint16_t width, uint16_t height) {
|
|
return GFX_SetSDLWindowMode(width, height, SCREEN_SURFACE);
|
|
}
|
|
|
|
// Returns the rectangle in the current window to be used for scaling a
|
|
// sub-window with the given dimensions, like the mapper UI.
|
|
SDL_Rect GFX_GetSDLSurfaceSubwindowDims(uint16_t width, uint16_t height) {
|
|
SDL_Rect rect;
|
|
rect.x=rect.y=0;
|
|
rect.w=width;
|
|
rect.h=height;
|
|
return rect;
|
|
}
|
|
|
|
# if !defined(C_SDL2)
|
|
// Currently used for an initial test here
|
|
static SDL_Window * GFX_SetSDLOpenGLWindow(uint16_t width, uint16_t height) {
|
|
return GFX_SetSDLWindowMode(width, height, SCREEN_OPENGL);
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
#if !defined(C_SDL2)
|
|
/* Reset the screen with current values in the sdl structure */
|
|
Bitu GFX_GetBestMode(Bitu flags)
|
|
{
|
|
Bitu retFlags = 0;
|
|
|
|
switch (sdl.desktop.want_type)
|
|
{
|
|
case SCREEN_SURFACE:
|
|
retFlags = OUTPUT_SURFACE_GetBestMode(flags);
|
|
break;
|
|
|
|
#if C_OPENGL
|
|
case SCREEN_OPENGL:
|
|
retFlags = OUTPUT_OPENGL_GetBestMode(flags);
|
|
break;
|
|
#endif
|
|
|
|
#if C_DIRECT3D
|
|
case SCREEN_DIRECT3D:
|
|
retFlags = OUTPUT_DIRECT3D_GetBestMode(flags);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(USE_TTF)
|
|
case SCREEN_TTF:
|
|
retFlags = GFX_CAN_32 | GFX_SCALING;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
// we should never reach here
|
|
retFlags = 0;
|
|
break;
|
|
}
|
|
|
|
if (!retFlags)
|
|
{
|
|
if (sdl.desktop.want_type != SCREEN_SURFACE)
|
|
{
|
|
// try falling back down to surface
|
|
OUTPUT_SURFACE_Select();
|
|
retFlags = OUTPUT_SURFACE_GetBestMode(flags);
|
|
}
|
|
if (retFlags == 0)
|
|
LOG_MSG("SDL: Failed everything including falling back to surface GFX_GetBestMode"); // completely failed it seems
|
|
}
|
|
|
|
return retFlags;
|
|
}
|
|
#endif
|
|
|
|
/* FIXME: This prepares the SDL library to accept Win32 drag+drop events from the Windows shell.
|
|
* So it should be named something like EnableDragAcceptFiles() not SDL_Prepare() */
|
|
void SDL_Prepare(void) {
|
|
if (menu_compatible) return;
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS) // Microsoft Windows specific
|
|
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
|
#if !defined(C_SDL2)
|
|
LOG(LOG_MISC,LOG_DEBUG)("Win32: Preparing main window to accept files dragged in from the Windows shell");
|
|
|
|
SDL_PumpEvents();
|
|
DragAcceptFiles(GetHWND(), TRUE);
|
|
#endif
|
|
#endif
|
|
#if !defined(C_SDL2)
|
|
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
|
|
#endif
|
|
}
|
|
|
|
void GFX_ForceRedrawScreen(void) {
|
|
GFX_Stop();
|
|
if (sdl.draw.callback)
|
|
(sdl.draw.callback)( GFX_CallBackReset );
|
|
GFX_Start();
|
|
#if defined(USE_TTF)
|
|
if (TTF_using() && CurMode->type==M_TEXT) ttf.inUse = true;
|
|
if (ttf.inUse)
|
|
GFX_EndTextLines(true);
|
|
#endif
|
|
}
|
|
|
|
void GFX_ResetScreen(void) {
|
|
fullscreen_switch=false;
|
|
if(glide.enabled) {
|
|
GLIDE_ResetScreen(true);
|
|
return;
|
|
}
|
|
SDL_Rect *rect = &sdl.updateRects[0];
|
|
rect->x = 0; rect->y = 0; rect->w = 0; rect->h = 0;
|
|
#if defined(C_SDL2)
|
|
SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
|
|
#else
|
|
SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
|
|
#endif
|
|
GFX_Stop();
|
|
if (sdl.draw.callback)
|
|
(sdl.draw.callback)( GFX_CallBackReset );
|
|
GFX_Start();
|
|
CPU_Reset_AutoAdjust();
|
|
fullscreen_switch=true;
|
|
DOSBox_RefreshMenu(); // for menu
|
|
}
|
|
|
|
void GFX_ForceFullscreenExit(void) {
|
|
if (sdl.desktop.lazy_fullscreen) {
|
|
LOG_MSG("GFX LF: invalid screen change");
|
|
} else {
|
|
sdl.desktop.fullscreen=false;
|
|
GFX_ResetScreen();
|
|
}
|
|
}
|
|
|
|
uint32_t GFX_Rmask;
|
|
unsigned char GFX_Rshift;
|
|
uint32_t GFX_Gmask;
|
|
unsigned char GFX_Gshift;
|
|
uint32_t GFX_Bmask;
|
|
unsigned char GFX_Bshift;
|
|
uint32_t GFX_Amask;
|
|
unsigned char GFX_Ashift;
|
|
unsigned char GFX_bpp;
|
|
|
|
unsigned int GFX_GetBShift() {
|
|
return sdl.surface->format->Bshift;
|
|
}
|
|
|
|
void GFX_LogSDLState(void)
|
|
{
|
|
LOG(LOG_MISC,LOG_DEBUG)("SDL video mode: %ux%u (clip %ux%u with upper-left at %ux%u) %ubpp",
|
|
(unsigned)sdl.surface->w,(unsigned)sdl.surface->h,
|
|
(unsigned)sdl.clip.w,(unsigned)sdl.clip.h,
|
|
(unsigned)sdl.clip.x,(unsigned)sdl.clip.y,
|
|
(unsigned)sdl.surface->format->BitsPerPixel);
|
|
LOG(LOG_MISC,LOG_DEBUG)(" red: shift=%u mask=0x%08lx",
|
|
(unsigned)sdl.surface->format->Rshift,
|
|
(unsigned long)sdl.surface->format->Rmask);
|
|
LOG(LOG_MISC,LOG_DEBUG)(" green: shift=%u mask=0x%08lx",
|
|
(unsigned)sdl.surface->format->Gshift,
|
|
(unsigned long)sdl.surface->format->Gmask);
|
|
LOG(LOG_MISC,LOG_DEBUG)(" blue: shift=%u mask=0x%08lx",
|
|
(unsigned)sdl.surface->format->Bshift,
|
|
(unsigned long)sdl.surface->format->Bmask);
|
|
LOG(LOG_MISC,LOG_DEBUG)(" alpha: shift=%u mask=0x%08lx",
|
|
(unsigned)sdl.surface->format->Ashift,
|
|
(unsigned long)sdl.surface->format->Amask);
|
|
|
|
GFX_bpp = sdl.surface->format->BitsPerPixel;
|
|
GFX_Rmask = sdl.surface->format->Rmask;
|
|
GFX_Rshift = sdl.surface->format->Rshift;
|
|
GFX_Gmask = sdl.surface->format->Gmask;
|
|
GFX_Gshift = sdl.surface->format->Gshift;
|
|
GFX_Bmask = sdl.surface->format->Bmask;
|
|
GFX_Bshift = sdl.surface->format->Bshift;
|
|
GFX_Amask = sdl.surface->format->Amask;
|
|
GFX_Ashift = sdl.surface->format->Ashift;
|
|
}
|
|
|
|
void GFX_TearDown(void) {
|
|
if (sdl.updating)
|
|
GFX_EndUpdate( 0 );
|
|
|
|
if (sdl.blit.surface) {
|
|
SDL_FreeSurface(sdl.blit.surface);
|
|
sdl.blit.surface=0;
|
|
}
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
bool DOSBox_isMenuVisible(void);
|
|
void MenuShadeRect(int x,int y,int w,int h);
|
|
void MenuDrawRect(int x,int y,int w,int h,Bitu color);
|
|
void GFX_DrawSDLMenu(DOSBoxMenu &menu, DOSBoxMenu::displaylist &dl) {
|
|
if (!menu.needsRedraw() || (sdl.updating && !OpenGL_using())) {
|
|
return;
|
|
}
|
|
if (!DOSBox_isMenuVisible() || sdl.desktop.fullscreen) {
|
|
// BUGFIX: If the menu is hidden then silently clear "needs redraw" to avoid excess redraw of nothing
|
|
menu.clearRedraw();
|
|
return;
|
|
}
|
|
|
|
bool mustLock = !OpenGL_using() && SDL_MUSTLOCK(sdl.surface);
|
|
|
|
if (mustLock) {
|
|
SDL_LockSurface(sdl.surface);
|
|
}
|
|
|
|
if (&dl == &menu.display_list) { /* top level menu, draw background */
|
|
MenuDrawRect(menu.menuBox.x, menu.menuBox.y, menu.menuBox.w, menu.menuBox.h - 1, GFX_GetRGB(63, 63, 63));
|
|
MenuDrawRect(menu.menuBox.x, menu.menuBox.y + menu.menuBox.h - 1, menu.menuBox.w, 1,
|
|
GFX_GetRGB(31, 31, 31));
|
|
}
|
|
|
|
if (mustLock) {
|
|
SDL_UnlockSurface(sdl.surface);
|
|
}
|
|
|
|
int cp = dos.loaded_codepage;
|
|
if (!cp) InitCodePage();
|
|
if (IS_PC98_ARCH || IS_JEGA_ARCH || isDBCSCP()) InitFontHandle();
|
|
dos.loaded_codepage = cp;
|
|
#if 0
|
|
LOG_MSG("menudraw %u",(unsigned int)SDL_GetTicks());
|
|
#endif
|
|
|
|
menu.clearRedraw();
|
|
menu.display_list.DrawDisplayList(menu,/*updateScreen*/false);
|
|
|
|
if (!OpenGL_using()) {
|
|
#if defined(C_SDL2)
|
|
SDL_UpdateWindowSurfaceRects( sdl.window, &menu.menuBox, 1 );
|
|
#else
|
|
SDL_UpdateRects(sdl.surface, 1, &menu.menuBox);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void drawmenu(Bitu val) {
|
|
if (menu_gui && menu.toggle) GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
}
|
|
#endif
|
|
|
|
void RENDER_Reset(void);
|
|
|
|
Bitu GFX_SetSize(Bitu width, Bitu height, Bitu flags, double scalex, double scaley, GFX_CallBack_t callback)
|
|
{
|
|
if ((width == 0 || height == 0) && !TTF_using()) {
|
|
E_Exit("GFX_SetSize with width=%d height=%d zero dimensions not allowed",(int)width,(int)height);
|
|
return 0;
|
|
}
|
|
bool diff = width != sdl.draw.width || height != sdl.draw.height;
|
|
|
|
if (sdl.updating)
|
|
GFX_EndUpdate( 0 );
|
|
|
|
sdl.must_redraw_all = true;
|
|
|
|
sdl.draw.width = (uint32_t)width;
|
|
sdl.draw.height = (uint32_t)height;
|
|
#if defined(USE_TTF)
|
|
if (TTF_using()) {
|
|
#if C_OPENGL && defined(MACOSX) && !defined(C_SDL2)
|
|
sdl_opengl.framebuf = calloc(sdl.draw.width*sdl.draw.height, 4);
|
|
sdl.desktop.type = SCREEN_OPENGL;
|
|
#else
|
|
sdl.desktop.type = SCREEN_SURFACE;
|
|
#endif
|
|
return OUTPUT_TTF_SetSize();
|
|
}
|
|
#endif
|
|
sdl.draw.flags = flags;
|
|
sdl.draw.callback = callback;
|
|
sdl.draw.scalex = scalex;
|
|
sdl.draw.scaley = scaley;
|
|
|
|
LOG(LOG_MISC,LOG_DEBUG)("GFX_SetSize %ux%u flags=0x%x scale=%.3fx%.3f",
|
|
(unsigned int)width,(unsigned int)height,
|
|
(unsigned int)flags,
|
|
scalex,scaley);
|
|
|
|
// Bitu bpp = 0;
|
|
Bitu retFlags = 0;
|
|
|
|
if (sdl.blit.surface) {
|
|
SDL_FreeSurface(sdl.blit.surface);
|
|
sdl.blit.surface=0;
|
|
}
|
|
|
|
switch (sdl.desktop.want_type) {
|
|
case SCREEN_SURFACE:
|
|
retFlags = OUTPUT_SURFACE_SetSize();
|
|
break;
|
|
|
|
#if C_OPENGL
|
|
case SCREEN_OPENGL:
|
|
retFlags = OUTPUT_OPENGL_SetSize();
|
|
break;
|
|
#endif
|
|
|
|
#if C_DIRECT3D
|
|
case SCREEN_DIRECT3D:
|
|
retFlags = OUTPUT_DIRECT3D_SetSize();
|
|
break;
|
|
#endif
|
|
|
|
#if defined(USE_TTF)
|
|
case SCREEN_TTF:
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
// we should never reach here
|
|
retFlags = 0;
|
|
break;
|
|
}
|
|
|
|
if (!retFlags)
|
|
{
|
|
if (sdl.desktop.want_type != SCREEN_SURFACE)
|
|
{
|
|
// try falling back down to surface
|
|
OUTPUT_SURFACE_Select();
|
|
retFlags = OUTPUT_SURFACE_SetSize();
|
|
}
|
|
if (retFlags == 0)
|
|
LOG_MSG("SDL: Failed everything including falling back to surface in GFX_GetSize"); // completely failed it seems
|
|
}
|
|
|
|
// we have selected an actual desktop type
|
|
sdl.desktop.type = sdl.desktop.want_type;
|
|
|
|
GFX_LogSDLState();
|
|
|
|
if (retFlags)
|
|
GFX_Start();
|
|
|
|
if (!sdl.mouse.autoenable && !sdl.mouse.locked)
|
|
SDL_ShowCursor(sdl.mouse.autolock?SDL_DISABLE:SDL_ENABLE);
|
|
|
|
#if defined(C_SDL2)
|
|
if (diff && posx < 0 && posy < 0 && !(posx == -2 && posy == -2)) {
|
|
if (sdl.displayNumber==0)
|
|
SDL_SetWindowPosition(sdl.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
else {
|
|
int bx = 0, by = 0;
|
|
int displays = SDL_GetNumVideoDisplays();
|
|
SDL_Rect bound;
|
|
for( int i = 1; i <= displays; i++ ) {
|
|
bound = SDL_Rect();
|
|
SDL_GetDisplayBounds(i-1, &bound);
|
|
if (i == sdl.displayNumber) {
|
|
bx = bound.x;
|
|
by = bound.y;
|
|
break;
|
|
}
|
|
}
|
|
SDL_DisplayMode dm;
|
|
if (SDL_GetDesktopDisplayMode(sdl.displayNumber?sdl.displayNumber-1:0,&dm) == 0) {
|
|
bx += (dm.w - sdl.draw.width - sdl.clip.x)/2;
|
|
by += (dm.h - sdl.draw.height - sdl.clip.y)/2;
|
|
}
|
|
SDL_SetWindowPosition(sdl.window, bx, by);
|
|
}
|
|
}
|
|
#endif
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
if (!sdl.desktop.fullscreen && menu_gui && menu.toggle && ((width == 640 || (vga.draw.char9_set && width == 720)) && ((machine != MCH_CGA && !IS_VGA_ARCH && !IS_PC98_ARCH && height == 350) || height == 400)) || ((render.aspect || IS_DOSV) && checkmenuwidth)) {
|
|
RECT r;
|
|
bool res = GetWindowRect(GetHWND(), &r);
|
|
unsigned int maxWidth, maxHeight;
|
|
GetMaxWidthHeight(&maxWidth, &maxHeight);
|
|
int tWidth = menuwidth_atleast(r.right-r.left);
|
|
if (tWidth>0 && tWidth>maxWidth) tWidth = maxWidth;
|
|
int tHeight = r.bottom-r.top-height<0?(r.bottom-r.top):(double)(tWidth-((r.right-r.left-width)<0?0:(r.right-r.left-width)))*height/width+(r.bottom-r.top-height);
|
|
if (tHeight>0 && tHeight>maxHeight) tHeight = maxHeight;
|
|
if (res && tWidth>0 && tHeight>0) {
|
|
MoveWindow(GetHWND(), r.left, r.top, tWidth, tHeight, true);
|
|
LOG_MSG("SDL: Window size enlarged for the menus\n");
|
|
}
|
|
}
|
|
#endif
|
|
UpdateWindowDimensions();
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
WindowsTaskbarUpdatePreviewRegion();
|
|
#endif
|
|
|
|
return retFlags;
|
|
}
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
// WARNING: Not recommended, there is danger you cannot exit emulator because mouse+keyboard are taken
|
|
static bool enable_hook_everything = false;
|
|
#endif
|
|
|
|
// Whether or not to hook the keyboard and block special keys.
|
|
// Setting this is recommended so that your keyboard is fully usable in the guest OS when you
|
|
// enable the mouse+keyboard capture. But hooking EVERYTHING is not recommended because there is a
|
|
// danger you become trapped in the DOSBox-X emulator!
|
|
static bool enable_hook_special_keys = true;
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
// Whether or not to hook Num/Scroll/Caps lock in order to give the guest OS full control of the
|
|
// LEDs on the keyboard (i.e. the LEDs do not change until the guest OS changes their state).
|
|
// This flag also enables code to set the LEDs to guest state when setting mouse+keyboard capture,
|
|
// and restoring LED state when releasing capture.
|
|
static bool enable_hook_lock_toggle_keys = true;
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
|
|
// and this is where we store host LED state when capture is set.
|
|
static bool on_capture_num_lock_was_on = true; // reasonable guess
|
|
static bool on_capture_scroll_lock_was_on = false;
|
|
static bool on_capture_caps_lock_was_on = false;
|
|
#endif
|
|
|
|
static bool exthook_enabled = false;
|
|
#if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
|
|
static HHOOK exthook_winhook = NULL;
|
|
|
|
#if !defined(__MINGW32__)
|
|
extern "C" void SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(unsigned char x);
|
|
#endif
|
|
|
|
static LRESULT CALLBACK WinExtHookKeyboardHookProc(int nCode,WPARAM wParam,LPARAM lParam) {
|
|
if (nCode == HC_ACTION) {
|
|
HWND myHwnd = GetHWND();
|
|
|
|
if (exthook_enabled && GetFocus() == myHwnd) { /* intercept only if DOSBox-X is the focus and the keyboard is hooked */
|
|
if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN || wParam == WM_SYSKEYUP || wParam == WM_KEYUP) {
|
|
KBDLLHOOKSTRUCT *st_hook = (KBDLLHOOKSTRUCT*)lParam;
|
|
|
|
if (st_hook->flags & LLKHF_INJECTED) {
|
|
// injected keys are automatically allowed, especially if we are injecting keyboard input into ourself
|
|
// to control Num/Scroll/Caps Lock LEDs. If we don't check this we cannot control the LEDs. Injecting
|
|
// keydown/keyup for Num Lock is the only means provided by Windows to control those LEDs.
|
|
}
|
|
else if (st_hook->vkCode == VK_MENU/*alt*/ || st_hook->vkCode == VK_CONTROL ||
|
|
st_hook->vkCode == VK_LSHIFT || st_hook->vkCode == VK_RSHIFT) {
|
|
// always allow modifier keys through, so other applications are not left with state inconsistent from
|
|
// actual keyboard state.
|
|
}
|
|
else {
|
|
bool nopass = enable_hook_everything; // if the user wants us to hook ALL keys then that's where this signals it
|
|
bool alternate_message = false; // send as WM_USER+0x100 instead of WM_KEYDOWN
|
|
|
|
if (!nopass) {
|
|
// hook only certain keys Windows is likely to act on by itself.
|
|
|
|
// FIXME: Hooking the keyboard does NOT prevent Fn+SPACE (zoom) from triggering screen resolution
|
|
// changes in Windows 10! How do we stop that?
|
|
|
|
// FIXME: It might be nice to let the user decide whether or not Print Screen is intercepted.
|
|
|
|
// TODO: We do not hook the volume up/down/mute keys. This is to be kind to the user. They may
|
|
// appreciate the ability to dial down the volume if a loud DOS program comes up. But
|
|
// if the user WANTS us to, we should allow hooking those keys.
|
|
|
|
// TODO: Allow (if instructed) hooking the VK_SLEEP key so pushing the sleep key (the
|
|
// one with the icon of the moon on Microsoft keyboards) can be sent instead to the
|
|
// guest OS. Also add code where if we're not hooking the key, then we should listen
|
|
// for signals the guest OS is suspending or hibernating and auto-disconnect the
|
|
// mouse capture and keyboard hook.
|
|
|
|
switch (st_hook->vkCode) {
|
|
case VK_LWIN: // left Windows key (normally triggers Start menu)
|
|
case VK_RWIN: // right Windows key (normally triggers Start menu)
|
|
case VK_APPS: // Application key (normally open to the user, but just in case)
|
|
case VK_PAUSE: // pause key
|
|
case VK_SNAPSHOT: // print screen
|
|
case VK_TAB: // try to catch ALT+TAB too (not blocking VK_TAB will allow host OS to switch tasks)
|
|
case VK_ESCAPE: // try to catch CTRL+ESC as well (so Windows 95 Start Menu is accessible)
|
|
case VK_SPACE: // and space (catching VK_ZOOM isn't enough to prevent Windows 10 from changing res)
|
|
// these keys have no meaning to DOSBox-X and so we hook them by default to allow the guest OS to use them
|
|
case VK_BROWSER_BACK: // Browser Back key
|
|
case VK_BROWSER_FORWARD: // Browser Forward key
|
|
case VK_BROWSER_REFRESH: // Browser Refresh key
|
|
case VK_BROWSER_STOP: // Browser Stop key
|
|
case VK_BROWSER_SEARCH: // Browser Search key
|
|
case VK_BROWSER_FAVORITES: // Browser Favorites key
|
|
case VK_BROWSER_HOME: // Browser Start and Home key
|
|
case VK_MEDIA_NEXT_TRACK: // Next Track key
|
|
case VK_MEDIA_PREV_TRACK: // Previous Track key
|
|
case VK_MEDIA_STOP: // Stop Media key
|
|
case VK_MEDIA_PLAY_PAUSE: // Play / Pause Media key
|
|
case VK_LAUNCH_MAIL: // Start Mail key
|
|
case VK_LAUNCH_MEDIA_SELECT: // Select Media key
|
|
case VK_LAUNCH_APP1: // Start Application 1 key
|
|
case VK_LAUNCH_APP2: // Start Application 2 key
|
|
case VK_PLAY: // Play key
|
|
case VK_ZOOM: // Zoom key (the (+) magnifying glass keyboard shortcut laptops have these days on the spacebar?)
|
|
nopass = true;
|
|
break;
|
|
|
|
// IME Hiragana key, otherwise inaccessible to us
|
|
case 0xF2:
|
|
nopass = true; // FIXME: This doesn't (yet) cause a SDL key event.
|
|
break;
|
|
|
|
// we allow hooking Num/Scroll/Caps Lock keys so that pressing them does not toggle the LED.
|
|
// we then take Num/Scroll/Caps LED state from the guest and let THAT control the LED state.
|
|
case VK_CAPITAL:
|
|
case VK_NUMLOCK:
|
|
case VK_SCROLL:
|
|
nopass = enable_hook_lock_toggle_keys;
|
|
alternate_message = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nopass) {
|
|
// convert WM_KEYDOWN/WM_KEYUP if obfuscating the message to distinguish between real and injected events
|
|
if (alternate_message) {
|
|
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
|
|
wParam = WM_USER + 0x100;
|
|
else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
|
|
wParam = WM_USER + 0x101;
|
|
}
|
|
|
|
DWORD lParam =
|
|
(st_hook->scanCode << 8U) +
|
|
((st_hook->flags & LLKHF_EXTENDED) ? 0x01000000 : 0) +
|
|
((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) ? 0xC0000000 : 0);
|
|
|
|
// catch the keystroke, post it to ourself, do not pass it on
|
|
PostMessage(myHwnd, (UINT)wParam, st_hook->vkCode, lParam);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return CallNextHookEx(exthook_winhook, nCode, wParam, lParam);
|
|
}
|
|
|
|
// Microsoft doesn't have an outright "set toggle key state" call, they expect you
|
|
// to know the state and then fake input to toggle. Blegh. Fine.
|
|
void WinSetKeyToggleState(unsigned int vkCode, bool state) {
|
|
bool curState = (GetKeyState(vkCode) & 1) ? true : false;
|
|
INPUT inps;
|
|
|
|
// if we're already in that state, then there is nothing to do.
|
|
if (curState == state) return;
|
|
|
|
// fake keyboard input.
|
|
memset(&inps, 0, sizeof(inps));
|
|
inps.type = INPUT_KEYBOARD;
|
|
inps.ki.wVk = vkCode;
|
|
inps.ki.dwFlags = KEYEVENTF_EXTENDEDKEY; // pressed, use wVk.
|
|
SendInput(1, &inps, sizeof(INPUT));
|
|
|
|
memset(&inps, 0, sizeof(inps));
|
|
inps.type = INPUT_KEYBOARD;
|
|
inps.ki.wVk = vkCode;
|
|
inps.ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; // release, use wVk.
|
|
SendInput(1, &inps, sizeof(INPUT));
|
|
}
|
|
#endif
|
|
|
|
Bitu Keyboard_Guest_LED_State();
|
|
void UpdateKeyboardLEDState(Bitu led_state/* in the same bitfield arrangement as using command 0xED on PS/2 keyboards */);
|
|
|
|
void UpdateKeyboardLEDState(Bitu led_state/* in the same bitfield arrangement as using command 0xED on PS/2 keyboards */) {
|
|
(void)led_state;//POSSIBLY UNUSED
|
|
#if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) /* Microsoft Windows */
|
|
if (exthook_enabled) { // ONLY if ext hook is enabled, else we risk infinite loops with keyboard events
|
|
//WinSetKeyToggleState(VK_NUMLOCK, !!(led_state & 2));
|
|
//WinSetKeyToggleState(VK_SCROLL, !!(led_state & 1));
|
|
//WinSetKeyToggleState(VK_CAPITAL, !!(led_state & 4));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void DoExtendedKeyboardHook(bool enable) {
|
|
if (exthook_enabled == enable)
|
|
return;
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
|
|
if (enable) {
|
|
if (!exthook_winhook) {
|
|
exthook_winhook = SetWindowsHookEx(WH_KEYBOARD_LL, WinExtHookKeyboardHookProc, GetModuleHandle(NULL), 0);
|
|
if (exthook_winhook == NULL) return;
|
|
}
|
|
|
|
// it's on
|
|
exthook_enabled = enable;
|
|
|
|
// flush out and handle pending keyboard I/O
|
|
{
|
|
MSG msg;
|
|
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
#if !defined(__MINGW32__)
|
|
// Enable the SDL hack for Win32 to handle Num/Scroll/Caps
|
|
SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(1);
|
|
#endif
|
|
|
|
// if hooking Num/Scroll/Caps Lock then record the toggle state of those keys.
|
|
// then read from the keyboard emulation the LED state set by the guest and apply it to the host keyboard.
|
|
if (enable_hook_lock_toggle_keys) {
|
|
// record state
|
|
on_capture_num_lock_was_on = (GetKeyState(VK_NUMLOCK) & 1) ? true : false;
|
|
on_capture_scroll_lock_was_on = (GetKeyState(VK_SCROLL) & 1) ? true : false;
|
|
on_capture_caps_lock_was_on = (GetKeyState(VK_CAPITAL) & 1) ? true : false;
|
|
// change to guest state (FIXME: Read emulated keyboard state and apply!)
|
|
UpdateKeyboardLEDState(Keyboard_Guest_LED_State());
|
|
}
|
|
}
|
|
else {
|
|
if (exthook_winhook) {
|
|
if (enable_hook_lock_toggle_keys) {
|
|
// restore state
|
|
//WinSetKeyToggleState(VK_NUMLOCK, on_capture_num_lock_was_on);
|
|
//WinSetKeyToggleState(VK_SCROLL, on_capture_scroll_lock_was_on);
|
|
//WinSetKeyToggleState(VK_CAPITAL, on_capture_caps_lock_was_on);
|
|
}
|
|
|
|
{
|
|
MSG msg;
|
|
|
|
// before we disable the SDL hack make sure we flush out and handle any pending keyboard events.
|
|
// if we don't do this the posted Num/Scroll/Caps events will stay in the queue and will be handled
|
|
// by SDL after turning off the toggle key hack.
|
|
Sleep(1); // make sure Windows posts the keystrokes
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
#if !defined(__MINGW32__)
|
|
// Disable the SDL hack for Win32 to handle Num/Scroll/Caps
|
|
SDL_DOSBox_X_Hack_Set_Toggle_Key_WM_USER_Hack(0);
|
|
#endif
|
|
|
|
UnhookWindowsHookEx(exthook_winhook);
|
|
exthook_winhook = NULL;
|
|
}
|
|
|
|
exthook_enabled = enable;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void GFX_SetShader(const char* src) {
|
|
#if C_OPENGL
|
|
sdl_opengl.shader_def = render.shader_def;
|
|
if (!sdl_opengl.use_shader || src == sdl_opengl.shader_src)
|
|
return;
|
|
|
|
sdl_opengl.shader_src = src;
|
|
if (sdl_opengl.program_object) {
|
|
glDeleteProgram(sdl_opengl.program_object);
|
|
sdl_opengl.program_object = 0;
|
|
}
|
|
#else
|
|
(void)src;//UNUSED
|
|
#endif
|
|
}
|
|
|
|
void GFX_ReleaseMouse(void) {
|
|
if (sdl.mouse.locked)
|
|
GFX_CaptureMouse();
|
|
}
|
|
|
|
void GFX_CaptureMouse(void) {
|
|
GFX_CaptureMouse(!sdl.mouse.locked);
|
|
}
|
|
|
|
void GFX_CaptureMouse(bool capture) {
|
|
sdl.mouse.locked=capture;
|
|
if (sdl.mouse.locked) {
|
|
#if defined(C_SDL2)
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
#else
|
|
SDL_WM_GrabInput(SDL_GRAB_ON);
|
|
#endif
|
|
if (enable_hook_special_keys) DoExtendedKeyboardHook(true);
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
} else {
|
|
DoExtendedKeyboardHook(false);
|
|
#if defined(C_SDL2)
|
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
|
#else
|
|
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
|
#endif
|
|
if (vmware_mouse) SDL_ShowCursor(SDL_DISABLE);
|
|
else if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
|
|
}
|
|
mouselocked=sdl.mouse.locked;
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
|
|
if (sdl.mouse.locked) {
|
|
void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
}
|
|
#endif
|
|
|
|
GFX_SetTitle(-1,-1,-1,false);
|
|
|
|
/* keep the menu updated (it might not exist yet) */
|
|
if (mainMenu.item_exists("mapper_capmouse"))
|
|
mainMenu.get_item("mapper_capmouse").check(sdl.mouse.locked).refresh_item(mainMenu);
|
|
}
|
|
|
|
void GFX_UpdateSDLCaptureState(void) {
|
|
if (sdl.mouse.locked) {
|
|
#if defined(C_SDL2)
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
#else
|
|
SDL_WM_GrabInput(SDL_GRAB_ON);
|
|
#endif
|
|
if (enable_hook_special_keys) DoExtendedKeyboardHook(true);
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
} else {
|
|
DoExtendedKeyboardHook(false);
|
|
#if defined(C_SDL2)
|
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
|
#else
|
|
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
|
#endif
|
|
if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
|
|
}
|
|
CPU_Reset_AutoAdjust();
|
|
GFX_SetTitle(-1,-1,-1,false);
|
|
}
|
|
|
|
#if WIN32
|
|
void CaptureMouseNotifyWin32(bool lck)
|
|
{
|
|
switch (sdl.mouse.autolock_feedback)
|
|
{
|
|
case AUTOLOCK_FEEDBACK_NONE: break;
|
|
case AUTOLOCK_FEEDBACK_BEEP:
|
|
{
|
|
const auto lo = 1000;
|
|
const auto hi = 2000;
|
|
const auto t1 = 50;
|
|
const auto t2 = 25;
|
|
const auto f1 = lck ? hi : lo;
|
|
const auto f2 = lck ? lo : hi;
|
|
const auto tt = lck ? t1 : t2;
|
|
Beep(f1, tt);
|
|
Beep(f2, tt);
|
|
}
|
|
break;
|
|
case AUTOLOCK_FEEDBACK_FLASH:
|
|
{
|
|
# if !defined(HX_DOS)
|
|
const auto cnt = lck ? 4 : 2;
|
|
const auto tim = lck ? 80 : 40;
|
|
const auto wnd = GetHWND();
|
|
if (wnd != nullptr)
|
|
{
|
|
FLASHWINFO fi;
|
|
fi.cbSize = sizeof(FLASHWINFO);
|
|
fi.hwnd = wnd;
|
|
fi.dwFlags = FLASHW_CAPTION;
|
|
fi.uCount = cnt;
|
|
fi.dwTimeout = tim;
|
|
FlashWindowEx(&fi);
|
|
}
|
|
# endif
|
|
break;
|
|
}
|
|
default: ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CaptureMouseNotify()
|
|
{
|
|
CaptureMouseNotify(sdl.mouse.locked);
|
|
}
|
|
|
|
void CaptureMouseNotify(bool capture)
|
|
{
|
|
#if WIN32
|
|
CaptureMouseNotifyWin32(capture);
|
|
#else
|
|
(void)capture;
|
|
// TODO
|
|
#endif
|
|
}
|
|
|
|
static void CaptureMouse(bool pressed) {
|
|
if (!pressed || is_paused)
|
|
return;
|
|
|
|
CaptureMouseNotify();
|
|
GFX_CaptureMouse();
|
|
}
|
|
|
|
#if defined (WIN32)
|
|
STICKYKEYS stick_keys = {sizeof(STICKYKEYS), 0};
|
|
void sticky_keys(bool restore){
|
|
static bool inited = false;
|
|
if (!inited){
|
|
inited = true;
|
|
SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0);
|
|
}
|
|
if (restore) {
|
|
SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0);
|
|
return;
|
|
}
|
|
//Get current sticky keys layout:
|
|
STICKYKEYS s = {sizeof(STICKYKEYS), 0};
|
|
SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0);
|
|
if ( !(s.dwFlags & SKF_STICKYKEYSON)) { //Not on already
|
|
s.dwFlags &= ~SKF_HOTKEYACTIVE;
|
|
SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0);
|
|
}
|
|
}
|
|
#else
|
|
#define sticky_keys(a)
|
|
#endif
|
|
|
|
void GetDesktopResolution(int* width, int* height)
|
|
{
|
|
#ifdef WIN32
|
|
RECT rDdesk;
|
|
auto hDesk = GetDesktopWindow();
|
|
GetWindowRect(hDesk, &rDdesk);
|
|
*width = rDdesk.right - rDdesk.left;
|
|
*height = rDdesk.bottom - rDdesk.top;
|
|
#elif defined(LINUX)
|
|
void Linux_GetDesktopResolution(int *width,int *height);
|
|
Linux_GetDesktopResolution(width,height); /* this is MESSY but there's too much namespace collision going on here */
|
|
#else
|
|
*width = 1024; // guess
|
|
*height = 768;
|
|
#endif
|
|
}
|
|
|
|
void res_init(void) {
|
|
Section * sec = control->GetSection("sdl");
|
|
Section_prop * section=static_cast<Section_prop *>(sec);
|
|
sdl.desktop.full.fixed=false;
|
|
const char* fullresolution=section->Get_string("fullresolution");
|
|
sdl.desktop.full.width = 0; sdl.desktop.full.height = 0;
|
|
if(fullresolution && *fullresolution) {
|
|
char res[100];
|
|
safe_strncpy( res, fullresolution, sizeof( res ));
|
|
fullresolution = lowcase (res);//so x and X are allowed
|
|
if (strcmp(fullresolution,"original")) {
|
|
sdl.desktop.full.fixed = true;
|
|
if (strcmp(fullresolution,"desktop")) { //desktop = 0x0
|
|
char* height = const_cast<char*>(strchr(fullresolution,'x'));
|
|
if(height && * height) {
|
|
*height = 0;
|
|
sdl.desktop.full.height = atoi(height+1);
|
|
sdl.desktop.full.width = atoi(res);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sdl.desktop.window.width = 0;
|
|
sdl.desktop.window.height = 0;
|
|
const char* windowresolution=section->Get_string("windowresolution");
|
|
if(windowresolution && *windowresolution) {
|
|
//if(sdl.desktop.type==SCREEN_SURFACE) return;
|
|
char res[100];
|
|
safe_strncpy( res,windowresolution, sizeof( res ));
|
|
windowresolution = lowcase (res);//so x and X are allowed
|
|
if(strcmp(windowresolution,"original")) {
|
|
char* height = const_cast<char*>(strchr(windowresolution,'x'));
|
|
if(height && *height) {
|
|
*height = 0;
|
|
sdl.desktop.window.height = (uint16_t)atoi(height+1);
|
|
sdl.desktop.window.width = (uint16_t)atoi(res);
|
|
}
|
|
}
|
|
}
|
|
sdl.desktop.doublebuf=section->Get_bool("fulldouble");
|
|
|
|
int width = 1024;
|
|
int height = 768;
|
|
|
|
// fullresolution == desktop -> get/set desktop size
|
|
auto sdlSection = control->GetSection("sdl");
|
|
auto sdlSectionProp = static_cast<Section_prop*>(sdlSection);
|
|
auto fullRes = sdlSectionProp->Get_string("fullresolution");
|
|
if (!strcmp(fullRes, "desktop")) GetDesktopResolution(&width, &height);
|
|
|
|
if (!sdl.desktop.full.width) {
|
|
sdl.desktop.full.width=width;
|
|
}
|
|
if (!sdl.desktop.full.height) {
|
|
sdl.desktop.full.height=height;
|
|
}
|
|
if(sdl.desktop.type==SCREEN_SURFACE && !sdl.desktop.fullscreen) return;
|
|
else {
|
|
GFX_Stop();
|
|
if (sdl.draw.callback)
|
|
(sdl.draw.callback)( GFX_CallBackReset );
|
|
GFX_Start();
|
|
}
|
|
}
|
|
|
|
void res_input(bool type, const char * res) {
|
|
Section* sec = control->GetSection("sdl");
|
|
|
|
if(sec) {
|
|
char win_res[11];
|
|
strcpy(win_res,res);
|
|
if(type) {
|
|
std::string tmp("windowresolution="); tmp.append(win_res);
|
|
sec->HandleInputline(tmp);
|
|
} else {
|
|
std::string tmp("fullresolution="); tmp.append(win_res);
|
|
sec->HandleInputline(tmp);
|
|
}
|
|
|
|
res_init();
|
|
}
|
|
}
|
|
|
|
#if !defined(HX_DOS) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
extern "C" void SDL_hax_SetFSWindowPosition(int x,int y,int w,int h);
|
|
#endif
|
|
|
|
void modeSwitched(bool full) {
|
|
LOG_MSG("INFO: switched to %s mode", full ? "full screen" : "window");
|
|
|
|
#if defined (WIN32)
|
|
// (re-)assign menu to window
|
|
DOSBox_SetSysMenu();
|
|
#endif
|
|
|
|
// ensure mouse capture when fullscreen || (re-)capture if user said so when windowed
|
|
auto locked = sdl.mouse.locked;
|
|
if ((full && !locked) || (!full && locked)) GFX_CaptureMouse();
|
|
}
|
|
|
|
void GFX_SwitchFullScreen(void)
|
|
{
|
|
#if defined(USE_TTF)
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
if (ttf.fullScrn && ttf.inUse && !control->opt_nomenu && static_cast<Section_prop *>(control->GetSection("sdl"))->Get_bool("showmenu")) {
|
|
DOSBox_SetMenu();
|
|
lastmenu = true;
|
|
}
|
|
#endif
|
|
|
|
if (ttf.inUse) {
|
|
if (ttf.fullScrn) {
|
|
sdl.desktop.fullscreen = false;
|
|
if (lastfontsize>0)
|
|
OUTPUT_TTF_Select(lastfontsize);
|
|
else
|
|
OUTPUT_TTF_Select(1);
|
|
resetFontSize();
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
if (lastmenu) DOSBox_SetMenu();
|
|
#endif
|
|
#if defined(C_SDL2)
|
|
if (posx >= 0 && posy >= 0)
|
|
SDL_SetWindowPosition(sdl.window, posx, posy);
|
|
else if (sdl.displayNumber==0 && !(posx == -2 && posy == -2))
|
|
SDL_SetWindowPosition(sdl.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
else if (posx != -2 || posy != -2) {
|
|
int bx = 0, by = 0;
|
|
int displays = SDL_GetNumVideoDisplays();
|
|
SDL_Rect bound;
|
|
for( int i = 1; i <= displays; i++ ) {
|
|
bound = SDL_Rect();
|
|
SDL_GetDisplayBounds(i-1, &bound);
|
|
if (i == sdl.displayNumber) {
|
|
bx = bound.x;
|
|
by = bound.y;
|
|
break;
|
|
}
|
|
}
|
|
SDL_DisplayMode dm;
|
|
if (SDL_GetDesktopDisplayMode(sdl.displayNumber?sdl.displayNumber-1:0,&dm) == 0) {
|
|
bx += (dm.w - sdl.draw.width - sdl.clip.x)/2;
|
|
by += (dm.h - sdl.draw.height - sdl.clip.y)/2;
|
|
}
|
|
SDL_SetWindowPosition(sdl.window, bx, by);
|
|
}
|
|
#endif
|
|
resetreq = true;
|
|
GFX_ResetScreen();
|
|
resetFontSize();
|
|
if (lastfontsize<1) PIC_AddEvent(ResetTTFSize,0.001);
|
|
lastfontsize = 0;
|
|
} else {
|
|
lastfontsize = ttf.pointsize;
|
|
sdl.desktop.fullscreen = true;
|
|
OUTPUT_TTF_Select(3);
|
|
resetFontSize();
|
|
}
|
|
modeSwitched(sdl.desktop.fullscreen);
|
|
return;
|
|
}
|
|
#endif
|
|
#if !defined(HX_DOS)
|
|
if (sdl.desktop.prevent_fullscreen)
|
|
return;
|
|
|
|
if (!sdl.desktop.fullscreen) {/*is GOING fullscreen*/
|
|
UpdateWindowDimensions();
|
|
|
|
if (screen_size_info.screen_dimensions_pixels.width != 0 && screen_size_info.screen_dimensions_pixels.height != 0) {
|
|
if (sdl.desktop.full.width_auto)
|
|
sdl.desktop.full.width = (unsigned int)screen_size_info.screen_dimensions_pixels.width;
|
|
if (sdl.desktop.full.height_auto)
|
|
sdl.desktop.full.height = (unsigned int)screen_size_info.screen_dimensions_pixels.height;
|
|
|
|
#if !defined(C_SDL2) && defined(MACOSX)
|
|
/* macOS has this annoying problem with their API where the System Preferences app, display settings panel
|
|
allows setting 720p and 1080i/1080p modes on monitors who's native resolution is less than 1920x1080 but
|
|
supports 1920x1080.
|
|
|
|
This is a problem with HDTV sets made in the late 2000s early 2010s when "HD" apparently meant LCD displays
|
|
with 1368x768 resolution that will nonetheless accept 1080i (and later, 1080p) and downscale on display.
|
|
|
|
The problem is that with these monitors, macOS's display API will NOT list 1920x1080 as one of the
|
|
display modes even when the freaking desktop is obviously set to 1920x1080 on that monitor!
|
|
|
|
If the screen reporting code says 1920x1080 and we try to go fullscreen, SDL1 will intervene and say
|
|
"hey wait there's no 1920x1080 in the mode list" and then fail the call.
|
|
|
|
So to work around this, we have to go check SDL's mode list before using the screen size returned by the
|
|
API.
|
|
|
|
Oh and for extra fun, macOS is one of those modern systems where "setting the video mode" apparently
|
|
means NOT setting the video hardware mode but just changing how the GPU scales the display... EXCEPT when
|
|
talking to these older displays where if the user set it to 1080i/1080p, our request to set 1368x768
|
|
actually DOES change the video mode. This is just wonderful considering the old HDTV set I bought back in
|
|
2008 takes 1-2 seconds to adjust to the mode change on it's HDMI input.
|
|
|
|
Frankly I wish I knew how to fix the SDL1 Quartz code to use whatever abracadabra magic code is required
|
|
to enumerate these extra HDTV modes so this hack isn't necessary, except that experience says this hack is
|
|
always necesary because it might not always work.
|
|
|
|
This magic hidden super secret API bullshit is annoying. You're worse than Microsoft with this stuff, Apple --J.C. */
|
|
{
|
|
SDL_Rect **ls = SDLCALL SDL_ListModes(NULL, SDL_FULLSCREEN);
|
|
if (ls != NULL) {
|
|
unsigned int maxwidth = 0,maxheight = 0;
|
|
|
|
for (size_t i=0;ls[i] != NULL;i++) {
|
|
unsigned int w = ls[i]->w;
|
|
unsigned int h = ls[i]->h;
|
|
|
|
if (maxwidth < w || maxheight < h) {
|
|
maxwidth = w;
|
|
maxheight = h;
|
|
}
|
|
}
|
|
|
|
if (maxwidth != 0 && maxheight != 0) {
|
|
LOG_MSG("macOS: Actual maximum screen resolution is %d x %d\n",maxwidth,maxheight);
|
|
|
|
if (sdl.desktop.full.width_auto) {
|
|
if (sdl.desktop.full.width > maxwidth)
|
|
sdl.desktop.full.width = maxwidth;
|
|
}
|
|
if (sdl.desktop.full.height_auto) {
|
|
if (sdl.desktop.full.height > maxheight)
|
|
sdl.desktop.full.height = maxheight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
SDL_hax_SetFSWindowPosition(
|
|
(int)screen_size_info.screen_position_pixels.x, (int)screen_size_info.screen_position_pixels.y,
|
|
(int)screen_size_info.screen_dimensions_pixels.width, (int)screen_size_info.screen_dimensions_pixels.height);
|
|
#endif
|
|
}
|
|
else {
|
|
#if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
SDL_hax_SetFSWindowPosition(0,0,0,0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
menu.resizeusing = true;
|
|
|
|
sdl.desktop.fullscreen = !sdl.desktop.fullscreen;
|
|
|
|
auto full = sdl.desktop.fullscreen;
|
|
|
|
// if we're going fullscreen and current scaler exceeds screen size,
|
|
// cancel the fullscreen change -> fixes scaler crashes
|
|
// TODO this will need further changes to accomodate different outputs (e.g. stretched)
|
|
if (full)
|
|
{
|
|
int width, height;
|
|
GetDesktopResolution(&width, &height);
|
|
auto width1 = sdl.draw.width;
|
|
auto height1 = sdl.draw.height;
|
|
if ((unsigned int)width < width1 || (unsigned int)height < height1) {
|
|
sdl.desktop.fullscreen = false;
|
|
LOG_MSG("WARNING: full screen canceled, surface size (%ix%i) exceeds screen size (%ix%i).",
|
|
width1, height1, width, height);
|
|
}
|
|
}
|
|
|
|
modeSwitched(full);
|
|
|
|
// disable/enable sticky keys for fullscreen/desktop
|
|
#if defined (WIN32)
|
|
sticky_keys(!full);
|
|
#endif
|
|
|
|
if (glide.enabled)
|
|
GLIDE_ResetScreen();
|
|
else
|
|
GFX_ResetScreen();
|
|
|
|
// set vsync to host
|
|
// NOTE why forcing ???
|
|
#ifdef WIN32
|
|
if (menu.startup) // NOTE should be always true I suppose ???
|
|
{
|
|
auto vsyncProp = static_cast<Section_prop *>(control->GetSection("vsync"));
|
|
if (vsyncProp)
|
|
{
|
|
auto vsyncMode = vsyncProp->Get_string("vsyncmode");
|
|
if (!strcmp(vsyncMode, "host")) SetVal("vsync", "vsyncmode", "host");
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void SwitchFullScreen(bool pressed) {
|
|
if (!pressed)
|
|
return;
|
|
|
|
GFX_LosingFocus();
|
|
if (sdl.desktop.lazy_fullscreen) {
|
|
LOG_MSG("GFX LF: fullscreen switching not supported");
|
|
} else {
|
|
GFX_SwitchFullScreen();
|
|
}
|
|
}
|
|
|
|
void GFX_SwitchLazyFullscreen(bool lazy) {
|
|
sdl.desktop.lazy_fullscreen=lazy;
|
|
sdl.desktop.lazy_fullscreen_req=false;
|
|
}
|
|
|
|
void GFX_SwitchFullscreenNoReset(void) {
|
|
sdl.desktop.fullscreen=!sdl.desktop.fullscreen;
|
|
}
|
|
|
|
bool GFX_LazyFullscreenRequested(void) {
|
|
if (sdl.desktop.lazy_fullscreen) return sdl.desktop.lazy_fullscreen_req;
|
|
return false;
|
|
}
|
|
|
|
bool GFX_GetPreventFullscreen(void) {
|
|
return sdl.desktop.prevent_fullscreen;
|
|
}
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
extern "C" unsigned char SDL1_hax_RemoveMinimize;
|
|
#endif
|
|
|
|
void GFX_PreventFullscreen(bool lockout) {
|
|
if (sdl.desktop.prevent_fullscreen != lockout) {
|
|
sdl.desktop.prevent_fullscreen = lockout;
|
|
#if defined(WIN32)
|
|
DOSBox_SetSysMenu();
|
|
#if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
SDL1_hax_RemoveMinimize = lockout ? 1 : 0;
|
|
#endif
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void GFX_RestoreMode(void) {
|
|
if (sdl.draw.width == 0 || sdl.draw.height == 0)
|
|
return;
|
|
|
|
GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
|
|
GFX_UpdateSDLCaptureState();
|
|
GFX_ResetScreen();
|
|
}
|
|
|
|
static bool GFX_GetSurfacePtrLock = false;
|
|
|
|
unsigned char *GFX_GetSurfacePtr(size_t *pitch, unsigned int x, unsigned int y) {
|
|
if (sdl.surface->pixels == NULL) {
|
|
if (!GFX_GetSurfacePtrLock) {
|
|
if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface))
|
|
return NULL;
|
|
|
|
GFX_GetSurfacePtrLock = true;
|
|
}
|
|
}
|
|
|
|
*pitch = sdl.surface->pitch;
|
|
if (sdl.surface->pixels != NULL) {
|
|
unsigned char *p = (unsigned char*)(sdl.surface->pixels);
|
|
p += y * sdl.surface->pitch;
|
|
p += x * ((unsigned int)sdl.surface->format->BitsPerPixel >> 3U);
|
|
return p;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void GFX_ReleaseSurfacePtr(void) {
|
|
if (GFX_GetSurfacePtrLock) {
|
|
if (SDL_MUSTLOCK(sdl.surface))
|
|
SDL_UnlockSurface(sdl.surface);
|
|
|
|
GFX_GetSurfacePtrLock = false;
|
|
}
|
|
}
|
|
|
|
bool GFX_StartUpdate(uint8_t* &pixels,Bitu &pitch)
|
|
{
|
|
if (!sdl.active || sdl.updating)
|
|
return false;
|
|
|
|
switch (sdl.desktop.type)
|
|
{
|
|
case SCREEN_SURFACE:
|
|
return OUTPUT_SURFACE_StartUpdate(pixels, pitch);
|
|
|
|
#if C_OPENGL
|
|
case SCREEN_OPENGL:
|
|
return OUTPUT_OPENGL_StartUpdate(pixels, pitch);
|
|
#endif
|
|
|
|
#if C_DIRECT3D
|
|
case SCREEN_DIRECT3D:
|
|
return OUTPUT_DIRECT3D_StartUpdate(pixels, pitch);
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void GFX_OpenGLRedrawScreen(void) {
|
|
#if C_OPENGL
|
|
if (OpenGL_using()) {
|
|
if (sdl_opengl.clear_countdown > 0) {
|
|
sdl_opengl.clear_countdown--;
|
|
glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
if (sdl_opengl.pixel_buffer_object) {
|
|
glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
|
|
glBindTexture(GL_TEXTURE_2D, sdl_opengl.texture);
|
|
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
|
|
glCallList(sdl_opengl.displaylist);
|
|
} else {
|
|
glBindTexture(GL_TEXTURE_2D, sdl_opengl.texture);
|
|
glCallList(sdl_opengl.displaylist);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void GFX_EndUpdate(const uint16_t *changedLines) {
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
#endif
|
|
|
|
/* don't present our output if 3Dfx is in OpenGL mode */
|
|
if (sdl.desktop.prevent_fullscreen)
|
|
return;
|
|
|
|
#if C_DIRECT3D
|
|
// we may have to do forced update in D3D case
|
|
if (d3d && d3d->getForceUpdate());
|
|
else
|
|
#endif
|
|
if (((sdl.desktop.type != SCREEN_OPENGL) || !RENDER_GetForceUpdate()) && !sdl.updating)
|
|
return;
|
|
#if C_OPENGL
|
|
bool actually_updating = sdl.updating;
|
|
#endif
|
|
sdl.updating = false;
|
|
#if defined(USE_TTF)
|
|
if (ttf.inUse) {
|
|
GFX_EndTextLines();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
switch (sdl.desktop.type)
|
|
{
|
|
case SCREEN_SURFACE:
|
|
OUTPUT_SURFACE_EndUpdate(changedLines);
|
|
break;
|
|
|
|
#if C_OPENGL
|
|
case SCREEN_OPENGL:
|
|
// Clear drawing area. Some drivers (on Linux) have more than 2 buffers and the screen might
|
|
// be dirty because of other programs.
|
|
if (!actually_updating) {
|
|
/* Don't really update; Just increase the frame counter.
|
|
* If we tried to update it may have not worked so well
|
|
* with VSync...
|
|
* (Think of 60Hz on the host with 70Hz on the client.)
|
|
*/
|
|
sdl_opengl.actual_frame_count++;
|
|
return;
|
|
}
|
|
OUTPUT_OPENGL_EndUpdate(changedLines);
|
|
break;
|
|
#endif
|
|
|
|
#if C_DIRECT3D
|
|
case SCREEN_DIRECT3D:
|
|
OUTPUT_DIRECT3D_EndUpdate(changedLines);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (changedLines != NULL)
|
|
{
|
|
sdl.must_redraw_all = false;
|
|
|
|
#if !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
sdl.surface->flags &= ~((unsigned int)SDL_HAX_NOREFRESH);
|
|
#endif
|
|
|
|
if (changedLines != NULL && sdl.deferred_resize)
|
|
{
|
|
sdl.deferred_resize = false;
|
|
#if !defined(C_SDL2)
|
|
void GFX_RedrawScreen(uint32_t nWidth, uint32_t nHeight);
|
|
GFX_RedrawScreen(sdl.draw.width, sdl.draw.height);
|
|
#endif
|
|
}
|
|
else if (sdl.gfx_force_redraw_count > 0)
|
|
{
|
|
void RENDER_CallBack( GFX_CallBackFunctions_t function );
|
|
RENDER_CallBack(GFX_CallBackRedraw);
|
|
sdl.gfx_force_redraw_count--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) {
|
|
(void)start;
|
|
(void)count;
|
|
(void)entries;
|
|
#if !defined(C_SDL2)
|
|
/* I should probably not change the GFX_PalEntry :) */
|
|
if (sdl.surface->flags & SDL_HWPALETTE) {
|
|
if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,(int)start,(int)count)) {
|
|
E_Exit("SDL: Cannot set palette");
|
|
}
|
|
} else {
|
|
if (!SDL_SetPalette(sdl.surface,SDL_LOGPAL,(SDL_Color *)entries,(int)start,(int)count)) {
|
|
E_Exit("SDL: Cannot set palette");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Bitu GFX_GetRGB(uint8_t red, uint8_t green, uint8_t blue) {
|
|
switch (sdl.desktop.type) {
|
|
case SCREEN_SURFACE:
|
|
return SDL_MapRGB(sdl.surface->format, red, green, blue);
|
|
|
|
#if C_OPENGL
|
|
case SCREEN_OPENGL:
|
|
# if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* macOS Intel builds use a weird RGBA order (alpha in the low 8 bits) */
|
|
//USE BGRA
|
|
return (((unsigned long)blue << 24ul) | ((unsigned long)green << 16ul) | ((unsigned long)red << 8ul)) | (255ul << 0ul);
|
|
# else
|
|
//USE ARGB
|
|
return (((unsigned long)blue << 0ul) | ((unsigned long)green << 8ul) | ((unsigned long)red << 16ul)) | (255ul << 24ul);
|
|
# endif
|
|
#endif
|
|
|
|
#if C_DIRECT3D
|
|
case SCREEN_DIRECT3D:
|
|
return SDL_MapRGB(sdl.surface->format, red, green, blue);
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void GFX_Stop() {
|
|
if (sdl.updating)
|
|
GFX_EndUpdate( 0 );
|
|
sdl.active=false;
|
|
}
|
|
|
|
void GFX_Start() {
|
|
sdl.active=true;
|
|
}
|
|
|
|
static void GUI_ShutDown(Section * /*sec*/) {
|
|
GFX_Stop();
|
|
if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop );
|
|
if (sdl.mouse.locked) GFX_CaptureMouse();
|
|
if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
|
|
switch (sdl.desktop.type)
|
|
{
|
|
case SCREEN_SURFACE:
|
|
OUTPUT_SURFACE_Shutdown();
|
|
break;
|
|
|
|
#if C_OPENGL
|
|
case SCREEN_OPENGL:
|
|
OUTPUT_OPENGL_Shutdown();
|
|
break;
|
|
#endif
|
|
|
|
#if C_DIRECT3D
|
|
case SCREEN_DIRECT3D:
|
|
OUTPUT_DIRECT3D_Shutdown();
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SetPriority(PRIORITY_LEVELS level) {
|
|
|
|
#if C_SET_PRIORITY
|
|
// Do nothing if priorties are not the same and not root, else the highest
|
|
// priority can not be set as users can only lower priority (not restore it)
|
|
|
|
if((sdl.priority.focus != sdl.priority.nofocus ) &&
|
|
(getuid()!=0) ) return;
|
|
|
|
#endif
|
|
switch (level) {
|
|
#ifdef WIN32
|
|
case PRIORITY_LEVEL_PAUSE: // if DOSBox-X is paused, assume idle priority
|
|
case PRIORITY_LEVEL_LOWEST:
|
|
SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS);
|
|
break;
|
|
case PRIORITY_LEVEL_LOWER:
|
|
SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS);
|
|
break;
|
|
case PRIORITY_LEVEL_NORMAL:
|
|
SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS);
|
|
break;
|
|
case PRIORITY_LEVEL_HIGHER:
|
|
SetPriorityClass(GetCurrentProcess(),ABOVE_NORMAL_PRIORITY_CLASS);
|
|
break;
|
|
case PRIORITY_LEVEL_HIGHEST:
|
|
SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
|
|
break;
|
|
#elif C_SET_PRIORITY
|
|
/* Linux use group as dosbox-x has mulitple threads under linux */
|
|
case PRIORITY_LEVEL_PAUSE: // if DOSBox-X is paused, assume idle priority
|
|
case PRIORITY_LEVEL_LOWEST:
|
|
setpriority (PRIO_PGRP, 0,PRIO_MAX);
|
|
break;
|
|
case PRIORITY_LEVEL_LOWER:
|
|
setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/3));
|
|
break;
|
|
case PRIORITY_LEVEL_NORMAL:
|
|
setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/2));
|
|
break;
|
|
case PRIORITY_LEVEL_HIGHER:
|
|
setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/5) );
|
|
break;
|
|
case PRIORITY_LEVEL_HIGHEST:
|
|
setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/4) );
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void OutputString(Bitu x,Bitu y,const char * text,uint32_t color,uint32_t color2,SDL_Surface * output_surface) {
|
|
uint32_t * draw=(uint32_t*)(((uint8_t *)output_surface->pixels)+((y)*output_surface->pitch))+x;
|
|
while (*text) {
|
|
uint8_t * font=&int10_font_14[(*text)*14];
|
|
Bitu i,j;
|
|
uint32_t * draw_line=draw;
|
|
for (i=0;i<14;i++) {
|
|
uint8_t map=*font++;
|
|
for (j=0;j<8;j++) {
|
|
if (map & 0x80) *((uint32_t*)(draw_line+j))=color; else *((uint32_t*)(draw_line+j))=color2;
|
|
map<<=1;
|
|
}
|
|
draw_line+=output_surface->pitch/4;
|
|
}
|
|
text++;
|
|
draw+=8;
|
|
}
|
|
}
|
|
|
|
void ResetSystem(bool pressed) {
|
|
if (!pressed) return;
|
|
|
|
if (is_paused) {
|
|
is_paused = false;
|
|
mainMenu.get_item("mapper_pause").check(false).refresh_item(mainMenu);
|
|
}
|
|
if (pausewithinterrupts_enable) {
|
|
pausewithinterrupts_enable = false;
|
|
mainMenu.get_item("mapper_pauseints").check(false).refresh_item(mainMenu);
|
|
}
|
|
bootvm=true;
|
|
throw int(3);
|
|
}
|
|
|
|
void RebootGuest(bool pressed) {
|
|
if (!pressed) return;
|
|
|
|
if (is_paused) {
|
|
is_paused = false;
|
|
mainMenu.get_item("mapper_pause").check(false).refresh_item(mainMenu);
|
|
}
|
|
if (pausewithinterrupts_enable) {
|
|
pausewithinterrupts_enable = false;
|
|
mainMenu.get_item("mapper_pauseints").check(false).refresh_item(mainMenu);
|
|
}
|
|
if (!dos_kernel_disabled) {
|
|
if (CurMode->type==M_TEXT || IS_PC98_ARCH) {
|
|
char msg[]="\033[2J";
|
|
uint16_t s = (uint16_t)strlen(msg);
|
|
DOS_WriteFile(STDERR,(uint8_t*)msg,&s);
|
|
throw int(6);
|
|
} else {
|
|
bootfast=true;
|
|
throw int(3);
|
|
}
|
|
}
|
|
bootguest=true;
|
|
throw int(3);
|
|
}
|
|
|
|
void LoadMapFile(bool bPressed) {
|
|
if (!bPressed) return;
|
|
void Load_mapper_file();
|
|
Load_mapper_file();
|
|
}
|
|
|
|
void QuickLaunch(bool bPressed) {
|
|
if (!bPressed) return;
|
|
if (dos_kernel_disabled) return;
|
|
MenuBrowseProgramFile();
|
|
}
|
|
|
|
void SendKey(std::string key) {
|
|
if (key == "sendkey_winlogo") {
|
|
KEYBOARD_AddKey(KBD_lwindows, true);
|
|
KEYBOARD_AddKey(KBD_lwindows, false);
|
|
} else if (key == "sendkey_winmenu") {
|
|
KEYBOARD_AddKey(KBD_rwinmenu, true);
|
|
KEYBOARD_AddKey(KBD_rwinmenu, false);
|
|
} else if (key == "sendkey_alttab") {
|
|
KEYBOARD_AddKey(KBD_leftalt, true);
|
|
KEYBOARD_AddKey(KBD_tab, true);
|
|
KEYBOARD_AddKey(KBD_leftalt, false);
|
|
KEYBOARD_AddKey(KBD_tab, false);
|
|
} else if (key == "sendkey_ctrlesc") {
|
|
KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(KBD_esc, true);
|
|
KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(KBD_esc, false);
|
|
} else if (key == "sendkey_ctrlbreak") {
|
|
KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(KBD_pause, true);
|
|
KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(KBD_pause, false);
|
|
} else if (key == "sendkey_cad") {
|
|
KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(KBD_leftalt, true);
|
|
KEYBOARD_AddKey(KBD_delete, true);
|
|
KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(KBD_leftalt, false);
|
|
KEYBOARD_AddKey(KBD_delete, false);
|
|
}
|
|
}
|
|
|
|
void Sendkeymapper(bool pressed) {
|
|
if (!pressed) return;
|
|
if (sendkeymap==1) SendKey("sendkey_winlogo");
|
|
else if (sendkeymap==2) SendKey("sendkey_winmenu");
|
|
else if (sendkeymap==3) SendKey("sendkey_alttab");
|
|
else if (sendkeymap==4) SendKey("sendkey_ctrlesc");
|
|
else if (sendkeymap==5) SendKey("sendkey_ctrlbreak");
|
|
else SendKey("sendkey_cad");
|
|
}
|
|
|
|
bool has_GUI_StartUp = false;
|
|
|
|
static void GUI_StartUp() {
|
|
DOSBoxMenu::item *item;
|
|
|
|
if (has_GUI_StartUp) return;
|
|
has_GUI_StartUp = true;
|
|
|
|
LOG(LOG_GUI,LOG_DEBUG)("Starting GUI");
|
|
|
|
#if defined(C_SDL2)
|
|
LOG(LOG_GUI,LOG_DEBUG)("This version compiled against SDL 2.x");
|
|
#else
|
|
LOG(LOG_GUI,LOG_DEBUG)("This version compiled against SDL 1.x");
|
|
#endif
|
|
|
|
#if defined(C_SDL2)
|
|
/* while we're here, SDL 2.0.5 has some issues with Linux/X11, encourage the user to update SDL2. */
|
|
{
|
|
SDL_version v;
|
|
SDL_GetVersion(&v);
|
|
LOG(LOG_GUI,LOG_DEBUG)("SDL2 version %u.%u.%u",v.major,v.minor,v.patch);
|
|
|
|
# if defined(LINUX)
|
|
/* Linux/X11 2.0.5 has window positioning issues i.e. with XFCE */
|
|
if (v.major == 2 && v.minor == 0 && v.patch == 5)
|
|
LOG_MSG("WARNING: Your SDL2 library is known to have some issues with Linux/X11, please update your SDL2 library");
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
AddExitFunction(AddExitFunctionFuncPair(GUI_ShutDown));
|
|
GUI_LoadFonts();
|
|
|
|
sdl.active=false;
|
|
sdl.updating=false;
|
|
#if defined(C_SDL2)
|
|
sdl.update_window=true;
|
|
#endif
|
|
|
|
GFX_SetIcon();
|
|
|
|
sdl.desktop.lazy_fullscreen=false;
|
|
sdl.desktop.lazy_fullscreen_req=false;
|
|
sdl.desktop.prevent_fullscreen=false;
|
|
|
|
Section_prop * section=static_cast<Section_prop *>(control->GetSection("sdl"));
|
|
assert(section != NULL);
|
|
|
|
maximize = section->Get_bool("maximize");
|
|
|
|
sdl.desktop.fullscreen=false;
|
|
sdl.wait_on_error=section->Get_bool("waitonerror");
|
|
|
|
Prop_multival* p=section->Get_multival("priority");
|
|
std::string focus = p->GetSection()->Get_string("active");
|
|
std::string notfocus = p->GetSection()->Get_string("inactive");
|
|
|
|
if (focus == "lowest") { sdl.priority.focus = PRIORITY_LEVEL_LOWEST; }
|
|
else if (focus == "lower") { sdl.priority.focus = PRIORITY_LEVEL_LOWER; }
|
|
else if (focus == "normal") { sdl.priority.focus = PRIORITY_LEVEL_NORMAL; }
|
|
else if (focus == "higher") { sdl.priority.focus = PRIORITY_LEVEL_HIGHER; }
|
|
else if (focus == "highest") { sdl.priority.focus = PRIORITY_LEVEL_HIGHEST; }
|
|
|
|
if (notfocus == "lowest") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWEST; }
|
|
else if (notfocus == "lower") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWER; }
|
|
else if (notfocus == "normal") { sdl.priority.nofocus=PRIORITY_LEVEL_NORMAL; }
|
|
else if (notfocus == "higher") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHER; }
|
|
else if (notfocus == "highest") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHEST; }
|
|
else if (notfocus == "pause") {
|
|
/* we only check for pause here, because it makes no sense
|
|
* for DOSBox-X to be paused while it has focus
|
|
*/
|
|
sdl.priority.nofocus=PRIORITY_LEVEL_PAUSE;
|
|
}
|
|
|
|
SetPriority(sdl.priority.focus); //Assume focus on startup
|
|
sdl.mouse.locked=false;
|
|
mouselocked=false; //Global for mapper
|
|
sdl.mouse.requestlock=false;
|
|
sdl.desktop.full.fixed=false;
|
|
const char* fullresolution=section->Get_string("fullresolution");
|
|
sdl.desktop.full.width = 0;
|
|
sdl.desktop.full.height = 0;
|
|
if(fullresolution && *fullresolution) {
|
|
char res[100];
|
|
safe_strncpy(res, fullresolution, sizeof(res));
|
|
fullresolution = lowcase (res);//so x and X are allowed
|
|
if (strcmp(fullresolution,"original")) {
|
|
sdl.desktop.full.fixed = true;
|
|
if (strcmp(fullresolution,"desktop")) { //desktop = 0x0
|
|
char* height = const_cast<char*>(strchr(fullresolution,'x'));
|
|
if (height && * height) {
|
|
*height = 0;
|
|
sdl.desktop.full.height = (uint16_t)atoi(height+1);
|
|
sdl.desktop.full.width = (uint16_t)atoi(res);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sdl.desktop.window.width = 0;
|
|
sdl.desktop.window.height = 0;
|
|
const char* windowresolution=section->Get_string("windowresolution");
|
|
if(windowresolution && *windowresolution) {
|
|
char res[100];
|
|
safe_strncpy(res, windowresolution, sizeof(res));
|
|
windowresolution = lowcase (res);//so x and X are allowed
|
|
if(strcmp(windowresolution,"original")) {
|
|
char* height = const_cast<char*>(strchr(windowresolution,'x'));
|
|
if(height && *height) {
|
|
*height = 0;
|
|
sdl.desktop.window.height = (uint16_t)atoi(height+1);
|
|
sdl.desktop.window.width = (uint16_t)atoi(res);
|
|
}
|
|
}
|
|
}
|
|
sdl.desktop.doublebuf=section->Get_bool("fulldouble");
|
|
#if defined(C_SDL2)
|
|
{
|
|
SDL_DisplayMode dm;
|
|
if (SDL_GetDesktopDisplayMode(0/*FIXME display index*/,&dm) == 0) {
|
|
if (sdl.desktop.full.width == 0) {
|
|
sdl.desktop.full.width_auto = true;
|
|
sdl.desktop.full.width = dm.w;
|
|
}
|
|
if (sdl.desktop.full.height == 0) {
|
|
sdl.desktop.full.height_auto = true;
|
|
sdl.desktop.full.height = dm.h;
|
|
}
|
|
LOG_MSG("SDL2 reports desktop display mode %u x %u",dm.w,dm.h);
|
|
}
|
|
else {
|
|
LOG_MSG("SDL2 unable to determine desktop display mode, error %s",SDL_GetError());
|
|
}
|
|
}
|
|
#endif
|
|
#if !defined(C_SDL2)
|
|
#if SDL_VERSION_ATLEAST(1, 2, 10)
|
|
#ifdef WIN32
|
|
/* NTS: This should not print any warning whatsoever because Windows builds by default will use
|
|
* the Windows API to disable DPI scaling of the main window, unless the user modifies the
|
|
* setting through dosbox-x.conf or the command line. */
|
|
/* NTS: macOS has high DPI scaling too, though Apple is wise to enable it by default only for
|
|
* Macbooks with "Retina" displays. On macOS, unless otherwise wanted by the user, it is
|
|
* wise to let macOS scale up the DOSBox-X window by 2x so that the DOS prompt is not
|
|
* a teeny tiny window on the screen. */
|
|
const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo();
|
|
if (vidinfo) {
|
|
int sdl_w = vidinfo->current_w;
|
|
int sdl_h = vidinfo->current_h;
|
|
int win_w = GetSystemMetrics(SM_CXSCREEN);
|
|
int win_h = GetSystemMetrics(SM_CYSCREEN);
|
|
if (sdl_w != win_w && sdl_h != win_h)
|
|
LOG_MSG("Windows DPI/blurry apps scaling detected as it might be a large screen.\n"
|
|
"Please see the DOSBox-X documentation for more details.\n");
|
|
}
|
|
#else
|
|
if (!sdl.desktop.full.width || !sdl.desktop.full.height){
|
|
//Can only be done on the very first call! Not restartable.
|
|
//On windows don't use it as SDL returns the values without taking in account the dpi scaling
|
|
const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo();
|
|
if (vidinfo) {
|
|
if (sdl.desktop.full.width == 0) {
|
|
sdl.desktop.full.width_auto = true;
|
|
sdl.desktop.full.width = vidinfo->current_w;
|
|
}
|
|
if (sdl.desktop.full.height == 0) {
|
|
sdl.desktop.full.height_auto = true;
|
|
sdl.desktop.full.height = vidinfo->current_h;
|
|
}
|
|
|
|
LOG_MSG("SDL1 auto-detected desktop as %u x %u",
|
|
(unsigned int)sdl.desktop.full.width,
|
|
(unsigned int)sdl.desktop.full.height);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
if (!sdl.desktop.full.width) {
|
|
int width=1024;
|
|
sdl.desktop.full.width_auto = true;
|
|
sdl.desktop.full.width=width;
|
|
}
|
|
if (!sdl.desktop.full.height) {
|
|
int height=768;
|
|
sdl.desktop.full.height_auto = true;
|
|
sdl.desktop.full.height=height;
|
|
}
|
|
sdl.mouse.autoenable=section->Get_bool("autolock");
|
|
if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE);
|
|
sdl.mouse.autolock=false;
|
|
|
|
const std::string feedback = section->Get_string("autolock_feedback");
|
|
if (feedback == "none")
|
|
sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_NONE;
|
|
else if (feedback == "beep")
|
|
sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_BEEP;
|
|
else if (feedback == "flash")
|
|
sdl.mouse.autolock_feedback = AUTOLOCK_FEEDBACK_FLASH;
|
|
|
|
const std::string munlock = section->Get_string("middle_unlock");
|
|
if (munlock == "none")
|
|
middleunlock = 0;
|
|
else if (munlock == "auto")
|
|
middleunlock = 2;
|
|
else if (munlock == "both")
|
|
middleunlock = 3;
|
|
else if (munlock == "manual")
|
|
middleunlock = 1;
|
|
|
|
const char *clip_mouse_button = section->Get_string("clip_mouse_button");
|
|
if (!strcmp(clip_mouse_button, "middle")) mbutton=2;
|
|
else if (!strcmp(clip_mouse_button, "right")) mbutton=3;
|
|
else if (!strcmp(clip_mouse_button, "arrows")) mbutton=4;
|
|
else mbutton=0;
|
|
modifier = section->Get_string("clip_key_modifier");
|
|
const char *pastebios = section->Get_string("clip_paste_bios");
|
|
if (!strcasecmp(pastebios, "true") || !strcmp(pastebios, "1")) clipboard_biospaste = true;
|
|
else if (!strcasecmp(pastebios, "false") || !strcmp(pastebios, "0")) clipboard_biospaste = false;
|
|
paste_speed = (unsigned int)section->Get_int("clip_paste_speed");
|
|
wheel_key = section->Get_int("mouse_wheel_key");
|
|
wheel_guest=wheel_key>0;
|
|
if (wheel_key<0) wheel_key=-wheel_key;
|
|
|
|
Prop_multival* p3 = section->Get_multival("sensitivity");
|
|
sdl.mouse.xsensitivity = p3->GetSection()->Get_int("xsens");
|
|
sdl.mouse.ysensitivity = p3->GetSection()->Get_int("ysens");
|
|
|
|
#if defined(C_SDL2)
|
|
// Apply raw mouse input setting
|
|
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, section->Get_bool("raw_mouse_input") ? "0" : "1", SDL_HINT_OVERRIDE);
|
|
#endif
|
|
|
|
/* Get some Event handlers */
|
|
MAPPER_AddHandler(ResetSystem, MK_r, MMODHOST, "reset", "Reset DOSBox-X", &item); /* Host+R (Host+CTRL+R acts funny on my Linux system) */
|
|
item->set_text("Reset virtual machine");
|
|
|
|
MAPPER_AddHandler(RebootGuest, MK_b, MMODHOST, "reboot", "Reboot DOS system", &item); /* Reboot guest system or integrated DOS */
|
|
item->set_text("Reboot guest system");
|
|
|
|
#if !defined(HX_DOS)
|
|
MAPPER_AddHandler(LoadMapFile, MK_nothing, 0, "loadmap", "Load mapper file", &item);
|
|
item->set_text("Load mapper file...");
|
|
|
|
MAPPER_AddHandler(QuickLaunch, MK_q, MMODHOST, "quickrun", "Quick launch program", &item);
|
|
item->set_text("Quick launch program...");
|
|
#endif
|
|
|
|
#if defined(MACOSX) && 0
|
|
MAPPER_AddHandler(NewInstanceEvent, MK_nothing, 0, "newinst", "Start new instance", &item);
|
|
item->set_text("Start new instance");
|
|
{
|
|
bool enable = false;
|
|
extern std::string MacOSXEXEPath;
|
|
if (!MacOSXEXEPath.empty()) {
|
|
if (MacOSXEXEPath.at(0) == '/') {
|
|
enable = true;
|
|
}
|
|
}
|
|
item->enable(enable);
|
|
item->refresh_item(mainMenu);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(C_EMSCRIPTEN)//FIXME: Shutdown causes problems with Emscripten
|
|
MAPPER_AddHandler(KillSwitch,MK_f9,MMOD1,"shutdown","Quit from DOSBox-X", &item); /* KEEP: Most DOSBox-X users may have muscle memory for this */
|
|
item->set_text("Quit from DOSBox-X");
|
|
#endif
|
|
|
|
MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Capture mouse", &item); /* KEEP: Most DOSBox-X users may have muscle memory for this */
|
|
item->set_text("Capture mouse");
|
|
|
|
#if defined(C_SDL2) || defined(WIN32) || defined(MACOSX)
|
|
MAPPER_AddHandler(QuickEdit,MK_nothing, 0,"fastedit", "Quick edit mode", &item);
|
|
item->set_text("Quick edit: copy on select and paste text");
|
|
|
|
MAPPER_AddHandler(CopyAllClipboard,MK_f5,MMOD1,"copyall", "Copy to clipboard", &item);
|
|
item->set_text("Copy all text on the DOS screen");
|
|
#endif
|
|
|
|
MAPPER_AddHandler(PasteClipboard,MK_f6,MMOD1,"paste", "Paste from clipboard", &item); //end emendelson; improved by Wengier
|
|
item->set_text("Pasting from the clipboard");
|
|
|
|
MAPPER_AddHandler(PasteClipStop,MK_nothing, 0,"pasteend", "Stop clipboard paste", &item);
|
|
item->set_text("Stop clipboard pasting");
|
|
|
|
#if C_PRINTER
|
|
MAPPER_AddHandler(&FormFeed, MK_f2 , MMOD1, "ejectpage", "Send form-feed", &item);
|
|
item->set_text("Send form-feed");
|
|
|
|
MAPPER_AddHandler(&PrintText, MK_nothing, 0, "printtext", "Print text screen", &item);
|
|
item->set_text("Print DOS text screen");
|
|
#endif
|
|
|
|
MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMODHOST, "pause", "Pause emulation");
|
|
|
|
MAPPER_AddHandler(&PauseWithInterrupts_mapper_shortcut, MK_nothing, 0, "pauseints", "Pause with interrupt", &item);
|
|
item->set_text("Pause with interrupts enabled");
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
|
|
pause_menu_item_tag = mainMenu.get_item("mapper_pause").get_master_id() + DOSBoxMenu::nsMenuMinimumID;
|
|
#endif
|
|
|
|
MAPPER_AddHandler(&GUI_Run, MK_c,MMODHOST, "gui", "Configuration tool", &item);
|
|
item->set_text("Configuration tool");
|
|
|
|
MAPPER_AddHandler(&MAPPER_Run,MK_m,MMODHOST,"mapper","Mapper editor",&item);
|
|
item->set_accelerator(DOSBoxMenu::accelerator('m'));
|
|
item->set_description("Bring up the mapper UI");
|
|
item->set_text("Mapper editor");
|
|
|
|
MAPPER_AddHandler(SwitchFullScreen,MK_f,MMODHOST,"fullscr","Toggle fullscreen", &item);
|
|
item->set_text("Toggle fullscreen");
|
|
|
|
MAPPER_AddHandler(&GUI_ResetResize, MK_backspace, MMODHOST, "resetsize", "Reset window size", &item);
|
|
item->set_text("Reset window size");
|
|
|
|
#if defined(USE_TTF)
|
|
void DBCSSBCS_mapper_shortcut(bool pressed);
|
|
MAPPER_AddHandler(&DBCSSBCS_mapper_shortcut, MK_nothing, 0, "dbcssbcs", "CJK: Switch between DBCS/SBCS modes", &item);
|
|
item->set_text("CJK: Switch between DBCS/SBCS modes");
|
|
|
|
void AutoBoxDraw_mapper_shortcut(bool pressed);
|
|
MAPPER_AddHandler(&AutoBoxDraw_mapper_shortcut, MK_nothing, 0, "autoboxdraw", "CJK: Auto-detect box-drawing characters", &item);
|
|
item->set_text("CJK: Auto-detect box-drawing characters");
|
|
|
|
MAPPER_AddHandler(&TTF_IncreaseSize, MK_uparrow, MMODHOST, "incsize", "Increase TTF size", &item);
|
|
item->set_text("Increase TTF font size");
|
|
|
|
MAPPER_AddHandler(&TTF_DecreaseSize, MK_downarrow, MMODHOST, "decsize", "Decrease TTF size", &item);
|
|
item->set_text("Decrease TTF font size");
|
|
|
|
void ResetColors_mapper_shortcut(bool pressed);
|
|
MAPPER_AddHandler(&ResetColors_mapper_shortcut, MK_nothing, 0, "resetcolor", "Reset color scheme", &item);
|
|
item->set_text("Reset TTF color scheme");
|
|
#endif
|
|
|
|
void GEN_PowerButton(bool pressed);
|
|
MAPPER_AddHandler(&GEN_PowerButton,MK_nothing,0,"pwrbutton","APM power button", &item);
|
|
item->set_text("APM power button");
|
|
|
|
MAPPER_AddHandler(&HideMenu_mapper_shortcut, MK_escape, MMODHOST, "togmenu", "Toggle menu bar", &item);
|
|
item->set_text("Hide/show menu bar");
|
|
|
|
const int display = section->Get_int("display");
|
|
int numscreen = GetNumScreen();
|
|
if (display >= 0 && display <= numscreen)
|
|
sdl.displayNumber = display;
|
|
else {
|
|
LOG_MSG("SDL: Display number should be between 0 and %d, fallback to default display", numscreen);
|
|
sdl.displayNumber = 0;
|
|
}
|
|
std::string output=section->Get_string("output");
|
|
|
|
if (output == "default") {
|
|
output=GetDefaultOutput();
|
|
LOG_MSG("The default output for the video system: %s", output.c_str());
|
|
}
|
|
|
|
const std::string emulation = section->Get_string("mouse_emulation");
|
|
if (emulation == "always")
|
|
sdl.mouse.emulation = MOUSE_EMULATION_ALWAYS;
|
|
else if (emulation == "locked")
|
|
sdl.mouse.emulation = MOUSE_EMULATION_LOCKED;
|
|
else if (emulation == "integration")
|
|
sdl.mouse.emulation = MOUSE_EMULATION_INTEGRATION;
|
|
else if (emulation == "never")
|
|
sdl.mouse.emulation = MOUSE_EMULATION_NEVER;
|
|
|
|
usesystemcursor = section->Get_bool("usesystemcursor");
|
|
|
|
#if C_XBRZ
|
|
// initialize xBRZ parameters and check output type for compatibility
|
|
xBRZ_Initialize();
|
|
|
|
if (sdl_xbrz.enable) {
|
|
// xBRZ requirements
|
|
if ((output != "surface") && (output != "direct3d") && (output != "opengl") && (output != "openglhq") && (output != "openglnb") && (output != "openglpp") && (output != "ttf"))
|
|
output = "surface";
|
|
}
|
|
#endif
|
|
|
|
// output type selection
|
|
// "overlay" and "ddraw" were removed, pre-map to Direct3D or OpenGL or surface
|
|
if (output == "overlay" || output == "ddraw"
|
|
#if !C_DIRECT3D
|
|
|| output == "direct3d"
|
|
#endif
|
|
#if !defined(USE_TTF)
|
|
|| output == "ttf"
|
|
#endif
|
|
) {
|
|
if (output == "overlay" || output == "ddraw")
|
|
LOG_MSG("The %s output has been removed.", output.c_str());
|
|
else
|
|
LOG_MSG("The %s output is not enabled.", output == "ttf" ? "TrueType font (TTF)":"Direct3D");
|
|
#if C_DIRECT3D
|
|
output = "direct3d";
|
|
#elif C_OPENGL
|
|
output = "opengl";
|
|
#else
|
|
output = "surface";
|
|
#endif
|
|
LOG_MSG("The following output will be switched to: %s\n", output.c_str());
|
|
}
|
|
|
|
// FIXME: this selection of output is duplicated in change_output:
|
|
sdl.desktop.isperfect = false; /* Reset before selection */
|
|
if (output == "surface")
|
|
{
|
|
OUTPUT_SURFACE_Select();
|
|
#if C_OPENGL
|
|
}
|
|
else if (output == "opengl" || output == "openglhq")
|
|
{
|
|
OUTPUT_OPENGL_Select(GLBilinear);
|
|
}
|
|
else if (output == "openglnb")
|
|
{
|
|
OUTPUT_OPENGL_Select(GLNearest);
|
|
}
|
|
else if (output == "openglpp")
|
|
{
|
|
OUTPUT_OPENGL_Select(GLPerfect);
|
|
#endif
|
|
#if C_DIRECT3D
|
|
}
|
|
else if (output == "direct3d")
|
|
{
|
|
OUTPUT_DIRECT3D_Select();
|
|
#if LOG_D3D
|
|
LOG_MSG("SDL: Direct3D activated");
|
|
#endif
|
|
#endif
|
|
}
|
|
#if defined(USE_TTF)
|
|
else if (output == "ttf")
|
|
{
|
|
OUTPUT_TTF_Select(0);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
LOG_MSG("SDL: Unsupported output device %s, switching back to surface",output.c_str());
|
|
OUTPUT_SURFACE_Select(); // should not reach there anymore
|
|
}
|
|
|
|
sdl.overscan_width=(unsigned int)section->Get_int("overscan");
|
|
// sdl.overscan_color=section->Get_int("overscancolor");
|
|
|
|
// Getting window position (if configured)
|
|
posx = -1;
|
|
posy = -1;
|
|
const char* windowposition = section->Get_string("windowposition");
|
|
LOG_MSG("Configured windowposition: %s", windowposition);
|
|
if (windowposition && !strcmp(windowposition, "-"))
|
|
posx = posy = -2;
|
|
else if (windowposition && *windowposition && strcmp(windowposition, ",")) {
|
|
char result[100];
|
|
safe_strncpy(result, windowposition, sizeof(result));
|
|
char* y = strchr(result, ',');
|
|
if (y && *y) {
|
|
*y = 0;
|
|
posx = atoi(result);
|
|
posy = atoi(y + 1);
|
|
}
|
|
}
|
|
|
|
// Setting SDL1 window position before a call to SDL_SetVideoMode() is made. If the user provided
|
|
// SDL_VIDEO_WINDOW_POS environment variable then "windowposition" setting should have no effect.
|
|
// SDL2 position is set later, using SDL_SetWindowPosition()
|
|
#if !defined(C_SDL2)
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
MONITORINFO info;
|
|
if (sdl.displayNumber>0) {
|
|
xyp xy={0};
|
|
xy.x=-1;
|
|
xy.y=-1;
|
|
curscreen=0;
|
|
EnumDisplayMonitors(0, 0, EnumDispProc, reinterpret_cast<LPARAM>(&xy));
|
|
HMONITOR monitor = MonitorFromRect(&monrect, MONITOR_DEFAULTTONEAREST);
|
|
info.cbSize = sizeof(MONITORINFO);
|
|
GetMonitorInfo(monitor, &info);
|
|
if (posx >=0 && posy >=0) {
|
|
posx+=info.rcMonitor.left;
|
|
posy+=info.rcMonitor.top;
|
|
}
|
|
}
|
|
#endif
|
|
char pos[100];
|
|
if (posx >= 0 && posy >= 0 && SDL_getenv("SDL_VIDEO_WINDOW_POS") == NULL) {
|
|
#if defined(WIN32)
|
|
safe_strncpy(pos, "SDL_VIDEO_WINDOW_POS=", sizeof(pos));
|
|
safe_strcat(pos, (std::to_string(posx)+","+std::to_string(posy)).c_str());
|
|
SDL_putenv(pos);
|
|
#else
|
|
setenv("SDL_VIDEO_WINDOW_POS",(std::to_string(posx)+","+std::to_string(posy)).c_str(),0);
|
|
#endif
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
} else if (sdl.displayNumber>0 && !(posx == -2 && posy == -2)) {
|
|
safe_strncpy(pos, "SDL_VIDEO_WINDOW_POS=", sizeof(pos));
|
|
safe_strcat(pos, (std::to_string(info.rcMonitor.left+200)+","+std::to_string(info.rcMonitor.top+200)).c_str());
|
|
SDL_putenv(pos);
|
|
#endif
|
|
} else if (posx != -2 || posy != -2)
|
|
putenv((char*)"SDL_VIDEO_CENTERED=center");
|
|
#endif
|
|
|
|
/* Initialize screen for first time */
|
|
#if defined(C_SDL2)
|
|
if (!initgl) {
|
|
GFX_SetResizeable(true);
|
|
if (!GFX_SetSDLSurfaceWindow(640,400))
|
|
E_Exit("Could not initialize video: %s",SDL_GetError());
|
|
sdl.surface = SDL_GetWindowSurface(sdl.window);
|
|
}
|
|
//SDL_Rect splash_rect=GFX_GetSDLSurfaceSubwindowDims(640,400);
|
|
sdl.desktop.pixelFormat = SDL_GetWindowPixelFormat(sdl.window);
|
|
LOG_MSG("SDL: Current window pixel format: %s", SDL_GetPixelFormatName(sdl.desktop.pixelFormat));
|
|
sdl.desktop.bpp=8*SDL_BYTESPERPIXEL(sdl.desktop.pixelFormat);
|
|
if (SDL_BITSPERPIXEL(sdl.desktop.pixelFormat) == 24)
|
|
LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!");
|
|
#else
|
|
if (!initgl) {
|
|
sdl.surface=SDL_SetVideoMode(640,400,0,SDL_RESIZABLE);
|
|
if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError());
|
|
}
|
|
sdl.deferred_resize = false;
|
|
sdl.must_redraw_all = true;
|
|
sdl.desktop.bpp=sdl.surface->format->BitsPerPixel;
|
|
if (sdl.desktop.bpp==24)
|
|
LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!");
|
|
#endif
|
|
|
|
GFX_LogSDLState();
|
|
GFX_Stop();
|
|
|
|
#if C_DIRECT3D
|
|
if (sdl.desktop.want_type == SCREEN_DIRECT3D)
|
|
d3d_init();
|
|
#endif
|
|
|
|
#if defined(C_SDL2)
|
|
SDL_SetWindowTitle(sdl.window,"DOSBox-X");
|
|
if (posx >= 0 && posy >= 0) {
|
|
if (sdl.displayNumber>0) {
|
|
int displays = SDL_GetNumVideoDisplays();
|
|
SDL_Rect bound;
|
|
for( int i = 1; i <= displays; i++ ) {
|
|
bound = SDL_Rect();
|
|
SDL_GetDisplayBounds(i-1, &bound);
|
|
if (i == sdl.displayNumber) {
|
|
posx += bound.x;
|
|
posy += bound.y;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SDL_SetWindowPosition(sdl.window, posx, posy);
|
|
}
|
|
#else
|
|
SDL_WM_SetCaption("DOSBox-X",VERSION);
|
|
#endif
|
|
|
|
/* Please leave the Splash screen stuff in working order in DOSBox-X. We spend a lot of time making DOSBox-X. */
|
|
//ShowSplashScreen(); /* I will keep the splash screen alive. But now, the BIOS will do it --J.C. */
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
if (section->Get_bool("forcesquarecorner") && UpdateWindows11RoundCorners(GetHWND(), CornerPreference::DoNotRound))
|
|
LOG_MSG("SDL: Windows 11 round corners will be disabled.");
|
|
#endif
|
|
transparency = 0;
|
|
SetWindowTransparency(section->Get_int("transparency"));
|
|
UpdateWindowDimensions();
|
|
}
|
|
|
|
void Mouse_AutoLock(bool enable) {
|
|
if (sdl.mouse.autolock == enable)
|
|
return;
|
|
|
|
sdl.mouse.autolock=enable;
|
|
if (sdl.mouse.autoenable) sdl.mouse.requestlock=enable;
|
|
else {
|
|
SDL_ShowCursor(enable?SDL_DISABLE:SDL_ENABLE);
|
|
sdl.mouse.requestlock=false;
|
|
}
|
|
}
|
|
|
|
bool Mouse_IsLocked()
|
|
{
|
|
return sdl.mouse.locked;
|
|
}
|
|
|
|
static void RedrawScreen(uint32_t nWidth, uint32_t nHeight) {
|
|
(void)nWidth;//UNUSED
|
|
(void)nHeight;//UNUSED
|
|
// int width;
|
|
// int height;
|
|
#ifdef __WIN32__
|
|
// width=sdl.clip.w;
|
|
// height=sdl.clip.h;
|
|
#else
|
|
// width=sdl.draw.width;
|
|
// height=sdl.draw.height;
|
|
#endif
|
|
void RENDER_CallBack( GFX_CallBackFunctions_t function );
|
|
#if 0
|
|
while (sdl.desktop.fullscreen) {
|
|
int temp_size;
|
|
temp_size=render.scale.size;
|
|
if(!sdl.desktop.fullscreen) { render.scale.size=temp_size; RENDER_CallBack( GFX_CallBackReset); return; }
|
|
}
|
|
#endif
|
|
#ifdef WIN32
|
|
if(menu.resizeusing) {
|
|
RENDER_CallBack( GFX_CallBackReset);
|
|
return;
|
|
}
|
|
#endif
|
|
#if 0 /* FIXME: This code misbehaves when doublescan=false on Linux/X11 */
|
|
if((Bitu)nWidth == (Bitu)width && (Bitu)nHeight == (Bitu)height) {
|
|
RENDER_CallBack( GFX_CallBackReset);
|
|
return;
|
|
}
|
|
Section_prop * section=static_cast<Section_prop *>(control->GetSection("sdl"));
|
|
if ((!strcmp(section->Get_string("windowresolution"),"original") || (!strcmp(section->Get_string("windowresolution"),"desktop"))) && (render.src.dblw && render.src.dblh)) {
|
|
switch (render.scale.op) {
|
|
case scalerOpNormal:
|
|
if(!render.scale.hardware) {
|
|
if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) {
|
|
if (render.scale.size <= 4 && render.scale.size >=1) ++render.scale.size; break;
|
|
} else {
|
|
if (render.scale.size <= 5 && render.scale.size >= 2) --render.scale.size; break;
|
|
}
|
|
} else {
|
|
if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) {
|
|
if (render.scale.size == 1) { render.scale.size=4; break; }
|
|
if (render.scale.size == 4) { render.scale.size=6; break; }
|
|
if (render.scale.size == 6) { render.scale.size=8; break; }
|
|
if (render.scale.size == 8) { render.scale.size=10; break; }
|
|
}
|
|
if((Bitu)nWidth<(Bitu)width || (Bitu)nHeight<(Bitu)height) {
|
|
if (render.scale.size == 10) { render.scale.size=8; break; }
|
|
if (render.scale.size == 8) { render.scale.size=6; break; }
|
|
if (render.scale.size == 6) { render.scale.size=4; break; }
|
|
if (render.scale.size == 4) { render.scale.size=1; break; }
|
|
}
|
|
}
|
|
break;
|
|
case scalerOpAdvMame:
|
|
case scalerOpHQ:
|
|
case scalerOpAdvInterp:
|
|
case scalerOpTV:
|
|
case scalerOpRGB:
|
|
case scalerOpScan:
|
|
if((Bitu)nWidth>(Bitu)width || (Bitu)nHeight>(Bitu)height) { if (render.scale.size == 2) ++render.scale.size; }
|
|
if((Bitu)nWidth<(Bitu)width || (Bitu)nHeight<(Bitu)height) { if (render.scale.size == 3) --render.scale.size; }
|
|
break;
|
|
case scalerOpSaI:
|
|
case scalerOpSuperSaI:
|
|
case scalerOpSuperEagle:
|
|
default: // other scalers
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
RENDER_CallBack( GFX_CallBackReset);
|
|
}
|
|
|
|
void GFX_RedrawScreen(uint32_t nWidth, uint32_t nHeight) {
|
|
RedrawScreen(nWidth, nHeight);
|
|
}
|
|
|
|
bool GFX_MustActOnResize() {
|
|
if (!GFX_IsFullscreen())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
void GFX_HandleVideoResize(int width, int height) {
|
|
|
|
/* don't act if 3Dfx OpenGL emulation is active */
|
|
if (GFX_GetPreventFullscreen()) {
|
|
#if C_OPENGL
|
|
if (new_width == width && new_height == height) return;
|
|
new_width = width;
|
|
new_height = height;
|
|
voodoo_ogl_update_dimensions();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* Maybe a screen rotation has just occurred, so we simply resize.
|
|
There may be a different cause for a forced resized, though. */
|
|
if (sdl.desktop.full.display_res && IsFullscreen()) {
|
|
/* Note: We should not use GFX_ObtainDisplayDimensions
|
|
(SDL_GetDisplayBounds) on Android after a screen rotation:
|
|
The older values from application startup are returned. */
|
|
sdl.desktop.full.width = width;
|
|
sdl.desktop.full.height = height;
|
|
}
|
|
|
|
/* assume the resize comes from user preference UNLESS the window
|
|
* is fullscreen or maximized */
|
|
if (!menu.maxwindow && !sdl.desktop.fullscreen && !sdl.init_ignore && NonUserResizeCounter == 0 && !window_was_maximized) {
|
|
UpdateWindowDimensions();
|
|
UpdateWindowDimensions((unsigned int)width, (unsigned int)height);
|
|
|
|
/* if the dimensions actually changed from our surface dimensions, then
|
|
assume it's the user's input. Linux/X11 is good at doing this anyway,
|
|
but the Windows SDL 1.x support will return us a resize event for the
|
|
window size change resulting from SDL mode set. */
|
|
if (width != sdl.surface->w || height != sdl.surface->h) {
|
|
userResizeWindowWidth = (unsigned int)width;
|
|
userResizeWindowHeight = (unsigned int)height;
|
|
}
|
|
}
|
|
else {
|
|
UpdateWindowDimensions();
|
|
if (window_was_maximized && !menu.maxwindow) {
|
|
userResizeWindowWidth = currentWindowWidth==sdl.surface->w?0:(unsigned int)currentWindowWidth;
|
|
userResizeWindowHeight = currentWindowHeight==sdl.surface->h?0:(unsigned int)currentWindowHeight;
|
|
}
|
|
}
|
|
|
|
/* TODO: Only if FULLSCREEN_DESKTOP */
|
|
if (screen_size_info.screen_dimensions_pixels.width != 0 && screen_size_info.screen_dimensions_pixels.height != 0) {
|
|
sdl.desktop.full.width = (uint16_t)screen_size_info.screen_dimensions_pixels.width;
|
|
sdl.desktop.full.height = (uint16_t)screen_size_info.screen_dimensions_pixels.height;
|
|
}
|
|
else {
|
|
SDL_DisplayMode dm;
|
|
if (SDL_GetDesktopDisplayMode(0/*FIXME display index*/,&dm) == 0) {
|
|
sdl.desktop.full.width = dm.w;
|
|
sdl.desktop.full.height = dm.h;
|
|
LOG_MSG("SDL2 reports desktop display mode %u x %u",dm.w,dm.h);
|
|
}
|
|
else {
|
|
LOG_MSG("SDL2 unable to determine desktop display mode, error %s",SDL_GetError());
|
|
}
|
|
}
|
|
|
|
window_was_maximized = menu.maxwindow;
|
|
if (NonUserResizeCounter > 0)
|
|
NonUserResizeCounter--;
|
|
|
|
/* Even if the new window's dimensions are actually the desired ones
|
|
* we may still need to re-obtain a new window surface or do
|
|
* a different thing. So we basically call GFX_SetSize, but without
|
|
* touching the window itself (or else we may end in an infinite loop).
|
|
*
|
|
* Furthermore, if the new dimensions are *not* the desired ones, we
|
|
* don't fight it. Rather than attempting to resize it back, we simply
|
|
* keep the window as-is and disable screen updates. This is done
|
|
* in SDL_SetSDLWindowSurface by setting sdl.update_display_contents
|
|
* to false.
|
|
*/
|
|
sdl.update_window = false;
|
|
GFX_ResetScreen();
|
|
sdl.update_window = true;
|
|
}
|
|
#else
|
|
static void HandleVideoResize(void * event) {
|
|
if(sdl.desktop.fullscreen) return;
|
|
|
|
/* don't act on resize events if we made the window non-resizeable.
|
|
* especially if 3Dfx voodoo emulation is active. */
|
|
if (!(sdl.surface->flags & SDL_RESIZABLE)) return;
|
|
|
|
SDL_ResizeEvent* ResizeEvent = (SDL_ResizeEvent*)event;
|
|
|
|
/* don't act if 3Dfx OpenGL emulation is active */
|
|
if (GFX_GetPreventFullscreen()) {
|
|
#if C_OPENGL
|
|
if (new_width == ResizeEvent->w && new_height == ResizeEvent->h) return;
|
|
new_width = ResizeEvent->w;
|
|
new_height = ResizeEvent->h;
|
|
voodoo_ogl_update_dimensions();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* assume the resize comes from user preference UNLESS the window
|
|
* is fullscreen or maximized */
|
|
if (!menu.maxwindow && !sdl.desktop.fullscreen && !sdl.init_ignore && NonUserResizeCounter == 0 && !window_was_maximized) {
|
|
UpdateWindowDimensions();
|
|
UpdateWindowDimensions((unsigned int)ResizeEvent->w, (unsigned int)ResizeEvent->h);
|
|
|
|
/* if the dimensions actually changed from our surface dimensions, then
|
|
assume it's the user's input. Linux/X11 is good at doing this anyway,
|
|
but the Windows SDL 1.x support will return us a resize event for the
|
|
window size change resulting from SDL mode set. */
|
|
if (ResizeEvent->w != sdl.surface->w || ResizeEvent->h != sdl.surface->h) {
|
|
userResizeWindowWidth = (unsigned int)ResizeEvent->w;
|
|
userResizeWindowHeight = (unsigned int)ResizeEvent->h;
|
|
}
|
|
}
|
|
else {
|
|
UpdateWindowDimensions();
|
|
}
|
|
|
|
window_was_maximized = menu.maxwindow;
|
|
if (NonUserResizeCounter > 0)
|
|
NonUserResizeCounter--;
|
|
|
|
if (sdl.updating && !GFX_MustActOnResize()) {
|
|
/* act on resize when updating is complete */
|
|
sdl.deferred_resize = true;
|
|
}
|
|
else {
|
|
sdl.deferred_resize = false;
|
|
RedrawScreen((unsigned int)ResizeEvent->w, (unsigned int)ResizeEvent->h);
|
|
}
|
|
#ifdef WIN32
|
|
menu.resizeusing=false;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
extern unsigned int mouse_notify_mode;
|
|
|
|
bool user_cursor_locked = false;
|
|
MOUSE_EMULATION user_cursor_emulation = MOUSE_EMULATION_NEVER;
|
|
int user_cursor_x = 0,user_cursor_y = 0;
|
|
int user_cursor_sw = 640,user_cursor_sh = 480;
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
|
|
void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id) {
|
|
(void)menu;//UNUSED
|
|
if (mainMenu.menuUserHoverAt != item_id) {
|
|
if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
|
|
auto &item = mainMenu.get_item(mainMenu.menuUserHoverAt);
|
|
item.setHover(mainMenu,false);
|
|
if (item.checkResetRedraw()) {
|
|
item.drawMenuItem(mainMenu);
|
|
item.updateScreenFromItem(mainMenu);
|
|
}
|
|
}
|
|
|
|
mainMenu.menuUserHoverAt = item_id;
|
|
|
|
if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
|
|
auto &item = mainMenu.get_item(mainMenu.menuUserHoverAt);
|
|
item.setHover(mainMenu,true);
|
|
if (item.checkResetRedraw()) {
|
|
item.drawMenuItem(mainMenu);
|
|
item.updateScreenFromItem(mainMenu);
|
|
}
|
|
}
|
|
|
|
if (OpenGL_using())
|
|
mainMenu.setRedraw();
|
|
}
|
|
}
|
|
|
|
void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id) {
|
|
(void)menu;//UNUSED
|
|
if (mainMenu.menuUserAttentionAt != item_id) {
|
|
if (mainMenu.menuUserAttentionAt != DOSBoxMenu::unassigned_item_handle) {
|
|
auto &item = mainMenu.get_item(mainMenu.menuUserAttentionAt);
|
|
item.setHilight(mainMenu,false);
|
|
if (item.checkResetRedraw()) {
|
|
item.drawMenuItem(mainMenu);
|
|
item.updateScreenFromItem(mainMenu);
|
|
}
|
|
}
|
|
|
|
mainMenu.menuUserAttentionAt = item_id;
|
|
|
|
if (mainMenu.menuUserAttentionAt != DOSBoxMenu::unassigned_item_handle) {
|
|
auto &item = mainMenu.get_item(mainMenu.menuUserAttentionAt);
|
|
item.setHilight(mainMenu,true);
|
|
if (item.checkResetRedraw()) {
|
|
item.drawMenuItem(mainMenu);
|
|
item.updateScreenFromItem(mainMenu);
|
|
}
|
|
}
|
|
|
|
if (OpenGL_using())
|
|
mainMenu.setRedraw();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
uint8_t Mouse_GetButtonState(void);
|
|
|
|
bool GFX_CursorInOrNearScreen(int wx,int wy) {
|
|
#if defined(USE_TTF)
|
|
if (ttf.inUse) return true;
|
|
#endif
|
|
int minx = sdl.clip.x - (sdl.clip.w / 10);
|
|
int miny = sdl.clip.y - (sdl.clip.h / 10);
|
|
int maxx = sdl.clip.x + sdl.clip.w + (sdl.clip.w / 10);
|
|
int maxy = sdl.clip.y + sdl.clip.h + (sdl.clip.h / 10);
|
|
|
|
return (wx >= minx && wx < maxx) && (wy >= miny && wy < maxy);
|
|
}
|
|
|
|
static void HandleMouseMotion(SDL_MouseMotionEvent * motion) {
|
|
bool inputToScreen = false;
|
|
|
|
/* limit mouse input to whenever the cursor is on the screen, or near the edge of the screen. */
|
|
if (is_paused)
|
|
inputToScreen = false;
|
|
else if (vmware_mouse || sdl.mouse.locked || Mouse_GetButtonState() != 0)
|
|
inputToScreen = true;
|
|
else {
|
|
inputToScreen = GFX_CursorInOrNearScreen(motion->x,motion->y);
|
|
#if defined(WIN32) || defined(MACOSX) || defined(C_SDL2)
|
|
if (mouse_start_x >= 0 && mouse_start_y >= 0) {
|
|
if (fx>=0 && fy>=0)
|
|
Mouse_Select(mouse_start_x-sdl.clip.x,mouse_start_y-sdl.clip.y,fx-sdl.clip.x,fy-sdl.clip.y,sdl.clip.w,sdl.clip.h, false);
|
|
Mouse_Select(mouse_start_x-sdl.clip.x,mouse_start_y-sdl.clip.y,motion->x-sdl.clip.x,motion->y-sdl.clip.y,sdl.clip.w,sdl.clip.h, true);
|
|
fx=motion->x;
|
|
fy=motion->y;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
|
|
if (GFX_GetPreventFullscreen()) {
|
|
/* do NOT draw SDL menu in OpenGL mode when 3Dfx emulation is using OpenGL */
|
|
}
|
|
else if (!sdl.mouse.locked && !sdl.desktop.fullscreen && mainMenu.isVisible() && motion->y < mainMenu.menuBox.h && Mouse_GetButtonState() == 0) {
|
|
skipdraw=true;
|
|
GFX_SDLMenuTrackHover(mainMenu,mainMenu.display_list.itemFromPoint(mainMenu,motion->x,motion->y));
|
|
skipdraw=false;
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
if (OpenGL_using() && mainMenu.needsRedraw()) {
|
|
#if C_OPENGL
|
|
sdl_opengl.menudraw_countdown = 2; // two GL buffers
|
|
GFX_OpenGLRedrawScreen();
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
# if defined(C_SDL2)
|
|
SDL_GL_SwapWindow(sdl.window);
|
|
# else
|
|
SDL_GL_SwapBuffers();
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
else {
|
|
skipdraw=true;
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
skipdraw=false;
|
|
|
|
if (OpenGL_using() && mainMenu.needsRedraw()) {
|
|
#if C_OPENGL
|
|
sdl_opengl.menudraw_countdown = 2; // two GL buffers
|
|
GFX_OpenGLRedrawScreen();
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
# if defined(C_SDL2)
|
|
SDL_GL_SwapWindow(sdl.window);
|
|
# else
|
|
SDL_GL_SwapBuffers();
|
|
# endif
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!inputToScreen) {
|
|
#if defined(C_SDL2)
|
|
if (!sdl.mouse.locked)
|
|
#else
|
|
/* SDL1 has some sort of weird mouse warping bug in fullscreen mode no matter whether the mouse is captured or not (Windows, Linux/X11) */
|
|
if (!sdl.mouse.locked && !sdl.desktop.fullscreen)
|
|
#endif
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
return;
|
|
}
|
|
|
|
user_cursor_x = motion->x - sdl.clip.x;
|
|
user_cursor_y = motion->y - sdl.clip.y;
|
|
user_cursor_locked = sdl.mouse.locked;
|
|
user_cursor_emulation = sdl.mouse.emulation;
|
|
user_cursor_sw = sdl.clip.w;
|
|
user_cursor_sh = sdl.clip.h;
|
|
|
|
auto xrel = static_cast<float>(motion->xrel) * sdl.mouse.xsensitivity / 100.0f;
|
|
auto yrel = static_cast<float>(motion->yrel) * sdl.mouse.ysensitivity / 100.0f;
|
|
auto x = static_cast<float>(motion->x - sdl.clip.x) / (sdl.clip.w - 1) * sdl.mouse.xsensitivity / 100.0f;
|
|
auto y = static_cast<float>(motion->y - sdl.clip.y) / (sdl.clip.h - 1) * sdl.mouse.ysensitivity / 100.0f;
|
|
auto emu = sdl.mouse.locked;
|
|
|
|
const auto inside =
|
|
motion->x >= sdl.clip.x && motion->x < sdl.clip.x + sdl.clip.w &&
|
|
motion->y >= sdl.clip.y && motion->y < sdl.clip.y + sdl.clip.h;
|
|
|
|
if (mouse_notify_mode != 0)
|
|
{
|
|
/* for mouse integration driver */
|
|
if (!sdl.mouse.locked)
|
|
xrel = yrel = x = y = 0.0f;
|
|
|
|
emu = sdl.mouse.locked;
|
|
const auto isdown = Mouse_GetButtonState() != 0;
|
|
|
|
#if defined(C_SDL2)
|
|
if (!vmware_mouse && !sdl.mouse.locked)
|
|
#else
|
|
/* SDL1 has some sort of weird mouse warping bug in fullscreen mode no matter whether the mouse is captured or not (Windows, Linux/X11) */
|
|
if (!vmware_mouse && !sdl.mouse.locked && !sdl.desktop.fullscreen)
|
|
#endif
|
|
SDL_ShowCursor((isdown || inside) ? SDL_DISABLE : SDL_ENABLE);
|
|
}
|
|
else if (!user_cursor_locked)
|
|
{
|
|
extern bool MOUSE_HasInterruptSub();
|
|
extern bool MOUSE_IsBeingPolled();
|
|
extern bool MOUSE_IsHidden();
|
|
/* Show only when DOS app is not using mouse */
|
|
|
|
#if defined(C_SDL2)
|
|
if (!vmware_mouse && !sdl.mouse.locked)
|
|
#else
|
|
/* SDL1 has some sort of weird mouse warping bug in fullscreen mode no matter whether the mouse is captured or not (Windows, Linux/X11) */
|
|
if (!vmware_mouse && !sdl.mouse.locked && !sdl.desktop.fullscreen)
|
|
#endif
|
|
SDL_ShowCursor(((!inside) || ((MOUSE_IsHidden()) && !(MOUSE_IsBeingPolled() || MOUSE_HasInterruptSub()))) ? SDL_ENABLE : SDL_DISABLE);
|
|
}
|
|
Mouse_CursorMoved(xrel, yrel, x, y, emu);
|
|
VMWARE_MousePosition(motion->x, motion->y);
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
|
|
void MenuFullScreenRedraw(void) {
|
|
#if defined(C_SDL2)
|
|
SDL_UpdateWindowSurface(sdl.window);
|
|
#else
|
|
SDL_Flip(sdl.surface);
|
|
#endif
|
|
}
|
|
|
|
static struct {
|
|
unsigned char* bmp = NULL;
|
|
unsigned int stride = 0,height = 0;
|
|
} menuSavedScreen;
|
|
|
|
void MenuSaveScreen(void) {
|
|
if (!OpenGL_using()) {
|
|
if (menuSavedScreen.bmp == NULL) {
|
|
menuSavedScreen.height = (unsigned int)sdl.surface->h;
|
|
menuSavedScreen.stride = (unsigned int)sdl.surface->pitch;
|
|
menuSavedScreen.bmp = new unsigned char[menuSavedScreen.height * menuSavedScreen.stride];
|
|
}
|
|
|
|
if (SDL_MUSTLOCK(sdl.surface))
|
|
SDL_LockSurface(sdl.surface);
|
|
|
|
memcpy(menuSavedScreen.bmp, sdl.surface->pixels, menuSavedScreen.height * menuSavedScreen.stride);
|
|
|
|
if (SDL_MUSTLOCK(sdl.surface))
|
|
SDL_UnlockSurface(sdl.surface);
|
|
}
|
|
}
|
|
|
|
void MenuRestoreScreen(void) {
|
|
if (!OpenGL_using()) {
|
|
if (menuSavedScreen.bmp == NULL)
|
|
return;
|
|
|
|
if (SDL_MUSTLOCK(sdl.surface))
|
|
SDL_LockSurface(sdl.surface);
|
|
|
|
memcpy(sdl.surface->pixels, menuSavedScreen.bmp, menuSavedScreen.height * menuSavedScreen.stride);
|
|
|
|
if (SDL_MUSTLOCK(sdl.surface))
|
|
SDL_UnlockSurface(sdl.surface);
|
|
}
|
|
}
|
|
|
|
void MenuFreeScreen(void) {
|
|
if (menuSavedScreen.bmp == NULL)
|
|
return;
|
|
|
|
delete[] menuSavedScreen.bmp;
|
|
menuSavedScreen.bmp = NULL;
|
|
}
|
|
#endif
|
|
|
|
static void HandleMouseWheel(bool normal, int amount) {
|
|
if (amount != 0) {
|
|
Mouse_WheelMoved(normal ? -amount : amount);
|
|
if (vmware_mouse) VMWARE_MouseWheel(normal ? -amount : amount);
|
|
}
|
|
}
|
|
|
|
static void HandleMouseButton(SDL_MouseButtonEvent * button, SDL_MouseMotionEvent * motion) {
|
|
#if !defined(WIN32)
|
|
(void)motion;
|
|
#endif
|
|
bool inputToScreen = false;
|
|
bool inMenu = false;
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
|
|
if (GFX_GetPreventFullscreen()) {
|
|
/* do NOT draw SDL menu in OpenGL mode when 3Dfx emulation is using OpenGL */
|
|
mainMenu.menuUserHoverAt = DOSBoxMenu::unassigned_item_handle;
|
|
}
|
|
else if (!sdl.mouse.locked && !sdl.desktop.fullscreen && mainMenu.isVisible() && button->y < mainMenu.menuBox.h) {
|
|
GFX_SDLMenuTrackHover(mainMenu,mainMenu.display_list.itemFromPoint(mainMenu,button->x,button->y));
|
|
inMenu = true;
|
|
}
|
|
else {
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
}
|
|
|
|
if (button->button == SDL_BUTTON_LEFT) {
|
|
if (button->state == SDL_PRESSED) {
|
|
GFX_SDLMenuTrackHilight(mainMenu,mainMenu.menuUserHoverAt);
|
|
if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
|
|
std::vector<DOSBoxMenu::item_handle_t> popup_stack;
|
|
DOSBoxMenu::item_handle_t choice_item;
|
|
DOSBoxMenu::item_handle_t psel_item;
|
|
DOSBoxMenu::item_handle_t sel_item;
|
|
bool button_holding=true;
|
|
bool redrawAll=false;
|
|
bool resized=false;
|
|
bool runloop=true;
|
|
SDL_Rect uprect;
|
|
SDL_Event event;
|
|
|
|
psel_item = DOSBoxMenu::unassigned_item_handle;
|
|
choice_item = mainMenu.menuUserHoverAt = mainMenu.menuUserAttentionAt;
|
|
|
|
popup_stack.push_back(mainMenu.menuUserAttentionAt);
|
|
|
|
#if C_DIRECT3D
|
|
if (sdl.desktop.want_type == SCREEN_DIRECT3D) {
|
|
/* In output=direct3d mode, SDL still has a surface but this code ignores SDL
|
|
* and draws directly to a Direct3D9 backbuffer which is presented to the window
|
|
* client area. However, GDI output to the window still works, and this code
|
|
* uses the SDL surface still. Therefore, for menus to draw correctly atop the
|
|
* Direct3D output, this code copies the Direct3D backbuffer to the SDL surface
|
|
* first.
|
|
*
|
|
* WARNING: This happens to work with Windows (even Windows 10 build 18xx as of
|
|
* 2018/05/21) because Windows appears to permit mixing Direct3D and GDI rendering
|
|
* to the window.
|
|
*
|
|
* Someday, if Microsoft should break that ability, this code will need to be
|
|
* revised to send screen "updates" to the Direct3D backbuffer first, then
|
|
* Present to the window client area. */
|
|
if (d3d) d3d->UpdateRectToSDLSurface(0, 0, sdl.surface->w, sdl.surface->h);
|
|
}
|
|
#endif
|
|
|
|
if (OpenGL_using()) {
|
|
#if C_OPENGL
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,false);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,false);
|
|
|
|
/* show the menu */
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,true);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,true);
|
|
|
|
glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
GFX_OpenGLRedrawScreen();
|
|
|
|
/* give the menu bar a drop shadow */
|
|
MenuShadeRect(
|
|
(int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX,
|
|
(int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h,
|
|
(int)mainMenu.menuBox.w,
|
|
(int)DOSBoxMenu::dropshadowY - 1/*menubar border*/);
|
|
|
|
mainMenu.setRedraw();
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
|
|
for (auto i=popup_stack.begin();i!=popup_stack.end();i++) {
|
|
if (mainMenu.get_item(*i).get_type() == DOSBoxMenu::submenu_type_id) {
|
|
mainMenu.get_item(*i).drawBackground(mainMenu);
|
|
mainMenu.get_item(*i).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
|
|
}
|
|
}
|
|
|
|
# if defined(C_SDL2)
|
|
SDL_GL_SwapWindow(sdl.window);
|
|
# else
|
|
SDL_GL_SwapBuffers();
|
|
# endif
|
|
#endif
|
|
}
|
|
else {
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,false);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,false);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).drawMenuItem(mainMenu);
|
|
MenuSaveScreen();
|
|
|
|
/* give the menu bar a drop shadow */
|
|
MenuShadeRect(
|
|
(int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX,
|
|
(int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h,
|
|
(int)mainMenu.menuBox.w,
|
|
(int)DOSBoxMenu::dropshadowY - 1/*menubar border*/);
|
|
|
|
uprect.x = 0;
|
|
uprect.y = mainMenu.menuBox.y + mainMenu.menuBox.h;
|
|
uprect.w = mainMenu.menuBox.w;
|
|
uprect.h = DOSBoxMenu::dropshadowY;
|
|
#if defined(C_SDL2)
|
|
SDL_UpdateWindowSurfaceRects(sdl.window, &uprect, 1);
|
|
#else
|
|
SDL_UpdateRects( sdl.surface, 1, &uprect );
|
|
#endif
|
|
|
|
/* show the menu */
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHilight(mainMenu,true);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).setHover(mainMenu,true);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).drawBackground(mainMenu);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
|
|
mainMenu.get_item(mainMenu.menuUserAttentionAt).updateScreenFromPopup(mainMenu);
|
|
}
|
|
|
|
/* hack */
|
|
mainMenu.menuUserAttentionAt = DOSBoxMenu::unassigned_item_handle;
|
|
|
|
/* fall into another loop to process the menu */
|
|
while (runloop) {
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
if (!SDL_PollEvent(&event)) continue;
|
|
#else
|
|
if (!SDL_WaitEvent(&event)) break;
|
|
#endif
|
|
|
|
#if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
|
|
switch (event.type) {
|
|
case SDL_FINGERDOWN:
|
|
if (touchscreen_finger_lock == no_finger_id &&
|
|
touchscreen_touch_lock == no_touch_id) {
|
|
touchscreen_finger_lock = event.tfinger.fingerId;
|
|
touchscreen_touch_lock = event.tfinger.touchId;
|
|
Sint32 x,y;
|
|
|
|
x = (Sint32)(event.tfinger.x * currentWindowWidth);
|
|
y = (Sint32)(event.tfinger.y * currentWindowHeight);
|
|
|
|
memset(&event.button,0,sizeof(event.button));
|
|
event.type = SDL_MOUSEBUTTONDOWN;
|
|
event.button.x = x;
|
|
event.button.y = y;
|
|
}
|
|
else {
|
|
event.type = -1;
|
|
}
|
|
break;
|
|
case SDL_FINGERUP:
|
|
if (touchscreen_finger_lock == event.tfinger.fingerId &&
|
|
touchscreen_touch_lock == event.tfinger.touchId) {
|
|
touchscreen_finger_lock = no_finger_id;
|
|
touchscreen_touch_lock = no_touch_id;
|
|
Sint32 x,y;
|
|
|
|
x = (Sint32)(event.tfinger.x * currentWindowWidth);
|
|
y = (Sint32)(event.tfinger.y * currentWindowHeight);
|
|
|
|
memset(&event.button,0,sizeof(event.button));
|
|
event.type = SDL_MOUSEBUTTONUP;
|
|
event.button.x = x;
|
|
event.button.y = y;
|
|
}
|
|
else {
|
|
event.type = -1;
|
|
}
|
|
break;
|
|
case SDL_FINGERMOTION:
|
|
if (touchscreen_finger_lock == event.tfinger.fingerId &&
|
|
touchscreen_touch_lock == event.tfinger.touchId) {
|
|
Sint32 x,y;
|
|
|
|
x = (Sint32)(event.tfinger.x * currentWindowWidth);
|
|
y = (Sint32)(event.tfinger.y * currentWindowHeight);
|
|
|
|
memset(&event.button,0,sizeof(event.button));
|
|
event.type = SDL_MOUSEMOTION;
|
|
event.button.x = x;
|
|
event.button.y = y;
|
|
}
|
|
else if (touchscreen_finger_lock != no_finger_id ||
|
|
touchscreen_touch_lock != no_touch_id) {
|
|
event.type = -1;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
switch (event.type) {
|
|
case SDL_QUIT:
|
|
if (CheckQuit()) throw(0);
|
|
break;
|
|
case SDL_KEYUP:
|
|
if (event.key.keysym.sym == SDLK_ESCAPE) {
|
|
choice_item = DOSBoxMenu::unassigned_item_handle;
|
|
runloop = false;
|
|
}
|
|
break;
|
|
#if defined(C_SDL2)
|
|
case SDL_WINDOWEVENT:
|
|
switch (event.window.event) {
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
GFX_HandleVideoResize(event.window.data1, event.window.data2);
|
|
runloop = false;
|
|
resized = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
#if !defined(C_SDL2)
|
|
case SDL_VIDEORESIZE:
|
|
UpdateWindowDimensions(); // FIXME: Use SDL window dimensions, except that on Windows, SDL won't tell us our actual dimensions
|
|
HandleVideoResize(&event.resize);
|
|
|
|
runloop = false;
|
|
resized = true;
|
|
break;
|
|
#endif
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
button_holding=true;
|
|
choice_item = mainMenu.menuUserHoverAt;
|
|
if (choice_item != DOSBoxMenu::unassigned_item_handle) {
|
|
DOSBoxMenu::item &item = mainMenu.get_item(choice_item);
|
|
item.setHilight(mainMenu,true);
|
|
item.drawMenuItem(mainMenu);
|
|
if (OpenGL_using())
|
|
redrawAll = true;
|
|
else
|
|
item.updateScreenFromItem(mainMenu);
|
|
}
|
|
else {
|
|
/* clicking on nothing should dismiss */
|
|
runloop = false;
|
|
}
|
|
break;
|
|
case SDL_MOUSEBUTTONUP:
|
|
button_holding=false;
|
|
choice_item = mainMenu.menuUserHoverAt;
|
|
if (choice_item != DOSBoxMenu::unassigned_item_handle) {
|
|
if (choice_item == psel_item) { /* clicking something twice should dismiss */
|
|
runloop = false;
|
|
}
|
|
else {
|
|
DOSBoxMenu::item &item = mainMenu.get_item(choice_item);
|
|
if (item.get_type() == DOSBoxMenu::item_type_id && item.is_enabled())
|
|
runloop = false;
|
|
}
|
|
|
|
psel_item = choice_item;
|
|
}
|
|
else {
|
|
/* not selecting anything counts as a reason to exit */
|
|
runloop = false;
|
|
}
|
|
break;
|
|
case SDL_MOUSEMOTION:
|
|
{
|
|
sel_item = DOSBoxMenu::unassigned_item_handle;
|
|
|
|
auto search = popup_stack.end();
|
|
if (search != popup_stack.begin()) {
|
|
do {
|
|
search--;
|
|
|
|
sel_item = mainMenu.get_item(*search).display_list.itemFromPoint(mainMenu,event.button.x,event.button.y);
|
|
if (sel_item != DOSBoxMenu::unassigned_item_handle) {
|
|
assert(search != popup_stack.end());
|
|
search++;
|
|
break;
|
|
}
|
|
} while (search != popup_stack.begin());
|
|
}
|
|
|
|
if (sel_item == DOSBoxMenu::unassigned_item_handle)
|
|
sel_item = mainMenu.display_list.itemFromPoint(mainMenu,event.button.x,event.button.y);
|
|
|
|
/* at this point:
|
|
* sel_item = item under cursor, or unassigned if no item
|
|
* search = iterator just past the item's level (to remove items if changing) */
|
|
|
|
if (mainMenu.menuUserHoverAt != sel_item) {
|
|
bool noRedrawNew = false,noRedrawOld = false;
|
|
|
|
if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle) {
|
|
mainMenu.get_item(mainMenu.menuUserHoverAt).setHover(mainMenu,false);
|
|
if (mainMenu.get_item(mainMenu.menuUserHoverAt).get_type() == DOSBoxMenu::item_type_id)
|
|
mainMenu.get_item(mainMenu.menuUserHoverAt).setHilight(mainMenu,false);
|
|
else if (mainMenu.get_item(mainMenu.menuUserHoverAt).get_type() == DOSBoxMenu::submenu_type_id) {
|
|
if (mainMenu.get_item(mainMenu.menuUserHoverAt).isHilight()) {
|
|
noRedrawOld = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sel_item != DOSBoxMenu::unassigned_item_handle) {
|
|
if (mainMenu.get_item(sel_item).get_type() == DOSBoxMenu::submenu_type_id) {
|
|
if (!mainMenu.get_item(sel_item).isHilight()) {
|
|
/* use a copy of the iterator to scan forward and un-hilight the menu items.
|
|
* then use the original iterator to erase from the vector. */
|
|
for (auto ss=search;ss != popup_stack.end();ss++) {
|
|
for (auto &id : mainMenu.get_item(*ss).display_list.get_disp_list())
|
|
mainMenu.get_item(id).setHilight(mainMenu,false).setHover(mainMenu,false);
|
|
|
|
mainMenu.get_item(*ss).setHilight(mainMenu,false).setHover(mainMenu,false);
|
|
}
|
|
|
|
popup_stack.erase(search,popup_stack.end());
|
|
mainMenu.get_item(sel_item).setHilight(mainMenu,true).setHover(mainMenu,true);
|
|
popup_stack.push_back(sel_item);
|
|
redrawAll = true;
|
|
}
|
|
else {
|
|
/* no change in item state, don't bother redrawing */
|
|
noRedrawNew = true;
|
|
}
|
|
}
|
|
else {
|
|
/* use a copy of the iterator to scan forward and un-hilight the menu items.
|
|
* then use the original iterator to erase from the vector. */
|
|
for (auto ss=search;ss != popup_stack.end();ss++) {
|
|
for (auto &id : mainMenu.get_item(*ss).display_list.get_disp_list())
|
|
mainMenu.get_item(id).setHilight(mainMenu,false).setHover(mainMenu,false);
|
|
|
|
mainMenu.get_item(*ss).setHilight(mainMenu,false).setHover(mainMenu,false);
|
|
redrawAll = true;
|
|
}
|
|
|
|
popup_stack.erase(search,popup_stack.end());
|
|
}
|
|
|
|
if (OpenGL_using())
|
|
redrawAll = true;
|
|
|
|
mainMenu.get_item(sel_item).setHover(mainMenu,true);
|
|
if (mainMenu.get_item(sel_item).get_type() == DOSBoxMenu::item_type_id && button_holding)
|
|
mainMenu.get_item(sel_item).setHilight(mainMenu,true);
|
|
}
|
|
else {
|
|
if (OpenGL_using())
|
|
redrawAll = true;
|
|
}
|
|
|
|
if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle && !OpenGL_using() && !redrawAll && !noRedrawOld) {
|
|
if (mainMenu.get_item(mainMenu.menuUserHoverAt).checkResetRedraw()) {
|
|
mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu);
|
|
mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu);
|
|
}
|
|
}
|
|
|
|
mainMenu.menuUserHoverAt = sel_item;
|
|
|
|
if (mainMenu.menuUserHoverAt != DOSBoxMenu::unassigned_item_handle && !OpenGL_using() && !redrawAll && !noRedrawNew) {
|
|
if (mainMenu.get_item(mainMenu.menuUserHoverAt).checkResetRedraw()) {
|
|
mainMenu.get_item(mainMenu.menuUserHoverAt).drawMenuItem(mainMenu);
|
|
mainMenu.get_item(mainMenu.menuUserHoverAt).updateScreenFromItem(mainMenu);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (redrawAll) {
|
|
redrawAll = false;
|
|
|
|
#if 0/*DEBUG*/
|
|
LOG_MSG("Redraw %u",(unsigned int)SDL_GetTicks());
|
|
#endif
|
|
|
|
if (OpenGL_using()) {
|
|
#if C_OPENGL
|
|
glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
GFX_OpenGLRedrawScreen();
|
|
|
|
mainMenu.setRedraw();
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
#endif
|
|
}
|
|
else {
|
|
MenuRestoreScreen();
|
|
mainMenu.display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
|
|
}
|
|
|
|
/* give the menu bar a drop shadow */
|
|
MenuShadeRect(
|
|
(int)mainMenu.menuBox.x + (int)DOSBoxMenu::dropshadowX,
|
|
(int)mainMenu.menuBox.y + (int)mainMenu.menuBox.h,
|
|
(int)mainMenu.menuBox.w,
|
|
(int)DOSBoxMenu::dropshadowY - 1/*menubar border*/);
|
|
|
|
for (auto i=popup_stack.begin();i!=popup_stack.end();i++) {
|
|
if (mainMenu.get_item(*i).get_type() == DOSBoxMenu::submenu_type_id) {
|
|
mainMenu.get_item(*i).drawBackground(mainMenu);
|
|
mainMenu.get_item(*i).display_list.DrawDisplayList(mainMenu,/*updateScreen*/false);
|
|
}
|
|
}
|
|
|
|
#if C_OPENGL
|
|
if (OpenGL_using()) {
|
|
# if defined(C_SDL2)
|
|
SDL_GL_SwapWindow(sdl.window);
|
|
# else
|
|
SDL_GL_SwapBuffers();
|
|
# endif
|
|
}
|
|
else
|
|
#endif
|
|
MenuFullScreenRedraw();
|
|
}
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
/* force touchscreen mapping to let go */
|
|
touchscreen_finger_lock = no_finger_id;
|
|
touchscreen_touch_lock = no_touch_id;
|
|
#endif
|
|
|
|
/* then return */
|
|
GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
if (!resized) {
|
|
MenuRestoreScreen();
|
|
if (!OpenGL_using())
|
|
MenuFullScreenRedraw();
|
|
}
|
|
MenuFreeScreen();
|
|
|
|
while (!popup_stack.empty()) {
|
|
DOSBoxMenu::item &item = mainMenu.get_item(popup_stack.back());
|
|
|
|
for (auto &id : item.display_list.get_disp_list()) {
|
|
mainMenu.get_item(id).setHilight(mainMenu,false);
|
|
mainMenu.get_item(id).setHover(mainMenu,false);
|
|
}
|
|
|
|
item.setHilight(mainMenu,false);
|
|
item.setHover(mainMenu,false);
|
|
popup_stack.pop_back();
|
|
}
|
|
|
|
if (OpenGL_using()) {
|
|
#if C_OPENGL
|
|
glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
GFX_OpenGLRedrawScreen();
|
|
|
|
mainMenu.setRedraw();
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
|
|
# if defined(C_SDL2)
|
|
SDL_GL_SwapWindow(sdl.window);
|
|
# else
|
|
SDL_GL_SwapBuffers();
|
|
# endif
|
|
|
|
sdl_opengl.clear_countdown = 2;
|
|
sdl_opengl.menudraw_countdown = 2; // two GL buffers
|
|
#endif
|
|
}
|
|
|
|
/* action! */
|
|
if (!resized && choice_item != DOSBoxMenu::unassigned_item_handle) {
|
|
DOSBoxMenu::item &item = mainMenu.get_item(choice_item);
|
|
|
|
if (item.get_type() == DOSBoxMenu::item_type_id && item.is_enabled())
|
|
mainMenu.dispatchItemCommand(item);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* limit mouse input to whenever the cursor is on the screen, or near the edge of the screen. */
|
|
if (is_paused)
|
|
inputToScreen = false;
|
|
else if (sdl.mouse.locked)
|
|
inputToScreen = true;
|
|
else if (!inMenu)
|
|
inputToScreen = GFX_CursorInOrNearScreen(button->x, button->y);
|
|
|
|
switch (button->state) {
|
|
case SDL_PRESSED:
|
|
if (inMenu || !inputToScreen) return;
|
|
#if defined(WIN32) || defined(MACOSX) || defined(C_SDL2)
|
|
if (!sdl.mouse.locked && button->button == SDL_BUTTON_LEFT && isModifierApplied()) {
|
|
if (mbutton==4 && selsrow>-1 && selscol>-1) {
|
|
Mouse_Select(selscol, selsrow, selmark?selecol:selscol, selmark?selerow:selsrow, -1, -1, false);
|
|
selmark = false;
|
|
selsrow = selscol = selerow = selecol = -1;
|
|
} else if (mouse_start_x >= 0 && mouse_start_y >= 0 && fx >= 0 && fy >= 0) {
|
|
Mouse_Select(mouse_start_x-sdl.clip.x,mouse_start_y-sdl.clip.y,fx-sdl.clip.x,fy-sdl.clip.y,sdl.clip.w,sdl.clip.h, false);
|
|
mouse_start_x = mouse_start_y = -1;
|
|
mouse_end_x = mouse_end_y = -1;
|
|
fx = fy = -1;
|
|
}
|
|
}
|
|
if (!sdl.mouse.locked && ((mbutton==2 && button->button == SDL_BUTTON_MIDDLE) || (mbutton==3 && button->button == SDL_BUTTON_RIGHT)) && isModifierApplied()) {
|
|
mouse_start_x=motion->x;
|
|
mouse_start_y=motion->y;
|
|
break;
|
|
} else
|
|
#endif
|
|
if (sdl.mouse.requestlock && !vmware_mouse && !sdl.mouse.locked && mouse_notify_mode == 0) {
|
|
CaptureMouseNotify();
|
|
GFX_CaptureMouse();
|
|
// Don't pass click to mouse handler
|
|
break;
|
|
}
|
|
if (((middleunlock == 1 && !sdl.mouse.autoenable) || (middleunlock == 2 && sdl.mouse.autoenable) || middleunlock == 3) && sdl.mouse.autolock && mouse_notify_mode == 0 && button->button == SDL_BUTTON_MIDDLE) {
|
|
GFX_CaptureMouse();
|
|
break;
|
|
}
|
|
switch (button->button) {
|
|
case SDL_BUTTON_LEFT:
|
|
Mouse_ButtonPressed(0);
|
|
VMWARE_MouseButtonPressed(0);
|
|
break;
|
|
case SDL_BUTTON_RIGHT:
|
|
Mouse_ButtonPressed(1);
|
|
VMWARE_MouseButtonPressed(1);
|
|
break;
|
|
case SDL_BUTTON_MIDDLE:
|
|
Mouse_ButtonPressed(2);
|
|
VMWARE_MouseButtonPressed(2);
|
|
break;
|
|
#if !defined(C_SDL2)
|
|
case SDL_BUTTON_WHEELUP: /* Ick, really SDL? */
|
|
if (wheel_key && (wheel_guest || !dos_kernel_disabled)) {
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
if (wheel_key<4) {
|
|
INPUT ip = {0};
|
|
ip.type = INPUT_KEYBOARD;
|
|
ip.ki.wScan = wheel_key==2?75:(wheel_key==3?73:72);
|
|
ip.ki.time = 0;
|
|
ip.ki.dwExtraInfo = 0;
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
} else
|
|
#endif
|
|
if (wheel_key==7) {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(KBD_w, true);
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(KBD_w, false);
|
|
} else {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_left:(wheel_key==3||wheel_key==6?KBD_pageup:KBD_up), true);
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_left:(wheel_key==3||wheel_key==6?KBD_pageup:KBD_up), false);
|
|
}
|
|
} else {
|
|
Mouse_ButtonPressed(100-1);
|
|
HandleMouseWheel(true, 1);
|
|
}
|
|
break;
|
|
case SDL_BUTTON_WHEELDOWN: /* Ick, really SDL? */
|
|
if (wheel_key && (wheel_guest || !dos_kernel_disabled)) {
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
if (wheel_key<4) {
|
|
INPUT ip = {0};
|
|
ip.type = INPUT_KEYBOARD;
|
|
ip.ki.wScan = wheel_key==2?77:(wheel_key==3?81:80);
|
|
ip.ki.time = 0;
|
|
ip.ki.dwExtraInfo = 0;
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
} else
|
|
#endif
|
|
if (wheel_key==7) {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(KBD_z, true);
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(KBD_z, false);
|
|
} else {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_right:(wheel_key==3||wheel_key==6?KBD_pagedown:KBD_down), true);
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_right:(wheel_key==3||wheel_key==6?KBD_pagedown:KBD_down), false);
|
|
}
|
|
} else {
|
|
Mouse_ButtonPressed(100+1);
|
|
HandleMouseWheel(false, 1);
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
case SDL_RELEASED:
|
|
#if defined(WIN32) || defined(MACOSX) || defined(C_SDL2)
|
|
if (!sdl.mouse.locked && ((mbutton==2 && button->button == SDL_BUTTON_MIDDLE) || (mbutton==3 && button->button == SDL_BUTTON_RIGHT)) && mouse_start_x >= 0 && mouse_start_y >= 0) {
|
|
mouse_end_x=motion->x;
|
|
mouse_end_y=motion->y;
|
|
if (mouse_start_x == mouse_end_x && mouse_start_y == mouse_end_y) {
|
|
PasteClipboard(true);
|
|
#ifdef USE_TTF
|
|
if (ttf.inUse) resetFontSize();
|
|
#endif
|
|
} else {
|
|
if (abs(mouse_end_x - mouse_start_x) + abs(mouse_end_y - mouse_start_y)<5) {
|
|
PasteClipboard(true);
|
|
#ifdef USE_TTF
|
|
if (ttf.inUse) resetFontSize();
|
|
#endif
|
|
} else
|
|
CopyClipboard(0);
|
|
if (fx >= 0 && fy >= 0)
|
|
Mouse_Select(mouse_start_x-sdl.clip.x,mouse_start_y-sdl.clip.y,fx-sdl.clip.x,fy-sdl.clip.y,sdl.clip.w,sdl.clip.h, false);
|
|
}
|
|
mouse_start_x = -1;
|
|
mouse_start_y = -1;
|
|
mouse_end_x = -1;
|
|
mouse_end_y = -1;
|
|
fx = -1;
|
|
fy = -1;
|
|
break;
|
|
}
|
|
#endif
|
|
switch (button->button) {
|
|
case SDL_BUTTON_LEFT:
|
|
Mouse_ButtonReleased(0);
|
|
VMWARE_MouseButtonReleased(0);
|
|
break;
|
|
case SDL_BUTTON_RIGHT:
|
|
Mouse_ButtonReleased(1);
|
|
VMWARE_MouseButtonReleased(1);
|
|
break;
|
|
case SDL_BUTTON_MIDDLE:
|
|
Mouse_ButtonReleased(2);
|
|
VMWARE_MouseButtonReleased(2);
|
|
break;
|
|
#if !defined(C_SDL2)
|
|
case SDL_BUTTON_WHEELUP: /* Ick, really SDL? */
|
|
Mouse_ButtonReleased(100-1);
|
|
break;
|
|
case SDL_BUTTON_WHEELDOWN: /* Ick, really SDL? */
|
|
Mouse_ButtonReleased(100+1);
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GFX_LosingFocus(void) {
|
|
sdl.laltstate=SDL_KEYUP;
|
|
sdl.raltstate=SDL_KEYUP;
|
|
sdl.lctrlstate=SDL_KEYUP;
|
|
sdl.rctrlstate=SDL_KEYUP;
|
|
sdl.lshiftstate=SDL_KEYUP;
|
|
sdl.rshiftstate=SDL_KEYUP;
|
|
MAPPER_LosingFocus();
|
|
DoExtendedKeyboardHook(false);
|
|
}
|
|
|
|
bool GFX_IsFullscreen(void) {
|
|
return sdl.desktop.fullscreen;
|
|
}
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
static bool CheckEnableImmOnKey(SDL_KeyboardEvent key)
|
|
{
|
|
if(key.keysym.sym == 0 || (!SDL_IM_Composition() && (key.keysym.sym == 0x08 || key.keysym.sym == 0x09 || (key.keysym.sym >= 0x20 && key.keysym.sym <= 0x7F) || (key.keysym.sym >= 0x100 && key.keysym.sym <= 0x119) || key.keysym.sym == 0x12C || key.keysym.sym == 0x12D) || (strPasteBuffer.length() && key.keysym.sym >= 0x80))) {
|
|
// BS, <-, ->, PgUp, PgDn, etc.
|
|
return true;
|
|
}
|
|
if(key.keysym.scancode == 0x01 || key.keysym.scancode == 0x1d || key.keysym.scancode == 0x2a || key.keysym.scancode == 0x36 || key.keysym.scancode == 0x38) {
|
|
// ESC, shift, control, alt
|
|
return true;
|
|
}
|
|
if(key.keysym.scancode >= 0x3b && key.keysym.scancode <= 0x44) {
|
|
// function
|
|
return true;
|
|
}
|
|
if(key.keysym.mod & (KMOD_CTRL|KMOD_ALT)) {
|
|
// ctrl+, alt+
|
|
return true;
|
|
}
|
|
if((key.keysym.mod & 0x03) != 0 && key.keysym.scancode == 0x39) {
|
|
// shift + space
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#elif defined(WIN32) && !defined(HX_DOS) && defined(C_SDL2)
|
|
static bool CheckEnableImmOnKey(SDL_KeyboardEvent key)
|
|
{
|
|
if(key.keysym.scancode == 0x29 ||
|
|
#if defined(SDL_DOSBOX_X_IME)
|
|
(!SDL_IM_Composition() && (key.keysym.sym == 0x08 || key.keysym.sym == 0x09 || (key.keysym.sym >= 0x20 && key.keysym.sym <= 0x7F) || key.keysym.scancode == 0x39 || (key.keysym.scancode >= 0x53 && key.keysym.scancode <= 0x63))) ||
|
|
#endif
|
|
(key.keysym.scancode >= 0x49 && key.keysym.scancode <= 0x52) || (key.keysym.scancode >= 0xe0 && key.keysym.scancode <= 0xe6) || (strPasteBuffer.length() && key.keysym.sym >= 0x20)) {
|
|
// ESC, shift, control, alt, PgUp, PgDn, etc.
|
|
return true;
|
|
} else if((key.keysym.mod & 0x03) != 0 && key.keysym.scancode == 0x2c) {
|
|
// shift + space
|
|
return true;
|
|
} else if(key.keysym.mod & (KMOD_CTRL|KMOD_ALT)) {
|
|
// ctrl+, alt+
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool sdl_wait_on_error() {
|
|
return sdl.wait_on_error;
|
|
}
|
|
|
|
#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && (defined(C_SDL2) || defined(SDL_DOSBOX_X_SPECIAL))
|
|
static uint8_t im_x, im_y;
|
|
static uint32_t last_ticks;
|
|
void SetIMPosition() {
|
|
uint8_t x, y;
|
|
uint8_t page = IS_PC98_ARCH?0:real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
|
|
if (IS_PC98_ARCH) {
|
|
uint16_t address = vga.config.cursor_start;
|
|
x = address % 80;
|
|
y = address / 80;
|
|
} else
|
|
INT10_GetCursorPos(&y, &x, page);
|
|
/* UNUSED
|
|
int nrows = 25, ncols = 80;
|
|
if (IS_PC98_ARCH)
|
|
nrows=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
|
|
else {
|
|
nrows=(IS_EGAVGA_ARCH?real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS):24)+1;
|
|
ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
|
|
}*/
|
|
if ((im_x != x || im_y != y) && GetTicks() - last_ticks > 100) {
|
|
last_ticks = GetTicks();
|
|
im_x = x;
|
|
im_y = y;
|
|
#if defined(LINUX)
|
|
y++;
|
|
#endif
|
|
uint8_t height = IS_PC98_ARCH?16:real_readb(BIOSMEM_SEG, BIOSMEM_CHAR_HEIGHT);
|
|
uint8_t width = CurMode && DOSV_CheckCJKVideoMode() ? CurMode->cwidth : (height / 2);
|
|
SDL_Rect rect;
|
|
#if defined(USE_TTF)
|
|
if (ttf.inUse) {
|
|
rect.x = x * ttf.width;
|
|
rect.y = y * ttf.height + (ttf.height - TTF_FontAscent(ttf.SDL_font)) / 2;
|
|
} else {
|
|
#endif
|
|
double sx = sdl.clip.w>0&&sdl.draw.width>0?((double)sdl.clip.w/sdl.draw.width):1, sy = sdl.clip.h>0&&sdl.draw.height>0?((double)sdl.clip.h/sdl.draw.height):1;
|
|
rect.x = x * width * sx;
|
|
rect.y = y * height * sy - (J3_IsJapanese()?2:(IS_DOSV?-1:(DOSV_CheckCJKVideoMode()?2:0)));
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
|
|
rect.y += mainMenu.menuBarHeight;
|
|
#endif
|
|
#if defined(USE_TTF)
|
|
}
|
|
#endif
|
|
if(IS_PC98_ARCH)
|
|
rect.y--;
|
|
#if defined(C_SDL2)
|
|
rect.w = 0;
|
|
rect.h = 0;
|
|
SDL_SetTextInputRect(&rect);
|
|
#else
|
|
SDL_SetIMPosition(rect.x, rect.y);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
char *CodePageHostToGuest(const uint16_t *s);
|
|
|
|
#if defined(C_SDL2) && !defined(IGNORE_TOUCHSCREEN)
|
|
static void FingerToFakeMouseMotion(SDL_TouchFingerEvent * finger) {
|
|
SDL_MouseMotionEvent fake;
|
|
|
|
memset(&fake,0,sizeof(fake));
|
|
/* NTS: Windows versions of SDL2 do normalize the coordinates */
|
|
fake.x = (Sint32)(finger->x * currentWindowWidth);
|
|
fake.y = (Sint32)(finger->y * currentWindowHeight);
|
|
fake.xrel = (Sint32)finger->dx;
|
|
fake.yrel = (Sint32)finger->dy;
|
|
HandleMouseMotion(&fake);
|
|
|
|
if (finger->type == SDL_FINGERDOWN || finger->type == SDL_FINGERUP) {
|
|
SDL_MouseButtonEvent fakeb;
|
|
|
|
memset(&fakeb,0,sizeof(fakeb));
|
|
|
|
fakeb.state = (finger->type == SDL_FINGERDOWN) ? SDL_PRESSED : SDL_RELEASED;
|
|
fakeb.button = SDL_BUTTON_LEFT;
|
|
fakeb.x = fake.x;
|
|
fakeb.y = fake.y;
|
|
HandleMouseButton(&fakeb,&fake);
|
|
}
|
|
}
|
|
|
|
static void HandleTouchscreenFinger(SDL_TouchFingerEvent * finger) {
|
|
/* Now that SDL2 can tell my mouse from my laptop touchscreen, let's
|
|
* map tap events to the left mouse button. Now I can use my laptop
|
|
* touchscreen with Windows 3.11 again! --J.C. */
|
|
/* Now let's handle The Finger (har har) */
|
|
|
|
/* NTS: This code is written to map ONLY one finger to the mouse.
|
|
* If multiple fingers are touching the screen, this code will
|
|
* only respond to the first finger that touched the screen. */
|
|
|
|
if (finger->type == SDL_FINGERDOWN) {
|
|
if (touchscreen_finger_lock == no_finger_id &&
|
|
touchscreen_touch_lock == no_touch_id) {
|
|
touchscreen_finger_lock = finger->fingerId;
|
|
touchscreen_touch_lock = finger->touchId;
|
|
FingerToFakeMouseMotion(finger);
|
|
}
|
|
}
|
|
else if (finger->type == SDL_FINGERUP) {
|
|
if (touchscreen_finger_lock == finger->fingerId &&
|
|
touchscreen_touch_lock == finger->touchId) {
|
|
touchscreen_finger_lock = no_finger_id;
|
|
touchscreen_touch_lock = no_touch_id;
|
|
FingerToFakeMouseMotion(finger);
|
|
}
|
|
}
|
|
else if (finger->type == SDL_FINGERMOTION) {
|
|
if (touchscreen_finger_lock == finger->fingerId &&
|
|
touchscreen_touch_lock == finger->touchId) {
|
|
FingerToFakeMouseMotion(finger);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
void MSG_WM_COMMAND_handle(SDL_SysWMmsg &Message);
|
|
#endif
|
|
|
|
struct mouse_pos
|
|
{
|
|
long x = 0;
|
|
long y = 0;
|
|
} mouse_pos;
|
|
|
|
bool mouse_inside = false;
|
|
|
|
void GFX_EventsMouseProcess(const long x, const long y, const long rx, const long ry)
|
|
{
|
|
const auto x1 = sdl.clip.x;
|
|
const auto x2 = x1 + sdl.clip.w - 1;
|
|
const auto y1 = sdl.clip.y;
|
|
const auto y2 = y1 + sdl.clip.h - 1;
|
|
const auto in = x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
|
|
|
if (mouse_inside && !in)
|
|
{
|
|
const auto x3 = max((int)x1, min((int)x2, (int)x));
|
|
const auto y3 = max((int)y1, min((int)y2, (int)y));
|
|
SDL_Event evt;
|
|
evt.type = SDL_MOUSEMOTION;
|
|
evt.motion.state = 0;
|
|
evt.motion.which = 0;
|
|
evt.motion.x = x3;
|
|
evt.motion.y = y3;
|
|
evt.motion.xrel = (Sint16)((rx >= 0) ? min(rx, 32767l) : max(rx, -32768l));
|
|
evt.motion.yrel = (Sint16)((ry >= 0) ? min(ry, 32767l) : max(ry, -32768l));
|
|
SDL_PushEvent(&evt);
|
|
}
|
|
|
|
mouse_inside = in;
|
|
}
|
|
|
|
#if defined(WIN32)
|
|
void GFX_EventsMouseWin32()
|
|
{
|
|
/* Compute relative mouse movement */
|
|
|
|
POINT point;
|
|
|
|
if (!GetCursorPos(&point))
|
|
return;
|
|
|
|
const auto hwnd = GetSurfaceHWND();
|
|
|
|
if (hwnd == nullptr || !ScreenToClient(hwnd, &point))
|
|
return;
|
|
|
|
const auto x = point.x;
|
|
const auto y = point.y;
|
|
const auto rx = x - mouse_pos.x;
|
|
const auto ry = y - mouse_pos.y;
|
|
|
|
mouse_pos.x = x;
|
|
mouse_pos.y = y;
|
|
|
|
/* Let the method do the heavy uplifting */
|
|
GFX_EventsMouseProcess(x, y, rx, ry);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* \brief Processes mouse movements when outside the window.
|
|
*
|
|
* This method will send an extra mouse event to the SDL pump
|
|
* when some relative movement has occurred.
|
|
*/
|
|
void GFX_EventsMouse()
|
|
{
|
|
if (sdl.desktop.fullscreen || sdl.mouse.locked)
|
|
return;
|
|
|
|
#if WIN32
|
|
GFX_EventsMouseWin32();
|
|
#else
|
|
// TODO
|
|
#endif
|
|
}
|
|
|
|
bool eatRestoredWindow = false;
|
|
|
|
/* DOSBox SVN revision 4176:4177: For Linux/X11, Xorg 1.20.1
|
|
* will make spurious focus gain and loss events when locking the mouse in windowed mode.
|
|
*
|
|
* This has not been tested with DOSBox-X yet becaus I do not run Xorg 1.20.1, yet */
|
|
#if defined(LINUX)
|
|
#define SDL_XORG_FIX 1
|
|
#else
|
|
#define SDL_XORG_FIX 0
|
|
#endif
|
|
/* end patch fragment */
|
|
|
|
bool gfx_in_mapper = false;
|
|
|
|
#if defined(MACOSX)
|
|
#define DB_POLLSKIP 3
|
|
#else
|
|
//Not used yet, see comment below
|
|
#define DB_POLLSKIP 1
|
|
#endif
|
|
|
|
void GFX_Events() {
|
|
CheckMapperKeyboardLayout();
|
|
#if defined(C_SDL2) /* SDL 2.x---------------------------------- */
|
|
//Don't poll too often. This can be heavy on the OS, especially Macs.
|
|
//In idle mode 3000-4000 polls are done per second without this check.
|
|
//Macs, with this code, max 250 polls per second. (non-macs unused default max 500)
|
|
//Currently not implemented for all platforms, given the ALT-TAB stuff for WIN32.
|
|
#if defined (MACOSX)
|
|
static int last_check = 0;
|
|
int current_check = GetTicks();
|
|
if (current_check - last_check <= DB_POLLSKIP) return;
|
|
last_check = current_check;
|
|
#endif
|
|
|
|
SDL_Event event;
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
static int poll_delay = 0;
|
|
int time = GetTicks();
|
|
if (time - poll_delay > 20) {
|
|
poll_delay = time;
|
|
if (sdl.num_joysticks > 0)
|
|
{
|
|
SDL_JoystickUpdate();
|
|
MAPPER_UpdateJoysticks();
|
|
}
|
|
}
|
|
#endif
|
|
#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && (defined(C_SDL2) || defined(SDL_DOSBOX_X_SPECIAL))
|
|
if(IS_PC98_ARCH) {
|
|
static uint32_t poll98_delay = 0;
|
|
uint32_t time = GetTicks();
|
|
if((int32_t)(time - poll98_delay) > 50) {
|
|
poll98_delay = time;
|
|
SetIMPosition();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
GFX_EventsMouse();
|
|
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
#endif
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
#if defined(C_SDL2)
|
|
/* SDL2 hack: There seems to be a problem where calling the SetWindowSize function,
|
|
even for the same size, still causes a resize event, and sometimes for no apparent
|
|
reason, will cause an endless feed of Windows Restored events. */
|
|
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESTORED) {
|
|
if (eatRestoredWindow) continue;
|
|
}
|
|
else {
|
|
eatRestoredWindow = false;
|
|
}
|
|
#endif
|
|
|
|
switch (event.type) {
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
case SDL_SYSWMEVENT:
|
|
switch( event.syswm.msg->msg.win.msg ) {
|
|
case WM_COMMAND:
|
|
MSG_WM_COMMAND_handle(/*&*/(*event.syswm.msg));
|
|
break;
|
|
case WM_SYSCOMMAND:
|
|
switch (event.syswm.msg->msg.win.wParam) {
|
|
case ID_WIN_SYSMENU_RESTOREMENU:
|
|
/* prevent removing the menu in 3Dfx mode */
|
|
if (!GFX_GetPreventFullscreen()) {
|
|
DOSBox_SetMenu();
|
|
mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
|
|
}
|
|
break;
|
|
case ID_WIN_SYSMENU_TOGGLEMENU:
|
|
/* prevent removing the menu in 3Dfx mode */
|
|
if (!GFX_GetPreventFullscreen())
|
|
{
|
|
ToggleMenu(true);
|
|
mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
|
|
#if defined(USE_TTF) && DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
int last = 0;
|
|
while (TTF_using() && !sdl.desktop.fullscreen && menu_gui && menu.toggle && menuwidth_atleast(ttf.cols*ttf.width+ttf.offX*2+GetSystemMetrics(SM_CXBORDER)*2)>0 && ttf.pointsize>last) {
|
|
last = ttf.pointsize;
|
|
increaseFontSize();
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case ID_WIN_SYSMENU_MAPPER:
|
|
extern void MAPPER_Run(bool pressed);
|
|
MAPPER_Run(false);
|
|
break;
|
|
case ID_WIN_SYSMENU_CFG_GUI:
|
|
extern void GUI_Run(bool pressed);
|
|
GUI_Run(false);
|
|
break;
|
|
case ID_WIN_SYSMENU_PAUSE:
|
|
void PauseDOSBox(bool pressed);
|
|
PauseDOSBox(true);
|
|
break;
|
|
case ID_WIN_SYSMENU_RESETSIZE:
|
|
void GUI_ResetResize(bool pressed);
|
|
GUI_ResetResize(true);
|
|
break;
|
|
#if defined(USE_TTF)
|
|
case ID_WIN_SYSMENU_TTFINCSIZE:
|
|
increaseFontSize();
|
|
break;
|
|
case ID_WIN_SYSMENU_TTFDECSIZE:
|
|
decreaseFontSize();
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
#endif
|
|
case SDL_WINDOWEVENT:
|
|
switch (event.window.event) {
|
|
case SDL_WINDOWEVENT_MOVED:
|
|
#if defined(USE_TTF)
|
|
if (ttf.inUse)
|
|
GFX_EndTextLines(true);
|
|
#endif
|
|
continue;
|
|
case SDL_WINDOWEVENT_RESTORED:
|
|
GFX_ResetScreen();
|
|
eatRestoredWindow = true;
|
|
continue;
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
GFX_HandleVideoResize(event.window.data1, event.window.data2);
|
|
continue;
|
|
case SDL_WINDOWEVENT_EXPOSED:
|
|
if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw );
|
|
continue;
|
|
case SDL_WINDOWEVENT_LEAVE:
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
|
|
skipdraw=true;
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
skipdraw=false;
|
|
GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
#endif
|
|
break;
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
|
if (IsFullscreen() && !sdl.mouse.locked)
|
|
GFX_CaptureMouse();
|
|
SetPriority(sdl.priority.focus);
|
|
CPU_Disable_SkipAutoAdjust();
|
|
#if defined(USE_TTF)
|
|
resetFontSize();
|
|
#endif
|
|
break;
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
if (sdl.mouse.locked) {
|
|
CaptureMouseNotify();
|
|
GFX_CaptureMouse();
|
|
}
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
#endif
|
|
SetPriority(sdl.priority.nofocus);
|
|
GFX_LosingFocus();
|
|
if( sdl.priority.nofocus != PRIORITY_LEVEL_PAUSE ) {
|
|
CPU_Enable_SkipAutoAdjust();
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
/* Non-focus priority is set to pause; check to see if we've lost window or input focus
|
|
* i.e. has the window been minimised or made inactive?
|
|
*/
|
|
if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) {
|
|
if ((event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (event.window.event == SDL_WINDOWEVENT_MINIMIZED)) {
|
|
/* Window has lost focus, pause the emulator.
|
|
* This is similar to what PauseDOSBox() does, but the exit criteria is different.
|
|
* Instead of waiting for the user to hit Alt-Break, we wait for the window to
|
|
* regain window or input focus.
|
|
*/
|
|
bool paused = true;
|
|
SDL_Event ev;
|
|
|
|
GFX_SetTitle(-1,-1,-1,true);
|
|
KEYBOARD_ClrBuffer();
|
|
// SDL_Delay(500);
|
|
// while (SDL_PollEvent(&ev)) {
|
|
// flush event queue.
|
|
// }
|
|
|
|
while (paused) {
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
SDL_PollEvent(&ev);
|
|
#else
|
|
// WaitEvent waits for an event rather than polling, so CPU usage drops to zero
|
|
SDL_WaitEvent(&ev);
|
|
#endif
|
|
|
|
switch (ev.type) {
|
|
case SDL_QUIT:
|
|
if (CheckQuit()) throw(0);
|
|
break; // a bit redundant at linux at least as the active events gets before the quit event.
|
|
case SDL_WINDOWEVENT: // wait until we get window focus back
|
|
if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (ev.window.event == SDL_WINDOWEVENT_MINIMIZED) || (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) {
|
|
// We've got focus back, so unpause and break out of the loop
|
|
if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) {
|
|
paused = false;
|
|
GFX_SetTitle(-1,-1,-1,false);
|
|
}
|
|
|
|
/* Now poke a "release ALT" command into the keyboard buffer
|
|
* we have to do this, otherwise ALT will 'stick' and cause
|
|
* problems with the app running in the DOSBox-X.
|
|
*/
|
|
KEYBOARD_AddKey(KBD_leftalt, false);
|
|
KEYBOARD_AddKey(KBD_rightalt, false);
|
|
if (ev.window.event == SDL_WINDOWEVENT_RESTORED) {
|
|
// We may need to re-create a texture and more
|
|
GFX_ResetScreen();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SDL_MOUSEMOTION:
|
|
#if defined(C_SDL2)
|
|
if (touchscreen_finger_lock == no_finger_id &&
|
|
touchscreen_touch_lock == no_touch_id &&
|
|
event.motion.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
|
|
HandleMouseMotion(&event.motion);
|
|
}
|
|
#else
|
|
HandleMouseMotion(&event.motion);
|
|
#endif
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
#if defined(C_SDL2)
|
|
if (touchscreen_finger_lock == no_finger_id &&
|
|
touchscreen_touch_lock == no_touch_id &&
|
|
event.button.which != SDL_TOUCH_MOUSEID) { /* don't handle mouse events faked by touchscreen */
|
|
HandleMouseButton(&event.button,&event.motion);
|
|
}
|
|
#else
|
|
HandleMouseButton(&event.button,&event.motion);
|
|
#endif
|
|
break;
|
|
#if !defined(IGNORE_TOUCHSCREEN)
|
|
case SDL_FINGERDOWN:
|
|
case SDL_FINGERUP:
|
|
case SDL_FINGERMOTION:
|
|
HandleTouchscreenFinger(&event.tfinger);
|
|
break;
|
|
#endif
|
|
case SDL_QUIT:
|
|
if (CheckQuit()) throw(0);
|
|
break;
|
|
case SDL_MOUSEWHEEL:
|
|
if (wheel_key && (wheel_guest || !dos_kernel_disabled)) {
|
|
if(event.wheel.y > 0) {
|
|
#if defined (WIN32) && !defined(HX_DOS)
|
|
if (wheel_key<4) {
|
|
INPUT ip = {0};
|
|
ip.type = INPUT_KEYBOARD;
|
|
ip.ki.wScan = wheel_key==2?75:(wheel_key==3?73:72);
|
|
ip.ki.time = 0;
|
|
ip.ki.dwExtraInfo = 0;
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
} else
|
|
#endif
|
|
if (wheel_key==7) {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(KBD_w, true);
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(KBD_w, false);
|
|
} else {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_left:(wheel_key==3||wheel_key==6?KBD_pageup:KBD_up), true);
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_left:(wheel_key==3||wheel_key==6?KBD_pageup:KBD_up), false);
|
|
}
|
|
} else if(event.wheel.y < 0) {
|
|
#if defined (WIN32) && !defined(HX_DOS)
|
|
if (wheel_key<4) {
|
|
INPUT ip = {0};
|
|
ip.type = INPUT_KEYBOARD;
|
|
ip.ki.wScan = wheel_key==2?77:(wheel_key==3?81:80);
|
|
ip.ki.time = 0;
|
|
ip.ki.dwExtraInfo = 0;
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
|
|
SendInput(1, &ip, sizeof(INPUT));
|
|
} else
|
|
#endif
|
|
if (wheel_key==7) {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(KBD_z, true);
|
|
if (ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(KBD_z, false);
|
|
} else {
|
|
bool ctrlup = sdl.lctrlstate==SDL_KEYUP && sdl.rctrlstate==SDL_KEYUP;
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, true);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_right:(wheel_key==3||wheel_key==6?KBD_pagedown:KBD_down), true);
|
|
if (wheel_key >= 4 && wheel_key <= 6 && ctrlup) KEYBOARD_AddKey(KBD_leftctrl, false);
|
|
KEYBOARD_AddKey(wheel_key==2||wheel_key==5?KBD_right:(wheel_key==3||wheel_key==6?KBD_pagedown:KBD_down), false);
|
|
}
|
|
}
|
|
} else
|
|
HandleMouseWheel(event.wheel.direction == SDL_MOUSEWHEEL_NORMAL, event.wheel.y);
|
|
break;
|
|
#if defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11
|
|
case SDL_TEXTEDITING:
|
|
ime_text = event.edit.text;
|
|
break;
|
|
case SDL_TEXTINPUT:
|
|
{
|
|
int len = strlen(event.text.text);
|
|
if(ime_text == event.text.text || len > 1) {
|
|
uint8_t* buff;
|
|
if((buff = (uint8_t *)malloc(len * 2)) != NULL) {
|
|
if(CodePageHostToGuestUTF8((char *)buff, event.text.text)) {
|
|
for(int no = 0 ; buff[no] != 0 ; no++) {
|
|
if (IS_PC98_ARCH || isDBCSCP()) {
|
|
if(dos.loaded_codepage == 932 && isKanji1(buff[no]) && isKanji2(buff[no + 1])) {
|
|
BIOS_AddKeyToBuffer(0xf100 | buff[no++]);
|
|
BIOS_AddKeyToBuffer(0xf000 | buff[no]);
|
|
} else {
|
|
BIOS_AddKeyToBuffer(buff[no]);
|
|
}
|
|
} else {
|
|
BIOS_AddKeyToBuffer(buff[no]);
|
|
}
|
|
}
|
|
}
|
|
free(buff);
|
|
}
|
|
SetIMPosition();
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP:
|
|
#if defined (WIN32) || defined(MACOSX) || defined(C_SDL2)
|
|
if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_LCTRL) sdl.lctrlstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_RCTRL) sdl.rctrlstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_LSHIFT) sdl.lshiftstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_RSHIFT) sdl.rshiftstate = event.key.type;
|
|
if (event.type == SDL_KEYDOWN && isModifierApplied())
|
|
ClipKeySelect(event.key.keysym.sym);
|
|
if(dos.im_enable_flag) {
|
|
#if defined (WIN32) && !defined(HX_DOS)
|
|
if(event.type == SDL_KEYDOWN && IME_GetEnable()) {
|
|
// Enter, BS, TAB, <-, ->
|
|
if(event.key.keysym.sym == 0x0d || event.key.keysym.sym == 0x08 || event.key.keysym.sym == 0x09 || event.key.keysym.scancode == 0x4f || event.key.keysym.scancode == 0x50) {
|
|
if(ime_text.size() != 0) {
|
|
break;
|
|
}
|
|
} else if((event.key.keysym.mod & 0x03) == 0 && event.key.keysym.scancode == 0x2c && ime_text.size() == 0 && dos.loaded_codepage == 932) {
|
|
// Zenkaku space
|
|
BIOS_AddKeyToBuffer(0xf100 | 0x81);
|
|
BIOS_AddKeyToBuffer(0xf000 | 0x40);
|
|
break;
|
|
} else if(!CheckEnableImmOnKey(event.key))
|
|
break;
|
|
}
|
|
#endif
|
|
// Hankaku/Zenkaku
|
|
if(event.key.keysym.scancode == 0x35) {
|
|
MAPPER_CheckKeyboardLayout();
|
|
if (isJPkeyboard) break;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined (MACOSX)
|
|
/* On macs CMD-Q is the default key to close an application */
|
|
if (event.key.keysym.sym == SDLK_q &&
|
|
(event.key.keysym.mod == KMOD_RGUI ||
|
|
event.key.keysym.mod == KMOD_LGUI)
|
|
) {
|
|
KillSwitch(true);
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
gfx_in_mapper = true;
|
|
if (ticksLocked && event.type == SDL_KEYDOWN && static_cast<Section_prop *>(control->GetSection("cpu"))->Get_bool("stop turbo on key")) DOSBOX_UnlockSpeed2(true);
|
|
MAPPER_CheckEvent(&event);
|
|
gfx_in_mapper = false;
|
|
}
|
|
}
|
|
#else /* SDL 1.x---------------------------------- */
|
|
SDL_Event event;
|
|
#if defined (REDUCE_JOYSTICK_POLLING)
|
|
static uint32_t poll_delay=0;
|
|
uint32_t time=GetTicks();
|
|
if ((int32_t)(time-poll_delay)>20) {
|
|
poll_delay=time;
|
|
if (sdl.num_joysticks>0)
|
|
{
|
|
SDL_JoystickUpdate();
|
|
MAPPER_UpdateJoysticks();
|
|
}
|
|
}
|
|
#endif
|
|
#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && (defined(C_SDL2) || defined(SDL_DOSBOX_X_SPECIAL))
|
|
if(IS_PC98_ARCH) {
|
|
static uint32_t poll98_delay = 0;
|
|
uint32_t time = GetTicks();
|
|
if((int32_t)(time - poll98_delay) > 50) {
|
|
poll98_delay = time;
|
|
SetIMPosition();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
GFX_EventsMouse();
|
|
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
#endif
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
/* DOSBox SVN revision 4176:4177: For Linux/X11, Xorg 1.20.1
|
|
* will make spurious focus gain and loss events when locking the mouse in windowed mode.
|
|
*
|
|
* This has not been tested with DOSBox-X yet because I do not run Xorg 1.20.1, yet */
|
|
#if SDL_XORG_FIX
|
|
// Special code for broken SDL with Xorg 1.20.1, where pairs of inputfocus gain and loss events are generated
|
|
// when locking the mouse in windowed mode.
|
|
if (event.type == SDL_ACTIVEEVENT && event.active.state == SDL_APPINPUTFOCUS && event.active.gain == 0) {
|
|
SDL_Event test; //Check if the next event would undo this one.
|
|
if (SDL_PeepEvents(&test,1,SDL_PEEKEVENT,SDL_ACTIVEEVENTMASK) == 1 && test.active.state == SDL_APPINPUTFOCUS && test.active.gain == 1) {
|
|
// Skip both events.
|
|
SDL_PeepEvents(&test,1,SDL_GETEVENT,SDL_ACTIVEEVENTMASK);
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
/* end patch fragment */
|
|
|
|
switch (event.type) {
|
|
#ifdef __WIN32__
|
|
case SDL_SYSWMEVENT : {
|
|
switch( event.syswm.msg->msg ) {
|
|
#if !defined(HX_DOS)
|
|
case WM_COMMAND:
|
|
MSG_WM_COMMAND_handle(/*&*/(*event.syswm.msg));
|
|
break;
|
|
#endif
|
|
case WM_SYSCOMMAND:
|
|
switch (event.syswm.msg->wParam) {
|
|
case 0xF032: // FIXME: What is this?
|
|
case SC_MAXIMIZE:
|
|
userResizeWindowWidth = 0;
|
|
userResizeWindowHeight = 0;
|
|
menu.maxwindow = true;
|
|
break;
|
|
case 0xF122: // FIXME: What is this?
|
|
case SC_RESTORE:
|
|
if (sdl.desktop.fullscreen)
|
|
GFX_SwitchFullScreen();
|
|
menu.maxwindow = false;
|
|
UpdateWindowDimensions();
|
|
RENDER_Reset();
|
|
if (OpenGL_using()) {
|
|
UpdateWindowDimensions();
|
|
RENDER_Reset();
|
|
}
|
|
break;
|
|
case ID_WIN_SYSMENU_RESTOREMENU:
|
|
/* prevent removing the menu in 3Dfx mode */
|
|
if (!GFX_GetPreventFullscreen()) {
|
|
DOSBox_SetMenu();
|
|
mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
|
|
}
|
|
break;
|
|
case ID_WIN_SYSMENU_TOGGLEMENU:
|
|
/* prevent removing the menu in 3Dfx mode */
|
|
if (!GFX_GetPreventFullscreen())
|
|
{
|
|
if (menu.toggle) DOSBox_NoMenu(); else DOSBox_SetMenu();
|
|
mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
|
|
#if defined(USE_TTF) && DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
int last = 0;
|
|
while (TTF_using() && !sdl.desktop.fullscreen && menu_gui && menu.toggle && menuwidth_atleast(ttf.cols*ttf.width+ttf.offX*2+GetSystemMetrics(SM_CXBORDER)*2)>0 && ttf.pointsize>last) {
|
|
last = ttf.pointsize;
|
|
increaseFontSize();
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
#if !defined(HX_DOS)
|
|
case ID_WIN_SYSMENU_MAPPER:
|
|
extern void MAPPER_Run(bool pressed);
|
|
MAPPER_Run(false);
|
|
break;
|
|
case ID_WIN_SYSMENU_CFG_GUI:
|
|
extern void GUI_Run(bool pressed);
|
|
GUI_Run(false);
|
|
break;
|
|
case ID_WIN_SYSMENU_PAUSE:
|
|
void PauseDOSBox(bool pressed);
|
|
PauseDOSBox(true);
|
|
break;
|
|
case ID_WIN_SYSMENU_RESETSIZE:
|
|
void GUI_ResetResize(bool pressed);
|
|
GUI_ResetResize(true);
|
|
break;
|
|
#if defined(USE_TTF)
|
|
case ID_WIN_SYSMENU_TTFINCSIZE:
|
|
increaseFontSize();
|
|
break;
|
|
case ID_WIN_SYSMENU_TTFDECSIZE:
|
|
decreaseFontSize();
|
|
break;
|
|
#endif
|
|
#endif
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
case SDL_ACTIVEEVENT:
|
|
if (event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) {
|
|
if (event.active.gain) {
|
|
#ifdef WIN32
|
|
if (!sdl.desktop.fullscreen) sdl.focus_ticks = GetTicks();
|
|
#endif
|
|
if (sdl.desktop.fullscreen && !sdl.mouse.locked)
|
|
GFX_CaptureMouse();
|
|
SetPriority(sdl.priority.focus);
|
|
CPU_Disable_SkipAutoAdjust();
|
|
if (strcmp(RunningProgram, "LOADLIN")) {
|
|
BIOS_SynchronizeNumLock();
|
|
BIOS_SynchronizeCapsLock();
|
|
BIOS_SynchronizeScrollLock();
|
|
}
|
|
} else {
|
|
if (sdl.mouse.locked)
|
|
{
|
|
CaptureMouseNotify();
|
|
GFX_CaptureMouse();
|
|
}
|
|
|
|
#if defined(WIN32)
|
|
if (sdl.desktop.fullscreen)
|
|
GFX_ForceFullscreenExit();
|
|
#endif
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
#endif
|
|
|
|
SetPriority(sdl.priority.nofocus);
|
|
GFX_LosingFocus();
|
|
|
|
if( sdl.priority.nofocus != PRIORITY_LEVEL_PAUSE ) {
|
|
CPU_Enable_SkipAutoAdjust();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
if (event.active.state & SDL_APPMOUSEFOCUS) {
|
|
if (!(event.active.gain & SDL_APPMOUSEFOCUS)) {
|
|
/* losing focus or moving the mouse outside the window should un-hilight the currently selected menu item */
|
|
void GFX_SDLMenuTrackHover(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
void GFX_SDLMenuTrackHilight(DOSBoxMenu &menu,DOSBoxMenu::item_handle_t item_id);
|
|
|
|
GFX_SDLMenuTrackHover(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
GFX_SDLMenuTrackHilight(mainMenu,DOSBoxMenu::unassigned_item_handle);
|
|
|
|
}
|
|
}
|
|
|
|
GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
|
|
#endif
|
|
|
|
/* Non-focus priority is set to pause; check to see if we've lost window or input focus
|
|
* i.e. has the window been minimised or made inactive?
|
|
*/
|
|
if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) {
|
|
if ((event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) && (!event.active.gain)) {
|
|
/* Window has lost focus, pause the emulator.
|
|
* This is similar to what PauseDOSBox() does, but the exit criteria is different.
|
|
* Instead of waiting for the user to hit Alt-Break, we wait for the window to
|
|
* regain window or input focus.
|
|
*/
|
|
bool paused = true;
|
|
SDL_Event ev;
|
|
|
|
GFX_SetTitle(-1,-1,-1,true);
|
|
KEYBOARD_ClrBuffer();
|
|
// SDL_Delay(500);
|
|
// while (SDL_PollEvent(&ev)) {
|
|
// flush event queue.
|
|
// }
|
|
|
|
while (paused) {
|
|
#if C_EMSCRIPTEN
|
|
emscripten_sleep(0);
|
|
SDL_PollEvent(&ev);
|
|
#else
|
|
// WaitEvent waits for an event rather than polling, so CPU usage drops to zero
|
|
SDL_WaitEvent(&ev);
|
|
#endif
|
|
|
|
switch (ev.type) {
|
|
case SDL_QUIT: if (CheckQuit()) throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event.
|
|
case SDL_ACTIVEEVENT: // wait until we get window focus back
|
|
if (ev.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) {
|
|
// We've got focus back, so unpause and break out of the loop
|
|
if (ev.active.gain) {
|
|
paused = false;
|
|
GFX_SetTitle(-1,-1,-1,false);
|
|
}
|
|
|
|
/* Now poke a "release ALT" command into the keyboard buffer
|
|
* we have to do this, otherwise ALT will 'stick' and cause
|
|
* problems with the app running in the DOSBox-X.
|
|
*/
|
|
KEYBOARD_AddKey(KBD_leftalt, false);
|
|
KEYBOARD_AddKey(KBD_rightalt, false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SDL_MOUSEMOTION:
|
|
HandleMouseMotion(&event.motion);
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
HandleMouseButton(&event.button,&event.motion);
|
|
break;
|
|
case SDL_VIDEORESIZE:
|
|
UpdateWindowDimensions(); // FIXME: Use SDL window dimensions, except that on Windows, SDL won't tell us our actual dimensions
|
|
HandleVideoResize(&event.resize);
|
|
break;
|
|
case SDL_QUIT:
|
|
if (CheckQuit()) throw(0);
|
|
break;
|
|
case SDL_VIDEOEXPOSE:
|
|
if (sdl.draw.callback && !glide.enabled) sdl.draw.callback( GFX_CallBackRedraw );
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP:
|
|
// ignore event alt+tab
|
|
if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_LCTRL) sdl.lctrlstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_RCTRL) sdl.rctrlstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_LSHIFT) sdl.lshiftstate = event.key.type;
|
|
if (event.key.keysym.sym==SDLK_RSHIFT) sdl.rshiftstate = event.key.type;
|
|
#if defined(LINUX) && C_X11
|
|
if (event.type == SDL_KEYDOWN) {
|
|
if((IS_PC98_ARCH || isDBCSCP()) && event.key.keysym.unicode != 0) {
|
|
SetIMPosition();
|
|
char chars[10];
|
|
uint16_t uname[2];
|
|
uname[0]=event.key.keysym.unicode;
|
|
uname[1]=0;
|
|
if (CodePageHostToGuestUTF16(chars, uname)) {
|
|
for (size_t i=0; i<strlen(chars); i++) {
|
|
if (dos.loaded_codepage == 932 && strlen(chars) == 2 && isKanji1(chars[0]))
|
|
BIOS_AddKeyToBuffer((i==0?0xf100:0xf000) | (unsigned char)chars[i]);
|
|
else
|
|
BIOS_AddKeyToBuffer((unsigned char)chars[i]);
|
|
}
|
|
} else
|
|
BIOS_AddKeyToBuffer((unsigned char)uname[0]);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(WIN32)
|
|
if (event.type == SDL_KEYDOWN && isModifierApplied())
|
|
ClipKeySelect(event.key.keysym.sym);
|
|
if (((event.key.keysym.sym==SDLK_TAB)) &&
|
|
((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) { MAPPER_LosingFocus(); break; }
|
|
// This can happen as well.
|
|
if (((event.key.keysym.sym == SDLK_TAB )) && (event.key.keysym.mod & KMOD_ALT)) break;
|
|
// ignore tab events that arrive just after regaining focus. (likely the result of alt-tab)
|
|
if ((event.key.keysym.sym == SDLK_TAB) && (GetTicks() - sdl.focus_ticks < 2)) break;
|
|
#if !defined(HX_DOS) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
int onoff;
|
|
if(SDL_GetIMValues(SDL_IM_ONOFF, &onoff, NULL) == NULL) {
|
|
if(onoff != 0 && event.type == SDL_KEYDOWN) {
|
|
if(event.key.keysym.sym == 0x0d) {
|
|
if(sdl.ime_ticks != 0) {
|
|
if(GetTicks() - sdl.ime_ticks < 10) {
|
|
sdl.ime_ticks = 0;
|
|
break;
|
|
}
|
|
}
|
|
} else if(!CheckEnableImmOnKey(event.key)) {
|
|
sdl.ime_ticks = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
sdl.ime_ticks = 0;
|
|
if(event.key.keysym.scancode == 0 && event.key.keysym.sym == 0) {
|
|
int len;
|
|
char chars[10];
|
|
if(len = SDL_FlushIMString(NULL)) {
|
|
uint16_t *buff = (uint16_t *)malloc((len + 1)*sizeof(uint16_t)), uname[2];
|
|
SDL_FlushIMString(buff);
|
|
SetIMPosition();
|
|
for(int no = 0 ; no < len ; no++) {
|
|
unsigned char ch = (unsigned char)buff[no];
|
|
if ((IS_PC98_ARCH || isDBCSCP()) && ch != buff[no]) {
|
|
uname[0]=buff[no];
|
|
uname[1]=0;
|
|
if (CodePageHostToGuestUTF16(chars, uname)) {
|
|
for (size_t i=0; i<strlen(chars); i++) {
|
|
if (dos.loaded_codepage == 932 && strlen(chars) == 2 && isKanji1(chars[0]))
|
|
BIOS_AddKeyToBuffer((i==0?0xf100:0xf000) | (unsigned char)chars[i]);
|
|
else
|
|
BIOS_AddKeyToBuffer((unsigned char)chars[i]);
|
|
}
|
|
} else
|
|
BIOS_AddKeyToBuffer(ch);
|
|
} else
|
|
BIOS_AddKeyToBuffer(ch);
|
|
}
|
|
free(buff);
|
|
sdl.ime_ticks = GetTicks();
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#if defined (MACOSX)
|
|
if (event.type == SDL_KEYDOWN && isModifierApplied())
|
|
ClipKeySelect(event.key.keysym.sym);
|
|
/* On macs CMD-Q is the default key to close an application */
|
|
if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
|
|
KillSwitch(true);
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
#if defined(WIN32) && !defined(HX_DOS) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
if(dos.im_enable_flag) {
|
|
if(event.key.keysym.scancode == 0x94) {
|
|
break;
|
|
} else if(event.key.keysym.scancode == 0x29) {
|
|
MAPPER_CheckKeyboardLayout();
|
|
if (isJPkeyboard) break;
|
|
} else if(event.key.keysym.scancode == 0x70) {
|
|
event.type = SDL_KEYDOWN;
|
|
}
|
|
}
|
|
#endif
|
|
if (ticksLocked && event.type == SDL_KEYDOWN && static_cast<Section_prop *>(control->GetSection("cpu"))->Get_bool("stop turbo on key")) DOSBOX_UnlockSpeed2(true);
|
|
MAPPER_CheckEvent(&event);
|
|
}
|
|
}
|
|
#endif
|
|
// start emendelson from dbDOS; improved by Wengier
|
|
// Disabled multiple characters per dispatch b/c occasionally
|
|
// keystrokes get lost in the spew. (Prob b/c of DI usage on Win32, sadly..)
|
|
// while (PasteClipboardNext());
|
|
// Doesn't really matter though, it's fast enough as it is...
|
|
if (paste_speed < 0) paste_speed = 30;
|
|
|
|
static Bitu iPasteTicker = 0;
|
|
if (paste_speed && (iPasteTicker++ % paste_speed) == 0) // emendelson: was 20 - good for WP51; Wengier: changed to 30 for better compatibility
|
|
PasteClipboardNext(); // end added emendelson from dbDOS; improved by Wengier
|
|
}
|
|
|
|
void Null_Init(Section *sec);
|
|
|
|
void SDL_SetupConfigSection() {
|
|
Section_prop * sdl_sec=control->AddSection_prop("sdl",&Null_Init);
|
|
|
|
Prop_bool* Pbool;
|
|
Prop_string* Pstring;
|
|
Prop_int* Pint;
|
|
Prop_multival* Pmulti;
|
|
|
|
Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false);
|
|
Pbool->Set_help("Start DOSBox-X directly in fullscreen. (Press [F11/F12]+F to go back)");
|
|
Pbool->SetBasic(true);
|
|
|
|
Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false);
|
|
Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox-X.");
|
|
Pbool->SetBasic(true);
|
|
|
|
//Pbool = sdl_sec->Add_bool("sdlresize",Property::Changeable::Always,false);
|
|
//Pbool->Set_help("Makes window resizable (depends on scalers)");
|
|
|
|
Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"desktop");
|
|
Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n"
|
|
" Using your monitor's native resolution with aspect=true might give the best results.\n"
|
|
" If you end up with small window on a large screen, try an output different from surface.");
|
|
Pstring->SetBasic(true);
|
|
|
|
Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original");
|
|
Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n"
|
|
" (output=surface does not!)");
|
|
Pstring->SetBasic(true);
|
|
|
|
Pstring = sdl_sec->Add_string("windowposition", Property::Changeable::Always, "-");
|
|
Pstring->Set_help("Set the window position at startup in the positionX,positionY format (e.g.: 1300,200).\n"
|
|
"The window will be centered with \",\" (or empty), and will be in the original position with \"-\".");
|
|
Pstring->SetBasic(true);
|
|
|
|
const char* outputs[] = {
|
|
"default", "surface", "overlay", "ttf",
|
|
#if C_OPENGL
|
|
"opengl", "openglnb", "openglhq", "openglpp",
|
|
#endif
|
|
"ddraw", "direct3d",
|
|
0 };
|
|
|
|
Pint = sdl_sec->Add_int("display", Property::Changeable::Always, 0);
|
|
Pint->Set_help("Specify a screen/display number to use for a multi-screen setup (0 = default).");
|
|
Pint->SetBasic(true);
|
|
|
|
Pstring = sdl_sec->Add_string("output", Property::Changeable::Always, "default");
|
|
Pstring->Set_help("What video system to use for output (openglnb = OpenGL nearest; openglpp = OpenGL perfect; ttf = TrueType font output).");
|
|
Pstring->Set_values(outputs);
|
|
Pstring->SetBasic(true);
|
|
|
|
Pstring = sdl_sec->Add_string("videodriver",Property::Changeable::OnlyAtStart, "");
|
|
Pstring->Set_help("Forces a video driver (e.g. windib/windows, directx, x11, fbcon, dummy, etc) for the SDL library to use.");
|
|
Pstring->SetBasic(true);
|
|
|
|
Pint = sdl_sec->Add_int("transparency", Property::Changeable::WhenIdle, 0);
|
|
Pint->Set_help("Set the transparency of the DOSBox-X screen (both windowed and full-screen modes, on SDL2 and Windows SDL1 builds).\n"
|
|
"The valid value is from 0 (no transparency, the default setting) to 90 (high transparency).");
|
|
Pint->SetMinMax(0,90);
|
|
Pint->SetBasic(true);
|
|
|
|
Pbool = sdl_sec->Add_bool("maximize",Property::Changeable::OnlyAtStart,false);
|
|
Pbool->Set_help("If set, the DOSBox-X window will be maximized at start (SDL2 and Windows SDL1 builds only; use fullscreen for TTF output).");
|
|
Pbool->SetBasic(true);
|
|
|
|
Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::WhenIdle, false);
|
|
Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)");
|
|
Pbool->SetBasic(true);
|
|
|
|
const char* feeds[] = { "none", "beep", "flash", nullptr};
|
|
Pstring = sdl_sec->Add_string("autolock_feedback", Property::Changeable::Always, feeds[1]);
|
|
Pstring->Set_help("Autolock status feedback type, i.e. visual, auditive, none.");
|
|
Pstring->Set_values(feeds);
|
|
Pstring->SetBasic(true);
|
|
|
|
const char* unlocks[] = { "none", "manual", "auto", "both", nullptr};
|
|
Pstring = sdl_sec->Add_string("middle_unlock",Property::Changeable::Always, unlocks[1]);
|
|
Pstring->Set_help("Whether you can press the middle mouse button to unlock the mouse when the mouse has been locked.\n"
|
|
"If set to \"manual\", it works only with \"autolock=false\"; if set to \"auto\", it works only with \"autolock=true\".");
|
|
Pstring->Set_values(unlocks);
|
|
Pstring->SetBasic(true);
|
|
|
|
const char* clipboardbutton[] = { "none", "middle", "right", "arrows", 0};
|
|
Pstring = sdl_sec->Add_string("clip_mouse_button",Property::Changeable::Always, "right");
|
|
Pstring->Set_values(clipboardbutton);
|
|
Pstring->Set_help("Select the mouse button or use arrow keys for the shared clipboard copy/paste function.\n"
|
|
"The default mouse button is \"right\", which means using the right mouse button to select text, copy to and paste from the host clipboard.\n"
|
|
"Set to \"middle\" to use the middle mouse button, \"arrows\" to use arrow keys instead of a mouse button, or \"none\" to disable this feature.\n"
|
|
"For \"arrows\", press Home key (or Fn+Shift+Left on Mac laptops) to start selection, and End key (or Fn+Shift+Right on Mac laptops) to end selection.");
|
|
Pstring->SetBasic(true);
|
|
|
|
const char* clipboardmodifier[] = { "none", "ctrl", "lctrl", "rctrl", "alt", "lalt", "ralt", "shift", "lshift", "rshift", "ctrlalt", "ctrlshift", "altshift", "lctrlalt", "lctrlshift", "laltshift", "rctrlalt", "rctrlshift", "raltshift", 0};
|
|
Pstring = sdl_sec->Add_string("clip_key_modifier",Property::Changeable::Always, "shift");
|
|
Pstring->Set_values(clipboardmodifier);
|
|
Pstring->Set_help("Change the keyboard modifier for the shared clipboard copy/paste function using a mouse button or arrow keys.\n"
|
|
"The default modifier is \"shift\" (both left and right shift keys). Set to \"none\" if no modifier is desired.");
|
|
Pstring->SetBasic(true);
|
|
|
|
const char* truefalsedefaultopt[] = { "true", "false", "1", "0", "default", 0};
|
|
Pstring = sdl_sec->Add_string("clip_paste_bios",Property::Changeable::WhenIdle, "default");
|
|
Pstring->Set_values(truefalsedefaultopt);
|
|
Pstring->Set_help("Specify whether to use BIOS keyboard functions for the clipboard pasting instead of the keystroke method.\n"
|
|
"For pasting clipboard text into Windows 3.x/9x applications (e.g. Notepad), make sure to use the keystroke method.");
|
|
Pstring->SetBasic(true);
|
|
|
|
Pint = sdl_sec->Add_int("clip_paste_speed", Property::Changeable::WhenIdle, 30);
|
|
Pint->Set_help("Set keyboard speed for pasting text from the shared clipboard.\n"
|
|
"If the default setting of 30 causes lost keystrokes, increase the number.\n"
|
|
"Or experiment with decreasing the number for applications that accept keystrokes quickly.");
|
|
Pint->SetBasic(true);
|
|
|
|
Pmulti = sdl_sec->Add_multi("sensitivity",Property::Changeable::Always, ",");
|
|
Pmulti->Set_help("Mouse sensitivity. The optional second parameter specifies vertical sensitivity (e.g. 100,-50).");
|
|
Pmulti->SetValue("100");
|
|
Pmulti->SetBasic(true);
|
|
Pint = Pmulti->GetSection()->Add_int("xsens",Property::Changeable::Always,100);
|
|
Pint->SetMinMax(-1000,1000);
|
|
Pint = Pmulti->GetSection()->Add_int("ysens",Property::Changeable::Always,100);
|
|
Pint->SetMinMax(-1000,1000);
|
|
|
|
#if defined(C_SDL2)
|
|
Pbool = sdl_sec->Add_bool("raw_mouse_input", Property::Changeable::OnlyAtStart, false);
|
|
Pbool->Set_help("Enable this setting to bypass your operating system's mouse acceleration and sensitivity settings.\n"
|
|
"This works in fullscreen or when the mouse is captured in window mode (SDL2 builds only).");
|
|
#endif
|
|
|
|
Pbool = sdl_sec->Add_bool("usesystemcursor",Property::Changeable::OnlyAtStart,false);
|
|
Pbool->Set_help("Use the mouse cursor of the host system instead of drawing a DOS mouse cursor. Activated when the mouse is not locked.");
|
|
Pbool->SetBasic(true);
|
|
|
|
const char * emulation[] = {"integration", "locked", "always", "never", nullptr};
|
|
Pstring = sdl_sec->Add_string("mouse_emulation", Property::Changeable::Always, emulation[1]);
|
|
Pstring->Set_help(
|
|
"When is mouse emulated ?\n"
|
|
"integration: when not locked\n"
|
|
"locked: when locked\n"
|
|
"always: every time\n"
|
|
"never: at no time\n"
|
|
"If disabled, the mouse position in DOSBox-X is exactly where the host OS reports it.\n"
|
|
"When using a high DPI mouse, the emulation of mouse movement can noticeably reduce the\n"
|
|
"sensitiveness of your device, i.e. the mouse is slower but more precise.");
|
|
Pstring->Set_values(emulation);
|
|
Pstring->SetBasic(true);
|
|
|
|
Pint = sdl_sec->Add_int("mouse_wheel_key", Property::Changeable::WhenIdle, -1);
|
|
Pint->SetMinMax(-7,7);
|
|
Pint->Set_help("Convert mouse wheel movements into keyboard presses such as arrow keys.\n"
|
|
"0: disabled; 1: up/down arrows; 2: left/right arrows; 3: PgUp/PgDn keys.\n"
|
|
"4: Ctrl+up/down arrows; 5: Ctrl+left/right arrows; 6: Ctrl+PgUp/PgDn keys.\n"
|
|
"7: Ctrl+W/Z, as supported by text editors like WordStar and MS-DOS EDIT.\n"
|
|
"Putting a minus sign in front will disable the conversion for guest systems.");
|
|
Pint->SetBasic(true);
|
|
|
|
Pbool = sdl_sec->Add_bool("waitonerror",Property::Changeable::Always, true);
|
|
Pbool->Set_help("Wait before closing the console if DOSBox-X has an error.");
|
|
Pbool->SetBasic(true);
|
|
|
|
Pmulti = sdl_sec->Add_multi("priority", Property::Changeable::Always, ",");
|
|
Pmulti->SetValue("higher,normal",/*init*/true);
|
|
Pmulti->Set_help("Priority levels for DOSBox-X. Second entry behind the comma is for when DOSBox-X is not focused/minimized.\n"
|
|
" pause is only valid for the second entry.");
|
|
Pmulti->SetBasic(true);
|
|
|
|
const char* actt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0};
|
|
Pstring = Pmulti->GetSection()->Add_string("active",Property::Changeable::Always,"higher");
|
|
Pstring->Set_values(actt);
|
|
|
|
const char* inactt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0};
|
|
Pstring = Pmulti->GetSection()->Add_string("inactive",Property::Changeable::Always,"normal");
|
|
Pstring->Set_values(inactt);
|
|
|
|
Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE);
|
|
Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the default value.");
|
|
Pstring->SetBasic(true);
|
|
|
|
Pstring = sdl_sec->Add_path("mapperfile_sdl1",Property::Changeable::Always,"");
|
|
Pstring->Set_help("File used to load/save the key/event mappings from DOSBox-X SDL1 builds. If set it will override \"mapperfile\" for SDL1 builds.");
|
|
|
|
Pstring = sdl_sec->Add_path("mapperfile_sdl2",Property::Changeable::Always,"");
|
|
Pstring->Set_help("File used to load/save the key/event mappings from DOSBox-X SDL2 builds. If set it will override \"mapperfile\" for SDL2 builds.");
|
|
|
|
Pbool = sdl_sec->Add_bool("forcesquarecorner", Property::Changeable::OnlyAtStart, true);
|
|
Pbool->Set_help("If set, DOSBox-X will force square corners (instead of round corners) for the DOSBox-X window when running in Windows 11.");
|
|
|
|
const char* truefalseautoopt[] = { "true", "false", "1", "0", "auto", 0};
|
|
Pstring = sdl_sec->Add_string("usescancodes",Property::Changeable::OnlyAtStart,"auto");
|
|
Pstring->Set_values(truefalseautoopt);
|
|
Pstring->Set_help("Avoid usage of symkeys, in favor of scancodes. Might not work on all operating systems.\n"
|
|
"If set to \"auto\" (default), it is enabled when using non-US keyboards in SDL1 builds.");
|
|
Pstring->SetBasic(true);
|
|
|
|
Pint = sdl_sec->Add_int("overscan",Property::Changeable::Always, 0);
|
|
Pint->SetMinMax(0,10);
|
|
Pint->Set_help("Width of the overscan border (0 to 10) for the \"surface\" output.");
|
|
Pint->SetBasic(true);
|
|
|
|
Pstring = sdl_sec->Add_string("titlebar", Property::Changeable::Always, "");
|
|
Pstring->Set_help("Change the string displayed in the DOSBox-X title bar.");
|
|
Pstring->SetBasic(true);
|
|
|
|
Pbool = sdl_sec->Add_bool("showbasic", Property::Changeable::Always, true);
|
|
Pbool->Set_help("If set, DOSBox-X will show basic information including the DOSBox-X version number and current running speed in the title bar.");
|
|
Pbool->SetBasic(true);
|
|
|
|
Pbool = sdl_sec->Add_bool("showdetails", Property::Changeable::Always, false);
|
|
Pbool->Set_help("If set, DOSBox-X will show the cycles count (FPS) and emulation speed relative to realtime in the title bar.");
|
|
Pbool->SetBasic(true);
|
|
|
|
Pbool = sdl_sec->Add_bool("showmenu", Property::Changeable::Always, true);
|
|
Pbool->Set_help("Whether to show the menu bar (if supported). Default true.");
|
|
Pbool->SetBasic(true);
|
|
|
|
// Pint = sdl_sec->Add_int("overscancolor",Property::Changeable::Always, 0);
|
|
// Pint->SetMinMax(0,1000);
|
|
// Pint->Set_help("Value of overscan color.");
|
|
}
|
|
|
|
void SDL_OnSectionPropChange(Section *x) {
|
|
(void)x;//UNUSED
|
|
Section_prop * section = static_cast<Section_prop *>(control->GetSection("sdl"));
|
|
|
|
{
|
|
bool cfg_want_menu = section->Get_bool("showmenu");
|
|
|
|
/* -- -- decide whether to set menu */
|
|
if (menu_gui && !control->opt_nomenu && cfg_want_menu)
|
|
DOSBox_SetMenu();
|
|
else
|
|
DOSBox_NoMenu();
|
|
}
|
|
}
|
|
|
|
static void show_warning(char const * const message) {
|
|
bool textonly = true;
|
|
#ifdef WIN32
|
|
textonly = false;
|
|
if ( !sdl.inited && SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE) < 0 ) textonly = true;
|
|
sdl.inited = true;
|
|
#endif
|
|
LOG_MSG( "Warning: %s", message);
|
|
if(textonly) return;
|
|
#if defined(C_SDL2)
|
|
if (!sdl.window)
|
|
if (!GFX_SetSDLSurfaceWindow(640,400)) return;
|
|
sdl.surface = SDL_GetWindowSurface(sdl.window);
|
|
#else
|
|
if(!sdl.surface) sdl.surface = SDL_SetVideoMode(640,400,0,SDL_RESIZABLE);
|
|
sdl.deferred_resize = false;
|
|
sdl.must_redraw_all = true;
|
|
#endif
|
|
if(!sdl.surface) return;
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
uint32_t rmask = 0xff000000;
|
|
uint32_t gmask = 0x00ff0000;
|
|
uint32_t bmask = 0x0000ff00;
|
|
#else
|
|
uint32_t rmask = 0x000000ff;
|
|
uint32_t gmask = 0x0000ff00;
|
|
uint32_t bmask = 0x00ff0000;
|
|
#endif
|
|
SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0);
|
|
if (!splash_surf) return;
|
|
|
|
int x = 120,y = 20;
|
|
std::string m(message),m2;
|
|
std::string::size_type a,b,c,d;
|
|
|
|
while(m.size()) { //Max 50 characters. break on space before or on a newline
|
|
c = m.find('\n');
|
|
d = m.rfind(' ',50);
|
|
if(c>d) a=b=d; else a=b=c;
|
|
if( a != std::string::npos) b++;
|
|
m2 = m.substr(0,a); m.erase(0,b);
|
|
OutputString((unsigned int)x,(unsigned int)y,m2.c_str(),0xffffffffu,0,splash_surf);
|
|
y += 20;
|
|
}
|
|
|
|
SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
|
|
#if defined(C_SDL2)
|
|
SDL_UpdateWindowSurface(sdl.window);
|
|
#else
|
|
SDL_Flip(sdl.surface);
|
|
#endif
|
|
SDL_Delay(12000);
|
|
}
|
|
|
|
static void launcheditor(std::string edit) {
|
|
if (control->configfiles.size() && control->configfiles.front().size())
|
|
execlp(edit.c_str(),edit.c_str(),control->configfiles.front().c_str(),(char*) 0);
|
|
std::string path,file;
|
|
Cross::CreatePlatformConfigDir(path);
|
|
Cross::GetPlatformConfigName(file);
|
|
path += file;
|
|
FILE* f = fopen(path.c_str(),"r");
|
|
if(!f && !control->PrintConfig(path.c_str())) {
|
|
printf("tried creating %s. but failed.\n",path.c_str());
|
|
exit(1);
|
|
}
|
|
if(f) fclose(f);
|
|
|
|
execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
|
|
|
|
//if you get here the launching failed!
|
|
printf("can't find editor(s) specified at the command line.\n");
|
|
exit(1);
|
|
}
|
|
#if C_DEBUG
|
|
extern void DEBUG_ShutDown(Section * /*sec*/);
|
|
#endif
|
|
|
|
static void launchcaptures(std::string const& edit) {
|
|
std::string path,file;
|
|
struct stat cstat;
|
|
Section* t = control->GetSection("dosbox");
|
|
if(t) file = t->GetPropValue("captures");
|
|
if(!t || file == NO_SUCH_PROPERTY) {
|
|
printf("Config system messed up.\n");
|
|
exit(1);
|
|
}
|
|
path = ".";
|
|
path += CROSS_FILESPLIT;
|
|
path += file;
|
|
|
|
stat(path.c_str(),&cstat);
|
|
if(cstat.st_mode & S_IFDIR) {
|
|
execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
|
|
//if you get here the launching failed!
|
|
printf("can't find filemanager %s\n",edit.c_str());
|
|
exit(1);
|
|
} else {
|
|
path = "";
|
|
Cross::CreatePlatformConfigDir(path);
|
|
path += file;
|
|
Cross::CreateDir(path);
|
|
stat(path.c_str(),&cstat);
|
|
if((cstat.st_mode & S_IFDIR) == 0) {
|
|
printf("%s doesn't exist or isn't a directory.\n",path.c_str());
|
|
exit(1);
|
|
}
|
|
execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
|
|
//if you get here the launching failed!
|
|
printf("can't find filemanager %s\n",edit.c_str());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void launchsaves(std::string const& edit) {
|
|
std::string path,file;
|
|
struct stat cstat;
|
|
file="SAVE";
|
|
path = ".";
|
|
path += CROSS_FILESPLIT;
|
|
path += file;
|
|
stat(path.c_str(),&cstat);
|
|
if(cstat.st_mode & S_IFDIR) {
|
|
execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
|
|
//if you get here the launching failed!
|
|
printf("can't find filemanager %s\n",edit.c_str());
|
|
exit(1);
|
|
} else {
|
|
path = "";
|
|
Cross::CreatePlatformConfigDir(path);
|
|
path += file;
|
|
Cross::CreateDir(path);
|
|
stat(path.c_str(),&cstat);
|
|
if((cstat.st_mode & S_IFDIR) == 0) {
|
|
printf("%s doesn't exists or isn't a directory.\n",path.c_str());
|
|
exit(1);
|
|
}
|
|
execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0);
|
|
//if you get here the launching failed!
|
|
printf("can't find filemanager %s\n",edit.c_str());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void printconfiglocation() {
|
|
std::string path,file;
|
|
Cross::CreatePlatformConfigDir(path);
|
|
Cross::GetPlatformConfigName(file);
|
|
path += file;
|
|
|
|
FILE* f = fopen(path.c_str(),"r");
|
|
if(!f && !control->PrintConfig(path.c_str())) {
|
|
printf("tried creating %s. but failed",path.c_str());
|
|
exit(1);
|
|
}
|
|
if(f) fclose(f);
|
|
printf("%s\n",path.c_str());
|
|
exit(0);
|
|
}
|
|
|
|
static void eraseconfigfile() {
|
|
FILE* f = fopen("dosbox-x.conf","r");
|
|
if (!f) f = fopen("dosbox.conf","r");
|
|
if (f) {
|
|
fclose(f);
|
|
show_warning("Warning: dosbox-x.conf (or dosbox.conf) exists in current working directory.\nThis will override the configuration file at runtime.\n");
|
|
}
|
|
std::string path,file;
|
|
Cross::GetPlatformConfigDir(path);
|
|
Cross::GetPlatformConfigName(file);
|
|
path += file;
|
|
f = fopen(path.c_str(),"r");
|
|
if (!f) exit(0);
|
|
fclose(f);
|
|
unlink(path.c_str());
|
|
exit(0);
|
|
}
|
|
|
|
static void erasemapperfile() {
|
|
FILE* g = fopen("dosbox-x.conf","r");
|
|
if (!g) g = fopen("dosbox.conf","r");
|
|
if (g) {
|
|
fclose(g);
|
|
show_warning("Warning: dosbox-x.conf (or dosbox.conf) exists in current working directory.\nKeymapping might not be properly reset.\n"
|
|
"Please reset configuration as well and delete the dosbox-x.conf (or dosbox.conf).\n");
|
|
}
|
|
|
|
std::string path,file=MAPPERFILE;
|
|
Cross::GetPlatformConfigDir(path);
|
|
path += file;
|
|
FILE* f = fopen(path.c_str(),"r");
|
|
if (!f) exit(0);
|
|
fclose(f);
|
|
unlink(path.c_str());
|
|
exit(0);
|
|
}
|
|
|
|
void SetNumLock(void) {
|
|
#ifdef WIN32
|
|
if (control->opt_disable_numlock_check)
|
|
return;
|
|
|
|
// Simulate a key press
|
|
keybd_event(VK_NUMLOCK,0x45,KEYEVENTF_EXTENDEDKEY | 0,0);
|
|
|
|
// Simulate a key release
|
|
keybd_event(VK_NUMLOCK,0x45,KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,0);
|
|
#endif
|
|
}
|
|
|
|
void CheckNumLockState(void) {
|
|
#ifdef WIN32
|
|
BYTE keyState[256];
|
|
|
|
GetKeyboardState((LPBYTE)(&keyState));
|
|
if (keyState[VK_NUMLOCK] & 1) {
|
|
startup_state_numlock = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CheckCapsLockState(void) {
|
|
#ifdef WIN32
|
|
BYTE keyState[256];
|
|
|
|
GetKeyboardState((LPBYTE)(&keyState));
|
|
if (keyState[VK_CAPITAL] & 1) {
|
|
startup_state_capslock = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CheckScrollLockState(void) {
|
|
#ifdef WIN32
|
|
BYTE keyState[256];
|
|
|
|
GetKeyboardState((LPBYTE)(&keyState));
|
|
if (keyState[VK_SCROLL] & 1) {
|
|
startup_state_scrlock = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
extern bool log_keyboard_scan_codes;
|
|
|
|
bool showconsole_init = false;
|
|
|
|
#if C_DEBUG
|
|
bool DEBUG_IsDebuggerConsoleVisible(void);
|
|
#endif
|
|
|
|
void DOSBox_ShowConsole() {
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
COORD crd;
|
|
HWND hwnd;
|
|
|
|
#if C_DEBUG
|
|
/* if the debugger has already taken the console, do nothing */
|
|
if (DEBUG_IsDebuggerConsoleVisible())
|
|
return;
|
|
#endif
|
|
|
|
/* if WE have already opened the console, do nothing */
|
|
if (showconsole_init)
|
|
return;
|
|
|
|
/* Microsoft Windows: Allocate a console and begin spewing to it.
|
|
DOSBox-X is compiled on Windows platforms as a Win32 application, and therefore, no console. */
|
|
/* FIXME: What about "file handles" 0/1/2 emulated by C library, for use with _open/_close/_lseek/etc? */
|
|
AllocConsole();
|
|
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
|
crd = csbi.dwSize;
|
|
crd.X = 130;
|
|
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), crd);
|
|
|
|
hwnd = GetConsoleWindow();
|
|
ShowWindow(hwnd, SW_MAXIMIZE);
|
|
|
|
freopen("CONIN$", "r", stdin);
|
|
freopen("CONOUT$", "w", stdout);
|
|
freopen("CONOUT$", "w", stderr);
|
|
|
|
showconsole_init = true;
|
|
#endif
|
|
}
|
|
|
|
void DOSBox_ConsolePauseWait() {
|
|
char c;
|
|
|
|
printf("Press ENTER key to continue\n");
|
|
do {
|
|
if (fread(&c, 1, 1, stdin) != 1) break;
|
|
} while (!(c == 13 || c == 10)); /* wait for Enter key */
|
|
}
|
|
|
|
bool usecfgdir = false;
|
|
bool DOSBOX_parse_argv() {
|
|
std::string tmp,optname,localname;
|
|
|
|
assert(control != NULL);
|
|
assert(control->cmdline != NULL);
|
|
|
|
control->cmdline->ChangeOptStyle(CommandLine::either_except);
|
|
control->cmdline->BeginOpt(true/*eat argv*/);
|
|
while (control->cmdline->GetOpt(optname)) {
|
|
std::transform(optname.begin(), optname.end(), optname.begin(), ::tolower);
|
|
|
|
if (optname == "v" || optname == "ver" || optname == "version") {
|
|
DOSBox_ShowConsole();
|
|
|
|
fprintf(stderr,"\nDOSBox-X version %s %s, copyright 2011-%s The DOSBox-X Team.\n",VERSION,SDL_STRING,COPYRIGHT_END_YEAR);
|
|
fprintf(stderr,"DOSBox-X project maintainer: joncampbell123 (The Great Codeholio)\n\n");
|
|
fprintf(stderr,"DOSBox-X comes with ABSOLUTELY NO WARRANTY. This is free software,\n");
|
|
fprintf(stderr,"and you are welcome to redistribute it under certain conditions;\n");
|
|
fprintf(stderr,"please read the COPYING file thoroughly before doing so.\n\n");
|
|
|
|
#if defined(WIN32)
|
|
DOSBox_ConsolePauseWait();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
else if (optname == "?" || optname == "h" || optname == "help") {
|
|
DOSBox_ShowConsole();
|
|
|
|
fprintf(stderr,"\nDOSBox-X version %s %s, copyright 2011-%s The DOSBox-X Team.\n",VERSION,SDL_STRING,COPYRIGHT_END_YEAR);
|
|
fprintf(stderr,"DOSBox-X project maintainer: joncampbell123 (The Great Codeholio)\n\n");
|
|
fprintf(stderr,"dosbox-x [name] [options]\n\n");
|
|
fprintf(stderr,"Options can be started with either \"-\" or \"/\" (e.g. \"-help\" or \"/help\"):\n\n");
|
|
fprintf(stderr," -?, -h, -help Show this help screen\n");
|
|
fprintf(stderr," -v, -ver, -version Display DOSBox-X version information\n");
|
|
fprintf(stderr," -fs, -fullscreen Start DOSBox-X in fullscreen mode\n");
|
|
fprintf(stderr," -conf <configfile> Start DOSBox-X with the specific config file\n");
|
|
fprintf(stderr," -editconf <editor> Edit the config file with the specific editor\n");
|
|
fprintf(stderr," -userconf Create user level config file\n");
|
|
fprintf(stderr," -printconf Print config file location\n");
|
|
fprintf(stderr," -eraseconf (or -resetconf) Erase loaded config file (or user config file and exit)\n");
|
|
fprintf(stderr," -erasemapper (or -resetmapper) Erase loaded mapper file (or user mapper file and exit)\n");
|
|
fprintf(stderr," -opencaptures <param> Launch captures\n");
|
|
fprintf(stderr," -opensaves <param> Launch saves\n");
|
|
fprintf(stderr," -startui, -startgui, -starttool Start DOSBox-X with GUI configuration tool\n");
|
|
fprintf(stderr," -startmapper Start DOSBox-X with the mapper editor\n");
|
|
fprintf(stderr," -promptfolder Prompt for the working directory when DOSBox-X starts\n");
|
|
fprintf(stderr," -nopromptfolder Do not prompt for the working directory when DOSBox-X starts\n");
|
|
fprintf(stderr," -nogui Do not show GUI\n");
|
|
fprintf(stderr," -nomenu Do not show menu\n");
|
|
fprintf(stderr," -showcycles Show cycles count (FPS) in the title\n");
|
|
fprintf(stderr," -showrt Show emulation speed relative to realtime in the title\n");
|
|
fprintf(stderr," -socket <socketnum> Specify the socket number for null-modem emulation\n");
|
|
fprintf(stderr," -savedir <path> Set path for the save slots\n");
|
|
fprintf(stderr," -defaultdir <path> Set the default working path for DOSBox-X\n");
|
|
fprintf(stderr," -defaultconf Use the default config settings for DOSBox-X\n");
|
|
fprintf(stderr," -defaultmapper Use the default key mappings for DOSBox-X\n");
|
|
#if defined(WIN32)
|
|
fprintf(stderr," -disable-numlock-check Disable NumLock check (Windows version only)\n");
|
|
#endif
|
|
fprintf(stderr," -date-host-forced Force synchronization of date with host\n");
|
|
#if C_DEBUG
|
|
fprintf(stderr," -display2 <color> Enable standard & monochrome dual-screen mode with <color>\n");
|
|
#endif
|
|
fprintf(stderr," -lang (or -langcp) <message file> Use specific message file instead of language= setting\n");
|
|
fprintf(stderr," -machine <type> Start DOSBox-X with a specific machine <type>\n");
|
|
fprintf(stderr," -nodpiaware Ignore (do not signal) Windows DPI awareness\n");
|
|
fprintf(stderr," -securemode Enable secure mode (no drive mounting etc)\n");
|
|
fprintf(stderr," -prerun If [name] is given, run it before AUTOEXEC.BAT config section\n");
|
|
#if defined(WIN32) && !defined(HX_DOS) || defined(MACOSX) || defined(LINUX)
|
|
fprintf(stderr," -hostrun Enable running host program via START command and LFN support\n");
|
|
#endif
|
|
fprintf(stderr," -noconfig Do not execute CONFIG.SYS config section\n");
|
|
fprintf(stderr," -noautoexec Do not execute AUTOEXEC.BAT config section\n");
|
|
fprintf(stderr," -exit Exit after executing AUTOEXEC.BAT\n");
|
|
fprintf(stderr," -silent Run DOSBox-X silently and exit after executing AUTOEXEC.BAT.\n");
|
|
fprintf(stderr," -o <option string> Provide command-line option(s) for [name] if specified.\n");
|
|
fprintf(stderr," -c <command string> Execute this command in addition to AUTOEXEC.BAT.\n");
|
|
fprintf(stderr," Make sure to surround the command in quotes to cover spaces.\n");
|
|
fprintf(stderr," -set <section property=value> Set the config option (overriding the config file).\n");
|
|
fprintf(stderr," Make sure to surround the string in quotes to cover spaces.\n");
|
|
fprintf(stderr," -time-limit <n> Kill the emulator after 'n' seconds\n");
|
|
fprintf(stderr," -fastlaunch Fast launch mode (skip the BIOS logo and welcome banner)\n");
|
|
#if C_DEBUG
|
|
fprintf(stderr," -helpdebug Show debug-related options\n");
|
|
#endif
|
|
fprintf(stderr,"\n");
|
|
|
|
#if defined(WIN32)
|
|
DOSBox_ConsolePauseWait();
|
|
#endif
|
|
|
|
return 0;
|
|
} else if (optname == "helpdebug") {
|
|
DOSBox_ShowConsole();
|
|
|
|
fprintf(stderr,"\ndosbox-x [options]\n");
|
|
fprintf(stderr,"\nDOSBox-X version %s %s, copyright 2011-%s The DOSBox-X Team.\n",VERSION,SDL_STRING,COPYRIGHT_END_YEAR);
|
|
fprintf(stderr,"DOSBox-X project maintainer: joncampbell123 (The Great Codeholio)\n\n");
|
|
fprintf(stderr,"Based on DOSBox by the DOSBox Team (See AUTHORS file)\n\n");
|
|
fprintf(stderr,"Debugging options:\n\n");
|
|
fprintf(stderr," -debug Set all logging levels to debug\n");
|
|
fprintf(stderr," -early-debug Log early initialization messages in DOSBox-X (implies -console)\n");
|
|
fprintf(stderr," -keydbg Log all SDL key events\n");
|
|
fprintf(stderr," -break-start Break into debugger at startup\n");
|
|
fprintf(stderr," -console Show logging console (Windows builds only)\n");
|
|
fprintf(stderr," -noconsole Do not show logging console (Windows debug builds only)\n");
|
|
fprintf(stderr," -log-con Log CON output to a log file\n");
|
|
fprintf(stderr," -log-int21 Log calls to INT 21h (debug level)\n");
|
|
fprintf(stderr," -log-fileio Log file I/O through INT 21h (debug level)\n");
|
|
fprintf(stderr," -nolog Do not log anything to log file\n");
|
|
fprintf(stderr," -tests Run unit tests to test the DOSBox-X code\n\n");
|
|
|
|
#if defined(WIN32)
|
|
DOSBox_ConsolePauseWait();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
else if (optname == "o") {
|
|
if (!control->cmdline->NextOptArgv(tmp)) return false;
|
|
control->opt_o.push_back(tmp);
|
|
}
|
|
else if (optname == "c") {
|
|
if (!control->cmdline->NextOptArgv(tmp)) return false;
|
|
control->opt_c.push_back(tmp);
|
|
}
|
|
else if (optname == "set") {
|
|
if (!control->cmdline->NextOptArgv(tmp)) return false;
|
|
control->opt_set.push_back(tmp);
|
|
}
|
|
else if (optname == "alt-vga") {
|
|
control->opt_alt_vga_render = true;
|
|
}
|
|
else if (optname == "log-con") {
|
|
control->opt_log_con = true;
|
|
}
|
|
else if (optname == "nolog") {
|
|
control->opt_nolog = true;
|
|
}
|
|
else if (optname == "machine") {
|
|
if (!control->cmdline->NextOptArgv(control->opt_machine)) return false;
|
|
}
|
|
else if (optname == "time-limit") {
|
|
if (!control->cmdline->NextOptArgv(tmp)) return false;
|
|
control->opt_time_limit = atof(tmp.c_str());
|
|
}
|
|
else if (optname == "break-start") {
|
|
control->opt_break_start = true;
|
|
}
|
|
else if (optname == "silent") {
|
|
putenv(const_cast<char*>("SDL_AUDIODRIVER=dummy"));
|
|
putenv(const_cast<char*>("SDL_VIDEODRIVER=dummy"));
|
|
control->opt_exit = true;
|
|
control->opt_silent = true;
|
|
control->opt_nomenu = true;
|
|
control->opt_fastlaunch = true;
|
|
}
|
|
else if (optname == "test" || optname == "tests" || optname == "gtest_list_tests") {
|
|
putenv(const_cast<char*>("SDL_VIDEODRIVER=dummy"));
|
|
control->opt_test = true;
|
|
control->opt_nolog = true;
|
|
control->opt_noconsole = false;
|
|
control->opt_console = true;
|
|
control->opt_nomenu = true;
|
|
control->opt_fastlaunch = true;
|
|
}
|
|
else if (optname == "exit") {
|
|
control->opt_exit = true;
|
|
}
|
|
else if (optname == "noconfig") {
|
|
control->opt_noconfig = true;
|
|
}
|
|
else if (optname == "noautoexec") {
|
|
control->opt_noautoexec = true;
|
|
}
|
|
else if (optname == "prerun") {
|
|
control->opt_prerun = true;
|
|
}
|
|
#if defined(WIN32) && !defined(HX_DOS) || defined(MACOSX) || defined(LINUX)
|
|
else if (optname == "hostrun") {
|
|
winrun = true;
|
|
}
|
|
#endif
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
else if (optname == "winrun") {
|
|
winrun = true;
|
|
}
|
|
#endif
|
|
else if (optname == "securemode") {
|
|
control->opt_securemode = true;
|
|
}
|
|
else if (optname == "nodpiaware") {
|
|
control->opt_disable_dpi_awareness = true;
|
|
}
|
|
else if (optname == "keydbg") {
|
|
log_keyboard_scan_codes = true;
|
|
}
|
|
else if (optname == "date-host-forced" || optname == "date_host_forced") {
|
|
control->opt_date_host_forced = true;
|
|
}
|
|
else if (optname == "showrt") {
|
|
control->opt_showrt = true;
|
|
}
|
|
else if (optname == "showcycles") {
|
|
control->opt_showcycles = true;
|
|
}
|
|
else if (optname == "startmapper") {
|
|
control->opt_startmapper = true;
|
|
}
|
|
else if (optname == "fs" || optname == "fullscreen") {
|
|
control->opt_fullscreen = true;
|
|
}
|
|
else if (optname == "startui" || optname == "startgui" || optname == "starttool") {
|
|
control->opt_startui = true;
|
|
}
|
|
else if (optname == "disable-numlock-check" || optname == "disable_numlock_check") {
|
|
/* mainline DOSBox expects -disable_numlock_check so we support that here too */
|
|
control->opt_disable_numlock_check = true;
|
|
}
|
|
else if (optname == "savedir") {
|
|
if (!control->cmdline->NextOptArgv(custom_savedir)) return false;
|
|
#if defined(WIN32) && defined(C_SDL2)
|
|
localname = custom_savedir;
|
|
if (!FileDirExistCP(custom_savedir.c_str()) && FileDirExistUTF8(localname, custom_savedir.c_str()) == 2)
|
|
custom_savedir = localname;
|
|
#endif
|
|
}
|
|
else if (optname == "defaultdir") {
|
|
control->opt_used_defaultdir = true;
|
|
if (control->cmdline->NextOptArgv(tmp)) {
|
|
localname = tmp;
|
|
if (FileDirExistCP(tmp.c_str()) == 2)
|
|
chdir(tmp.c_str());
|
|
#if defined(WIN32) && defined(C_SDL2)
|
|
else if (FileDirExistUTF8(localname, tmp.c_str()) == 2)
|
|
chdir(localname.c_str());
|
|
#endif
|
|
} else
|
|
usecfgdir = true;
|
|
}
|
|
else if (optname == "userconf") {
|
|
control->opt_userconf = true;
|
|
}
|
|
else if (optname == "lang" || optname == "langcp") {
|
|
if (!control->cmdline->NextOptArgv(control->opt_lang)) return false;
|
|
if (optname == "langcp") control->opt_langcp = true;
|
|
}
|
|
else if (optname == "fastbioslogo") {
|
|
control->opt_fastbioslogo = true;
|
|
}
|
|
else if (optname == "fastlaunch") {
|
|
control->opt_fastlaunch = true;
|
|
}
|
|
else if (optname == "conf") {
|
|
if (!control->cmdline->NextOptArgv(tmp)) return false;
|
|
control->config_file_list.push_back(tmp);
|
|
}
|
|
else if (optname == "defaultconf") {
|
|
control->opt_defaultconf = true;
|
|
}
|
|
else if (optname == "editconf") {
|
|
if (!control->cmdline->NextOptArgv(control->opt_editconf)) return false;
|
|
}
|
|
else if (optname == "opencaptures") {
|
|
if (!control->cmdline->NextOptArgv(control->opt_opencaptures)) {
|
|
#if defined(LINUX)
|
|
control->opt_opencaptures = "xdg-open";
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
}
|
|
else if (optname == "opensaves") {
|
|
if (!control->cmdline->NextOptArgv(control->opt_opensaves)) {
|
|
#if defined(LINUX)
|
|
control->opt_opensaves = "xdg-open";
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
}
|
|
else if (optname == "eraseconf") {
|
|
control->opt_eraseconf = true;
|
|
}
|
|
else if (optname == "resetconf") {
|
|
control->opt_resetconf = true;
|
|
}
|
|
else if (optname == "printconf") {
|
|
control->opt_printconf = true;
|
|
}
|
|
else if (optname == "defaultmapper") {
|
|
control->opt_defaultmapper = true;
|
|
}
|
|
else if (optname == "erasemapper") {
|
|
control->opt_erasemapper = true;
|
|
}
|
|
else if (optname == "resetmapper") {
|
|
control->opt_resetmapper = true;
|
|
}
|
|
else if (optname == "log-int21") {
|
|
control->opt_logint21 = true;
|
|
}
|
|
else if (optname == "log-fileio") {
|
|
control->opt_logfileio = true;
|
|
}
|
|
else if (optname == "noconsole") {
|
|
control->opt_noconsole = true;
|
|
control->opt_console = false;
|
|
}
|
|
else if (optname == "console") {
|
|
control->opt_noconsole = false;
|
|
control->opt_console = true;
|
|
}
|
|
#if C_DEBUG
|
|
else if (optname == "display2") {
|
|
uint8_t disp2_color = 0;
|
|
if (control->cmdline->NextOptArgv(tmp)) {
|
|
if (strcasecmp(tmp.c_str(),"amber")==0) disp2_color=1;
|
|
else if (strcasecmp(tmp.c_str(),"green")==0) disp2_color=2;
|
|
}
|
|
DISP2_Init(disp2_color);
|
|
control->opt_display2 = true;
|
|
}
|
|
#endif
|
|
else if (optname == "nomenu") {
|
|
control->opt_nomenu = true;
|
|
}
|
|
else if (optname == "nogui") {
|
|
control->opt_nogui = true;
|
|
}
|
|
else if (optname == "nopromptfolder") {
|
|
control->opt_promptfolder = 0;
|
|
}
|
|
else if (optname == "promptfolder") {
|
|
control->opt_promptfolder = 2;
|
|
}
|
|
else if (optname == "debug") {
|
|
control->opt_debug = true;
|
|
}
|
|
else if (optname == "early-debug") {
|
|
control->opt_earlydebug = true;
|
|
control->opt_console = true;
|
|
}
|
|
else if (optname == "socket") {
|
|
if (!control->cmdline->NextOptArgv(tmp)) return false;
|
|
socknum = std::stoi(tmp);
|
|
} else {
|
|
printf("WARNING: Unknown option %s (first parsing stage)\n",optname.c_str());
|
|
}
|
|
}
|
|
if (control->opt_display2) {
|
|
control->opt_break_start = false;
|
|
control->opt_console = false;
|
|
}
|
|
|
|
/* now that the above loop has eaten all the options from the command
|
|
* line, scan the command line for batch files to run.
|
|
* https://github.com/joncampbell123/dosbox-x/issues/369 */
|
|
control->cmdline->BeginOpt(/*don't eat*/false);
|
|
while (!control->cmdline->CurrentArgvEnd()) {
|
|
control->cmdline->GetCurrentArgv(tmp);
|
|
trim(tmp);
|
|
localname = tmp;
|
|
int rescp = FileDirExistCP(tmp.c_str()), resutf8 = rescp||!tmp.size()?0:FileDirExistUTF8(localname, tmp.c_str());
|
|
if (!rescp && resutf8) {
|
|
tmp = localname;
|
|
rescp = resutf8;
|
|
}
|
|
const char *ext = strrchr(tmp.c_str(),'.'); /* if it looks like a file... with an extension */
|
|
if (rescp) {
|
|
if (rescp == 2 || (ext != NULL && rescp == 1 && (!strcasecmp(ext,".zip") || !strcasecmp(ext,".7z")))) {
|
|
control->auto_bat_additional.push_back("@mount c: \""+tmp+"\" -nl");
|
|
control->cmdline->EatCurrentArgv();
|
|
continue;
|
|
} else if (ext != NULL && rescp == 1 && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".exe") || !strcasecmp(ext,".com"))) { /* .BAT files given on the command line trigger automounting C: to run it */
|
|
control->auto_bat_additional.push_back(tmp);
|
|
control->cmdline->EatCurrentArgv();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
control->cmdline->NextArgv();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MSG_Init();
|
|
void DOSBOX_RealInit();
|
|
void DOSBOX_InitTickLoop();
|
|
void TIMER_ShutdownTickHandlers();
|
|
void DOSBOX_SetupConfigSections(void);
|
|
void PAGING_Init();
|
|
void IO_Init();
|
|
void Init_VGABIOS();
|
|
void Init_AddressLimitAndGateMask();
|
|
void Init_RAM();
|
|
void Init_MemHandles();
|
|
void Init_MemoryAccessArray();
|
|
void Init_PCJR_CartridgeROM();
|
|
void Init_PS2_Port_92h();
|
|
void Init_A20_Gate();
|
|
void HARDWARE_Init();
|
|
void CAPTURE_Init();
|
|
void ROMBIOS_Init();
|
|
void CALLBACK_Init();
|
|
void Init_DMA();
|
|
void Init_PIC();
|
|
void PCIBUS_Init();
|
|
void PROGRAMS_Init();
|
|
void RENDER_Init();
|
|
void TIMER_Init();
|
|
void CMOS_Init();
|
|
void VGA_Init();
|
|
void CPU_Init();
|
|
void ISAPNP_Cfg_Init();
|
|
#if C_FPU
|
|
void FPU_Init();
|
|
#endif
|
|
void KEYBOARD_Init();
|
|
void VOODOO_Init();
|
|
void GLIDE_Init();
|
|
void MIXER_Init();
|
|
void MIDI_Init();
|
|
|
|
/* Init all the sections */
|
|
void MPU401_Init();
|
|
#if C_DEBUG
|
|
void DEBUG_Init();
|
|
#endif
|
|
void SBLASTER_Init();
|
|
void GUS_Init();
|
|
void INNOVA_Init();
|
|
void PCSPEAKER_Init();
|
|
void TANDYSOUND_Init();
|
|
void DISNEY_Init();
|
|
void PS1SOUND_Init();
|
|
void BIOS_Init();
|
|
void INT10_Init();
|
|
void JOYSTICK_Init();
|
|
void SERIAL_Init();
|
|
#if C_PRINTER
|
|
void PRINTER_Init();
|
|
#endif
|
|
void PARALLEL_Init();
|
|
void DONGLE_Init();
|
|
void DOS_Init();
|
|
void XMS_Init();
|
|
void EMS_Init();
|
|
void MOUSE_Init();
|
|
void DOS_KeyboardLayout_Init();
|
|
void CDROM_Image_Init();
|
|
void MSCDEX_Init();
|
|
void DRIVES_Init();
|
|
void IPX_Init();
|
|
void IDE_Init();
|
|
void NE2K_Init();
|
|
void FDC_Primary_Init();
|
|
void AUTOEXEC_Init();
|
|
|
|
#if defined(WIN32)
|
|
// NTS: I intend to add code that not only indicates High DPI awareness but also queries the monitor DPI
|
|
// and then factor the DPI into DOSBox-X's scaler and UI decisions.
|
|
void Windows_DPI_Awareness_Init() {
|
|
// if the user says not to from the command line, or disables it from dosbox-x.conf, then don't enable DPI awareness.
|
|
if (!dpi_aware_enable || control->opt_disable_dpi_awareness)
|
|
return;
|
|
|
|
/* log it */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Win32: I will announce High DPI awareness to Windows to eliminate upscaling");
|
|
|
|
// turn off DPI scaling so DOSBox-X doesn't look so blurry on Windows 8 & Windows 10.
|
|
// use GetProcAddress and LoadLibrary so that these functions are not hard dependencies that prevent us from
|
|
// running under Windows 7 or XP.
|
|
HRESULT (WINAPI *__SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS) = NULL; // windows 8.1
|
|
BOOL (WINAPI *__SetProcessDPIAware)(void) = NULL; // vista/7/8/10
|
|
HMODULE __user32;
|
|
HMODULE __shcore;
|
|
|
|
__user32 = GetModuleHandle("USER32.DLL");
|
|
__shcore = GetModuleHandle("SHCORE.DLL");
|
|
|
|
if (__user32)
|
|
__SetProcessDPIAware = (BOOL(WINAPI *)(void))GetProcAddress(__user32, "SetProcessDPIAware");
|
|
if (__shcore)
|
|
__SetProcessDpiAwareness = (HRESULT (WINAPI *)(PROCESS_DPI_AWARENESS))GetProcAddress(__shcore, "SetProcessDpiAwareness");
|
|
|
|
if (__SetProcessDpiAwareness) {
|
|
LOG(LOG_MISC,LOG_DEBUG)("SHCORE.DLL exports SetProcessDpiAwareness function, calling it to signal we are DPI aware.");
|
|
if (__SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) != S_OK)
|
|
LOG(LOG_MISC,LOG_DEBUG)("SetProcessDpiAwareness failed");
|
|
}
|
|
if (__SetProcessDPIAware) {
|
|
LOG(LOG_MISC,LOG_DEBUG)("USER32.DLL exports SetProcessDPIAware function, calling it to signal we are DPI aware.");
|
|
__SetProcessDPIAware();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool VM_Boot_DOSBox_Kernel() {
|
|
if (!dos_kernel_disabled) {
|
|
RemoveEMSPageFrame();
|
|
RemoveUMBBlock();
|
|
DisableINT33();
|
|
DOS_GetMemory_unmap();
|
|
VFILE_Shutdown();
|
|
PROGRAMS_Shutdown();
|
|
DOS_UninstallMisc();
|
|
SBLASTER_DOS_Shutdown();
|
|
GUS_DOS_Shutdown();
|
|
EMS_DoShutDown();
|
|
XMS_DoShutDown();
|
|
DOS_DoShutDown();
|
|
|
|
DispatchVMEvent(VM_EVENT_DOS_SURPRISE_REBOOT); // <- apparently we rebooted without any notification (such as jmp'ing to FFFF:0000)
|
|
|
|
dos_kernel_disabled = true;
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
}
|
|
|
|
if (dos_kernel_disabled) {
|
|
/* in case of reboot */
|
|
Init_MemHandles();
|
|
|
|
DispatchVMEvent(VM_EVENT_DOS_BOOT); // <- just starting the DOS kernel now
|
|
|
|
/* DOS kernel init */
|
|
dos_kernel_disabled = false; // FIXME: DOS_Init should install VM callback handler to set this
|
|
void DOS_Startup(Section* sec);
|
|
DOS_Startup(NULL);
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
void update_pc98_function_row(unsigned char setting,bool force_redraw=false);
|
|
|
|
void DRIVES_Startup(Section *s);
|
|
DRIVES_Startup(NULL);
|
|
|
|
/* NEC's function key row seems to be deeply embedded in the CON driver. Am I wrong? */
|
|
if (IS_PC98_ARCH) update_pc98_function_row(1);
|
|
|
|
DispatchVMEvent(VM_EVENT_DOS_INIT_KERNEL_READY); // <- kernel is ready
|
|
|
|
/* keyboard mapping, at this point in CONFIG.SYS parsing, right? */
|
|
void DOS_KeyboardLayout_Startup(Section* sec);
|
|
DOS_KeyboardLayout_Startup(NULL);
|
|
|
|
/* Most MS-DOS installations have a DEVICE=C:\HIMEM.SYS somewhere near the top of their CONFIG.SYS */
|
|
void XMS_Startup(Section *sec);
|
|
XMS_Startup(NULL);
|
|
|
|
/* And then after that, usually a DEVICE=C:\EMM386.EXE just after HIMEM.SYS */
|
|
void EMS_Startup(Section* sec);
|
|
EMS_Startup(NULL);
|
|
|
|
DispatchVMEvent(VM_EVENT_DOS_INIT_CONFIG_SYS_DONE); // <- we just finished executing CONFIG.SYS
|
|
SHELL_Init(); // <- NTS: this will change CPU instruction pointer!
|
|
DispatchVMEvent(VM_EVENT_DOS_INIT_SHELL_READY); // <- we just finished loading the shell (COMMAND.COM)
|
|
|
|
/* it's time to init parsing AUTOEXEC.BAT */
|
|
void AUTOEXEC_Startup(Section *sec);
|
|
AUTOEXEC_Startup(NULL);
|
|
|
|
/* Most MS-DOS installations run MSCDEX.EXE from somewhere in AUTOEXEC.BAT. We do the same here, in a fashion. */
|
|
/* TODO: Can we make this an OPTION if the user doesn't want to make MSCDEX.EXE resident? */
|
|
/* TODO: When we emulate executing AUTOEXEC.BAT between INIT_SHELL_READY and AUTOEXEC_BAT_DONE, can we make a fake MSCDEX.EXE within drive Z:\
|
|
* and auto-add a Z:\DOS\MSCDEX.EXE to the top of AUTOEXEC.BAT, command line switches and all. if the user has not already added it? */
|
|
void MSCDEX_Startup(Section* sec);
|
|
MSCDEX_Startup(NULL);
|
|
|
|
/* Some installations load the MOUSE.COM driver from AUTOEXEC.BAT as well */
|
|
/* TODO: Can we make this an option? Can we add a fake MOUSE.COM to the Z: drive as well? */
|
|
void MOUSE_Startup(Section *sec);
|
|
MOUSE_Startup(NULL);
|
|
|
|
DispatchVMEvent(VM_EVENT_DOS_INIT_AUTOEXEC_BAT_DONE); // <- we just finished executing AUTOEXEC.BAT
|
|
DispatchVMEvent(VM_EVENT_DOS_INIT_AT_PROMPT); // <- now, we're at the DOS prompt
|
|
SHELL_Run();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VM_PowerOn() {
|
|
if (!guest_machine_power_on) {
|
|
// powering on means power on event, followed by reset assert, then reset deassert
|
|
guest_machine_power_on = true;
|
|
DispatchVMEvent(VM_EVENT_POWERON);
|
|
DispatchVMEvent(VM_EVENT_RESET);
|
|
DispatchVMEvent(VM_EVENT_RESET_END);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if defined(LINUX) && C_X11
|
|
# include <X11/Xlib.h>
|
|
# include <X11/Xatom.h>
|
|
#endif
|
|
|
|
#if !defined(C_EMSCRIPTEN)
|
|
void update_capture_fmt_menu(void);
|
|
#endif
|
|
|
|
void SetWindowTransparency(int trans) {
|
|
if (trans == transparency) return;
|
|
double alpha = (double)(100-trans)/100;
|
|
#if defined(C_SDL2)
|
|
SDL_SetWindowOpacity(sdl.window,alpha);
|
|
#elif defined(WIN32) && !defined(HX_DOS)
|
|
SetWindowLong(GetHWND(), GWL_EXSTYLE, GetWindowLong(GetHWND(), GWL_EXSTYLE) | WS_EX_LAYERED);
|
|
SetLayeredWindowAttributes(GetHWND(), 0, 255 * alpha, LWA_ALPHA);
|
|
#elif defined(LINUX) && C_X11
|
|
Display *dpy = XOpenDisplay(NULL);
|
|
if (!dpy) return;
|
|
unsigned long opacity = (unsigned long)(0xFFFFFFFFul * alpha);
|
|
Atom atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False);
|
|
XChangeProperty(dpy, DefaultRootWindow(dpy), atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&opacity, 1L);
|
|
#endif
|
|
transparency = trans;
|
|
}
|
|
|
|
void GetDrawWidthHeight(unsigned int *pdrawWidth, unsigned int *pdrawHeight) {
|
|
*pdrawWidth = sdl.draw.width;
|
|
*pdrawHeight = sdl.draw.height;
|
|
}
|
|
|
|
void GetMaxWidthHeight(unsigned int *pmaxWidth, unsigned int *pmaxHeight) {
|
|
unsigned int maxWidth = sdl.desktop.full.width;
|
|
unsigned int maxHeight = sdl.desktop.full.height;
|
|
|
|
#if defined(C_SDL2)
|
|
SDL_DisplayMode dm;
|
|
if (SDL_GetDesktopDisplayMode(sdl.displayNumber?sdl.displayNumber-1:0,&dm) == 0) {
|
|
maxWidth = dm.w;
|
|
maxHeight = dm.h;
|
|
}
|
|
#elif defined(WIN32)
|
|
maxWidth = GetSystemMetrics(SM_CXSCREEN);
|
|
maxHeight = GetSystemMetrics(SM_CYSCREEN);
|
|
#elif defined(MACOSX)
|
|
auto mainDisplayId = CGMainDisplayID();
|
|
maxWidth = CGDisplayPixelsWide(mainDisplayId);
|
|
maxHeight = CGDisplayPixelsHigh(mainDisplayId);
|
|
#elif defined(LINUX) && C_X11
|
|
Display *dpy = XOpenDisplay(NULL);
|
|
if (dpy) {
|
|
int snum = DefaultScreen(dpy);
|
|
maxWidth = DisplayWidth(dpy, snum);
|
|
maxHeight = DisplayHeight(dpy, snum);
|
|
}
|
|
#endif
|
|
*pmaxWidth = maxWidth;
|
|
*pmaxHeight = maxHeight;
|
|
}
|
|
|
|
#if defined(LINUX)
|
|
bool x11_on_top = false;
|
|
#endif
|
|
|
|
#if defined(MACOSX) && !defined(C_SDL2)
|
|
bool macosx_on_top = false;
|
|
#endif
|
|
|
|
bool is_always_on_top(void) {
|
|
#if defined(_WIN32)
|
|
DWORD dwExStyle = ::GetWindowLong(GetHWND(), GWL_EXSTYLE);
|
|
return !!(dwExStyle & WS_EX_TOPMOST);
|
|
#elif defined(MACOSX) && !defined(C_SDL2)
|
|
return macosx_on_top;
|
|
#elif defined(MACOSX) && defined(C_SDL2) && SDL_VERSION_ATLEAST(2, 0, 16)
|
|
return SDL_GetWindowFlags(GFX_GetSDLWindow()) & SDL_WINDOW_ALWAYS_ON_TOP;
|
|
#elif defined(LINUX)
|
|
return x11_on_top;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool custom_bios = false;
|
|
|
|
// OK why isn't this being set for Linux??
|
|
#ifndef SDL_MAIN_NOEXCEPT
|
|
#define SDL_MAIN_NOEXCEPT
|
|
#endif
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
#include "Shlobj.h"
|
|
int CALLBACK FolderBrowserCallback(HWND h_Dlg, UINT uMsg, LPARAM lParam, LPARAM lpData) {
|
|
if (uMsg == BFFM_INITIALIZED)
|
|
SendMessageW(h_Dlg, BFFM_SETEXPANDED, TRUE, lpData);
|
|
return 0;
|
|
}
|
|
|
|
std::wstring win32_prompt_folder(const char *default_folder) {
|
|
std::wstring res;
|
|
const WCHAR text[] = L"Select folder where to run emulation, which will become DOSBox-X's working directory:";
|
|
const size_t size = default_folder == NULL? 0 : strlen(default_folder)+1;
|
|
wchar_t* wfolder = default_folder == NULL ? NULL : new wchar_t[size];
|
|
if (default_folder != NULL) mbstowcs (wfolder, default_folder, size);
|
|
|
|
#if 0 // Browse for folder using SHBrowseForFolder, which works on Windows XP and higher
|
|
WCHAR szDir[MAX_PATH];
|
|
BROWSEINFOW bInfo;
|
|
bInfo.hwndOwner = GetHWND();
|
|
bInfo.pidlRoot = NULL;
|
|
bInfo.pszDisplayName = szDir;
|
|
bInfo.lpszTitle = text;
|
|
bInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
|
|
if (wfolder != NULL) {
|
|
bInfo.lpfn = FolderBrowserCallback;
|
|
bInfo.lParam = (LPARAM)wfolder;
|
|
} else {
|
|
bInfo.lpfn = NULL;
|
|
bInfo.lParam = 0;
|
|
}
|
|
LPITEMIDLIST lpItem = SHBrowseForFolderW(&bInfo);
|
|
if (lpItem != NULL) {
|
|
SHGetPathFromIDListW(lpItem, szDir);
|
|
res = std::wstring(szDir);
|
|
CoTaskMemFree(lpItem);
|
|
} else
|
|
return std::wstring();
|
|
#else // Use IFileDialog (Visual Studio builds) or OPENFILENAME (MinGW builds)
|
|
# if !defined(__MINGW32__) /* MinGW does not have these headers */
|
|
IFileDialog* ifd; /* Windows Vista file/folder picker interface COM object (shobjidl_core.h) */
|
|
/* Try the new picker first (Windows Vista or higher) which makes it possible to pick a folder */
|
|
if(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_IFileDialog, (void**)(&ifd)) == S_OK) {
|
|
HRESULT hr;
|
|
HMODULE __shell32 = GetModuleHandle("SHELL32.DLL");
|
|
if (wfolder != NULL && __shell32) {
|
|
PIDLIST_ABSOLUTE pidl = NULL;
|
|
SHParseDisplayName(wfolder, NULL, &pidl, 0, NULL);
|
|
HRESULT (WINAPI *__SHCreateItemFromIDList)(PCIDLIST_ABSOLUTE, REFIID, void**) = NULL;
|
|
__SHCreateItemFromIDList = (HRESULT (WINAPI *)(PCIDLIST_ABSOLUTE, REFIID, void**))GetProcAddress(__shell32,"SHCreateItemFromIDList");
|
|
if (pidl != NULL && __SHCreateItemFromIDList) {
|
|
IShellItem *item = NULL;
|
|
__SHCreateItemFromIDList(pidl, IID_IShellItem, (LPVOID*)&item);
|
|
if(item != NULL) {
|
|
ifd->SetDefaultFolder(item);
|
|
item->Release();
|
|
}
|
|
CoTaskMemFree(pidl);
|
|
}
|
|
}
|
|
ifd->SetOptions(FOS_PICKFOLDERS|FOS_FORCEFILESYSTEM|FOS_PATHMUSTEXIST|FOS_DONTADDTORECENT);
|
|
ifd->SetTitle(text);
|
|
ifd->SetOkButtonLabel(L"Choose");
|
|
hr = ifd->Show(NULL);
|
|
if(hr == S_OK) {
|
|
IShellItem* sh = NULL;
|
|
if(ifd->GetFolder(&sh) == S_OK) {
|
|
LPWSTR str = NULL;
|
|
|
|
if(sh->GetDisplayName(SIGDN_FILESYSPATH, &str) == S_OK) {
|
|
res = str;
|
|
CoTaskMemFree(str);
|
|
}
|
|
|
|
sh->Release();
|
|
}
|
|
|
|
ifd->Release();
|
|
return res;
|
|
}
|
|
else if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
|
|
/* the user clicked cancel, sorry */
|
|
ifd->Release();
|
|
return std::wstring();
|
|
}
|
|
ifd->Release();
|
|
/* didn't work, try the other method below for Windows XP and below */
|
|
}
|
|
# endif
|
|
OPENFILENAMEW of;
|
|
WCHAR tmp[1024];
|
|
tmp[0] = 0;
|
|
memset(&of, 0, sizeof(of));
|
|
of.lStructSize = sizeof(of);
|
|
of.lpstrFile = tmp;
|
|
of.nMaxFile = sizeof(tmp) / sizeof(tmp[0]); // Size in CHARACTERS not bytes
|
|
of.lpstrInitialDir = wfolder;
|
|
of.lpstrTitle = text;
|
|
of.Flags = OFN_LONGNAMES | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
|
of.lpstrFilter = L"DOSBox-X configuration file\0" L"dosbox-x.conf;dosbox.conf\0";
|
|
if (GetOpenFileNameW(&of)) {
|
|
if (of.nFileOffset >= sizeof(tmp)) return std::wstring();
|
|
while (of.nFileOffset > 0 && tmp[of.nFileOffset - 1] == '/' || tmp[of.nFileOffset - 1] == '\\') of.nFileOffset--;
|
|
if (of.nFileOffset == 0) return std::wstring();
|
|
res = std::wstring(tmp, (size_t)of.nFileOffset);
|
|
}
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
void DISP2_Init(uint8_t color);
|
|
//extern void UI_Init(void);
|
|
void grGlideShutdown(void);
|
|
int main(int argc, char* argv[]) SDL_MAIN_NOEXCEPT {
|
|
CommandLine com_line(argc,argv);
|
|
Config myconf(&com_line);
|
|
bool saved_opt_test;
|
|
|
|
control=&myconf;
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
#endif
|
|
|
|
/* -- parse command line arguments */
|
|
if (!DOSBOX_parse_argv()) return 1;
|
|
|
|
if (control->opt_time_limit > 0)
|
|
time_limit_ms = (Bitu)(control->opt_time_limit * 1000);
|
|
|
|
if (control->opt_console)
|
|
DOSBox_ShowConsole();
|
|
|
|
/* -- Handle some command line options */
|
|
if (control->opt_resetconf)
|
|
eraseconfigfile();
|
|
if (control->opt_printconf)
|
|
printconfiglocation();
|
|
if (control->opt_resetmapper)
|
|
erasemapperfile();
|
|
|
|
/* -- Early logging init, in case these details are needed to debug problems at this level */
|
|
/* If --early-debug was given this opens up logging to STDERR until Log::Init() */
|
|
LOG::EarlyInit();
|
|
|
|
/* -- Init the configuration system and add default values */
|
|
CheckNumLockState();
|
|
CheckCapsLockState();
|
|
CheckScrollLockState();
|
|
|
|
/* -- setup the config sections for config parsing */
|
|
SDL_SetupConfigSection();
|
|
LOG::SetupConfigSection();
|
|
DOSBOX_SetupConfigSections();
|
|
|
|
#if 0 /* VGA_Draw_2 self test: dot clock */
|
|
{
|
|
const double start_time = 0;
|
|
signed long long count = 0;
|
|
VGA_Draw_2 t;
|
|
|
|
fprintf(stderr,"VGA_Draw_2: 1000Hz\n");
|
|
|
|
t.dotclock.set_rate(1000,start_time);/*hz*/
|
|
for (int i=0;i < 10000;i++) {
|
|
t.dotclock.update(start_time + i);
|
|
|
|
if (labs(t.dotclock.ticks - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
|
|
fprintf(stderr,"* TEST FAILURE:\n");
|
|
fprintf(stderr," ticks=%lld ticks_prev=%lld\n",(signed long long)i,(signed long long)t.dotclock.ticks);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
t.dotclock.reset(start_time);
|
|
assert(t.dotclock.base == start_time);
|
|
assert(t.dotclock.ticks_prev == 0);
|
|
assert(t.dotclock.ticks == 0);
|
|
|
|
fprintf(stderr,"VGA_Draw_2: 1000Hz incremental\n");
|
|
|
|
count = 0;
|
|
t.dotclock.set_rate(1000,start_time);/*hz*/
|
|
for (int i=0;i < 10000;i++) {
|
|
t.dotclock.update(start_time + i);
|
|
count += t.dotclock.delta_get();
|
|
assert(t.dotclock.ticks == t.dotclock.ticks_prev);
|
|
|
|
if (labs(count - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
|
|
fprintf(stderr,"* TEST FAILURE:\n");
|
|
fprintf(stderr," count=%lld ticks=%lld ticks_prev=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks);
|
|
return 1;
|
|
}
|
|
|
|
signed long long rc = t.dotclock.ticks2pic(count);
|
|
if (labs(rc - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
|
|
fprintf(stderr,"* TEST FAILURE:\n");
|
|
fprintf(stderr," count=%lld ticks=%lld ticks_prev=%lld rc=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks,rc);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr,"VGA_Draw_2: 1000Hz inc then 100Hz inc\n");
|
|
|
|
count = 0;
|
|
t.dotclock.set_rate(100,start_time + 1000);/*hz, rate change*/
|
|
for (int i=0;i < 10000;i++) {
|
|
t.dotclock.update(start_time + 1000 + (i * 10));/*1ms * 10 = 10ms = 100Hz */
|
|
count += t.dotclock.delta_get();
|
|
assert(t.dotclock.ticks == t.dotclock.ticks_prev);
|
|
|
|
if (labs(count - (signed long long)i) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
|
|
fprintf(stderr,"* TEST FAILURE:\n");
|
|
fprintf(stderr," count=%lld ticks=%lld ticks_prev=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks);
|
|
return 1;
|
|
}
|
|
|
|
signed long long rc = t.dotclock.ticks2pic(count);
|
|
if (labs(rc - ((signed long long)(i * 10) + 1000 + start_time)) > 1ll) { /* NTS: Expect possible +/- 1 error due to floating point */
|
|
fprintf(stderr,"* TEST FAILURE:\n");
|
|
fprintf(stderr," count=%lld ticks=%lld ticks_prev=%lld rc=%lld\n",count,(signed long long)i,(signed long long)t.dotclock.ticks,rc);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if C_EMSCRIPTEN
|
|
control->opt_debug = true;
|
|
control->opt_earlydebug = true;
|
|
#endif
|
|
|
|
bitop::self_test();
|
|
ptrop::self_test();
|
|
|
|
// initialize output libraries
|
|
OUTPUT_SURFACE_Initialize();
|
|
#if C_OPENGL
|
|
OUTPUT_OPENGL_Initialize();
|
|
#endif
|
|
#if C_DIRECT3D
|
|
OUTPUT_DIRECT3D_Initialize();
|
|
#endif
|
|
|
|
// initialize some defaults in SDL structure here
|
|
sdl.srcAspect.x = aspect_ratio_x>0?aspect_ratio_x:4;
|
|
sdl.srcAspect.y = aspect_ratio_y>0?aspect_ratio_y:3;
|
|
sdl.srcAspect.xToY = (double)sdl.srcAspect.x / sdl.srcAspect.y;
|
|
sdl.srcAspect.yToX = (double)sdl.srcAspect.y / sdl.srcAspect.x;
|
|
|
|
#if defined(MACOSX)
|
|
/* The resource system of DOSBox-X relies on being able to locate the Resources subdirectory
|
|
within the DOSBox-X .app bundle. To do this, we have to first know where our own executable
|
|
is, which macOS helpfully puts int argv[0] for us */
|
|
/* NTS: Experimental testing shows that when we are run from the desktop (double-clicking on
|
|
the .app bundle from the Finder) the current working directory is / (fs root). */
|
|
extern std::string MacOSXEXEPath;
|
|
extern std::string MacOSXResPath;
|
|
MacOSXEXEPath = argv[0];
|
|
|
|
/* The path should be something like /blah/blah/dosbox-x.app/Contents/MacOS/DosBox */
|
|
/* If that's true, then we can move one level up the tree and look for */
|
|
/* /blah/blah/dosbox-x.app/Contents/Resources */
|
|
{
|
|
const char *ref = argv[0];
|
|
const char *s = strrchr(ref,'/');
|
|
if (s != NULL) {
|
|
if (s > ref) s--;
|
|
while (s > ref && *s != '/') s--;
|
|
if (!strncasecmp(s,"/MacOS/",7)) {
|
|
MacOSXResPath = std::string(ref,(size_t)(s-ref)) + "/Resources";
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
configfile = "";
|
|
std::string workdiropt = "default";
|
|
std::string workdirdef = "";
|
|
std::string exepath=GetDOSBoxXPath();
|
|
struct stat st;
|
|
if (!control->opt_defaultconf && control->config_file_list.empty() && stat("dosbox-x.conf", &st) && stat("dosbox.conf", &st)) {
|
|
/* load the global config file first */
|
|
std::string tmp,config_path,config_combined;
|
|
|
|
/* -- Parse configuration files */
|
|
Cross::GetPlatformConfigDir(config_path);
|
|
Cross::GetPlatformConfigName(tmp);
|
|
|
|
if (exepath.size()) {
|
|
control->ParseConfigFile((exepath + "dosbox-x.conf").c_str());
|
|
if (!control->configfiles.size()) control->ParseConfigFile((exepath + "dosbox.conf").c_str());
|
|
}
|
|
|
|
config_combined = config_path + "dosbox-x.conf";
|
|
if (!control->configfiles.size() && stat(config_combined.c_str(),&st) == 0 && S_ISREG(st.st_mode))
|
|
control->ParseConfigFile(config_combined.c_str());
|
|
|
|
config_combined = config_path + tmp;
|
|
if (!control->configfiles.size() && stat(config_combined.c_str(),&st) == 0 && S_ISREG(st.st_mode))
|
|
control->ParseConfigFile(config_combined.c_str());
|
|
if (control->configfiles.size()) configfile = control->configfiles.front();
|
|
|
|
Section_prop *section = static_cast<Section_prop *>(control->GetSection("dosbox"));
|
|
workdiropt = section->Get_string("working directory option");
|
|
workdirdef = section->Get_path("working directory default")->realpath;
|
|
std::string resolvestr = section->Get_string("resolve config path");
|
|
resolveopt = resolvestr=="true"||resolvestr=="1"?1:(resolvestr=="dosvar"?2:(resolvestr=="tilde"?3:0));
|
|
void ResolvePath(std::string& in);
|
|
ResolvePath(workdirdef);
|
|
|
|
control->ClearExtraData();
|
|
control->configfiles.clear();
|
|
}
|
|
|
|
if (workdiropt == "prompt" && control->opt_promptfolder < 0) control->opt_promptfolder = 1;
|
|
else if (((workdiropt == "custom" && !control->opt_used_defaultdir) || workdiropt == "force") && workdirdef.size()) {
|
|
if (chdir(workdirdef.c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'custom' or 'force'.");
|
|
}
|
|
control->opt_used_defaultdir = true;
|
|
usecfgdir = false;
|
|
} else if (workdiropt == "userconfig") {
|
|
std::string config_path;
|
|
Cross::GetPlatformConfigDir(config_path);
|
|
if (config_path.size()) {
|
|
if (chdir(config_path.c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'userconfig'.");
|
|
}
|
|
}
|
|
control->opt_used_defaultdir = true;
|
|
usecfgdir = false;
|
|
} else if (workdiropt == "program") {
|
|
std::string exepath = GetDOSBoxXPath();
|
|
if (exepath.size()) {
|
|
if (chdir(exepath.c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'program'.");
|
|
}
|
|
}
|
|
control->opt_used_defaultdir = true;
|
|
usecfgdir = false;
|
|
} else if (workdiropt == "config") {
|
|
control->opt_used_defaultdir = true;
|
|
usecfgdir = true;
|
|
}
|
|
|
|
/* default do not prompt if -set, -conf, -userconf, -defaultconf, or -defaultdir is used */
|
|
if (control->opt_promptfolder < 0 && (!control->config_file_list.empty() || !control->opt_set.empty() || control->opt_userconf || control->opt_defaultconf || control->opt_used_defaultdir || control->opt_fastlaunch || control->opt_test || workdiropt == "noprompt")) {
|
|
control->opt_promptfolder = 0;
|
|
}
|
|
|
|
int workdirsave = 0;
|
|
std::string workdirsaveas = "";
|
|
#if defined(MACOSX) || defined(LINUX) || (defined(WIN32) && !defined(HX_DOS))
|
|
{
|
|
char cwd[512] = {0};
|
|
if(getcwd(cwd, sizeof(cwd) - 1) == NULL) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to get the current working directory.");
|
|
}
|
|
|
|
#if !defined(MACOSX)
|
|
if(control->opt_promptfolder < 0) {
|
|
struct stat st;
|
|
|
|
/* if dosbox.conf or dosbox-x.conf already exists in the current working directory, then skip folder prompt */
|
|
if(stat("dosbox-x.conf", &st) == 0 || stat("dosbox.conf", &st) == 0) {
|
|
if(S_ISREG(st.st_mode)) {
|
|
control->opt_promptfolder = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
/* A Windows application cannot detect with isatty() if run from the command prompt.
|
|
* isatty() returns true even though STDIN/STDOUT/STDERR do not exist even if run from the command prompt. */
|
|
if (control->opt_promptfolder < 0)
|
|
control->opt_promptfolder = 1;
|
|
#else
|
|
if (control->opt_promptfolder < 0)
|
|
control->opt_promptfolder = (!isatty(0) || !strcmp(cwd,"/")) ? 1 : 0;
|
|
#endif
|
|
if (control->opt_promptfolder == 1 && workdiropt == "default" && workdirdef.size()) {
|
|
control->opt_promptfolder = 0;
|
|
if(chdir(workdirdef.c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'default'.");
|
|
}
|
|
control->opt_used_defaultdir = true;
|
|
usecfgdir = false;
|
|
}
|
|
|
|
/* When we're run from the Finder, the current working directory is often / (the
|
|
root filesystem) and there is no terminal. What to run, what directory to run
|
|
it from, and the dosbox-x.conf to read, is not obvious. If run from the Finder,
|
|
prompt the user where to run from, and then set it as the current working
|
|
directory and continue. If they cancel, then exit. */
|
|
/* Assume that if STDIN is not a TTY, or if the current working directory is "/",
|
|
that we were started by the Finder */
|
|
/* FIXME: Is there a better way to detect whether we were started by the Finder
|
|
or any other part of the macOS desktop? */
|
|
if (control->opt_promptfolder > 0) {
|
|
#if defined(MACOSX)
|
|
/* NTS: Do NOT call macosx_prompt_folder() to show a modal NSOpenPanel without
|
|
first initializing the SDL video subsystem. SDL1 must initialize
|
|
the Cocoa NS objects first, or else strange things happen, like
|
|
DOSBox-X as an NSWindow with no apparent icon in the task manager,
|
|
inability to change or maintain the menu at the top, etc. */
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
|
E_Exit("Can't init SDL %s",SDL_GetError());
|
|
#endif
|
|
|
|
char folder[512], *default_folder=folder;
|
|
std::string dir = workdirdef.empty()?(exepath.size()?exepath:""):workdirdef;
|
|
if (dir.size()&&dir.size()<512)
|
|
strcpy(default_folder, dir.c_str());
|
|
else
|
|
default_folder = NULL;
|
|
const char *confirmstr = "Do you want to use the selected folder as the DOSBox-X working directory in future sessions?\n\nIf you select Yes, DOSBox-X will not prompt for a folder again.\nIf you select No, DOSBox-X will always prompt for a folder when it runs.\nIf you select Cancel, DOSBox-X will ask this question again next time.";
|
|
const char *quitstr = "You have not selected a valid path. Do you want to run DOSBox-X with the current path as the DOSBox-X working directory?\n\nDOSBox-X will exit if you select No.";
|
|
#if defined(MACOSX)
|
|
std::string path = macosx_prompt_folder(default_folder);
|
|
if (path.empty()) {
|
|
if (!macosx_yesno("Run DOSBox-X?", quitstr)) {
|
|
fprintf(stderr,"No path chosen by user, exiting\n");
|
|
return 1;
|
|
}
|
|
} else if (workdiropt == "default") {
|
|
int ans=macosx_yesnocancel("DOSBox-X working directory", confirmstr);
|
|
if (ans == 1) {workdirsave=1;workdirsaveas=path;}
|
|
else if (ans == 0) workdirsave=2;
|
|
}
|
|
#elif defined(WIN32) && !defined(HX_DOS)
|
|
std::wstring path = win32_prompt_folder(default_folder);
|
|
if (path.empty()) {
|
|
if (MessageBox(NULL, quitstr, "Run DOSBox-X?", MB_YESNO)==IDNO) {
|
|
fprintf(stderr, "No path chosen by user, exiting\n");
|
|
return 1;
|
|
}
|
|
} else if (workdiropt == "default") {
|
|
int ans=MessageBox(NULL, confirmstr, "DOSBox-X working directory", MB_YESNOCANCEL);
|
|
const wchar_t *input = path.c_str();
|
|
size_t size = (wcslen(input) + 1) * sizeof(wchar_t);
|
|
char *buffer = new char[size];
|
|
wcstombs(buffer, input, size);
|
|
if (ans == IDYES) {workdirsave=1;workdirsaveas=buffer;}
|
|
else if (ans == IDNO) workdirsave=2;
|
|
}
|
|
#else
|
|
char *cpath = tinyfd_selectFolderDialog("Select folder where to run emulation, which will become the DOSBox-X working directory:",default_folder);
|
|
std::string path = (cpath != NULL) ? cpath : "";
|
|
if (path.empty()) {
|
|
if (!systemmessagebox("Run DOSBox-X?", quitstr, "yesno","question", 1)) {
|
|
fprintf(stderr,"No path chosen by user, exiting\n");
|
|
return 1;
|
|
}
|
|
} else if (workdiropt == "default") {
|
|
confirmstr = "Do you want to use the selected folder as the DOSBox-X working directory in future sessions?\n\nIf you select Yes, DOSBox-X will not prompt for a folder again.\nIf you select No, DOSBox-X will always prompt for a folder when it runs.";
|
|
int ans=systemmessagebox("DOSBox-X working directory",confirmstr,"yesno", "question", 1);
|
|
if (ans == 1) {workdirsave=1;workdirsaveas=path;}
|
|
else if (ans == 0) workdirsave=2;
|
|
}
|
|
#endif
|
|
|
|
#if defined(MACOSX)
|
|
/* Thank you, no longer needed */
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
#endif
|
|
|
|
if(!path.empty()) {
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
/* our stat override makes wstat impossible */
|
|
if(_wchdir(path.c_str())) {
|
|
MessageBoxW(NULL, path.c_str(), L"Failed to change to the selected path.", MB_OK);
|
|
return 1;
|
|
}
|
|
#else
|
|
struct stat st;
|
|
if (stat(path.c_str(),&st)) {
|
|
fprintf(stderr,"Unable to stat path '%s'\n",path.c_str());
|
|
return 1;
|
|
}
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
fprintf(stderr,"Path '%s' is not S_ISDIR\n",path.c_str());
|
|
return 1;
|
|
}
|
|
if (chdir(path.c_str())) {
|
|
fprintf(stderr,"Failed to chdir() to path '%s', errno=%s\n",path.c_str(),strerror(errno));
|
|
return 1;
|
|
}
|
|
#endif
|
|
LOG_MSG("User selected folder '%s', making that the current working directory.\n",path.c_str());
|
|
control->opt_used_defaultdir = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
{
|
|
std::string tmp,config_path,config_combined;
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
|
|
{
|
|
DISPLAY_DEVICE dd;
|
|
unsigned int i = 0;
|
|
|
|
do {
|
|
memset(&dd, 0, sizeof(dd));
|
|
dd.cb = sizeof(dd);
|
|
if (!EnumDisplayDevices(NULL, i, &dd, 0)) break;
|
|
LOG_MSG("Win32 EnumDisplayDevices #%d: name=%s string=%s", i, dd.DeviceName, dd.DeviceString);
|
|
i++;
|
|
|
|
if (strstr(dd.DeviceString, "VirtualBox") != NULL)
|
|
isVirtualBox = true;
|
|
} while (1);
|
|
}
|
|
#endif
|
|
if (workdirsave>0&&configfile.size()) {
|
|
control->ParseConfigFile(configfile.c_str());
|
|
if (control->configfiles.size()) {
|
|
Section* tsec = control->GetSection("dosbox");
|
|
if (workdirsave==1 && workdirsaveas.size()) {
|
|
//tsec->HandleInputline("working directory option=custom");
|
|
tsec->HandleInputline(("working directory default="+workdirsaveas).c_str());
|
|
} else if (workdirsave==2)
|
|
tsec->HandleInputline("working directory option=autoprompt");
|
|
if (control->PrintConfig(configfile.c_str(), static_cast<Section_prop *>(tsec)->Get_bool("show advanced options")?1:-1)) {
|
|
workdirsave=0;
|
|
LOG_MSG("Saved the DOSBox-X working directory to %s", configfile.c_str());
|
|
}
|
|
control->ClearExtraData();
|
|
control->configfiles.clear();
|
|
}
|
|
}
|
|
|
|
/* -- Parse configuration files */
|
|
Cross::GetPlatformConfigDir(config_path);
|
|
|
|
/* -- -- first the user config file */
|
|
if (control->opt_userconf || workdirsave>0) {
|
|
tmp.clear();
|
|
Cross::GetPlatformConfigDir(config_path);
|
|
Cross::GetPlatformConfigName(tmp);
|
|
config_combined = config_path + tmp;
|
|
|
|
LOG(LOG_MISC,LOG_DEBUG)("Loading config file according to -userconf from %s",config_combined.c_str());
|
|
control->ParseConfigFile(config_combined.c_str());
|
|
if (!control->configfiles.size()) {
|
|
if (workdirsave>0) {
|
|
Section* tsec = control->GetSection("dosbox");
|
|
if (workdirsave==1 && workdirsaveas.size()) {
|
|
//tsec->HandleInputline("working directory option=custom");
|
|
tsec->HandleInputline(("working directory default="+workdirsaveas).c_str());
|
|
} else if (workdirsave==2)
|
|
tsec->HandleInputline("working directory option=autoprompt");
|
|
}
|
|
//Try to create the userlevel configfile.
|
|
tmp.clear();
|
|
Cross::CreatePlatformConfigDir(config_path);
|
|
Cross::GetPlatformConfigName(tmp);
|
|
config_combined = config_path + tmp;
|
|
|
|
LOG(LOG_MISC,LOG_DEBUG)("Attempting to write config file according to -userconf, to %s",config_combined.c_str());
|
|
if (control->PrintConfig(config_combined.c_str())) {
|
|
workdirsave = 0;
|
|
if (!control->opt_userconf) LOG_MSG("Saved the DOSBox-X working directory to %s", config_combined.c_str());
|
|
LOG(LOG_MISC,LOG_NORMAL)("Generating default configuration. Writing it to %s",config_combined.c_str());
|
|
//Load them as well. Makes relative paths much easier
|
|
control->ParseConfigFile(config_combined.c_str());
|
|
}
|
|
if (!control->opt_userconf) {control->ClearExtraData();control->configfiles.clear();}
|
|
}
|
|
}
|
|
if (workdirsave>0) LOG_MSG("Unable to save the DOSBox-X working directory.");
|
|
|
|
/* -- -- second the -conf switches from the command line */
|
|
for (size_t si=0;si < control->config_file_list.size();si++) {
|
|
std::string &cfg = control->config_file_list[si];
|
|
#if defined(WIN32) && defined(C_SDL2)
|
|
std::string localname = cfg;
|
|
if (!FileDirExistCP(cfg.c_str()) && FileDirExistUTF8(localname, cfg.c_str())) cfg = localname;
|
|
#endif
|
|
if (!control->ParseConfigFile(cfg.c_str())) {
|
|
// try to load it from the user directory
|
|
if (!control->ParseConfigFile((config_path + cfg).c_str())) {
|
|
LOG_MSG("CONFIG: Can't open specified config file: %s",cfg.c_str());
|
|
} else if (control->opt_eraseconf) {
|
|
LOG_MSG("Erase config file: %s\n", (config_path + cfg).c_str());
|
|
unlink((config_path + cfg).c_str());
|
|
}
|
|
} else if (control->opt_eraseconf) {
|
|
LOG_MSG("Erase config file: %s\n", cfg.c_str());
|
|
unlink(cfg.c_str());
|
|
}
|
|
}
|
|
|
|
if (!control->opt_defaultconf) {
|
|
/* -- -- if none found, use dosbox-x.conf or dosbox.conf */
|
|
if (!control->configfiles.size()) control->ParseConfigFile("dosbox-x.conf");
|
|
if (!control->configfiles.size()) control->ParseConfigFile("dosbox.conf");
|
|
if (!control->configfiles.size()) {
|
|
std::string exepath=GetDOSBoxXPath();
|
|
if (exepath.size()) {
|
|
control->ParseConfigFile((exepath + "dosbox-x.conf").c_str());
|
|
if (!control->configfiles.size()) control->ParseConfigFile((exepath + "dosbox.conf").c_str());
|
|
}
|
|
}
|
|
|
|
/* -- -- if none found, use userlevel conf */
|
|
if (!control->configfiles.size()) control->ParseConfigFile((config_path + "dosbox-x.conf").c_str());
|
|
if (!control->configfiles.size()) {
|
|
tmp.clear();
|
|
Cross::GetPlatformConfigName(tmp);
|
|
control->ParseConfigFile((config_path + tmp).c_str());
|
|
}
|
|
|
|
if (control->configfiles.size()) {
|
|
if (control->opt_eraseconf&&control->config_file_list.empty()) {
|
|
LOG_MSG("Erase config file: %s\n", control->configfiles.front().c_str());
|
|
unlink(control->configfiles.front().c_str());
|
|
}
|
|
if (usecfgdir) {
|
|
std::string configpath=control->configfiles.front();
|
|
size_t found=configpath.find_last_of("/\\");
|
|
if(found != string::npos) {
|
|
if(chdir(configpath.substr(0, found + 1).c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for .conf file.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Redirect existing PC-98 related settings from other sections to the [pc98] section if the latter is empty
|
|
Section_prop * pc98_section = static_cast<Section_prop *>(control->GetSection("pc98"));
|
|
assert(pc98_section != NULL);
|
|
const char * extra = const_cast<char*>(pc98_section->data.c_str());
|
|
if (!extra||!strlen(extra)) {
|
|
char linestr[CROSS_LEN+1], *p;
|
|
Section_prop * section = static_cast<Section_prop *>(control->GetSection("dosbox"));
|
|
extra = const_cast<char*>(section->data.c_str());
|
|
if (extra&&strlen(extra)) {
|
|
std::istringstream in(extra);
|
|
if (in) for (std::string line; std::getline(in, line); ) {
|
|
if (strncasecmp(line.c_str(), "pc-98 ", 6)) continue;
|
|
if (line.length()>CROSS_LEN) {
|
|
strncpy(linestr, line.c_str(), CROSS_LEN);
|
|
linestr[CROSS_LEN]=0;
|
|
} else
|
|
strcpy(linestr, line.c_str());
|
|
p=strchr(linestr, '=');
|
|
if (p!=NULL&&pc98_section->HandleInputline(line)) {
|
|
*p=0;
|
|
LOG_MSG("Redirected \"%s\" from [dosbox] to [pc98] section\n", trim(linestr));
|
|
}
|
|
}
|
|
}
|
|
section = static_cast<Section_prop *>(control->GetSection("dos"));
|
|
extra = const_cast<char*>(section->data.c_str());
|
|
if (extra&&strlen(extra)) {
|
|
std::istringstream in(extra);
|
|
if (in) for (std::string line; std::getline(in, line); ) {
|
|
if (strncasecmp(line.c_str(), "pc-98 ", 6)) continue;
|
|
if (line.length()>CROSS_LEN) {
|
|
strncpy(linestr, line.c_str(), CROSS_LEN);
|
|
linestr[CROSS_LEN]=0;
|
|
} else
|
|
strcpy(linestr, line.c_str());
|
|
p=strchr(linestr, '=');
|
|
if (p!=NULL&&pc98_section->HandleInputline(line)) {
|
|
*p=0;
|
|
LOG_MSG("Redirected \"%s\" from [dos] to [pc98] section\n", trim(linestr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Redirect existing TTF related settings from [render] section to the [ttf] section if the latter is empty
|
|
Section_prop * ttf_section = static_cast<Section_prop *>(control->GetSection("ttf"));
|
|
assert(ttf_section != NULL);
|
|
extra = const_cast<char*>(ttf_section->data.c_str());
|
|
if (!extra||!strlen(extra)) {
|
|
char linestr[CROSS_LEN+1], *p;
|
|
Section_prop * section = static_cast<Section_prop *>(control->GetSection("render"));
|
|
extra = const_cast<char*>(section->data.c_str());
|
|
if (extra&&strlen(extra)) {
|
|
std::istringstream in(extra);
|
|
if (in) for (std::string line; std::getline(in, line); ) {
|
|
if (strncasecmp(line.c_str(), "ttf.", 4)) continue;
|
|
if (line.length()>CROSS_LEN) {
|
|
strncpy(linestr, line.substr(4).c_str(), CROSS_LEN);
|
|
linestr[CROSS_LEN]=0;
|
|
} else
|
|
strcpy(linestr, line.substr(4).c_str());
|
|
p=strchr(linestr, '=');
|
|
if (p!=NULL&&ttf_section->HandleInputline(line.substr(4))) {
|
|
*p=0;
|
|
LOG_MSG("Redirected \"%s\" (\"ttf.%s\") from [render] to [ttf] section\n", trim(linestr), trim(linestr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Redirect existing video related settings from [dosbox] section to the [video] section if the latter is empty
|
|
Section_prop * video_section = static_cast<Section_prop *>(control->GetSection("video"));
|
|
assert(video_section != NULL);
|
|
extra = const_cast<char*>(video_section->data.c_str());
|
|
if (!extra||!strlen(extra)) {
|
|
char linestr[CROSS_LEN+1], *p;
|
|
Section_prop * section = static_cast<Section_prop *>(control->GetSection("dosbox"));
|
|
extra = const_cast<char*>(section->data.c_str());
|
|
if (extra&&strlen(extra)) {
|
|
std::istringstream in(extra);
|
|
if (in) for (std::string line; std::getline(in, line); ) {
|
|
if (!strstr(line.c_str(), "vmem")&&!strstr(line.c_str(), "vga")&&!strstr(line.c_str(), "video")&&!strstr(line.c_str(), "dac")&&!strstr(line.c_str(), "cga")&&!strstr(line.c_str(), "CGA")&&!strstr(line.c_str(), "vesa")&&!strstr(line.c_str(), "hpel")&&!strstr(line.c_str(), "hretrace")&&!strstr(line.c_str(), "debug line")&&!strstr(line.c_str(), "memory bit")&&!strstr(line.c_str(), "forcerate")&&!strstr(line.c_str(), "double-buffered")&&!strstr(line.c_str(), "vblank")&&!strstr(line.c_str(), "setmode")) continue;
|
|
if (line.length()>CROSS_LEN) {
|
|
strncpy(linestr, line.c_str(), CROSS_LEN);
|
|
linestr[CROSS_LEN]=0;
|
|
} else
|
|
strcpy(linestr, line.c_str());
|
|
p=strchr(linestr, '=');
|
|
if (p!=NULL&&video_section->HandleInputline(line)) {
|
|
*p=0;
|
|
LOG_MSG("Redirected \"%s\" from [dosbox] to [video] section\n", trim(linestr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Redirect existing files= setting from [dos] section to the [config] section if the latter is empty
|
|
Section_prop * config_section = static_cast<Section_prop *>(control->GetSection("config"));
|
|
assert(config_section != NULL);
|
|
extra = const_cast<char*>(config_section->data.c_str());
|
|
if (!extra||!strlen(extra)) {
|
|
char linestr[CROSS_LEN+1], *p;
|
|
Section_prop * section = static_cast<Section_prop *>(control->GetSection("dos"));
|
|
extra = const_cast<char*>(section->data.c_str());
|
|
if (extra&&strlen(extra)) {
|
|
std::istringstream in(extra);
|
|
if (in) for (std::string line; std::getline(in, line); ) {
|
|
if (strncasecmp(line.c_str(), "files", 5)&&strncasecmp(line.c_str(), "dos in hma", 10)) continue;
|
|
if (line.length()>CROSS_LEN) {
|
|
strncpy(linestr, line.c_str(), CROSS_LEN);
|
|
linestr[CROSS_LEN]=0;
|
|
} else
|
|
strcpy(linestr, line.c_str());
|
|
p=strchr(linestr, '=');
|
|
if (p!=NULL) {
|
|
if (!strncasecmp(line.c_str(), "dos in hma", 10)) {
|
|
if (!strcasecmp(trim(p+1), "true")) line="dos=high";
|
|
else if (!strcasecmp(trim(p+1), "false")) line="dos=low";
|
|
}
|
|
if (config_section->HandleInputline(line)) {
|
|
*p=0;
|
|
LOG_MSG("Redirected \"%s\" (\"%s\") from [dos] to [config] section\n", "dos", trim(linestr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Redirect NE2000 realnic settings to pcap section
|
|
Section_prop * ne2000_section = static_cast<Section_prop *>(control->GetSection("ne2000"));
|
|
Section_prop * pcap_section = static_cast<Section_prop *>(control->GetSection("ethernet, pcap"));
|
|
assert(ne2000_section != NULL);
|
|
assert(pcap_section != NULL);
|
|
std::istringstream in(ne2000_section->data.c_str());
|
|
if (in) for (std::string line; std::getline(in, line); ) {
|
|
size_t pcaptimeout_pos = line.find("pcaptimeout");
|
|
size_t realnic_pos = line.find("realnic");
|
|
size_t eq_pos = line.find("=");
|
|
if (pcaptimeout_pos != std::string::npos &&
|
|
eq_pos != std::string::npos &&
|
|
pcaptimeout_pos < eq_pos) {
|
|
pcap_section->HandleInputline("timeout=" + line.substr(eq_pos + 1, std::string::npos));
|
|
LOG_MSG("Migrated pcaptimeout from [ne2000] to [ethernet, pcap] section");
|
|
}
|
|
if (realnic_pos != std::string::npos &&
|
|
eq_pos != std::string::npos &&
|
|
realnic_pos < eq_pos) {
|
|
pcap_section->HandleInputline(line);
|
|
LOG_MSG("Migrated realnic from [ne2000] to [ethernet, pcap] section");
|
|
if (ne2000_section->Get_bool("ne2000")) {
|
|
LOG_MSG("Set ne2000 backend to pcap during migration");
|
|
Prop_string* backend_prop = static_cast<Prop_string*>(ne2000_section->Get_prop("backend"));
|
|
assert(backend_prop != NULL);
|
|
backend_prop->SetValue("pcap");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MSG_Add("PROGRAM_CONFIG_PROPERTY_ERROR","No such section or property.\n");
|
|
MSG_Add("PROGRAM_CONFIG_NO_PROPERTY","There is no property %s in section %s.\n");
|
|
MSG_Add("PROGRAM_CONFIG_SET_SYNTAX","The syntax for -set option is incorrect.\n");
|
|
for (auto it=control->opt_set.begin(); it!=control->opt_set.end();it++) { /* -set switches */
|
|
// add rest of command
|
|
std::string rest;
|
|
std::vector<std::string> pvars;
|
|
pvars.clear();
|
|
pvars.push_back(trim((char *)(*it).c_str()));
|
|
if (!strlen(pvars[0].c_str())) continue;
|
|
if (pvars[0][0]=='%'||pvars[0][0]=='\0'||pvars[0][0]=='#'||pvars[0][0]=='\n') continue;
|
|
|
|
|
|
// attempt to split off the first word
|
|
std::string::size_type spcpos = pvars[0].find_first_of(' ');
|
|
if (spcpos>1&&pvars[0].c_str()[spcpos-1]==',')
|
|
spcpos=pvars[0].find_first_of(' ', spcpos+1);
|
|
|
|
std::string::size_type equpos = pvars[0].find_first_of('=');
|
|
|
|
if ((equpos != std::string::npos) &&
|
|
((spcpos == std::string::npos) || (equpos < spcpos))) {
|
|
// If we have a '=' possibly before a ' ' split on the =
|
|
pvars.insert(pvars.begin()+1,pvars[0].substr(equpos+1));
|
|
pvars[0].erase(equpos);
|
|
// As we had a = the first thing must be a property now
|
|
Section* sec=control->GetSectionFromProperty(pvars[0].c_str());
|
|
if (!sec&&pvars[0].size()>4&&!strcasecmp(pvars[0].substr(0, 4).c_str(), "ttf.")) {
|
|
pvars[0].erase(0,4);
|
|
sec = control->GetSectionFromProperty(pvars[0].c_str());
|
|
}
|
|
if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName()));
|
|
else {
|
|
LOG_MSG("%s", MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
|
|
continue;
|
|
}
|
|
// order in the vector should be ok now
|
|
} else {
|
|
if (equpos != std::string::npos && spcpos < equpos) {
|
|
// ' ' before a possible '=', split on the ' '
|
|
pvars.insert(pvars.begin()+1,pvars[0].substr(spcpos+1));
|
|
pvars[0].erase(spcpos);
|
|
}
|
|
// check if the first parameter is a section or property
|
|
Section* sec = control->GetSection(pvars[0].c_str());
|
|
if (!sec) {
|
|
// not a section: little duplicate from above
|
|
sec=control->GetSectionFromProperty(pvars[0].c_str());
|
|
if (sec) pvars.insert(pvars.begin(),std::string(sec->GetName()));
|
|
else {
|
|
LOG_MSG("%s", MSG_Get("PROGRAM_CONFIG_PROPERTY_ERROR"));
|
|
continue;
|
|
}
|
|
} else {
|
|
// first of pvars is most likely a section, but could still be gus
|
|
// have a look at the second parameter
|
|
if (pvars.size() < 2) {
|
|
LOG_MSG("%s", MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
|
|
continue;
|
|
}
|
|
std::string::size_type equpos2 = pvars[1].find_first_of('=');
|
|
if (equpos2 != std::string::npos) {
|
|
// split on the =
|
|
pvars.insert(pvars.begin()+2,pvars[1].substr(equpos2+1));
|
|
pvars[1].erase(equpos2);
|
|
}
|
|
// is this a property?
|
|
Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str());
|
|
if (!sec2) {
|
|
// not a property,
|
|
Section* sec3 = control->GetSectionFromProperty(pvars[0].c_str());
|
|
if (sec3) {
|
|
// section and property name are identical
|
|
pvars.insert(pvars.begin(),pvars[0]);
|
|
} // else has been checked above already
|
|
}
|
|
}
|
|
}
|
|
if(pvars.size() < 3) {
|
|
LOG_MSG("%s", MSG_Get("PROGRAM_CONFIG_SET_SYNTAX"));
|
|
continue;
|
|
}
|
|
// check if the property actually exists in the section
|
|
Section* sec2 = control->GetSectionFromProperty(pvars[1].c_str());
|
|
if (!sec2) {
|
|
LOG_MSG(MSG_Get("PROGRAM_CONFIG_NO_PROPERTY"),
|
|
pvars[1].c_str(),pvars[0].c_str());
|
|
continue;
|
|
}
|
|
// Input has been parsed (pvar[0]=section, [1]=property, [2]=value)
|
|
// now execute
|
|
Section* tsec = control->GetSection(pvars[0]);
|
|
std::string value(pvars[2]);
|
|
//Due to parsing there can be a = at the start of value.
|
|
while (value.size() && (value.at(0) ==' ' ||value.at(0) =='=') ) value.erase(0,1);
|
|
for(Bitu i = 3; i < pvars.size(); i++) value += (std::string(" ") + pvars[i]);
|
|
std::string inputline = pvars[1] + "=" + value;
|
|
|
|
bool change_success = tsec->HandleInputline(inputline.c_str());
|
|
if (!change_success&&!value.empty()) LOG_MSG("Cannot set \"%s\"\n", inputline.c_str());
|
|
}
|
|
|
|
{
|
|
Section_prop *section = static_cast<Section_prop *>(control->GetSection("dosbox"));
|
|
workdiropt = section->Get_string("working directory option");
|
|
workdirdef = section->Get_path("working directory default")->realpath;
|
|
std::string resolvestr = section->Get_string("resolve config path");
|
|
resolveopt = resolvestr=="true"||resolvestr=="1"?1:(resolvestr=="dosvar"?2:(resolvestr=="tilde"?3:0));
|
|
void ResolvePath(std::string& in);
|
|
ResolvePath(workdirdef);
|
|
if (((workdiropt == "custom" && !control->opt_used_defaultdir) || workdiropt == "force") && workdirdef.size()) {
|
|
if(chdir(workdirdef.c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'custom' or 'force'.");
|
|
}
|
|
} else if (workdiropt == "userconfig") {
|
|
std::string config_path;
|
|
Cross::GetPlatformConfigDir(config_path);
|
|
if(config_path.size()) {
|
|
if(chdir(config_path.c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'userconfig'.");
|
|
}
|
|
}
|
|
} else if (workdiropt == "program" && exepath.size()) {
|
|
if(chdir(exepath.c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'program'.");
|
|
}
|
|
} else if (workdiropt == "config" && control->configfiles.size()) {
|
|
std::string configpath=control->configfiles.front();
|
|
size_t found=configpath.find_last_of("/\\");
|
|
if(found != string::npos) {
|
|
if(chdir(configpath.substr(0, found + 1).c_str()) == -1) {
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to change directories for workdiropt 'config'.");
|
|
}
|
|
}
|
|
}
|
|
|
|
char cwd[512] = {0};
|
|
if(getcwd(cwd, sizeof(cwd) - 1))
|
|
LOG_MSG("DOSBox-X's working directory: %s\n", cwd);
|
|
else
|
|
LOG(LOG_GUI, LOG_ERROR)("sdlmain.cpp main() failed to get the current working directory.");
|
|
|
|
const char *imestr = section->Get_string("ime");
|
|
enableime = !strcasecmp(imestr, "true") || !strcasecmp(imestr, "1");
|
|
if (!strcasecmp(imestr, "auto")) {
|
|
const char *machine = section->Get_string("machine");
|
|
if (!strcasecmp(machine, "pc98") || !strcasecmp(machine, "pc9801") || !strcasecmp(machine, "pc9821") || !strcasecmp(machine, "jega") || strcasecmp(static_cast<Section_prop *>(control->GetSection("dosv"))->Get_string("dosv"), "off")) enableime = true;
|
|
else {
|
|
force_conversion = true;
|
|
int cp=dos.loaded_codepage;
|
|
if (InitCodePage() && isDBCSCP()) enableime = true;
|
|
force_conversion = false;
|
|
dos.loaded_codepage=cp;
|
|
}
|
|
}
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
if (!enableime) ImmDisableIME((DWORD)(-1));
|
|
#endif
|
|
}
|
|
|
|
#if (ENVIRON_LINKED)
|
|
/* -- parse environment block (why?) */
|
|
control->ParseEnv(environ);
|
|
#endif
|
|
|
|
/* -- initialize logging first, so that higher level inits can report problems to the log file */
|
|
LOG::Init();
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
|
|
{
|
|
DISPLAY_DEVICE dd;
|
|
unsigned int i = 0;
|
|
|
|
do {
|
|
memset(&dd, 0, sizeof(dd));
|
|
dd.cb = sizeof(dd);
|
|
if (!EnumDisplayDevices(NULL, i, &dd, 0)) break;
|
|
LOG_MSG("Win32 EnumDisplayDevices #%d: name=%s string=%s", i, dd.DeviceName, dd.DeviceString);
|
|
i++;
|
|
} while (1);
|
|
}
|
|
|
|
if (isVirtualBox) LOG_MSG("Win32 VirtualBox graphics adapter detected");
|
|
#endif
|
|
|
|
/* -- Welcome to DOSBox-X! */
|
|
LOG_MSG("DOSBox-X version %s ("
|
|
#if defined(WIN32)
|
|
"Windows"
|
|
#elif defined(HX_DOS)
|
|
"DOS"
|
|
#elif defined(LINUX)
|
|
"Linux"
|
|
#elif defined(MACOSX)
|
|
"macOS"
|
|
#else
|
|
""
|
|
#endif
|
|
" %s)",VERSION,SDL_STRING);
|
|
LOG(LOG_MISC,LOG_NORMAL)(("Copyright 2011-%s The DOSBox-X Team. Project maintainer: joncampbell123 (The Great Codeholio). DOSBox-X published under GNU GPL."),std::string(COPYRIGHT_END_YEAR).c_str());
|
|
|
|
#if defined(MACOSX)
|
|
LOG_MSG("macOS EXE path: %s",MacOSXEXEPath.c_str());
|
|
LOG_MSG("macOS Resource path: %s",MacOSXResPath.c_str());
|
|
#endif
|
|
|
|
/* -- [debug] setup console */
|
|
#if C_DEBUG
|
|
# if defined(WIN32) && !defined(HX_DOS)
|
|
/* Can't disable the console with debugger enabled */
|
|
if (control->opt_noconsole) {
|
|
LOG(LOG_MISC,LOG_DEBUG)("-noconsole: hiding Win32 console window");
|
|
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
|
DestroyWindow(GetConsoleWindow());
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
/* -- Windows: set console control handler */
|
|
SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleEventHandler,TRUE);
|
|
#endif
|
|
|
|
#if !defined(C_SDL2)
|
|
{
|
|
int id, major, minor;
|
|
|
|
DOSBox_CheckOS(id, major, minor);
|
|
if (id == 1) menu.compatible=true;
|
|
|
|
/* use all variables to shut up the compiler about unused vars */
|
|
LOG(LOG_MISC,LOG_DEBUG)("DOSBox-X CheckOS results: id=%u major=%u minor=%u",id,major,minor);
|
|
}
|
|
#endif
|
|
|
|
/* -- SDL init hackery */
|
|
#if SDL_VERSION_ATLEAST(1, 2, 14)
|
|
/* hack: On debian/ubuntu with older libsdl version as they have done this themselves, but then differently.
|
|
* with this variable they will work correctly. I've only tested the 1.2.14 behaviour against the windows version of libsdl */
|
|
putenv(const_cast<char*>("SDL_DISABLE_LOCK_KEYS=1"));
|
|
LOG(LOG_GUI,LOG_DEBUG)("SDL 1.2.14 hack: SDL_DISABLE_LOCK_KEYS=1");
|
|
#endif
|
|
|
|
std::string videodriver = static_cast<Section_prop *>(control->GetSection("sdl"))->Get_string("videodriver");
|
|
if (videodriver.size()) {
|
|
videodriver = "SDL_VIDEODRIVER="+videodriver;
|
|
putenv((char *)videodriver.c_str());
|
|
}
|
|
|
|
#ifdef WIN32
|
|
/* hack: Encourage SDL to use windib if not otherwise specified */
|
|
if (getenv("SDL_VIDEODRIVER") == NULL) {
|
|
#if defined(C_SDL2)
|
|
LOG(LOG_GUI, LOG_DEBUG)("Win32 hack: setting SDL_VIDEODRIVER=windows because environ variable is not set");
|
|
putenv("SDL_VIDEODRIVER=windows");
|
|
#else
|
|
LOG(LOG_GUI,LOG_DEBUG)("Win32 hack: setting SDL_VIDEODRIVER=windib because environ variable is not set");
|
|
putenv("SDL_VIDEODRIVER=windib");
|
|
#endif
|
|
sdl.using_windib=true;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WIN32) && defined(C_SDL2)
|
|
/* HACK: WASAPI output on Windows 10 isn't working... */
|
|
if (getenv("SDL_AUDIODRIVER") == NULL) {
|
|
LOG(LOG_GUI, LOG_DEBUG)("Win32: using directsound audio driver");
|
|
putenv("SDL_AUDIODRIVER=directsound");
|
|
}
|
|
if (getenv("SDL_WINDOWS_NO_CLOSE_ON_ALT_F4") == NULL)
|
|
putenv("SDL_WINDOWS_NO_CLOSE_ON_ALT_F4=1");
|
|
#endif
|
|
|
|
sdl.init_ignore = true;
|
|
|
|
{
|
|
Section_prop *section = static_cast<Section_prop *>(control->GetSection("dosbox"));
|
|
assert(section != NULL);
|
|
|
|
// boot-time option whether or not to report ourself as "DPI aware" to Windows so the
|
|
// DWM doesn't upscale our window for backwards compat.
|
|
{
|
|
std::string dpiw = section->Get_string("dpi aware");
|
|
|
|
if (dpiw == "1" || dpiw == "true") {
|
|
dpi_aware_enable = true;
|
|
}
|
|
else if (dpiw == "0" || dpiw == "false") {
|
|
dpi_aware_enable = false;
|
|
}
|
|
else { /* auto */
|
|
#if defined(MACOSX)
|
|
/* Try not to look extra tiny on Retina displays */
|
|
dpi_aware_enable = false;
|
|
#elif defined(WIN32) && !defined(HX_DOS)
|
|
dpi_aware_enable = true;
|
|
#if !defined(C_SDL2)
|
|
if (!control->opt_fullscreen && !static_cast<Section_prop *>(control->GetSection("sdl"))->Get_bool("fullscreen"))
|
|
#endif
|
|
{
|
|
UINT dpi=0;
|
|
HMODULE __user32 = GetModuleHandle("USER32.DLL");
|
|
if (__user32) {
|
|
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
|
#ifndef DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
|
|
#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2)
|
|
#endif
|
|
DPI_AWARENESS_CONTEXT (WINAPI *__SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT) = NULL;
|
|
UINT (WINAPI *__GetDpiForSystem)() = NULL;
|
|
__SetThreadDpiAwarenessContext = (DPI_AWARENESS_CONTEXT (WINAPI *)(DPI_AWARENESS_CONTEXT))GetProcAddress(__user32,"SetThreadDpiAwarenessContext");
|
|
__GetDpiForSystem = (UINT (WINAPI *)())GetProcAddress(__user32,"GetDpiForSystem");
|
|
if (__SetThreadDpiAwarenessContext && __GetDpiForSystem) {
|
|
DPI_AWARENESS_CONTEXT previousDpiContext = __SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
|
|
dpi=__GetDpiForSystem();
|
|
if (previousDpiContext!=NULL) __SetThreadDpiAwarenessContext(previousDpiContext);
|
|
}
|
|
}
|
|
if (dpi&&dpi/96>1) dpi_aware_enable = false;
|
|
}
|
|
#else
|
|
dpi_aware_enable = true;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
/* Windows Vista/7/8/10 DPI awareness. If we don't tell Windows we're high DPI aware, the DWM will
|
|
* upscale our window to emulate a 96 DPI display which on high res screen will make our UI look blurry.
|
|
* But we obey the user if they don't want us to do that. */
|
|
Windows_DPI_Awareness_Init();
|
|
#endif
|
|
#if defined(MACOSX) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
/* Our SDL1 in-tree library has a High DPI awareness function for macOS now */
|
|
if (!control->opt_disable_dpi_awareness)
|
|
sdl1_hax_macosx_highdpi_set_enable(dpi_aware_enable);
|
|
#endif
|
|
|
|
#ifdef MACOSX
|
|
macosx_detect_nstouchbar();/*assigns to has_touch_bar_support*/
|
|
if (has_touch_bar_support) {
|
|
LOG_MSG("macOS: NSTouchBar support detected in system");
|
|
macosx_init_touchbar();
|
|
}
|
|
|
|
extern void macosx_init_dock_menu(void);
|
|
macosx_init_dock_menu();
|
|
|
|
void qz_set_match_monitor_cb(void);
|
|
qz_set_match_monitor_cb();
|
|
#endif
|
|
|
|
/* -- SDL init */
|
|
if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) >= 0)
|
|
sdl.inited = true;
|
|
else
|
|
E_Exit("Can't init SDL %s",SDL_GetError());
|
|
|
|
/* -- -- decide whether to show menu in GUI */
|
|
if (control->opt_nogui || menu.compatible)
|
|
menu.gui=false;
|
|
|
|
/* -- -- helpful advice */
|
|
LOG(LOG_GUI,LOG_NORMAL)("Press Ctrl-F10 to capture/release mouse, Alt-F10 for configuration.");
|
|
|
|
/* -- -- other steps to prepare SDL window/output */
|
|
SDL_Prepare();
|
|
|
|
/* -- NOW it is safe to send change events to SDL */
|
|
{
|
|
Section_prop *sdl_sec = static_cast<Section_prop*>(control->GetSection("sdl"));
|
|
sdl_sec->onpropchange.push_back(&SDL_OnSectionPropChange);
|
|
}
|
|
|
|
/* -- -- Keyboard layout detection and setup */
|
|
KeyboardLayoutDetect();
|
|
SetMapperKeyboardLayout(host_keyboard_layout);
|
|
|
|
/* -- -- Initialise Joystick and CD-ROM seperately. This way we can warn when it fails instead of exiting the application */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Initializing SDL joystick subsystem...");
|
|
glide.fullscreen = &sdl.desktop.fullscreen;
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0) {
|
|
sdl.num_joysticks = (Bitu)SDL_NumJoysticks();
|
|
LOG(LOG_MISC,LOG_DEBUG)("SDL reports %u joysticks",(unsigned int)sdl.num_joysticks);
|
|
}
|
|
else {
|
|
LOG(LOG_GUI,LOG_WARN)("Failed to init joystick support");
|
|
sdl.num_joysticks = 0;
|
|
}
|
|
|
|
#if defined(C_SDL2)
|
|
if (SDL_CDROMInit() < 0) {
|
|
#else
|
|
if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0) {
|
|
#endif
|
|
LOG(LOG_GUI,LOG_WARN)("Failed to init CD-ROM support");
|
|
}
|
|
|
|
/* must redraw after modeset */
|
|
sdl.must_redraw_all = true;
|
|
sdl.deferred_resize = false;
|
|
|
|
/* assume L+R ALT keys are up */
|
|
sdl.laltstate = SDL_KEYUP;
|
|
sdl.raltstate = SDL_KEYUP;
|
|
sdl.lctrlstate = SDL_KEYUP;
|
|
sdl.rctrlstate = SDL_KEYUP;
|
|
sdl.lshiftstate = SDL_KEYUP;
|
|
sdl.rshiftstate = SDL_KEYUP;
|
|
|
|
#if defined(WIN32) && defined(C_SDL2)
|
|
char* sdl_videodrv = getenv("SDL_VIDEODRIVER");
|
|
if (sdl_videodrv != NULL && !strcmp(sdl_videodrv,"windows")) sdl.using_windib = true;
|
|
#elif defined(WIN32) && !defined(C_SDL2)
|
|
# if SDL_VERSION_ATLEAST(1, 2, 10)
|
|
sdl.using_windib=true;
|
|
# else
|
|
sdl.using_windib=false;
|
|
# endif
|
|
|
|
if (getenv("SDL_VIDEODRIVER")==NULL) {
|
|
putenv("SDL_VIDEODRIVER=windib");
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) E_Exit("Can't init SDL Video %s",SDL_GetError());
|
|
sdl.using_windib=true;
|
|
} else {
|
|
char* sdl_videodrv = getenv("SDL_VIDEODRIVER");
|
|
|
|
LOG(LOG_MISC,LOG_DEBUG)("Win32: SDL_VIDEODRIVER is '%s', so I will obey it",sdl_videodrv);
|
|
if (strcmp(sdl_videodrv,"directx")==0) sdl.using_windib = false;
|
|
else if (strcmp(sdl_videodrv,"windib")==0) sdl.using_windib = true;
|
|
}
|
|
#endif
|
|
|
|
/* GUI init */
|
|
GUI_StartUp();
|
|
|
|
/* FIXME: We need a more general "init list", outside of the section-based design,
|
|
* that we then execute serially here. */
|
|
/* TODO: Each section currently uses "AddDestroyFunction" per section. We need to
|
|
* change over that code to a global destroy callback list instead. */
|
|
/* TODO: Get rid of "init" and "destroy" callback lists per section. */
|
|
/* TODO: Add a global (within the Config object) init and destroy callback list.
|
|
* On each call, init functions are added to the end of the list, and
|
|
* destroy functions added to the beginning of the list. That way, init
|
|
* is lowest level to highest, destroy is highest level to lowest. */
|
|
/* TODO: Config object should also have a "reset" callback list. On system
|
|
* reset each device would be notified so that it can emulate hardware
|
|
* reset (the RESET line on ISA/PCI bus), lowest level to highest. */
|
|
/* TODO: Each "init" function should do the work of getting the section object,
|
|
* whatever section it wants to read, instead of us doing the work. When
|
|
* that's complete, the call to init should be without parameters (void).
|
|
* The hope is that the init functions can read whatever sections it wants,
|
|
* both newer DOSBox-X sections and existing DOSBox (mainline) compatible
|
|
* sections. */
|
|
|
|
/* The order is important here:
|
|
* Init functions are called low-level first to high level last,
|
|
* because some init functions rely on others. */
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
AllocCallback1();
|
|
|
|
if (control->opt_editconf.length() != 0)
|
|
launcheditor(control->opt_editconf);
|
|
if (control->opt_opencaptures.length() != 0)
|
|
launchcaptures(control->opt_opencaptures);
|
|
if (control->opt_opensaves.length() != 0)
|
|
launchsaves(control->opt_opensaves);
|
|
|
|
{
|
|
#if defined(WIN32) && !defined(C_SDL2)
|
|
Section_prop *sec = static_cast<Section_prop *>(control->GetSection("dosbox"));
|
|
enable_hook_special_keys = sec->Get_bool("keyboard hook");
|
|
#endif
|
|
|
|
/* Some extra SDL Functions */
|
|
Section_prop* sdl_sec = static_cast<Section_prop*>(control->GetSection("sdl"));
|
|
|
|
if (control->opt_fullscreen || sdl_sec->Get_bool("fullscreen")) {
|
|
LOG(LOG_MISC, LOG_DEBUG)("Going fullscreen immediately, during startup");
|
|
|
|
#if defined(WIN32)
|
|
DOSBox_SetSysMenu();
|
|
#endif
|
|
//only switch if not already in fullscreen
|
|
if (!sdl.desktop.fullscreen) GFX_SwitchFullScreen();
|
|
|
|
/* Setup Mouse correctly if fullscreen */
|
|
if(sdl.desktop.fullscreen&&sdl.mouse.autoenable) GFX_CaptureMouse();
|
|
}
|
|
|
|
// Shows menu bar (window)
|
|
menu.startup = true;
|
|
|
|
menu.showrt = control->opt_showrt||sdl_sec->Get_bool("showdetails");
|
|
menu.hidecycles = (control->opt_showcycles||sdl_sec->Get_bool("showdetails") ? false : true);
|
|
}
|
|
|
|
/* Start up main machine */
|
|
|
|
MAPPER_StartUp();
|
|
DOSBOX_InitTickLoop();
|
|
DOSBOX_RealInit();
|
|
|
|
/* at this point: If the machine type is PC-98, and the mapper keyboard layout was "Japanese",
|
|
* then change the mapper layout to "Japanese PC-98" */
|
|
if (host_keyboard_layout == DKM_JPN && IS_PC98_ARCH)
|
|
SetMapperKeyboardLayout(DKM_JPN_PC98);
|
|
|
|
RENDER_Init();
|
|
CAPTURE_Init();
|
|
IO_Init();
|
|
HARDWARE_Init();
|
|
Init_AddressLimitAndGateMask(); /* <- need to init address mask so Init_RAM knows the maximum amount of RAM possible */
|
|
Init_MemoryAccessArray(); /* <- NTS: In DOSBox-X this is the "cache" of devices that responded to memory access */
|
|
Init_A20_Gate(); // FIXME: Should be handled by motherboard!
|
|
Init_PS2_Port_92h(); // FIXME: Should be handled by motherboard!
|
|
Init_RAM();
|
|
Init_DMA();
|
|
Init_PIC();
|
|
TIMER_Init();
|
|
PCIBUS_Init();
|
|
PAGING_Init(); /* <- NTS: At this time, must come before memory init because paging is so well integrated into emulation code */
|
|
CMOS_Init();
|
|
ROMBIOS_Init();
|
|
CALLBACK_Init(); /* <- NTS: This relies on ROM BIOS allocation and it must happen AFTER ROMBIOS init */
|
|
#if C_DEBUG
|
|
DEBUG_Init(); /* <- NTS: Relies on callback system */
|
|
#endif
|
|
Init_VGABIOS();
|
|
VOODOO_Init();
|
|
GLIDE_Init();
|
|
PROGRAMS_Init(); /* <- NTS: Does not init programs, it inits the callback used later when creating the .COM programs on drive Z: */
|
|
PCSPEAKER_Init();
|
|
TANDYSOUND_Init();
|
|
MPU401_Init();
|
|
MIXER_Init();
|
|
MIDI_Init();
|
|
{
|
|
DOSBoxMenu::item *item;
|
|
|
|
MAPPER_AddHandler(Sendkeymapper, MK_delete, MMODHOST, "sendkey_mapper", "Send special key", &item);
|
|
item->set_text("Send special key");
|
|
}
|
|
CPU_Init();
|
|
#if C_FPU
|
|
FPU_Init();
|
|
#endif
|
|
VGA_Init();
|
|
ISAPNP_Cfg_Init();
|
|
FDC_Primary_Init();
|
|
KEYBOARD_Init();
|
|
SBLASTER_Init();
|
|
JOYSTICK_Init();
|
|
PS1SOUND_Init();
|
|
DISNEY_Init();
|
|
GUS_Init();
|
|
IDE_Init();
|
|
INNOVA_Init();
|
|
BIOS_Init();
|
|
INT10_Init();
|
|
SERIAL_Init();
|
|
DONGLE_Init();
|
|
#if C_PRINTER
|
|
PRINTER_Init();
|
|
#endif
|
|
PARALLEL_Init();
|
|
NE2K_Init();
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
/* If PCjr emulation, map cartridge ROM */
|
|
if (machine == MCH_PCJR)
|
|
Init_PCJR_CartridgeROM();
|
|
|
|
/* let's assume motherboards are sane on boot because A20 gate is ENABLED on first boot */
|
|
MEM_A20_Enable(true);
|
|
|
|
#if C_DEBUG
|
|
{
|
|
DOSBoxMenu::item *item;
|
|
/* Add some keyhandlers */
|
|
MAPPER_AddHandler(DEBUG_Enable_Handler,
|
|
#if defined(MACOSX)
|
|
// MacOS NOTE: ALT-F12 to launch debugger. pause maps to F16 on macOS,
|
|
// which is not easy to input on a modern mac laptop
|
|
MK_f12
|
|
#else
|
|
MK_pause
|
|
#endif
|
|
,MMOD2,"debugger","Show debugger",&item);
|
|
item->set_text("Start DOSBox-X Debugger");
|
|
|
|
#if defined(MACOSX) || defined(LINUX)
|
|
/* Mac OS X does not have a console for us to just allocate on a whim like Windows does.
|
|
So the debugger interface is useless UNLESS the user has started us from a terminal
|
|
(whether over SSH or from the Terminal app).
|
|
|
|
Linux/X11 also does not have a console we can allocate on a whim. You either run
|
|
this program from XTerm for the debugger, or not. */
|
|
bool allow = true;
|
|
|
|
if (!isatty(0) || !isatty(1) || !isatty(2))
|
|
allow = false;
|
|
|
|
mainMenu.get_item("mapper_debugger").enable(allow).refresh_item(mainMenu);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/* OS init now */
|
|
DOS_Init();
|
|
DRIVES_Init();
|
|
DOS_KeyboardLayout_Init();
|
|
MOUSE_Init(); // FIXME: inits INT 15h and INT 33h at the same time. Also uses DOS_GetMemory() which is why DOS_Init must come first
|
|
XMS_Init();
|
|
EMS_Init();
|
|
AUTOEXEC_Init();
|
|
#if C_IPX
|
|
IPX_Init();
|
|
#endif
|
|
MSCDEX_Init();
|
|
CDROM_Image_Init();
|
|
|
|
/* Init memhandle system. This part is used by DOSBox-X's XMS/EMS emulation to associate handles
|
|
* per page. FIXME: I would like to push this down to the point that it's never called until
|
|
* XMS/EMS emulation needs it. I would also like the code to free the mhandle array immediately
|
|
* upon booting into a guest OS, since memory handles no longer have meaning in the guest OS
|
|
* memory layout. */
|
|
Init_MemHandles();
|
|
|
|
/* finally, the mapper */
|
|
MAPPER_Init();
|
|
AllocCallback2();
|
|
MSG_Init();
|
|
|
|
/* stop at this point, and show the configuration tool/mapper editor, if instructed */
|
|
if (control->opt_startui) {
|
|
LOG(LOG_MISC,LOG_DEBUG)("Running Configuration Tool, during startup, as instructed");
|
|
int cp=dos.loaded_codepage;
|
|
if (!cp) InitCodePage();
|
|
GUI_Run(false);
|
|
dos.loaded_codepage=cp;
|
|
}
|
|
if (control->opt_startmapper) {
|
|
LOG(LOG_MISC,LOG_DEBUG)("Running Mapper Editor, during startup, as instructed");
|
|
int cp=dos.loaded_codepage;
|
|
if (!cp) InitCodePage();
|
|
MAPPER_RunInternal();
|
|
dos.loaded_codepage=cp;
|
|
}
|
|
|
|
char name[6]="slot0";
|
|
if (!control->opt_silent)
|
|
for (unsigned int i=0; i<SaveState::SLOT_COUNT; i++) {
|
|
name[4]='0'+i;
|
|
std::string command=SaveState::instance().getName(page*SaveState::SLOT_COUNT+i);
|
|
std::string str=std::string(MSG_Get("SLOT"))+" "+to_string(page*SaveState::SLOT_COUNT+i+1)+(command==""?"":" "+command);
|
|
mainMenu.get_item(name).set_text(str.c_str());
|
|
}
|
|
mainMenu.get_item("wheel_updown").check(wheel_key==1).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_leftright").check(wheel_key==2).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_pageupdown").check(wheel_key==3).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_ctrlupdown").check(wheel_key==4).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_ctrlleftright").check(wheel_key==5).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_ctrlpageupdown").check(wheel_key==6).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_ctrlwz").check(wheel_key==7).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_none").check(wheel_key==0).refresh_item(mainMenu);
|
|
mainMenu.get_item("wheel_guest").check(wheel_guest).refresh_item(mainMenu);
|
|
mainMenu.get_item("sendkey_mapper_winlogo").check(sendkeymap==1).refresh_item(mainMenu);
|
|
mainMenu.get_item("sendkey_mapper_winmenu").check(sendkeymap==2).refresh_item(mainMenu);
|
|
mainMenu.get_item("sendkey_mapper_alttab").check(sendkeymap==3).refresh_item(mainMenu);
|
|
mainMenu.get_item("sendkey_mapper_ctrlesc").check(sendkeymap==4).refresh_item(mainMenu);
|
|
mainMenu.get_item("sendkey_mapper_ctrlbreak").check(sendkeymap==5).refresh_item(mainMenu);
|
|
mainMenu.get_item("sendkey_mapper_cad").check(!sendkeymap).refresh_item(mainMenu);
|
|
mainMenu.get_item("hostkey_ctrlalt").check(hostkeyalt==1).refresh_item(mainMenu);
|
|
mainMenu.get_item("hostkey_ctrlshift").check(hostkeyalt==2).refresh_item(mainMenu);
|
|
mainMenu.get_item("hostkey_altshift").check(hostkeyalt==3).refresh_item(mainMenu);
|
|
std::string mapper_keybind = mapper_event_keybind_string("host");
|
|
if (mapper_keybind.empty()) mapper_keybind = "unbound";
|
|
std::string text=mainMenu.get_item("hostkey_mapper").get_text();
|
|
std::size_t found = text.find(":");
|
|
if (found!=std::string::npos) text = text.substr(0, found);
|
|
mainMenu.get_item("hostkey_mapper").check(hostkeyalt==0).set_text(text+": "+mapper_keybind).refresh_item(mainMenu);
|
|
|
|
bool MENU_get_swapstereo(void);
|
|
mainMenu.get_item("mixer_swapstereo").check(MENU_get_swapstereo()).refresh_item(mainMenu);
|
|
|
|
bool MENU_get_mute(void);
|
|
mainMenu.get_item("mixer_mute").check(MENU_get_mute()).refresh_item(mainMenu);
|
|
|
|
mainMenu.get_item("mapper_fscaler").check(render.scale.forced).refresh_item(mainMenu);
|
|
mainMenu.get_item("3dfx_voodoo").check(strcasecmp(static_cast<Section_prop *>(control->GetSection("voodoo"))->Get_string("voodoo_card"), "false")).refresh_item(mainMenu);
|
|
mainMenu.get_item("3dfx_glide").check(addovl).refresh_item(mainMenu);
|
|
|
|
mainMenu.get_item("debug_logint21").check(log_int21).refresh_item(mainMenu);
|
|
mainMenu.get_item("debug_logfileio").check(log_fileio).refresh_item(mainMenu);
|
|
|
|
mainMenu.get_item("sync_host_datetime").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("vga_9widetext").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("doublescan").enable(!IS_PC98_ARCH);
|
|
|
|
blinking=static_cast<Section_prop *>(control->GetSection("video"))->Get_bool("high intensity blinking");
|
|
mainMenu.get_item("text_background").enable(!IS_PC98_ARCH&&machine!=MCH_CGA).check(!blinking).refresh_item(mainMenu);
|
|
mainMenu.get_item("text_blinking").enable(!IS_PC98_ARCH&&machine!=MCH_CGA).check(blinking).refresh_item(mainMenu);
|
|
mainMenu.get_item("line_80x25").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("line_80x43").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("line_80x50").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("line_80x60").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("line_132x25").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("line_132x43").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("line_132x50").enable(!IS_PC98_ARCH);
|
|
mainMenu.get_item("line_132x60").enable(!IS_PC98_ARCH);
|
|
#if defined(USE_TTF)
|
|
mainMenu.get_item("mapper_aspratio").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_1_1").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_3_2").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_4_3").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_16_9").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_16_10").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_18_10").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_original").enable(!TTF_using());
|
|
mainMenu.get_item("video_ratio_set").enable(!TTF_using());
|
|
mainMenu.get_item("mapper_incsize").enable(TTF_using());
|
|
mainMenu.get_item("mapper_decsize").enable(TTF_using());
|
|
mainMenu.get_item("mapper_resetcolor").enable(TTF_using());
|
|
mainMenu.get_item("ttf_showbold").enable(TTF_using()).check(showbold);
|
|
mainMenu.get_item("ttf_showital").enable(TTF_using()).check(showital);
|
|
mainMenu.get_item("ttf_showline").enable(TTF_using()).check(showline);
|
|
mainMenu.get_item("ttf_showsout").enable(TTF_using()).check(showsout);
|
|
mainMenu.get_item("ttf_wpno").enable(TTF_using()).check(!wpType);
|
|
mainMenu.get_item("ttf_wpwp").enable(TTF_using()).check(wpType==1);
|
|
mainMenu.get_item("ttf_wpws").enable(TTF_using()).check(wpType==2);
|
|
mainMenu.get_item("ttf_wpxy").enable(TTF_using()).check(wpType==3);
|
|
mainMenu.get_item("ttf_wpfe").enable(TTF_using()).check(wpType==4);
|
|
mainMenu.get_item("ttf_blinkc").enable(TTF_using()).check(blinkCursor>-1);
|
|
mainMenu.get_item("ttf_right_left").enable(TTF_using()).check(rtl);
|
|
#if C_PRINTER
|
|
mainMenu.get_item("ttf_printfont").enable(TTF_using()).check(printfont);
|
|
#endif
|
|
mainMenu.get_item("mapper_dbcssbcs").enable(TTF_using()&&!IS_PC98_ARCH&&!IS_JEGA_ARCH&&enable_dbcs_tables).check(dbcs_sbcs||IS_PC98_ARCH||IS_JEGA_ARCH);
|
|
mainMenu.get_item("mapper_autoboxdraw").enable(TTF_using()&&!IS_PC98_ARCH&&!IS_JEGA_ARCH&&enable_dbcs_tables).check(autoboxdraw||IS_PC98_ARCH||IS_JEGA_ARCH);
|
|
mainMenu.get_item("ttf_halfwidthkana").enable(TTF_using()&&!IS_PC98_ARCH&&!IS_JEGA_ARCH&&enable_dbcs_tables).check(halfwidthkana||IS_PC98_ARCH||IS_JEGA_ARCH);
|
|
mainMenu.get_item("ttf_extcharset").enable(TTF_using()&&!IS_PC98_ARCH&&!IS_JEGA_ARCH&&enable_dbcs_tables).check(dos.loaded_codepage==936?gbk:(dos.loaded_codepage==950||dos.loaded_codepage==951?chinasea:gbk&&chinasea));
|
|
#endif
|
|
#if C_PRINTER
|
|
mainMenu.get_item("mapper_printtext").enable(!IS_PC98_ARCH);
|
|
#endif
|
|
mainMenu.get_item("pc98_5mhz_gdc").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_allow_200scanline").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_allow_4partitions").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_enable_egc").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_enable_grcg").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_enable_analog").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_enable_analog256").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_enable_188user").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_clear_text").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_clear_graphics").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("pc98_use_uskb").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("dos_pc98_pit_4mhz").enable(IS_PC98_ARCH);
|
|
mainMenu.get_item("dos_pc98_pit_5mhz").enable(IS_PC98_ARCH);
|
|
|
|
extern bool Mouse_Vertical;
|
|
extern bool Mouse_Drv;
|
|
|
|
mainMenu.get_item("dos_mouse_enable_int33").check(Mouse_Drv).refresh_item(mainMenu);
|
|
mainMenu.get_item("dos_mouse_y_axis_reverse").check(Mouse_Vertical).refresh_item(mainMenu);
|
|
|
|
#ifdef C_OPENGL
|
|
mainMenu.get_item("load_glsl_shader").enable(OpenGL_using()&&initgl==2);
|
|
#endif
|
|
#ifdef C_DIRECT3D
|
|
mainMenu.get_item("load_d3d_shader").enable(Direct3D_using());
|
|
#endif
|
|
#ifdef USE_TTF
|
|
mainMenu.get_item("load_ttf_font").enable(TTF_using());
|
|
#endif
|
|
|
|
#if !defined(C_EMSCRIPTEN)
|
|
mainMenu.get_item("show_console").check(showconsole_init).refresh_item(mainMenu);
|
|
if (control->opt_display2) {
|
|
mainMenu.get_item("show_console").enable(false).refresh_item(mainMenu);
|
|
mainMenu.get_item("wait_on_error").enable(false).refresh_item(mainMenu);
|
|
}
|
|
#endif
|
|
#if C_DEBUG
|
|
if (control->opt_display2) mainMenu.get_item("mapper_debugger").enable(false).refresh_item(mainMenu);
|
|
#endif
|
|
mainMenu.get_item("mapper_speedlock2").check(ticksLocked).refresh_item(mainMenu);
|
|
|
|
OutputSettingMenuUpdate();
|
|
aspect_ratio_menu();
|
|
update_pc98_clock_pit_menu();
|
|
#if !defined(C_EMSCRIPTEN)
|
|
update_capture_fmt_menu();
|
|
#endif
|
|
|
|
/* The machine just "powered on", and then reset finished */
|
|
if (!VM_PowerOn()) E_Exit("VM failed to power on");
|
|
|
|
/* go! */
|
|
sdl.init_ignore = false;
|
|
UpdateWindowDimensions();
|
|
userResizeWindowWidth = 0;
|
|
userResizeWindowHeight = 0;
|
|
|
|
UpdateOverscanMenu();
|
|
|
|
void GUI_ResetResize(bool pressed);
|
|
GUI_ResetResize(true);
|
|
|
|
void ConstructMenu(void);
|
|
ConstructMenu();
|
|
|
|
#if 0
|
|
mainMenu.dump_log_debug(); /*DEBUG*/
|
|
#endif
|
|
mainMenu.rebuild();
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
|
|
#if defined(LINUX)
|
|
if (IS_PC98_ARCH || IS_JEGA_ARCH || isDBCSCP()) InitFontHandle();
|
|
#endif
|
|
mainMenu.screenWidth = (unsigned int)sdl.surface->w;
|
|
mainMenu.screenHeight = (unsigned int)sdl.surface->h;
|
|
mainMenu.updateRect();
|
|
#endif
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
/* Windows 7 taskbar extension support */
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_SERVER, IID_ITaskbarList3, (LPVOID*)(&winTaskbarList));
|
|
if (hr == S_OK) {
|
|
LOG_MSG("Windows: IID_ITaskbarList3 is available");
|
|
|
|
#if !defined(C_SDL2)
|
|
THUMBBUTTON buttons[8];
|
|
int buttoni = 0;
|
|
|
|
{
|
|
THUMBBUTTON &b = buttons[buttoni++];
|
|
memset(&b, 0, sizeof(b));
|
|
b.iId = ID_WIN_SYSMENU_MAPPER;
|
|
b.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAPPER));
|
|
b.dwMask = (THUMBBUTTONMASK)(THB_TOOLTIP | THB_FLAGS | THB_ICON);
|
|
b.dwFlags = (THUMBBUTTONFLAGS)(THBF_ENABLED | THBF_DISMISSONCLICK);
|
|
wcscpy(b.szTip, L"Mapper editor");
|
|
}
|
|
|
|
{
|
|
THUMBBUTTON &b = buttons[buttoni++];
|
|
memset(&b, 0, sizeof(b));
|
|
b.iId = ID_WIN_SYSMENU_CFG_GUI;
|
|
b.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_CFG_GUI));
|
|
b.dwMask = (THUMBBUTTONMASK)(THB_TOOLTIP | THB_FLAGS | THB_ICON);
|
|
b.dwFlags = (THUMBBUTTONFLAGS)(THBF_ENABLED | THBF_DISMISSONCLICK);
|
|
wcscpy(b.szTip, L"Configuration tool");
|
|
}
|
|
|
|
{
|
|
THUMBBUTTON &b = buttons[buttoni++];
|
|
memset(&b, 0, sizeof(b));
|
|
b.iId = 1;
|
|
b.dwMask = (THUMBBUTTONMASK)THB_FLAGS;
|
|
b.dwFlags = (THUMBBUTTONFLAGS)(THBF_DISABLED | THBF_NONINTERACTIVE | THBF_NOBACKGROUND);
|
|
}
|
|
|
|
{
|
|
THUMBBUTTON &b = buttons[buttoni++];
|
|
memset(&b, 0, sizeof(b));
|
|
b.iId = ID_WIN_SYSMENU_PAUSE;
|
|
b.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_PAUSE));
|
|
b.dwMask = (THUMBBUTTONMASK)(THB_TOOLTIP | THB_FLAGS | THB_ICON);
|
|
b.dwFlags = (THUMBBUTTONFLAGS)THBF_ENABLED;
|
|
wcscpy(b.szTip, L"Pause emulation");
|
|
}
|
|
|
|
winTaskbarList->ThumbBarAddButtons(GetHWND(), buttoni, buttons);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
{
|
|
Section_prop *section = static_cast<Section_prop *>(control->GetSection("SDL"));
|
|
assert(section != NULL);
|
|
|
|
bool cfg_want_menu = section->Get_bool("showmenu");
|
|
|
|
/* -- -- decide whether to set menu */
|
|
if (menu_gui && !control->opt_nomenu && cfg_want_menu) {
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
if (!TTF_using() || !sdl.desktop.fullscreen)
|
|
#endif
|
|
DOSBox_SetMenu();
|
|
}
|
|
else
|
|
DOSBox_NoMenu();
|
|
|
|
#if defined(WIN32) && !defined(C_SDL2)
|
|
if (maximize && !TTF_using() && !GFX_IsFullscreen()) ShowWindow(GetHWND(), SW_MAXIMIZE);
|
|
#endif
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
bool reboot_dos;
|
|
bool run_machine;
|
|
bool wait_debugger;
|
|
bool reboot_machine;
|
|
bool dos_kernel_shutdown;
|
|
|
|
fresh_boot:
|
|
reboot_dos = false;
|
|
run_machine = false;
|
|
wait_debugger = false;
|
|
reboot_machine = false;
|
|
dos_kernel_shutdown = false;
|
|
guest_msdos_mcb_chain = (uint16_t)(~0u);
|
|
|
|
#if C_DEBUG
|
|
if (control->opt_test) ::testing::InitGoogleTest(&argc, argv);
|
|
#endif
|
|
|
|
/* NTS: CPU reset handler, and BIOS init, has the instruction pointer poised to run through BIOS initialization,
|
|
* which will then "boot" into the DOSBox-X kernel, and then the shell, by calling VM_Boot_DOSBox_Kernel() */
|
|
/* FIXME: throwing int() is a stupid and nondescriptive way to signal shutdown/reset. */
|
|
try {
|
|
#if C_DEBUG
|
|
if (control->opt_break_start) DEBUG_EnableDebugger();
|
|
#endif
|
|
DOSBOX_RunMachine();
|
|
} catch (int x) {
|
|
if (x == 2) { /* booting a guest OS. "boot" has already done the work to load the image and setup CPU registers */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to boot guest OS");
|
|
|
|
run_machine = true; /* make note. don't run the whole shebang from an exception handler! */
|
|
dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
|
|
}
|
|
else if (x == 3) { /* reboot the system */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot the system");
|
|
|
|
reboot_machine = true;
|
|
dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
|
|
}
|
|
else if (x == 5) { /* go to PC-98 mode */
|
|
E_Exit("Obsolete int signal");
|
|
}
|
|
else if (x == 6) { /* reboot DOS kernel */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot DOS kernel");
|
|
|
|
reboot_dos = true;
|
|
dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
|
|
}
|
|
else if (x == 7) { /* DOS kernel corruption error (need to restart the DOS kernel) */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to reboot DOS kernel");
|
|
|
|
reboot_dos = true;
|
|
wait_debugger = true;
|
|
dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
|
|
}
|
|
else if (x == 8) { /* Booting to a BIOS, shutting down DOSBox-X BIOS */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Emulation threw a signal to boot into BIOS image");
|
|
|
|
reboot_machine = true;
|
|
dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
|
|
}
|
|
else if (x == 9) { /* BIOS caught a JMP to F000:FFF0 without any other hardware reset signal */
|
|
LOG(LOG_MISC,LOG_DEBUG)("Emulation detected JMP to BIOS POST routine");
|
|
|
|
reboot_machine = true;
|
|
dos_kernel_shutdown = !dos_kernel_disabled; /* only if DOS kernel enabled */
|
|
}
|
|
else {
|
|
LOG(LOG_MISC,LOG_DEBUG)("Emulation threw DOSBox-X kill switch signal");
|
|
|
|
// kill switch (see instances of throw(0) and throw(1) elsewhere in DOSBox)
|
|
run_machine = false;
|
|
dos_kernel_shutdown = false;
|
|
}
|
|
}
|
|
catch (...) {
|
|
throw;
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
checkmenuwidth = false;
|
|
if (dos_kernel_shutdown) {
|
|
|
|
inshell = false;
|
|
if (!IS_PC98_ARCH&&!IS_JEGA_ARCH&&!IS_J3100&&dos.loaded_codepage!=437) dos.loaded_codepage=437;
|
|
|
|
/* NTS: we take different paths depending on whether we're just shutting down DOS
|
|
* or doing a hard reboot. */
|
|
|
|
if (wait_debugger) {
|
|
#if C_DEBUG
|
|
Bitu DEBUG_EnableDebugger(void);
|
|
void DEBUG_WaitNoExecute(void);
|
|
|
|
LOG_MSG("Starting debugger.");
|
|
DEBUG_EnableDebugger();
|
|
DEBUG_WaitNoExecute();
|
|
#endif
|
|
}
|
|
|
|
/* new code: fire event */
|
|
if (reboot_machine)
|
|
DispatchVMEvent(VM_EVENT_DOS_EXIT_REBOOT_BEGIN);
|
|
else
|
|
DispatchVMEvent(VM_EVENT_DOS_EXIT_BEGIN);
|
|
|
|
/* older shutdown code */
|
|
RemoveEMSPageFrame();
|
|
|
|
/* remove UMB block */
|
|
if (!keep_umb_on_boot) RemoveUMBBlock();
|
|
|
|
/* disable INT 33h mouse services. it can interfere with guest OS paging and control of the mouse */
|
|
DisableINT33();
|
|
|
|
/* unmap the DOSBox-X kernel private segment. if the user told us not to,
|
|
* but the segment exists below 640KB, then we must, because the guest OS
|
|
* will trample it and assume control of that region of RAM. */
|
|
if (!keep_private_area_on_boot || reboot_machine)
|
|
DOS_GetMemory_unmap();
|
|
else if (DOS_PRIVATE_SEGMENT < 0xA000)
|
|
DOS_GetMemory_unmap();
|
|
|
|
/* revector some dos-allocated interrupts */
|
|
if (!reboot_machine) {
|
|
real_writed(0,0x01*4,(uint32_t)BIOS_DEFAULT_HANDLER_LOCATION);
|
|
real_writed(0,0x03*4,(uint32_t)BIOS_DEFAULT_HANDLER_LOCATION);
|
|
}
|
|
|
|
if (Voodoo_OGL_Active())
|
|
Voodoo_Output_Enable(false);
|
|
|
|
grGlideShutdown();
|
|
/* shutdown DOSBox-X's virtual drive Z */
|
|
VFILE_Shutdown();
|
|
|
|
/* shutdown the programs */
|
|
PROGRAMS_Shutdown(); /* FIXME: Is this safe? Or will this cause use-after-free bug? */
|
|
|
|
/* remove environment variables for some components */
|
|
DOS_UninstallMisc();
|
|
SBLASTER_DOS_Shutdown();
|
|
GUS_DOS_Shutdown();
|
|
/* disable Expanded Memory. EMM is a DOS API, not a BIOS API */
|
|
EMS_DoShutDown();
|
|
/* and XMS, also a DOS API */
|
|
XMS_DoShutDown();
|
|
/* and the DOS API in general */
|
|
DOS_DoShutDown();
|
|
|
|
/* mem handles too */
|
|
ShutDownMemHandles(NULL);
|
|
|
|
/* set the "disable DOS kernel" flag so other parts of this program
|
|
* do not attempt to manipulate now-defunct parts of the kernel
|
|
* such as the environment block */
|
|
dos_kernel_disabled = true;
|
|
|
|
std::string core(static_cast<Section_prop *>(control->GetSection("cpu"))->Get_string("core"));
|
|
if (!strcmp(RunningProgram, "LOADLIN") && core == "auto") {
|
|
cpudecoder=&CPU_Core_Normal_Run;
|
|
mainMenu.get_item("mapper_normal").check(true).refresh_item(mainMenu);
|
|
#if (C_DYNAMIC_X86) || (C_DYNREC)
|
|
mainMenu.get_item("mapper_dynamic").check(false).refresh_item(mainMenu);
|
|
#endif
|
|
}
|
|
|
|
/* new code: fire event */
|
|
if (reboot_machine)
|
|
DispatchVMEvent(VM_EVENT_DOS_EXIT_REBOOT_KERNEL);
|
|
else
|
|
DispatchVMEvent(VM_EVENT_DOS_EXIT_KERNEL);
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
#if defined(USE_TTF)
|
|
if (ttfswitch || switch_output_from_ttf) {
|
|
tooutttf = true;
|
|
showdbcs = switchttf = ttfswitch = switch_output_from_ttf = false;
|
|
mainMenu.get_item("output_ttf").enable(true).refresh_item(mainMenu);
|
|
}
|
|
#endif
|
|
|
|
if (run_machine) {
|
|
bootguest = false;
|
|
bool disable_a20 = static_cast<Section_prop *>(control->GetSection("dosbox"))->Get_bool("turn off a20 gate on boot");
|
|
|
|
/* if instructed, turn off A20 at boot */
|
|
if (disable_a20) MEM_A20_Enable(false);
|
|
|
|
/* PC-98: hide the cursor */
|
|
if (IS_PC98_ARCH) {
|
|
void PC98_show_cursor(bool show);
|
|
PC98_show_cursor(false);
|
|
}
|
|
|
|
/* new code: fire event */
|
|
DispatchVMEvent(VM_EVENT_GUEST_OS_BOOT);
|
|
|
|
LOG_MSG("Alright: DOS kernel shutdown, booting a guest OS\n");
|
|
LOG_MSG(" CS:IP=%04x:%04x SS:SP=%04x:%04x AX=%04x BX=%04x CX=%04x DX=%04x\n",
|
|
SegValue(cs),reg_ip,
|
|
SegValue(ss),reg_sp,
|
|
reg_ax,reg_bx,reg_cx,reg_dx);
|
|
|
|
#if C_DEBUG
|
|
if (boot_debug_break) {
|
|
boot_debug_break = false;
|
|
|
|
Bitu DEBUG_EnableDebugger(void);
|
|
DEBUG_EnableDebugger();
|
|
}
|
|
#endif
|
|
|
|
/* run again */
|
|
goto fresh_boot;
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
if (reboot_machine) {
|
|
LOG_MSG("Rebooting the system\n");
|
|
|
|
boothax = BOOTHAX_NONE;
|
|
guest_msdos_LoL = 0;
|
|
guest_msdos_mcb_chain = 0;
|
|
|
|
void CPU_Snap_Back_Forget();
|
|
/* Shutdown everything. For shutdown to work properly we must force CPU to real mode */
|
|
CPU_Snap_Back_To_Real_Mode();
|
|
CPU_Snap_Back_Forget();
|
|
|
|
/* new code: fire event */
|
|
DispatchVMEvent(VM_EVENT_RESET);
|
|
|
|
/* force the mapper to let go of all keys so that the host key is not stuck (Issue #1320) */
|
|
MAPPER_ReleaseAllKeys();
|
|
void MAPPER_LosingFocus(void);
|
|
MAPPER_LosingFocus();
|
|
|
|
if (custom_bios) {
|
|
/* need to relocate BIOS allocations */
|
|
void ROMBIOS_InitForCustomBIOS(void);
|
|
ROMBIOS_InitForCustomBIOS();
|
|
|
|
void CALLBACK_Init();
|
|
CALLBACK_Init();
|
|
|
|
#if C_DEBUG
|
|
void DEBUG_ReinitCallback(void);
|
|
DEBUG_ReinitCallback();
|
|
#endif
|
|
}
|
|
|
|
DispatchVMEvent(VM_EVENT_RESET_END);
|
|
|
|
/* HACK: EGA/VGA modes will need VGA BIOS mapped in, ready to go */
|
|
if (IS_EGAVGA_ARCH) {
|
|
void INT10_Startup(Section *sec);
|
|
INT10_Startup(NULL);
|
|
}
|
|
|
|
#if C_DEBUG
|
|
if (boot_debug_break) {
|
|
boot_debug_break = false;
|
|
|
|
Bitu DEBUG_EnableDebugger(void);
|
|
DEBUG_EnableDebugger();
|
|
}
|
|
#endif
|
|
|
|
/* run again */
|
|
goto fresh_boot;
|
|
}
|
|
else if (reboot_dos) { /* typically (at this time) to enter/exit PC-98 mode */
|
|
LOG_MSG("Rebooting DOS\n");
|
|
|
|
void CPU_Snap_Back_Forget();
|
|
/* Shutdown everything. For shutdown to work properly we must force CPU to real mode */
|
|
CPU_Snap_Back_To_Real_Mode();
|
|
CPU_Snap_Back_Forget();
|
|
|
|
/* all hardware devices need to know to reregister themselves PC-98 style */
|
|
|
|
/* begin booting DOS again. */
|
|
void BIOS_Enter_Boot_Phase(void);
|
|
BIOS_Enter_Boot_Phase();
|
|
|
|
/* run again */
|
|
goto fresh_boot;
|
|
}
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
|
|
Reflect_Menu();
|
|
#endif
|
|
|
|
/* and then shutdown */
|
|
GFX_ShutDown();
|
|
|
|
void CPU_Snap_Back_Forget();
|
|
/* Shutdown everything. For shutdown to work properly we must force CPU to real mode */
|
|
CPU_Snap_Back_To_Real_Mode();
|
|
CPU_Snap_Back_Forget();
|
|
|
|
void CALLBACK_Dump(void);
|
|
CALLBACK_Dump();
|
|
|
|
/* GUI font registry shutdown */
|
|
#if !defined(C_SDL2)
|
|
GUI::Font::registry_freeall();
|
|
#endif
|
|
DOS_ShutdownDrives();
|
|
DOS_ShutdownFiles();
|
|
DOS_ShutdownDevices();
|
|
CALLBACK_Shutdown();
|
|
#if C_DYNAMIC_X86
|
|
CPU_Core_Dyn_X86_Shutdown();
|
|
#endif
|
|
FreeBIOSDiskList();
|
|
MAPPER_Shutdown();
|
|
VFILE_Shutdown();
|
|
PROGRAMS_Shutdown();
|
|
TIMER_ShutdownTickHandlers();
|
|
#if C_DEBUG
|
|
DEBUG_ShutDown(NULL);
|
|
#endif
|
|
|
|
sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt
|
|
|
|
#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL)
|
|
if (!control->opt_silent) {
|
|
SDL_SetIMValues(SDL_IM_ONOFF, 0, NULL);
|
|
SDL_SetIMValues(SDL_IM_ENABLE, 0, NULL);
|
|
}
|
|
#elif defined(WIN32) && !defined(HX_DOS) && defined(C_SDL2)
|
|
if (!control->opt_silent) {
|
|
SDL_StopTextInput();
|
|
}
|
|
#endif
|
|
|
|
//Force visible mouse to end user. Somehow this sometimes doesn't happen
|
|
#if defined(C_SDL2)
|
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
|
#else
|
|
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
|
#endif
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
/* Exit functions */
|
|
if (machine != MCH_AMSTRAD) // FIXME
|
|
while (!exitfunctions.empty()) {
|
|
Function_wrapper &ent = exitfunctions.front();
|
|
|
|
LOG(LOG_MISC,LOG_DEBUG)("Calling exit function (%p) '%s'",(void*)((uintptr_t)ent.function),ent.name.c_str());
|
|
ent.function(NULL);
|
|
exitfunctions.pop_front();
|
|
}
|
|
|
|
LOG::Exit();
|
|
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU && defined(WIN32) && !defined(HX_DOS) && (!defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) || defined(C_SDL2))
|
|
ShowWindow(GetHWND(), SW_HIDE);
|
|
SDL1_hax_SetMenu(NULL);/* detach menu from window, or else Windows will destroy the menu out from under the C++ class */
|
|
#endif
|
|
#if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
|
|
void sdl_hax_macosx_setmenu(void *nsMenu);
|
|
sdl_hax_macosx_setmenu(NULL);
|
|
#endif
|
|
|
|
#if defined(C_SDL2)
|
|
SDL_CDROMQuit();
|
|
#endif
|
|
SDL_Quit();//Let's hope sdl will quit as well when it catches an exception
|
|
|
|
#if defined(WIN32) && !defined(HX_DOS)
|
|
if (winTaskbarList != NULL) {
|
|
winTaskbarList->Release();
|
|
winTaskbarList = NULL;
|
|
}
|
|
#endif
|
|
|
|
mainMenu.unbuild();
|
|
mainMenu.clear_all_menu_items();
|
|
|
|
#if defined(LINUX) && defined(HAVE_ALSA)
|
|
// force ALSA to release global cache, so that it's one less leak reported by Valgrind
|
|
snd_config_update_free_global();
|
|
#endif
|
|
|
|
/* the return statement below needs control->opt_test, save it before Config
|
|
* goes out of context and is deleted */
|
|
saved_opt_test = control->opt_test;
|
|
|
|
/* NTS: The "control" object destructor is called here because the "myconf" object leaves scope.
|
|
* The destructor calls all section destroy functions here. After this point, all sections have
|
|
* freed resources. */
|
|
control = NULL; /* any deref past this point and you deserve to segfault */
|
|
}
|
|
|
|
return saved_opt_test&&testerr?1:0;
|
|
}
|
|
|
|
void GFX_GetSizeAndPos(int &x,int &y,int &width, int &height, bool &fullscreen) {
|
|
x = sdl.clip.x;
|
|
y = sdl.clip.y;
|
|
width = sdl.clip.w; // draw.width
|
|
height = sdl.clip.h; // draw.height
|
|
fullscreen = sdl.desktop.fullscreen;
|
|
}
|
|
|
|
void GFX_GetSize(int &width, int &height, bool &fullscreen) {
|
|
width = sdl.clip.w; // draw.width
|
|
height = sdl.clip.h; // draw.height
|
|
fullscreen = sdl.desktop.fullscreen;
|
|
}
|
|
|
|
void GFX_ShutDown(void) {
|
|
LOG(LOG_MISC,LOG_DEBUG)("Shutting down GFX renderer");
|
|
GFX_Stop();
|
|
if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop );
|
|
if (sdl.mouse.locked) GFX_CaptureMouse();
|
|
if (sdl.desktop.fullscreen) GFX_SwitchFullScreen();
|
|
}
|
|
|
|
bool Direct3D_using(void) {
|
|
#if C_DIRECT3D
|
|
return (sdl.desktop.want_type==SCREEN_DIRECT3D?true:false);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool OpenGL_using(void) {
|
|
#if C_OPENGL
|
|
return (sdl.desktop.want_type==SCREEN_OPENGL?true:false);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool TTF_using(void) {
|
|
#if defined(USE_TTF)
|
|
return (sdl.desktop.want_type==SCREEN_TTF?true:false);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool Get_Custom_SaveDir(std::string& savedir) {
|
|
if (custom_savedir.length() != 0) {
|
|
savedir=custom_savedir;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GUI_ResetResize(bool pressed) {
|
|
#if defined(USE_TTF)
|
|
if (TTF_using()) {
|
|
resetFontSize();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
void RENDER_CallBack( GFX_CallBackFunctions_t function );
|
|
|
|
if (!pressed) return;
|
|
userResizeWindowWidth = 0;
|
|
userResizeWindowHeight = 0;
|
|
|
|
if (GFX_GetPreventFullscreen())
|
|
return;
|
|
|
|
if (sdl.updating && !GFX_MustActOnResize()) {
|
|
/* act on resize when updating is complete */
|
|
sdl.deferred_resize = true;
|
|
}
|
|
else {
|
|
sdl.deferred_resize = false;
|
|
RENDER_CallBack(GFX_CallBackReset);
|
|
}
|
|
}
|
|
|
|
bool MOUSE_IsLocked()
|
|
{
|
|
return sdl.mouse.locked;
|
|
}
|
|
|
|
#if defined(C_SDL2) && defined(C_OPENGL)/*HACK*/
|
|
void SDL_GL_SwapBuffers(void) {
|
|
SDL_GL_SwapWindow(sdl.window);
|
|
}
|
|
#endif
|
|
|
|
// save state support
|
|
void POD_Save_Sdlmain( std::ostream& stream )
|
|
{
|
|
// - pure data
|
|
WRITE_POD( &sdl.mouse.autolock, sdl.mouse.autolock );
|
|
WRITE_POD( &sdl.mouse.requestlock, sdl.mouse.requestlock );
|
|
}
|
|
|
|
void POD_Load_Sdlmain( std::istream& stream )
|
|
{
|
|
// - pure data
|
|
READ_POD( &sdl.mouse.autolock, sdl.mouse.autolock );
|
|
READ_POD( &sdl.mouse.requestlock, sdl.mouse.requestlock );
|
|
}
|