/*
 *  Copyright (C) 2002-2021  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <math.h>

#include "dosbox.h"
#include "logging.h"
#include "sdlmain.h"
#include "render.h"
#include "jfont.h"
#include "inout.h"
#include "bios.h"
#include "regs.h"
#include "cpu.h"
#include "control.h"
#include "menudef.h"
#include "callback.h"
#include "../ints/int10.h"

#include <output/output_ttf.h>

using namespace std;

std::map<int, int> lowboxdrawmap {
    {1, 201}, {2, 187}, {3, 200}, {4, 188}, {5, 186}, {6, 205}, {0xe, 178},
    {0x10, 206}, {0x14, 177}, {0x15, 202}, {0x16, 203}, {0x17, 185}, {0x19, 204}, {0x1a, 176},
};
std::map<int, int> pc98boxdrawmap {
    {0xB4, 0x67}, {0xB9, 0x6e}, {0xBA, 0x46}, {0xBB, 0x56}, {0xBC, 0x5E}, {0xB3, 0x45}, {0xBF, 0x53},
    {0xC0, 0x57}, {0xC1, 0x77}, {0xC2, 0x6f}, {0xC3, 0x5f}, {0xC4, 0x43}, {0xC5, 0x7f}, {0xC8, 0x5A},
    {0xC9, 0x52}, {0xCA, 0x7e}, {0xCB, 0x76}, {0xCC, 0x66}, {0xCD, 0x44}, {0xD9, 0x5B}, {0xDA, 0x4f},
};
uint16_t cpMap_AX[32] = {
	0x0020, 0x00b6, 0x2195, 0x2194, 0x2191, 0x2193, 0x2192, 0x2190, 0x2500, 0x2502, 0x250c, 0x2510, 0x2518, 0x2514, 0x251c, 0x252c,
	0x2524, 0x2534, 0x253c, 0x2550, 0x2551, 0x2554, 0x2557, 0x255d, 0x255a, 0x2560, 0x2566, 0x2563, 0x2569, 0x256c, 0x00ab, 0x00bb
};

#if defined(USE_TTF)
#include "cp437_uni.h"
#include "DOSBoxTTF.h"
#include "../gui/sdl_ttf.c"

#define MIN_PTSIZE 9

#ifdef _MSC_VER
# define MIN(a,b) (std::min)(a,b)
# define MAX(a,b) (std::max)(a,b)
#else
# define MIN(a,b) std::min(a,b)
# define MAX(a,b) std::max(a,b)
#endif

Render_ttf ttf;
bool enablePercLimit = true;
bool char512 = true;
bool showbold = true;
bool showital = true;
bool showline = true;
bool showsout = false;
bool dbcs_sbcs = true;
bool printfont = true;
bool autoboxdraw = true;
bool halfwidthkana = true;
bool ttf_dosv = false;
bool lesssize = false;
bool lastmenu = true;
bool initttf = false;
bool copied = false;
bool firstset = true;
bool forceswk = false;
bool notrysgf = false;
bool wpExtChar = false;
int wpType = 0;
int wpVersion = 0;
int wpBG = -1;
int wpFG = 7;
int lastset = 0;
int lastfontsize = 0;
int switchoutput = -1;
int checkcol = 0;

static unsigned long ttfSize = sizeof(DOSBoxTTFbi), ttfSizeb = 0, ttfSizei = 0, ttfSizebi = 0;
static void * ttfFont = DOSBoxTTFbi, * ttfFontb = NULL, * ttfFonti = NULL, * ttfFontbi = NULL;
extern int posx, posy, eurAscii, transparency, NonUserResizeCounter;
extern bool rtl, gbk, chinasea, switchttf, force_conversion, blinking, showdbcs, loadlang, window_was_maximized;
extern const char* RunningProgram;
extern uint8_t ccount;
extern uint16_t cpMap[512], cpMap_PC98[256];
uint16_t cpMap_copy[256];
static SDL_Color ttf_fgColor = {0, 0, 0, 0};
static SDL_Color ttf_bgColor = {0, 0, 0, 0};
static SDL_Rect ttf_textRect = {0, 0, 0, 0};
static SDL_Rect ttf_textClip = {0, 0, 0, 0};

ttf_cell curAttrChar[txtMaxLins*txtMaxCols];					// currently displayed textpage
ttf_cell newAttrChar[txtMaxLins*txtMaxCols];					// to be replaced by

extern alt_rgb altBGR0[16];
alt_rgb altBGR1[16];
int blinkCursor = -1;
static int prev_sline = -1;
static int charSet = 0;
static alt_rgb *rgbColors = (alt_rgb*)render.pal.rgb;
static bool blinkstate = false;
bool colorChanged = false, justChanged = false, staycolors = false, firstsize = true, ttfswitch = false, switch_output_from_ttf = false;
bool init_once = false, init_twice = false;
extern bool finish_prepare;
bool is_ttfswitched_on=false;

int menuwidth_atleast(int width), FileDirExistCP(const char *name), FileDirExistUTF8(std::string &localname, const char *name);
void AdjustIMEFontSize(void),refreshExtChar(void), initcodepagefont(void), change_output(int output), drawmenu(Bitu val), KEYBOARD_Clear(void), RENDER_Reset(void), DOSBox_SetSysMenu(void), GetMaxWidthHeight(unsigned int *pmaxWidth, unsigned int *pmaxHeight), SetWindowTransparency(int trans), resetFontSize(void), RENDER_CallBack( GFX_CallBackFunctions_t function );
bool isDBCSCP(void), InitCodePage(void), CodePageGuestToHostUTF16(uint16_t *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/), systemmessagebox(char const * aTitle, char const * aMessage, char const * aDialogType, char const * aIconType, int aDefaultButton);
bool CheckDBCSCP(int32_t codepage);
std::string GetDOSBoxXPath(bool withexe=false);

#if defined(C_SDL2)
void GFX_SetResizeable(bool enable);
SDL_Window * GFX_SetSDLSurfaceWindow(uint16_t width, uint16_t height);
#endif

Bitu OUTPUT_TTF_SetSize() {
    bool text=CurMode&&(CurMode->type==0||CurMode->type==2||CurMode->type==M_TEXT||IS_PC98_ARCH);
    if (text) {
        sdl.clip.x = sdl.clip.y = 0;
        sdl.draw.width = sdl.clip.w = ttf.cols*ttf.width;
        sdl.draw.height = sdl.clip.h = ttf.lins*ttf.height;
        ttf.inUse = true;

#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
        if (mainMenu.isVisible() && !ttf.fullScrn) {
            sdl.clip.y += mainMenu.menuBox.h;
            // hack
            ttf.offX = sdl.clip.x;
            ttf.offY = sdl.clip.y;
        }
#endif
    } else
        ttf.inUse = false;
#if defined(C_SDL2)
    int bx = 0, by = 0;
    if (sdl.displayNumber>0) {
        int displays = SDL_GetNumVideoDisplays();
        SDL_Rect bound;
        for( int i = 1; i <= displays; i++ ) {
            bound = SDL_Rect();
            SDL_GetDisplayBounds(i-1, &bound);
            if (i == sdl.displayNumber) {
                bx = bound.x;
                by = bound.y;
                break;
            }
        }
        SDL_DisplayMode dm;
        if (SDL_GetDesktopDisplayMode(sdl.displayNumber?sdl.displayNumber-1:0,&dm) == 0) {
            bx += (dm.w - sdl.draw.width - sdl.clip.x)/2;
            by += (dm.h - sdl.draw.height - sdl.clip.y)/2;
        }
    }
#endif
    if (ttf.inUse && ttf.fullScrn) {
        unsigned int maxWidth, maxHeight;
        GetMaxWidthHeight(&maxWidth, &maxHeight);
#if defined(C_SDL2)
        GFX_SetResizeable(false);
        sdl.window = GFX_SetSDLSurfaceWindow(maxWidth, maxHeight);
        sdl.surface = sdl.window?SDL_GetWindowSurface(sdl.window):NULL;
        if (posx != -2 || posy != -2) {
            if (sdl.displayNumber==0) SDL_SetWindowPosition(sdl.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
            else SDL_SetWindowPosition(sdl.window, bx, by);
        }
#else
        sdl.surface = SDL_SetVideoMode(maxWidth, maxHeight, 32, SDL_SWSURFACE|
#if defined(WIN32)
        SDL_NOFRAME
#else
        SDL_FULLSCREEN
#endif
        );
#if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS) && !defined(_WIN32_WINDOWS)
        if (sdl.displayNumber>0) {
            xyp xy={0};
            xy.x=-1;
            xy.y=-1;
            curscreen=0;
            EnumDisplayMonitors(0, 0, EnumDispProc, reinterpret_cast<LPARAM>(&xy));
            HMONITOR monitor = MonitorFromRect(&monrect, MONITOR_DEFAULTTONEAREST);
            MONITORINFO info;
            info.cbSize = sizeof(MONITORINFO);
            GetMonitorInfo(monitor, &info);
            MoveWindow(GetHWND(), info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right-info.rcMonitor.left, info.rcMonitor.bottom-info.rcMonitor.top, true);
        }
#endif
#endif
    } else {
#if defined(C_SDL2)
        GFX_SetResizeable(false);
        sdl.window = GFX_SetSDLSurfaceWindow(sdl.draw.width + sdl.clip.x, sdl.draw.height + sdl.clip.y);
        sdl.surface = sdl.window?SDL_GetWindowSurface(sdl.window):NULL;
        if (/* firstsize && */ (posx < 0 || posy < 0) && !(posx == -2 && posy == -2) && text) {
            firstsize=false;
            if (sdl.displayNumber==0) SDL_SetWindowPosition(sdl.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
            else SDL_SetWindowPosition(sdl.window, bx, by);
        }
#else
        sdl.surface = SDL_SetVideoMode(sdl.draw.width + sdl.clip.x, sdl.draw.height + sdl.clip.y, 32, SDL_SWSURFACE);
#endif
    }
	if (!sdl.surface)
		E_Exit("SDL: Failed to create surface");
	SDL_ShowCursor(!ttf.fullScrn);
	sdl.active = true;

