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