#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
    mainMenu.screenWidth = (size_t)sdl.surface->w;
    mainMenu.screenHeight = (size_t)sdl.surface->h;
    mainMenu.updateRect();
    mainMenu.setRedraw();
#endif

    AdjustIMEFontSize();
    ApplyPreventCap();

    return GFX_CAN_32 | GFX_SCALING;
}

void setVGADAC() {
    if (ttf.inUse&&CurMode&&IS_VGA_ARCH) {
        std::map<uint8_t,int> imap;
        for (uint8_t i = 0; i < 0x10; i++) {
            IO_ReadB(mem_readw(BIOS_VIDEO_PORT)+6);
            IO_WriteB(VGAREG_ACTL_ADDRESS, i+32);
            imap[i]=IO_ReadB(VGAREG_ACTL_READ_DATA);
            IO_WriteB(VGAREG_DAC_WRITE_ADDRESS, imap[i]);
            IO_WriteB(VGAREG_DAC_DATA, altBGR0[i].red>>2);   // Convert 8-bit to 6-bit for VGA DAC
            IO_WriteB(VGAREG_DAC_DATA, altBGR0[i].green>>2);
            IO_WriteB(VGAREG_DAC_DATA, altBGR0[i].blue>>2);
        }
    }
}

/* NTS: EGA/VGA etc have at least 16 DOS colors. Check also CGA etc. */
bool setColors(const char *colorArray, int n) {
    if (IS_PC98_ARCH) return false;
    staycolors = strlen(colorArray) && *colorArray == '+';
    const char* nextRGB = colorArray + (staycolors?1:0);
	uint8_t * altPtr = (uint8_t *)altBGR1;
	int32_t rgbVal[4] = {-1,-1,-1,-1};
    for(int colNo = n > -1 ? n : 0; colNo < (n > -1 ? n + 1 : 16); colNo++) {
        if(sscanf(nextRGB, " ( %d , %d , %d)", &rgbVal[0], &rgbVal[1], &rgbVal[2]) == 3) {	// Decimal: (red,green,blue)
            for(int i = 0; i < 3; i++) {
                if(rgbVal[i] < 0 || rgbVal[i] > 255)
                    return false;
            }
            while(*nextRGB != ')')
                nextRGB++;
            nextRGB++;
        }
        else if(sscanf(nextRGB, " #%6x", ((uint32_t*)(&rgbVal[3]))) == 1) {	// Hexadecimal
            if(rgbVal[3] < 0 || rgbVal[3] > 0xFFFFFF)
                return false;
            for(int i = 2; i >= 0; i--) {
                rgbVal[i] = rgbVal[3] & 255;
                rgbVal[3] >>= 8;
            }
            nextRGB = strchr(nextRGB, '#') + 7;
        }
        else
            return false;

        altBGR0[colNo].blue = rgbVal[2];
        altBGR0[colNo].green = rgbVal[1];
        altBGR0[colNo].red = rgbVal[0];
        rgbColors[colNo].blue = (uint8_t)rgbVal[2];
        rgbColors[colNo].green = (uint8_t)rgbVal[1];
        rgbColors[colNo].red = (uint8_t)rgbVal[0];
        altBGR1[colNo].blue = rgbVal[2];
        altBGR1[colNo].green = rgbVal[1];
        altBGR1[colNo].red = rgbVal[0];
    }
    setVGADAC();
    colorChanged=justChanged=true;
	return true;
}

bool readTTFStyle(unsigned long& size, void*& font, FILE * fh) {
    long pos = ftell(fh);
    if (pos != -1L) {
        size = pos;
        font = malloc((size_t)size);
        if (font && !fseek(fh, 0, SEEK_SET) && fread(font, 1, (size_t)size, fh) == (size_t)size) {
            fclose(fh);
            return true;
        }
    }
    return false;
}

std::string failName="";
bool readTTF(const char *fName, bool bold, bool ital) {
	FILE * ttf_fh = NULL;
	std::string exepath = "";
	char ttfPath[1024];

	strcpy(ttfPath, fName);													// Try to load it from working directory
	strcat(ttfPath, ".ttf");
	ttf_fh = fopen(ttfPath, "rb");
	if (!ttf_fh) {
		strcpy(ttfPath, fName);
		ttf_fh = fopen(ttfPath, "rb");
	}
    if (!ttf_fh) {
        exepath=GetDOSBoxXPath();
        if (exepath.size()) {
            strcpy(strrchr(strcpy(ttfPath, exepath.c_str()), CROSS_FILESPLIT)+1, fName);	// Try to load it from where DOSBox-X was started
            strcat(ttfPath, ".ttf");
            ttf_fh = fopen(ttfPath, "rb");
            if (!ttf_fh) {
                strcpy(strrchr(strcpy(ttfPath, exepath.c_str()), CROSS_FILESPLIT)+1, fName);
                ttf_fh = fopen(ttfPath, "rb");
            }
        }
    }
    if (!ttf_fh) {
        std::string config_path;
        config_path = Cross::GetPlatformConfigDir();
        struct stat info;
        if (!stat(config_path.c_str(), &info) && (info.st_mode & S_IFDIR)) {
            strcpy(ttfPath, config_path.c_str());
            strcat(ttfPath, fName);
            strcat(ttfPath, ".ttf");
            ttf_fh = fopen(ttfPath, "rb");
            if (!ttf_fh) {
                strcpy(ttfPath, config_path.c_str());
                strcat(ttfPath, fName);
                ttf_fh = fopen(ttfPath, "rb");
            }
        }
    }
    if (!ttf_fh) {
        std::string res_path;
        res_path = Cross::GetPlatformResDir();
        struct stat info;
        if (!stat(res_path.c_str(), &info) && (info.st_mode & S_IFDIR)) {
            strcpy(ttfPath, res_path.c_str());
            strcat(ttfPath, fName);
            strcat(ttfPath, ".ttf");
            ttf_fh = fopen(ttfPath, "rb");
            if (!ttf_fh) {
                strcpy(ttfPath, res_path.c_str());
                strcat(ttfPath, fName);
                ttf_fh = fopen(ttfPath, "rb");
            }
        }
    }
    if (!ttf_fh) {
        std::string basedir = static_cast<Section_prop *>(control->GetSection("printer"))->Get_string("fontpath");
        if (basedir.back()!='\\' && basedir.back()!='/') basedir += CROSS_FILESPLIT;
        strcpy(ttfPath, basedir.c_str());
        strcat(ttfPath, fName);
        strcat(ttfPath, ".ttf");
        ttf_fh = fopen(ttfPath, "rb");
        if (!ttf_fh) {
            strcpy(ttfPath, basedir.c_str());
            strcat(ttfPath, fName);
            ttf_fh = fopen(ttfPath, "rb");
        }
    }
    if (!ttf_fh) {
        char fontdir[300];
#if defined(WIN32)
        strcpy(fontdir, "C:\\WINDOWS\\fonts\\");
        struct stat wstat;
        if(stat(fontdir,&wstat) || !(wstat.st_mode & S_IFDIR)) {
            char dir[MAX_PATH];
            if (GetWindowsDirectory(dir, MAX_PATH)) {
                strcpy(fontdir, dir);
                strcat(fontdir, "\\fonts\\");
            }
        }
#elif defined(LINUX)
        strcpy(fontdir, "/usr/share/fonts/");
#elif defined(MACOSX)
        strcpy(fontdir, "/Library/Fonts/");
#else
        strcpy(fontdir, "/fonts/");
#endif
        strcpy(ttfPath, fontdir);
        strcat(ttfPath, fName);
        strcat(ttfPath, ".ttf");
        ttf_fh = fopen(ttfPath, "rb");
        if (!ttf_fh) {
            strcpy(ttfPath, fontdir);
            strcat(ttfPath, fName);
            ttf_fh = fopen(ttfPath, "rb");
#if defined(LINUX) || defined(MACOSX)
            if (!ttf_fh) {
#if defined(LINUX)
                strcpy(fontdir, "/usr/share/fonts/truetype/");
#else
                strcpy(fontdir, "/System/Library/Fonts/");
#endif
                strcpy(ttfPath, fontdir);
                strcat(ttfPath, fName);
                strcat(ttfPath, ".ttf");
                ttf_fh = fopen(ttfPath, "rb");
                if (!ttf_fh) {
                    strcpy(ttfPath, fontdir);
                    strcat(ttfPath, fName);
                    ttf_fh = fopen(ttfPath, "rb");
                    if (!ttf_fh) {
                        std::string in;
#if defined(LINUX)
                        in = "~/.fonts/";
#else
                        in = "~/Library/Fonts/";
#endif
                        Cross::ResolveHomedir(in);
                        strcpy(ttfPath, in.c_str());
                        strcat(ttfPath, fName);
                        strcat(ttfPath, ".ttf");
                        ttf_fh = fopen(ttfPath, "rb");
                        if (!ttf_fh) {
                            strcpy(ttfPath, in.c_str());
                            strcat(ttfPath, fName);
                            ttf_fh = fopen(ttfPath, "rb");
                        }
                    }
                }
            }
#endif
        }
    }
    if (ttf_fh) {
        if (!fseek(ttf_fh, 0, SEEK_END)) {
            if (bold && ital && readTTFStyle(ttfSizebi, ttfFontbi, ttf_fh))
                return true;
            else if (bold && !ital && readTTFStyle(ttfSizeb, ttfFontb, ttf_fh))
                return true;
            else if (!bold && ital && readTTFStyle(ttfSizei, ttfFonti, ttf_fh))
                return true;
            else if (readTTFStyle(ttfSize, ttfFont, ttf_fh))
                return true;
        }
        fclose(ttf_fh);
    }
    if (!failName.size()||failName.compare(fName)) {
        failName=std::string(fName);
        bool alllower=true;
        for (size_t i=0; i<strlen(fName); i++) {if ((unsigned char)fName[i]>0x7f) alllower=false;break;}
        std::string message=alllower?("Could not load font file: "+std::string(fName)+(strlen(fName)<5||strcasecmp(fName+strlen(fName)-4, ".ttf")?".ttf":"")):"Could not load the specified font file.";
        systemmessagebox("Warning", message.c_str(), "ok","warning", 1);
    }
	return false;
}

void SetBlinkRate(Section_prop* section) {
    const char * blinkCstr = section->Get_string("blinkc");
    unsigned int num=-1;
    if (!strcasecmp(blinkCstr, "false")||!strcmp(blinkCstr, "-1")) blinkCursor = -1;
    else if (1==sscanf(blinkCstr,"%u",&num)&&int(num)>=0&&int(num)<=7) blinkCursor = num;
    else blinkCursor = IS_PC98_ARCH?6:4; // default cursor blinking is slower on PC-98 systems
}

void CheckTTFLimit() {
    ttf.lins = MAX(24, MIN(IS_VGA_ARCH?txtMaxLins:60, (int)ttf.lins));
    ttf.cols = MAX(40, MIN(IS_VGA_ARCH?txtMaxCols:160, (int)ttf.cols));
    if (ttf.cols*ttf.lins>16384) {
        if (lastset==1) {
            ttf.lins=16384/ttf.cols;
            SetVal("ttf", "lins", std::to_string(ttf.lins));
        } else if (lastset==2) {
            ttf.cols=16384/ttf.lins;
            SetVal("ttf", "cols", std::to_string(ttf.cols));
        } else {
            ttf.lins = 25;
            ttf.cols = 80;
        }
    }
}

int setTTFMap(bool changecp) {
	char text[2];
	uint16_t uname[4], wcTest[256];
	int cp = dos.loaded_codepage;

	// BUGFIX: If not started with output=ttf, then ttf.SDL_font == NULL and this code will cause a segfault.
	if (ttf.SDL_font == NULL) return 256;

	for (int i = 0; i < 256; i++) {
		text[0]=i;
		text[1]=0;
		uname[0]=0;
		uname[1]=0;
		if (cp == 932 && (halfwidthkana || IS_JEGA_ARCH)) forceswk=true;
		if (CheckDBCSCP(cp)) dos.loaded_codepage = 437;
		if (CodePageGuestToHostUTF16(uname,text)) {
			wcTest[i] = uname[1]==0?uname[0]:i;
			if (cp == 932 && lowboxdrawmap.find(i)!=lowboxdrawmap.end() && TTF_GlyphIsProvided(ttf.SDL_font, wcTest[i]))
				cpMap[i] = wcTest[i];
		}
		forceswk=false;
		if (CheckDBCSCP(cp)) dos.loaded_codepage = cp;
	}
	uint16_t unimap;
	int notMapped = 0;
	for (int y = ((cpMap[1]!=1&&cpMap[1]!=0x263A)||cp==867||(customcp&&(dos.loaded_codepage==customcp||changecp))||(altcp&&(dos.loaded_codepage==altcp||changecp))?0:8); y < 16; y++)
		for (int x = 0; x < 16; x++) {
			if (y<8 && (wcTest[y*16+x] == y*16+x || wcTest[y*16+x] == cp437_to_unicode[y*16+x])) unimap = cpMap_copy[y*16+x];
			else unimap = wcTest[y*16+x];
			if (!TTF_GlyphIsProvided(ttf.SDL_font, unimap)) {
				cpMap[y*16+x] = 0;
				notMapped++;
				LOG_MSG("Unmapped character: %3d - %4x", y*16+x, unimap);
			} else
				cpMap[y*16+x] = unimap;
		}
	if (eurAscii != -1 && TTF_GlyphIsProvided(ttf.SDL_font, 0x20ac))
		cpMap[eurAscii] = 0x20ac;
	return notMapped;
}

int setTTFCodePage() {
	if (!copied) {
		memcpy(cpMap_copy,cpMap,sizeof(cpMap[0])*256);
		copied=true;
	}
	int cp = dos.loaded_codepage;
	if (IS_PC98_ARCH) {
		static_assert(sizeof(cpMap[0])*256 >= sizeof(cpMap_PC98), "sizeof err 1");
		static_assert(sizeof(cpMap[0]) == sizeof(cpMap_PC98[0]), "sizeof err 2");
		memcpy(cpMap,cpMap_PC98,sizeof(cpMap[0])*256);
		return 0;
	}

	if (cp) {
		LOG_MSG("Loaded system codepage: %d\n", cp);
		int notMapped = setTTFMap(true);
#if !defined(OSFREE)
		if (strcmp(RunningProgram, "LOADLIN") && !dos_kernel_disabled)
			initcodepagefont();
#endif
#if defined(WIN32) && !defined(HX_DOS)
		DOSBox_SetSysMenu();
#endif
		if(IS_JEGA_ARCH) memcpy(cpMap,cpMap_AX,sizeof(cpMap[0])*32);
		if (cp == 932 && halfwidthkana) resetFontSize();
		refreshExtChar();
		return notMapped;
	} else
		return -1;
}

void GFX_SelectFontByPoints(int ptsize) {
	bool initCP = true;
	if (ttf.SDL_font) {
		TTF_CloseFont(ttf.SDL_font);
		initCP = false;
	}
	if (ttf.SDL_fontb) TTF_CloseFont(ttf.SDL_fontb);
	if (ttf.SDL_fonti) TTF_CloseFont(ttf.SDL_fonti);
	if (ttf.SDL_fontbi) TTF_CloseFont(ttf.SDL_fontbi);
	SDL_RWops *rwfont = SDL_RWFromConstMem(ttfFont, (int)ttfSize);
	ttf.SDL_font = TTF_OpenFontRW(rwfont, 1, ptsize);
	if (ttfSizeb>0) {
		SDL_RWops *rwfont = SDL_RWFromConstMem(ttfFontb, (int)ttfSizeb);
		ttf.SDL_fontb = TTF_OpenFontRW(rwfont, 1, ptsize);
	} else
		ttf.SDL_fontb = NULL;
	if (ttfSizei>0) {
		SDL_RWops *rwfont = SDL_RWFromConstMem(ttfFonti, (int)ttfSizei);
		ttf.SDL_fonti = TTF_OpenFontRW(rwfont, 1, ptsize);
	} else
		ttf.SDL_fonti = NULL;
	if (ttfSizebi>0) {
		SDL_RWops *rwfont = SDL_RWFromConstMem(ttfFontbi, (int)ttfSizebi);
		ttf.SDL_fontbi = TTF_OpenFontRW(rwfont, 1, ptsize);
	} else
		ttf.SDL_fontbi = NULL;
	ttf.pointsize = ptsize;
	TTF_GlyphMetrics(ttf.SDL_font, 65, NULL, NULL, NULL, NULL, &ttf.width);
	ttf.height = TTF_FontAscent(ttf.SDL_font)-TTF_FontDescent(ttf.SDL_font);
	if (ttf.fullScrn) {
		unsigned int maxWidth, maxHeight;
		GetMaxWidthHeight(&maxWidth, &maxHeight);
		ttf.offX = (maxWidth-ttf.width*ttf.cols)/2;
		ttf.offY = (maxHeight-ttf.height*ttf.lins)/2;
	}
	else
		ttf.offX = ttf.offY = 0;
	if (initCP) setTTFCodePage();
}

void SetOutputSwitch(const char *outputstr) {
#if C_DIRECT3D
        if (!strcasecmp(outputstr, "direct3d") || !strcasecmp(outputstr, "auto"))
            switchoutput = 6;
        else
#endif
#if C_OPENGL
        if (!strcasecmp(outputstr, "openglpp"))
            switchoutput = 5;
        else if (!strcasecmp(outputstr, "openglnb"))
            switchoutput = 4;
        else if (!strcasecmp(outputstr, "opengl")||!strcasecmp(outputstr, "openglnq") || !strcasecmp(outputstr, "auto"))
            switchoutput = 3;
        else
#endif
        if (!strcasecmp(outputstr, "surface"))
            switchoutput = 0;
        else
            switchoutput = -1;
}

void OUTPUT_TTF_Select(int fsize) {
    if (!initttf&&TTF_Init()) {											// Init SDL-TTF
        std::string message = "Could not init SDL-TTF: " + std::string(SDL_GetError());
        systemmessagebox("Error", message.c_str(), "ok","error", 1);
        change_output(switchoutput == -1 ? 0 : switchoutput);
        return;
    }
    int fontSize = 0;
    int winPerc = 0;
    if (fsize==3)
        winPerc = 100;
    else if (fsize>=MIN_PTSIZE)
        fontSize = fsize;
    else {
        Section_prop * ttf_section=static_cast<Section_prop *>(control->GetSection("ttf"));
        const char * fName = ttf_section->Get_string("font");
        const char * fbName = ttf_section->Get_string("fontbold");
        const char * fiName = ttf_section->Get_string("fontital");
        const char * fbiName = ttf_section->Get_string("fontboit");
        LOG_MSG("SDL: TTF activated %s", fName);
        force_conversion = true;
        int cp = dos.loaded_codepage;
        bool trysgf = false;
        if (!*fName) {
            std::string mtype(static_cast<Section_prop *>(control->GetSection("dosbox"))->Get_string("machine"));
            if (IS_PC98_ARCH||mtype.substr(0, 4)=="pc98"||(!notrysgf&&InitCodePage()&&isDBCSCP())) trysgf = true;
        }
        force_conversion = false;
        dos.loaded_codepage = cp;
        if (trysgf) failName = "SarasaGothicFixed";
        if ((!*fName&&!trysgf)||!readTTF(*fName?fName:failName.c_str(), false, false)) {
            ttfFont = DOSBoxTTFbi;
            ttfFontb = NULL;
            ttfFonti = NULL;
            ttfFontbi = NULL;
            ttfSize = sizeof(DOSBoxTTFbi);
            ttfSizeb = ttfSizei = ttfSizebi = 0;
            ttf.DOSBox = true;
            std::string message="";
            if (*fbName)
                message="A valid ttf.font setting is required for the ttf.fontbold setting: "+std::string(fbName);
            else if (*fiName)
                message="A valid ttf.font setting is required for the ttf.fontital setting: "+std::string(fiName);
            else if (*fbiName)
                message="A valid ttf.font setting is required for the ttf.fontboit setting: "+std::string(fbiName);
            if (fsize==0&&message.size())
                systemmessagebox("Warning", message.c_str(), "ok","warning", 1);
        } else {
            if (!*fbName||!readTTF(fbName, true, false)) {
                ttfFontb = NULL;
                ttfSizeb = 0;
            }
            if (!*fiName||!readTTF(fiName, false, true)) {
                ttfFonti = NULL;
                ttfSizei = 0;
            }
            if (!*fbiName||!readTTF(fbiName, true, true)) {
                ttfFontbi = NULL;
                ttfSizebi = 0;
            }
        }
        const char * colors = ttf_section->Get_string("colors");
        staycolors = strlen(colors) && *colors == '+'; // Always switch to preset value when '+' is specified
        if ((*colors && (!init_once || !init_twice))|| (staycolors && !ttf.inUse)) {
            if (!setColors(colors,-1)) {
                LOG_MSG("Incorrect color scheme: %s", colors);
                //setColors("#000000 #0000aa #00aa00 #00aaaa #aa0000 #aa00aa #aa5500 #aaaaaa #555555 #5555ff #55ff55 #55ffff #ff5555 #ff55ff #ffff55 #ffffff",-1);
            }
            if(ttf.inUse) init_once = true;
            if(!init_once) init_once = true;
            else if(!init_twice) init_twice = true;
        }
        else if (IS_EGAVGA_ARCH) {
            alt_rgb *rgbcolors = (alt_rgb*)render.pal.rgb;
            std::string str = "";
            char value[30];
            for (int i = 0; i < 16; i++) {
                sprintf(value,"#%02x%02x%02x",rgbcolors[i].red,rgbcolors[i].green,rgbcolors[i].blue);
                str+=std::string(value)+" ";
            }
            if (str.size()) {
                setColors(str.c_str(),-1);
                colorChanged=justChanged=false;
            }
        }
        SetBlinkRate(ttf_section);
        const char *wpstr=ttf_section->Get_string("wp");
        wpType=0;
        wpVersion=0;
        if (strlen(wpstr)>1) {
            if (!strncasecmp(wpstr, "WP", 2)) wpType=1;
            else if (!strncasecmp(wpstr, "WS", 2)) wpType=2;
            else if (!strncasecmp(wpstr, "XY", 2)) wpType=3;
            else if (!strncasecmp(wpstr, "FE", 2)) wpType=4;
            if (strlen(wpstr)>2&&wpstr[2]>='1'&&wpstr[2]<='9') wpVersion=wpstr[2]-'0';
        }
        wpBG = ttf_section->Get_int("wpbg");
        wpFG = ttf_section->Get_int("wpfg");
        if (wpFG<0) wpFG = 7;
        winPerc = ttf_section->Get_int("winperc");
        if (winPerc>100||(fsize==2&&GFX_IsFullscreen())||(fsize!=1&&fsize!=2&&(control->opt_fullscreen||static_cast<Section_prop *>(control->GetSection("sdl"))->Get_bool("fullscreen")))) winPerc=100;
        else if (winPerc<25) winPerc=25;
        enablePercLimit = ttf_section->Get_bool("enableWinPercLimit");
#if defined(HX_DOS)
        winPerc=100;
#else
        if ((fsize==1||switchttf)&&winPerc==100) {
            winPerc=60;
            if (switchttf&&GFX_IsFullscreen()) GFX_SwitchFullScreen();
        }
#endif
        fontSize = ttf_section->Get_int("ptsize");
        char512 = ttf_section->Get_bool("char512");
        showbold = ttf_section->Get_bool("bold");
        showital = ttf_section->Get_bool("italic");
        showline = ttf_section->Get_bool("underline");
        showsout = ttf_section->Get_bool("strikeout");
        printfont = ttf_section->Get_bool("printfont");
        dbcs_sbcs = ttf_section->Get_bool("autodbcs");
        autoboxdraw = ttf_section->Get_bool("autoboxdraw");
        halfwidthkana = ttf_section->Get_bool("halfwidthkana");
        ttf_dosv = ttf_section->Get_bool("dosvfunc");
        SetOutputSwitch(ttf_section->Get_string("outputswitch"));
        rtl = ttf_section->Get_bool("righttoleft");
        ttf.lins = ttf_section->Get_int("lins");
        ttf.cols = ttf_section->Get_int("cols");
        if (fsize&&!IS_PC98_ARCH&&!IS_EGAVGA_ARCH) ttf.lins = 25;
        if ((!CurMode||CurMode->type!=M_TEXT)&&!IS_PC98_ARCH) {
            if (ttf.cols<1) ttf.cols=80;
            if (ttf.lins<1) ttf.lins=25;
            CheckTTFLimit();
        } else if (firstset) {
            bool alter_vmode=false;
            uint16_t c=0, r=0;
            if (IS_PC98_ARCH) {
                c=80;
                r=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
            } else {
                c=strcmp(RunningProgram, "LOADLIN")?real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS):80;
                r=(uint16_t)(IS_EGAVGA_ARCH&&strcmp(RunningProgram, "LOADLIN")?real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS):24)+1;
            }
            if (ttf.lins<1||ttf.cols<1)	{
                if (ttf.cols<1)
                    ttf.cols = c;
                else {
                    ttf.cols = MAX(40, MIN(txtMaxCols, (int)ttf.cols));
                    if (ttf.cols != c) alter_vmode = true;
                }
                if (ttf.lins<1)
                    ttf.lins = r;
                else {
                    ttf.lins = MAX(24, MIN(txtMaxLins, (int)ttf.lins));
                    if (ttf.lins != r) alter_vmode = true;
                }
            } else {
                CheckTTFLimit();
                if (ttf.cols != c || ttf.lins != r) alter_vmode = true;
            }
            if (alter_vmode) {
                for (Bitu i = 0; ModeList_VGA[i].mode <= 7; i++) {								// Set the cols and lins in video mode 2,3,7
                    ModeList_VGA[i].twidth = ttf.cols;
                    ModeList_VGA[i].theight = ttf.lins;
                }
                if (!IS_PC98_ARCH) {
                    real_writeb(BIOSMEM_SEG,BIOSMEM_NB_COLS,ttf.cols);
                    if (IS_EGAVGA_ARCH) real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,ttf.lins-1);
                }
            }
            firstset=false;
        } else {
            uint16_t c=0, r=0;
            if (IS_PC98_ARCH) {
                c=80;
                r=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
            } else {
                c=strcmp(RunningProgram, "LOADLIN")?real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS):80;
                r=(uint16_t)(IS_EGAVGA_ARCH&&strcmp(RunningProgram, "LOADLIN")?real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS):24)+1;
            }
            ttf.cols=c;
            ttf.lins=r;
        }
    }

    if (winPerc == 100) {
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
        lastmenu = menu.toggle;
        menu.toggle=false;
        NonUserResizeCounter=1;
#if !defined(C_SDL2)
        SDL1_hax_SetMenu(NULL);
#endif
        RENDER_CallBack( GFX_CallBackReset );
#endif
#if defined (WIN32)
        DOSBox_SetSysMenu();
#endif
        ttf.fullScrn = true;
    } else
        ttf.fullScrn = false;

    // FIXME: TTF output makes the window non-resizeable, which is a problem, because
    //        this code picks a font point size designed to fill as much screen as possible!
    //        Until resizing is an option, limit the window to 2/3 of each dimension. --J.C.

    unsigned int maxWidth, maxHeight;
    GetMaxWidthHeight(&maxWidth, &maxHeight);

    // Limit dimensions
    if(!ttf.fullScrn) {
        #if !defined(HX_DOS)
        if(enablePercLimit) {
            maxWidth = (maxWidth * 2) / 3;
            maxHeight = (maxHeight * 2) / 3;
        }
        #endif
    }
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
    maxHeight -= mainMenu.menuBarHeightBase * 2;
#endif

#if defined(WIN32)
    if (!ttf.fullScrn) {																			// 3D borders
        maxWidth -= GetSystemMetrics(SM_CXBORDER)*2;
        maxHeight -= GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYBORDER)*2;
    }
#endif
    int	curSize = fontSize>=MIN_PTSIZE?fontSize:12;
    int lastGood = -1;
    int trapLoop = 0;

    if (fontSize<MIN_PTSIZE) {
        while (curSize != lastGood) {
            GFX_SelectFontByPoints(curSize);
            if (ttf.cols*ttf.width <= maxWidth && ttf.lins*ttf.height <= maxHeight) {				// if it fits on screen
                lastGood = curSize;
                float coveredPerc = float(100*ttf.cols*ttf.width/maxWidth*ttf.lins*ttf.height/maxHeight);
                if (trapLoop++ > 4 && coveredPerc <= winPerc)										// we can get into a +/-/+/-... loop!
                    break;
                curSize = (int)(curSize*sqrt((float)winPerc/coveredPerc));							// rounding down is ok
                if (curSize < MIN_PTSIZE)															// minimum size = 9
                    curSize = MIN_PTSIZE;
            } else if (--curSize < MIN_PTSIZE)														// silly, but OK, one never can tell..
                E_Exit("Cannot accommodate a window for %dx%d", ttf.lins, ttf.cols);
        }
        if (ttf.DOSBox && curSize > MIN_PTSIZE)														// make it even for DOSBox-X internal font (a bit nicer)
            curSize &= ~1;
    }
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
resize1:
#endif
    GFX_SelectFontByPoints(curSize);
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
    if (!ttf.fullScrn && menu_gui && menu.toggle && menuwidth_atleast(ttf.cols*ttf.width+ttf.offX*2+GetSystemMetrics(SM_CXBORDER)*2)>0) {
        if (ttf.cols*ttf.width > maxWidth || ttf.lins*ttf.height > maxHeight) E_Exit("Cannot accommodate a window for %dx%d", ttf.cols, ttf.lins);
        curSize++;
        goto resize1;
    }
#endif
	GetMaxWidthHeight(&maxWidth, &maxHeight);
#if defined(WIN32)
	if (!ttf.fullScrn) {
		maxWidth -= GetSystemMetrics(SM_CXBORDER)*2;
		maxHeight -= GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYBORDER)*2;
	}
#endif
resize2:
    if ((fontSize>=MIN_PTSIZE && 100*ttf.cols*ttf.width/maxWidth*ttf.lins*ttf.height/maxHeight > 100) || (lesssize && (ttf.cols*ttf.width>maxWidth || ttf.lins*ttf.height>maxHeight))) {
        if (lesssize && curSize > MIN_PTSIZE) {
            curSize--;
            GFX_SelectFontByPoints(curSize);
            goto resize2;
        }
        E_Exit("Cannot accommodate a window for %dx%d", ttf.cols, ttf.lins);
    }
    if (ttf.SDL_font && ttf.width) {
        int widthb, widthm, widthx, width0, width1, width9;
        widthb = widthm = widthx = width0 = width1 = width9 = 0;
        TTF_GlyphMetrics(ttf.SDL_font, 'B', NULL, NULL, NULL, NULL, &widthb);
        TTF_GlyphMetrics(ttf.SDL_font, 'M', NULL, NULL, NULL, NULL, &widthm);
        TTF_GlyphMetrics(ttf.SDL_font, 'X', NULL, NULL, NULL, NULL, &widthx);
        TTF_GlyphMetrics(ttf.SDL_font, '0', NULL, NULL, NULL, NULL, &width0);
        if (abs(ttf.width-widthb)>1 || abs(ttf.width-widthm)>1 || abs(ttf.width-widthx)>1 || abs(ttf.width-width0)>1) LOG_MSG("TTF: The loaded font is not monospaced.");
        int cp=dos.loaded_codepage;
        if (!cp) InitCodePage();
        if ((IS_PC98_ARCH || isDBCSCP()) && dbcs_sbcs) {
            TTF_GlyphMetrics(ttf.SDL_font, 0x4E00, NULL, NULL, NULL, NULL, &width1);
            TTF_GlyphMetrics(ttf.SDL_font, 0x4E5D, NULL, NULL, NULL, NULL, &width9);
            if (width1 <= ttf.width || width9 <= ttf.width) LOG_MSG("TTF: The loaded font may not support DBCS characters.");
            else if ((ttf.width*2 != width1 || ttf.width*2 != width9) && ttf.width == widthb && ttf.width == widthm && ttf.width == widthx && ttf.width == width0) LOG_MSG("TTF: The loaded font is not monospaced.");
        }
        dos.loaded_codepage = cp;
    }
    resetreq=false;
    sdl.desktop.want_type = SCREEN_TTF;
}

void ResetTTFSize(Bitu /*val*/) {
    resetFontSize();
}

void processWP(uint8_t *pcolorBG, uint8_t *pcolorFG) {
    charSet = 0;
    if (!wpType) return;
    uint8_t colorBG = *pcolorBG, colorFG = *pcolorFG;
    int style = 0;
    if (CurMode->mode == 7) {														// Mono (Hercules)
        style = showline && (colorFG&7) == 1 ? TTF_STYLE_UNDERLINE : TTF_STYLE_NORMAL;
        if ((colorFG&0xa) == colorFG && (colorBG&15) == 0)
            colorFG = 8;
        else if (colorFG&7)
            colorFG |= 7;
    }
    else if (wpType == 1) {															// WordPerfect
        if (showital && colorFG == 0xe && (colorBG&15) == (wpBG > -1 ? wpBG : 1)) {
            style = TTF_STYLE_ITALIC;
            colorFG = wpFG;
        }
        else if (showline && (colorFG == 1 || colorFG == wpFG+8) && (colorBG&15) == 7) {
            style = TTF_STYLE_UNDERLINE;
            colorBG = wpBG > -1 ? wpBG : 1;
            colorFG = colorFG == 1 ? wpFG : (wpFG+8);
        }
        else if (showsout && colorFG == 0 && (colorBG&15) == 3) {
            style = TTF_STYLE_STRIKETHROUGH;
            colorBG = wpBG > -1 ? wpBG : 1;
            colorFG = wpFG;
        }
    } else if (wpType == 2) {														// WordStar
        if (colorBG&8) {
            if (showline && colorBG&1)
                style |= TTF_STYLE_UNDERLINE;
            if (showital && colorBG&2)
                style |= TTF_STYLE_ITALIC;
            if (showsout && colorBG&4)
                style |= TTF_STYLE_STRIKETHROUGH;
            if (style)
                colorBG = wpBG > -1 ? wpBG : 0;
        }
    } else if (wpType == 3) {														// XyWrite
        if (showital && (colorFG == 10 || colorFG == 14) && colorBG != 12 && !(!showline && colorBG == 3)) {
            style = TTF_STYLE_ITALIC;
            if (colorBG == 3) {
                style |= TTF_STYLE_UNDERLINE;
                colorBG = wpBG > -1 ? wpBG : 1;
            }
            colorFG = colorFG == 10 ? wpFG : (wpFG+8);
        }
        else if (showline && (colorFG == 3 || colorFG == 0xb)) {
            style = TTF_STYLE_UNDERLINE;
            colorFG = colorFG == 3 ? wpFG : (wpFG+8);
        }
        else if (!showsout || colorBG != 4)
            style = TTF_STYLE_NORMAL;
        if (showsout && colorBG == 4 && colorFG != 12 && colorFG != 13) {
            style |= TTF_STYLE_STRIKETHROUGH;
            colorBG = wpBG > -1 ? wpBG : (wpVersion < 4 ? 0 : 1);
            if (colorFG != wpFG+8) colorFG = wpFG;
        }
    } else if (wpType == 4 && colorFG == wpFG) {									// FastEdit
        if (colorBG == 1 || colorBG == 5 || colorBG == 6 || colorBG == 7 || colorBG == 11 || colorBG == 12 || colorBG == 13 || colorBG == 15)
            colorFG += 8;
        if (colorBG == 2 || colorBG == 5) {
            if (showital) style |= TTF_STYLE_ITALIC;
        } else if (colorBG == 3 || colorBG == 6) {
            if (showline) style |= TTF_STYLE_UNDERLINE;
        } else if (colorBG == 4 || colorBG == 7) {
            if (showsout) style |= TTF_STYLE_STRIKETHROUGH;
        } else if (colorBG == 8 || colorBG == 11) {
            style |= (showital?TTF_STYLE_ITALIC:0) | (showline?TTF_STYLE_UNDERLINE:0);
        } else if (colorBG == 9 || colorBG == 12) {
            style |= (showital?TTF_STYLE_ITALIC:0) | (showsout?TTF_STYLE_STRIKETHROUGH:0);
        } else if (colorBG == 10 || colorBG == 13) {
            style |= (showline?TTF_STYLE_UNDERLINE:0) | (showsout?TTF_STYLE_STRIKETHROUGH:0);
        } else if (colorBG == 14 || colorBG == 15) {
            style |= (showital?TTF_STYLE_ITALIC:0) | (showline?TTF_STYLE_UNDERLINE:0) | (showsout?TTF_STYLE_STRIKETHROUGH:0);
        }
        colorBG = wpBG;
    }
    if (char512 && wpType == 1) {
        if (wpExtChar && (colorFG&8)) {	// WordPerfect high bit = second character set (if 512char active)
            charSet = 1;
            colorFG &= 7;
        }
    }
    if (showbold && (colorFG == wpFG+8 || (wpType == 1 && (wpVersion < 1 || wpVersion > 5 ) && colorFG == 3 && (colorBG&15) == (wpBG > -1 ? wpBG : 1)))) {
        if (ttf.SDL_fontbi || !(style&TTF_STYLE_ITALIC) || wpType == 4) style |= TTF_STYLE_BOLD;
        if ((ttf.SDL_fontbi && (style&TTF_STYLE_ITALIC)) || (ttf.SDL_fontb && !(style&TTF_STYLE_ITALIC)) || wpType == 4) colorFG = wpFG;
    }
    if (style)
        TTF_SetFontStyle(ttf.SDL_font, style);
    else
        TTF_SetFontStyle(ttf.SDL_font, TTF_STYLE_NORMAL);
    *pcolorBG = colorBG;
    *pcolorFG = colorFG;
}

bool hasfocus = true, lastfocus = true;
void GFX_EndTextLines(bool force) {
    if (!force&&!IS_PC98_ARCH&&(!CurMode||CurMode->type!=M_TEXT)) return;
#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
    if (!ttf.fullScrn) {
        mainMenu.setRedraw();
        GFX_DrawSDLMenu(mainMenu,mainMenu.display_list);
    }
#endif
    static uint8_t bcount = 0;
	Uint16 unimap[txtMaxCols+1];							// max+1 characters in a line
	int xmin = ttf.cols;									// keep track of changed area
	int ymin = ttf.lins;
	int xmax = -1;
	int ymax = -1;
	ttf_cell *curAC = curAttrChar;							// pointer to old/current buffer
	ttf_cell *newAC = newAttrChar;							// pointer to new/changed buffer

	if (ttf.fullScrn && (ttf.offX || ttf.offY)) {
        unsigned int maxWidth, maxHeight;
        GetMaxWidthHeight(&maxWidth, &maxHeight);
        SDL_Rect *rect = &sdl.updateRects[0];
        rect->x = 0; rect->y = 0; rect->w = maxWidth; rect->h = ttf.offY;
#if defined(C_SDL2)
        SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
#else
        SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
#endif
        rect->w = ttf.offX; rect->h = maxHeight;
#if defined(C_SDL2)
        SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
#else
        SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
#endif
        rect->x = 0; rect->y = sdl.draw.height + ttf.offY; rect->w = maxWidth; rect->h = maxHeight - sdl.draw.height - ttf.offY;
#if defined(C_SDL2)
        SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
#else
        SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
#endif
        rect->x = sdl.draw.width + ttf.offX; rect->y = 0; rect->w = maxWidth - sdl.draw.width - ttf.offX; rect->h = maxHeight;
#if defined(C_SDL2)
        SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
#else
        SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
#endif
    }

	if (ttf.cursor < ttf.cols*ttf.lins)	// hide/restore (previous) cursor-character if we had one

//		if (cursor_enabled && (vga.draw.cursor.sline > vga.draw.cursor.eline || vga.draw.cursor.sline > 15))
//		if (ttf.cursor != vga.draw.cursor.address>>1 || (vga.draw.cursor.enabled !=  cursor_enabled) || vga.draw.cursor.sline > vga.draw.cursor.eline || vga.draw.cursor.sline > 15)
		if ((ttf.cursor != vga.draw.cursor.address>>1) || vga.draw.cursor.sline > vga.draw.cursor.eline || vga.draw.cursor.sline > 15) {
			curAC[ttf.cursor] = newAC[ttf.cursor];
            curAC[ttf.cursor].chr ^= 0xf0f0;	// force redraw (differs)
        }

	lastfocus = hasfocus;
	hasfocus =
#if defined(C_SDL2)
	SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_INPUT_FOCUS
#else
	SDL_GetAppState () & SDL_APPINPUTFOCUS
#endif
	? true : false;
	bool focuschanged = lastfocus != hasfocus, noframe = !menu.toggle || ttf.fullScrn;
	ttf_textClip.h = ttf.height;
	ttf_textClip.y = 0;
	for (unsigned int y = 0; y < ttf.lins; y++) {
		bool draw = false;
		ttf_textRect.y = ttf.offY+y*ttf.height;
		for (unsigned int x = 0; x < ttf.cols; x++) {
			if (((newAC[x] != curAC[x] || newAC[x].selected != curAC[x].selected || (colorChanged && (justChanged || draw)) || force) && (!newAC[x].skipped || force)) || (!y && focuschanged && noframe)) {
				draw = true;
				xmin = min((int)x, xmin);
				ymin = min((int)y, ymin);
				ymax = y;

				bool dw = false;
				const int x1 = x;
				uint8_t colorBG = newAC[x].bg;
				uint8_t colorFG = newAC[x].fg;
				processWP(&colorBG, &colorFG);
                if (newAC[x].selected) {
                    uint8_t color = colorBG;
                    colorBG = colorFG;
                    colorFG = color;
                }
                bool colornul = staycolors || (IS_VGA_ARCH && (altBGR1[colorBG&15].red > 4 || altBGR1[colorBG&15].green > 4 || altBGR1[colorBG&15].blue > 4 || altBGR1[colorFG&15].red > 4 || altBGR1[colorFG&15].green > 4 || altBGR1[colorFG&15].blue > 4) && rgbColors[colorBG].red < 5 && rgbColors[colorBG].green < 5 && rgbColors[colorBG].blue < 5 && rgbColors[colorFG].red < 5 && rgbColors[colorFG].green <5 && rgbColors[colorFG].blue < 5);
				ttf_textRect.x = ttf.offX+(rtl?(ttf.cols-x-1):x)*ttf.width;
                if(colornul||(colorChanged&&!IS_VGA_ARCH)){
                    ttf_bgColor.r = altBGR1[colorBG&15].red;
                    ttf_bgColor.g = altBGR1[colorBG&15].green;
                    ttf_bgColor.b = altBGR1[colorBG&15].blue;
                    ttf_fgColor.r = altBGR1[colorFG&15].red;
                    ttf_fgColor.g = altBGR1[colorFG&15].green;
                    ttf_fgColor.b = altBGR1[colorFG&15].blue;
                }
                else {
                    ttf_bgColor.r = rgbColors[colorBG].red;
                    ttf_bgColor.g = rgbColors[colorBG].green;
                    ttf_bgColor.b = rgbColors[colorBG].blue;
                    ttf_fgColor.r = rgbColors[colorFG].red;
                    ttf_fgColor.g = rgbColors[colorFG].green;
                    ttf_fgColor.b = rgbColors[colorFG].blue;
                }
                if(noframe&&!hasfocus&&!y) { // Dim top line if focus is lost in Fullscreen mode
                    ttf_bgColor.r = (((uint16_t)ttf_bgColor.r * 2 +128) / 4) & 0xFF;
                    ttf_bgColor.g = (((uint16_t)ttf_bgColor.g * 2 +128) / 4) & 0xFF;
                    ttf_bgColor.b = (((uint16_t)ttf_bgColor.b * 2 +128) / 4) & 0xFF;
                    ttf_fgColor.r = (((uint16_t)ttf_fgColor.r * 2 +128) / 4) & 0xFF;
                    ttf_fgColor.g = (((uint16_t)ttf_fgColor.g * 2 +128) / 4) & 0xFF;
                    ttf_fgColor.b = (((uint16_t)ttf_fgColor.b * 2 +128) / 4) & 0xFF;
                }

                if (newAC[x].unicode) {
                    dw = newAC[x].doublewide;
                    unimap[x-x1] = newAC[x].chr;
                    curAC[x] = newAC[x];
                    x++;

                    if (dw) {
                        curAC[x] = newAC[x];
                        x++;
                        if (rtl) ttf_textRect.x -= ttf.width;
                    }
                }
                else {
                    uint8_t ascii = newAC[x].chr&255;
                    if(ttf_dosv && ascii == 0x5c)
                        ascii = 0x9d;
                    curAC[x] = newAC[x];
                    if (ascii > 175 && ascii < 179 && !IS_PC98_ARCH && !IS_JEGA_ARCH && dos.loaded_codepage != 864 && dos.loaded_codepage != 868 && dos.loaded_codepage != 874 && dos.loaded_codepage != 3021 && !(dos.loaded_codepage == 932 && halfwidthkana) && (dos.loaded_codepage < 1250 || dos.loaded_codepage > 1258) && !(altcp && dos.loaded_codepage == altcp) && !(customcp && dos.loaded_codepage == customcp)) {	// special: shade characters 176-178 unless PC-98
                        ttf_bgColor.b = (ttf_bgColor.b*(179-ascii) + ttf_fgColor.b*(ascii-175))>>2;
                        ttf_bgColor.g = (ttf_bgColor.g*(179-ascii) + ttf_fgColor.g*(ascii-175))>>2;
                        ttf_bgColor.r = (ttf_bgColor.r*(179-ascii) + ttf_fgColor.r*(ascii-175))>>2;
                        unimap[x-x1] = ' ';							// shaded space
                    } else {
                        unimap[x-x1] = cpMap[ascii+charSet*256];
                    }

                    x++;
                }

                {
                    unimap[x-x1] = 0;
                    xmax = max((int)(x-1), xmax);

                    SDL_Surface* textSurface = TTF_RenderUNICODE_Shaded(ttf.SDL_font, unimap, ttf_fgColor, ttf_bgColor, ttf.width*(dw?2:1));
                    ttf_textClip.w = (x-x1)*ttf.width;
                    SDL_BlitSurface(textSurface, &ttf_textClip, sdl.surface, &ttf_textRect);
                    SDL_FreeSurface(textSurface);
                    x--;
                }
			}
		}
		curAC += ttf.cols;
		newAC += ttf.cols;
	}
    if (!force) justChanged = false;
    // NTS: Additional fix is needed for the cursor in PC-98 mode; also expect further cleanup
	bcount++;
	if (vga.draw.cursor.enabled && vga.draw.cursor.sline <= vga.draw.cursor.eline && vga.draw.cursor.sline <= 16 && blinkCursor) {	// Draw cursor?
		unsigned int newPos = (unsigned int)(vga.draw.cursor.address>>1);
		if (newPos < ttf.cols*ttf.lins) {								// If on screen
			int y = newPos/ttf.cols;
			int x = newPos%ttf.cols;
			if (IS_JEGA_ARCH) {
                ccount++;
                if (ccount>=0x20) {
                    ccount=0;
                    vga.draw.cursor.count++;
                }
			} else
				vga.draw.cursor.count++;

			if (blinkCursor>-1)
				vga.draw.cursor.blinkon = (vga.draw.cursor.count & 1<<blinkCursor) ? true : false;

			if (ttf.cursor != newPos || vga.draw.cursor.sline != prev_sline || ((blinkstate != vga.draw.cursor.blinkon) && blinkCursor>-1)) {				// If new position or shape changed, force draw
				if (blinkCursor>-1 && blinkstate == vga.draw.cursor.blinkon) {
					vga.draw.cursor.count = 4;
					vga.draw.cursor.blinkon = true;
				}
				prev_sline = vga.draw.cursor.sline;
				xmin = min(x, xmin);
				xmax = max(x, xmax);
				ymin = min(y, ymin);
				ymax = max(y, ymax);
			}
			blinkstate = vga.draw.cursor.blinkon;
			ttf.cursor = newPos;
//			if (x >= xmin && x <= xmax && y >= ymin && y <= ymax  && (GetTickCount()&0x400))	// If overdrawn previously (or new shape)
			if (x >= xmin && x <= xmax && y >= ymin && y <= ymax && !newAttrChar[ttf.cursor].skipped) {							// If overdrawn previously (or new shape)
				uint8_t colorBG = newAttrChar[ttf.cursor].bg;
				uint8_t colorFG = newAttrChar[ttf.cursor].fg;
				processWP(&colorBG, &colorFG);

				/* Don't do this in PC-98 mode, the bright pink cursor in EDIT.COM looks awful and not at all how the cursor is supposed to look --J.C. */
				if (!IS_PC98_ARCH && blinking && colorBG&8) {
					colorBG-=8;
					if ((bcount/8)%2)
						colorFG=colorBG;
				}
				bool dw = newAttrChar[ttf.cursor].unicode && newAttrChar[ttf.cursor].doublewide;
                if(colorChanged&&!IS_VGA_ARCH){
                    ttf_bgColor.r = altBGR1[colorBG&15].red;
                    ttf_bgColor.g = altBGR1[colorBG&15].green;
                    ttf_bgColor.b = altBGR1[colorBG&15].blue;
                    ttf_fgColor.r = altBGR1[colorFG&15].red;
                    ttf_fgColor.g = altBGR1[colorFG&15].green;
                    ttf_fgColor.b = altBGR1[colorFG&15].blue;
                }
                else {
                    ttf_bgColor.r = rgbColors[colorBG].red;
                    ttf_bgColor.g = rgbColors[colorBG].green;
                    ttf_bgColor.b = rgbColors[colorBG].blue;
                    ttf_fgColor.r = rgbColors[colorFG].red;
                    ttf_fgColor.g = rgbColors[colorFG].green;
                    ttf_fgColor.b = rgbColors[colorFG].blue;
                }
				unimap[0] = newAttrChar[ttf.cursor].unicode?newAttrChar[ttf.cursor].chr:cpMap[newAttrChar[ttf.cursor].chr&255];
                if (dw) {
                    unimap[1] = newAttrChar[ttf.cursor].chr;
                    unimap[2] = 0;
                    xmax = max(x+1, xmax);
                } else
                    unimap[1] = 0;
				// first redraw character
				SDL_Surface* textSurface = TTF_RenderUNICODE_Shaded(ttf.SDL_font, unimap, ttf_fgColor, ttf_bgColor, ttf.width*(dw?2:1));
				ttf_textClip.w = ttf.width*(dw?2:1);
				ttf_textRect.x = ttf.offX+(rtl?(ttf.cols-x-(dw?2:1)):x)*ttf.width;
				ttf_textRect.y = ttf.offY+y*ttf.height;
				SDL_BlitSurface(textSurface, &ttf_textClip, sdl.surface, &ttf_textRect);
				SDL_FreeSurface(textSurface);
				if (vga.draw.cursor.blinkon || blinkCursor<0) {
                    // second reverse lower lines
                    textSurface = TTF_RenderUNICODE_Shaded(ttf.SDL_font, unimap, ttf_bgColor, ttf_fgColor, ttf.width*(dw?2:1));
                    ttf_textClip.y = (ttf.height*(vga.draw.cursor.sline>15?15:vga.draw.cursor.sline))>>4;
                    ttf_textClip.h = ttf.height - ttf_textClip.y;								// for now, cursor to bottom
                    ttf_textRect.y = ttf.offY+y*ttf.height + ttf_textClip.y;
                    SDL_BlitSurface(textSurface, &ttf_textClip, sdl.surface, &ttf_textRect);
                    SDL_FreeSurface(textSurface);
				}
			}
		}
	}
	if (xmin <= xmax) {												// if any changes
        SDL_Rect *rect = &sdl.updateRects[0];
        rect->x = ttf.offX+(rtl?(ttf.cols-xmax-1):xmin)*ttf.width; rect->y = ttf.offY+ymin*ttf.height; rect->w = (xmax-xmin+1)*ttf.width; rect->h = (ymax-ymin+1)*ttf.height;
#if defined(C_SDL2)
        SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
#else
        SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
#endif
    }
}

FT_Face GetTTFFace() {
    if (ttf.inUse && ttfFont && ttfSize) {
        TTF_Font *font = TTF_OpenFontRW(SDL_RWFromConstMem(ttfFont, (int)ttfSize), 1, ttf.pointsize);
        return font?font->face:NULL;
    } else
        return NULL;
}

void resetFontSize() {
	if (ttf.inUse) {
		GFX_SelectFontByPoints(ttf.pointsize);
		GFX_SetSize(720+sdl.clip.x, 400+sdl.clip.y, sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
		wmemset((wchar_t*)curAttrChar, -1, ttf.cols*ttf.lins);
		if (ttf.fullScrn) {																// smaller content area leaves old one behind
			SDL_FillRect(sdl.surface, NULL, 0);
            SDL_Rect *rect = &sdl.updateRects[0];
            rect->x = 0; rect->y = 0; rect->w = 0; rect->h = 0;
#if defined(C_SDL2)
            SDL_UpdateWindowSurfaceRects(sdl.window, sdl.updateRects, 4);
#else
            SDL_UpdateRects(sdl.surface, 4, sdl.updateRects);
#endif
		}
		GFX_EndTextLines(true);
#if defined(HX_DOS)
		PIC_AddEvent(drawmenu, 200);
#endif
	}
}

void decreaseFontSize() {
	int dec=ttf.DOSBox ? 2 : 1;
	if (ttf.inUse && ttf.pointsize >= MIN_PTSIZE + dec) {
		GFX_SelectFontByPoints(ttf.pointsize - dec);
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
		if (!ttf.fullScrn && menu_gui && menu.toggle && menuwidth_atleast(ttf.cols*ttf.width+ttf.offX*2+GetSystemMetrics(SM_CXBORDER)*2)>0) GFX_SelectFontByPoints(ttf.pointsize + dec);
#endif
		GFX_SetSize(720+sdl.clip.x, 400+sdl.clip.y, sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
		wmemset((wchar_t*)curAttrChar, -1, ttf.cols*ttf.lins);
		if (ttf.fullScrn) {																// smaller content area leaves old one behind
            ttf.fullScrn = false;
            resetFontSize();
        } else
            GFX_EndTextLines(true);
	}
}

void increaseFontSize() {
	int inc=ttf.DOSBox ? 2 : 1;
	if (ttf.inUse) {																	// increase fontsize
        unsigned int maxWidth, maxHeight;
        GetMaxWidthHeight(&maxWidth, &maxHeight);
#if defined(WIN32)
		if (!ttf.fullScrn) {															// 3D borders
			maxWidth -= GetSystemMetrics(SM_CXBORDER)*2;
			maxHeight -= GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYBORDER)*2;
		}
#endif
		GFX_SelectFontByPoints(ttf.pointsize + inc);
		if (ttf.cols*ttf.width <= maxWidth && ttf.lins*ttf.height <= maxHeight) {		// if it fits on screen
			GFX_SetSize(720+sdl.clip.x, 400+sdl.clip.y, sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
			wmemset((wchar_t*)curAttrChar, -1, ttf.cols*ttf.lins);						// force redraw of complete window
			GFX_EndTextLines(true);
		} else
			GFX_SelectFontByPoints(ttf.pointsize - inc);
	}
}

void TTF_IncreaseSize(bool pressed) {
    if (!pressed||ttf.fullScrn) return;
    increaseFontSize();
    return;
}

void TTF_DecreaseSize(bool pressed) {
    if (!pressed||ttf.fullScrn) return;
    decreaseFontSize();
    return;
}

void DBCSSBCS_mapper_shortcut(bool pressed) {
    if (!pressed) return;
    if (!isDBCSCP()) {
        systemmessagebox("Warning", MSG_Get("TTF_DBCS_ONLY"), "ok","warning", 1);
        return;
    }
    dbcs_sbcs=!dbcs_sbcs;
    SetVal("ttf", "autodbcs", dbcs_sbcs?"true":"false");
    mainMenu.get_item("mapper_dbcssbcs").check(dbcs_sbcs).refresh_item(mainMenu);
    if (ttf.inUse) resetFontSize();
}

void AutoBoxDraw_mapper_shortcut(bool pressed) {
    if (!pressed) return;
    if (!isDBCSCP()) {
        systemmessagebox("Warning", MSG_Get("TTF_DBCS_ONLY"), "ok","warning", 1);
        return;
    }
    autoboxdraw=!autoboxdraw;
    SetVal("ttf", "autoboxdraw", autoboxdraw?"true":"false");
    mainMenu.get_item("mapper_autoboxdraw").check(autoboxdraw).refresh_item(mainMenu);
    if (ttf.inUse) resetFontSize();
}

bool setVGAColor(const char *colorArray, int i);
void ttf_reset_colors() {
    if (ttf.inUse) {
        SetVal("ttf", "colors", "");
        setColors("#000000 #0000aa #00aa00 #00aaaa #aa0000 #aa00aa #aa5500 #aaaaaa #555555 #5555ff #55ff55 #55ffff #ff5555 #ff55ff #ffff55 #ffffff",-1);
    } else {
        char value[128];
        for (int i=0; i<16; i++) {
            strcpy(value,i==0?"#000000":i==1?"#0000aa":i==2?"#00aa00":i==3?"#00aaaa":i==4?"#aa0000":i==5?"#aa00aa":i==6?"#aa5500":i==7?"#aaaaaa":i==8?"#555555":i==9?"#5555ff":i==10?"#55ff55":i==11?"#55ffff":i==12?"#ff5555":i==13?"#ff55ff":i==14?"#ffff55":"#ffffff");
            setVGAColor(value, i);
        }
    }
}

void ResetColors_mapper_shortcut(bool pressed) {
    if (!pressed||(!ttf.inUse&&!IS_VGA_ARCH)) return;
    ttf_reset_colors();
}

void ttf_reset() {
    OUTPUT_TTF_Select(2);
    resetFontSize();
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
    int last = 0;
    while (TTF_using() && !GFX_IsFullscreen() && menu_gui && menu.toggle && menuwidth_atleast(ttf.cols*ttf.width+ttf.offX*2+GetSystemMetrics(SM_CXBORDER)*2)>0 && ttf.pointsize>last) {
        last = ttf.pointsize;
        increaseFontSize();
    }
#endif
}

void ttfreset(Bitu /*val*/) {
    ttf_reset();
}

void ttf_setlines(int cols, int lins) {
    if (cols>0) SetVal("ttf", "cols", std::to_string(cols));
    if (lins>0) SetVal("ttf", "lins", std::to_string(lins));
    firstset=true;
    ttf_reset();
    real_writeb(BIOSMEM_SEG,BIOSMEM_NB_COLS,ttf.cols);
    if (IS_EGAVGA_ARCH) real_writeb(BIOSMEM_SEG,BIOSMEM_NB_ROWS,ttf.lins-1);
    vga.draw.address_add = ttf.cols * 2;
}

void ttf_switch_on(bool ss=true) {
    if ((ss&&ttfswitch)||(!ss&&switch_output_from_ttf)) {
        checkcol = 0;
        /* Do NOT run a real-mode guest interrupt while the guest CPU is in
         * protected mode (e.g. a game using a DOS extender). CALLBACK_RunRealInt()
         * would push a real-mode-style frame and corrupt the extender,
         * causing a #GP storm on the handler's IRETD. */
        if (strcmp(RunningProgram, "LOADLIN") && !cpu.pmode) {
            uint16_t oldax=reg_ax;
            reg_ax=0x1600;
            CALLBACK_RunRealInt(0x2F);
            if (reg_al!=0&&reg_al!=0x80) {reg_ax=oldax;return;}
            reg_ax=oldax;
        }
        if (window_was_maximized&&!GFX_IsFullscreen()) {
#if defined(WIN32)
            ShowWindow(GetHWND(), SW_RESTORE);
#elif defined(C_SDL2)
            SDL_RestoreWindow(sdl.window);
#endif
        }
        bool OpenGL_using(void), gl = OpenGL_using();
	(void)gl; // unused var warning
#if defined(WIN32) && !defined(C_SDL2)
        change_output(0); // call OUTPUT_SURFACE_Select() to initialize output before enabling TTF output on Windows builds
#endif
        change_output(10); // call OUTPUT_TTF_Select()
        SetVal("sdl", "output", "ttf");
        std::string showdbcsstr = static_cast<Section_prop *>(control->GetSection("dosv"))->Get_string("showdbcsnodosv");
        showdbcs = showdbcsstr=="true"||showdbcsstr=="1"||(showdbcsstr=="auto" && (loadlang || dbcs_sbcs));
        if (!IS_EGAVGA_ARCH) showdbcs = false;
        void OutputSettingMenuUpdate(void);
        OutputSettingMenuUpdate();
        if (ss) ttfswitch = false;
        else switch_output_from_ttf = false;
        mainMenu.get_item("output_ttf").enable(true).refresh_item(mainMenu);
        if (ttf.fullScrn) {
            if (!GFX_IsFullscreen()) GFX_SwitchFullscreenNoReset();
            OUTPUT_TTF_Select(3);
#if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
            if (gl && GFX_IsFullscreen()) { // Hack for full-screen switch from OpenGL outputs
                GFX_SwitchFullScreen();
                GFX_SwitchFullScreen();
            }
#endif
            resetreq = true;
        }
        resetFontSize();
#ifdef C_SDL2
        transparency = 0;
        SetWindowTransparency(static_cast<Section_prop *>(control->GetSection("sdl"))->Get_int("transparency"));
#endif
        if (!IS_PC98_ARCH && vga.draw.address_add != ttf.cols * 2) checkcol = ss?2:1;
    }
    is_ttfswitched_on = true;
}

void ttf_switch_off(bool ss=true) {
    checkcol = 0;
    if (!ss&&ttfswitch)
        ttf_switch_on();
    if (ttf.inUse) {
        std::string output="surface";
        int out=switchoutput;
        if (switchoutput==0)
            output = "surface";
#if C_OPENGL
        else if (switchoutput==3)
            output = "opengl";
        else if (switchoutput==4)
            output = "openglnb";
        else if (switchoutput==5)
            output = "openglpp";
#endif
#if C_DIRECT3D
        else if (switchoutput==6)
            output = "direct3d";
#endif
        else {
#if C_DIRECT3D
            out = 6;
            output = "direct3d";
#elif C_OPENGL
            out = 3;
            output = "opengl";
#else
            out = 0;
            output = "surface";
#endif
        }
        KEYBOARD_Clear();
        showdbcs = IS_EGAVGA_ARCH;
        change_output(out);
        SetVal("sdl", "output", output);
        void OutputSettingMenuUpdate(void);
        OutputSettingMenuUpdate();
        if (ss) ttfswitch = true;
        else switch_output_from_ttf = true;
        //if (GFX_IsFullscreen()) GFX_SwitchFullscreenNoReset();
        mainMenu.get_item("output_ttf").enable(false).refresh_item(mainMenu);
        RENDER_Reset();
#ifdef C_SDL2
        transparency = 0;
        SetWindowTransparency(static_cast<Section_prop *>(control->GetSection("sdl"))->Get_int("transparency"));
#endif
        if (Mouse_IsLocked()) GFX_CaptureMouse(true);
    }
    is_ttfswitched_on = false;
}
#endif
