From 07e8506a7eeffec2aa3b5342df97805244be767b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Fri, 12 May 2023 21:42:25 -0400
Subject: [PATCH] Add "external" scaling for when the driver has its own
 scaling method

Currently useful only in GDI and SDL modes.  The external scaling
method is likely lower CPU utilization and may or may not look
better.
---
 src/conio/ciolib.c      |  20 ++++++++
 src/conio/ciolib.h      |  39 ++++++++++------
 src/conio/sdl_con.c     |  74 +++++++++++++++++------------
 src/conio/sdl_con.h     |   2 +
 src/conio/win32gdi.c    | 100 ++++++++++++++++++++++++++++++----------
 src/conio/win32gdi.h    |   2 +
 src/syncterm/bbslist.c  |  38 +++++++++++++--
 src/syncterm/syncterm.c |   2 +
 src/syncterm/syncterm.h |   1 +
 9 files changed, 208 insertions(+), 70 deletions(-)

diff --git a/src/conio/ciolib.c b/src/conio/ciolib.c
index 96b18a4bbc..c5887eec72 100644
--- a/src/conio/ciolib.c
+++ b/src/conio/ciolib.c
@@ -89,6 +89,7 @@ CIOLIBEXPORT int ciolib_initial_window_height = -1;
 CIOLIBEXPORT int ciolib_initial_window_width = -1;
 CIOLIBEXPORT double ciolib_initial_scaling = 0;
 CIOLIBEXPORT int ciolib_initial_mode = C80;
+CIOLIBEXPORT enum ciolib_scaling ciolib_initial_scaling_type = CIOLIB_SCALING_INTERNAL;
 CIOLIBEXPORT const uint32_t *ciolib_r2yptr;
 CIOLIBEXPORT const uint32_t *ciolib_y2rptr;
 
@@ -151,6 +152,8 @@ CIOLIBEXPORT void ciolib_set_vmem_attr(struct vmem_cell *cell, uint8_t attr);
 CIOLIBEXPORT void ciolib_setwinsize(int width, int height);
 CIOLIBEXPORT void ciolib_setwinposition(int x, int y);
 CIOLIBEXPORT enum ciolib_codepage ciolib_getcodepage(void);
+CIOLIBEXPORT void ciolib_setscaling_type(enum ciolib_scaling);
+CIOLIBEXPORT enum ciolib_scaling ciolib_getscaling_type(void);
 
 #if defined(WITH_SDL)
 int sdl_video_initialized = 0;
@@ -202,6 +205,8 @@ static int try_gdi_init(int mode)
 		cio_api.replace_font = bitmap_replace_font;
 		cio_api.beep = gdi_beep;
 		cio_api.mousepointer=gdi_mousepointer;
+		cio_api.setscaling_type=gdi_setscaling_type;
+		cio_api.getscaling_type=gdi_getscaling_type;
 		return(1);
 	}
 	return(0);
@@ -255,6 +260,8 @@ static int try_sdl_init(int mode)
 		cio_api.replace_font = bitmap_replace_font;
 		cio_api.beep = sdl_beep;
 		cio_api.mousepointer=sdl_mousepointer;
+		cio_api.setscaling_type=sdl_setscaling_type;
+		cio_api.getscaling_type=sdl_getscaling_type;
 		return(1);
 	}
 	return(0);
@@ -1971,6 +1978,19 @@ CIOLIBEXPORT enum ciolib_codepage ciolib_getcodepage(void)
 	return conio_fontdata[font].cp;
 }
 
+CIOLIBEXPORT enum ciolib_scaling ciolib_getscaling_type(void)
+{
+	if (cio_api.getscaling != NULL)
+		return cio_api.getscaling();
+	return CIOLIB_SCALING_INTERNAL;
+}
+
+CIOLIBEXPORT void ciolib_setscaling_type(enum ciolib_scaling newval)
+{
+	if (cio_api.setscaling_type != NULL)
+		cio_api.setscaling_type(newval);
+}
+
 #if defined(__DARWIN__)
 #ifdef main
 #undef main
diff --git a/src/conio/ciolib.h b/src/conio/ciolib.h
index ad18824b01..2a08c827c9 100644
--- a/src/conio/ciolib.h
+++ b/src/conio/ciolib.h
@@ -228,6 +228,11 @@ enum
 	_NORMALCURSOR
 };
 
+enum ciolib_scaling {
+	CIOLIB_SCALING_INTERNAL,
+	CIOLIB_SCALING_EXTERNAL,
+};
+
 struct text_info {
 	unsigned char winleft;        /* left window coordinate */
 	unsigned char wintop;         /* top window coordinate */
@@ -303,19 +308,20 @@ typedef struct {
 	int		mode;
 	int		mouse;
 	uint64_t	options;
-#define	CONIO_OPT_LOADABLE_FONTS	1
-#define CONIO_OPT_BLINK_ALT_FONT	2
-#define CONIO_OPT_BOLD_ALT_FONT		4
-#define CONIO_OPT_BRIGHT_BACKGROUND	8
-#define CONIO_OPT_PALETTE_SETTING	16
-#define CONIO_OPT_SET_PIXEL			32
-#define CONIO_OPT_CUSTOM_CURSOR		64
-#define CONIO_OPT_FONT_SELECT		128
-#define CONIO_OPT_SET_TITLE			256
-#define CONIO_OPT_SET_NAME			512
-#define CONIO_OPT_SET_ICON			1024
-#define CONIO_OPT_EXTENDED_PALETTE	2048
-#define CONIO_OPT_BLOCKY_SCALING	4096
+#define	CONIO_OPT_LOADABLE_FONTS    (1 <<  1)
+#define CONIO_OPT_BLINK_ALT_FONT    (1 <<  2)
+#define CONIO_OPT_BOLD_ALT_FONT     (1 <<  3)
+#define CONIO_OPT_BRIGHT_BACKGROUND (1 <<  4)
+#define CONIO_OPT_PALETTE_SETTING   (1 <<  5)
+#define CONIO_OPT_SET_PIXEL         (1 <<  6)
+#define CONIO_OPT_CUSTOM_CURSOR     (1 <<  7)
+#define CONIO_OPT_FONT_SELECT       (1 <<  8)
+#define CONIO_OPT_SET_TITLE         (1 <<  9)
+#define CONIO_OPT_SET_NAME          (1 << 10)
+#define CONIO_OPT_SET_ICON          (1 << 11)
+#define CONIO_OPT_EXTENDED_PALETTE  (1 << 12)
+#define CONIO_OPT_BLOCKY_SCALING    (1 << 13)
+#define CONIO_OPT_EXTERNAL_SCALING  (1 << 14)
 	void	(*clreol)		(void);
 	int		(*puttext)		(int,int,int,int,void *);
 	int		(*vmem_puttext)		(int,int,int,int,struct vmem_cell *);
@@ -386,6 +392,8 @@ typedef struct {
 	int	(*checkfont)(int font_num);
 	void	(*setwinsize)	(int width, int height);
 	void	(*setwinposition)	(int x, int y);
+	void	(*setscaling_type)	(enum ciolib_scaling);
+	enum ciolib_scaling (*getscaling_type)	(void);
 } cioapi_t;
 
 #define _conio_kbhit()		kbhit()
@@ -408,6 +416,7 @@ CIOLIBEXPORTVAR int ciolib_initial_window_height;
 CIOLIBEXPORTVAR int ciolib_initial_window_width;
 CIOLIBEXPORTVAR double ciolib_initial_scaling;
 CIOLIBEXPORTVAR int ciolib_initial_mode;
+CIOLIBEXPORTVAR enum ciolib_scaling ciolib_initial_scaling_type;
 CIOLIBEXPORTVAR const uint32_t *ciolib_r2yptr;
 CIOLIBEXPORTVAR const uint32_t *ciolib_y2rptr;
 
@@ -489,6 +498,8 @@ CIOLIBEXPORT void ciolib_set_vmem_attr(struct vmem_cell *cell, uint8_t attr);
 CIOLIBEXPORT void ciolib_setwinsize(int width, int height);
 CIOLIBEXPORT void ciolib_setwinposition(int x, int y);
 CIOLIBEXPORT enum ciolib_codepage ciolib_getcodepage(void);
+CIOLIBEXPORT void ciolib_setscaling_type(enum ciolib_scaling);
+CIOLIBEXPORT enum ciolib_scaling ciolib_getscaling_type(void);
 
 /* DoorWay specific stuff that's only applicable to ANSI mode. */
 CIOLIBEXPORT void ansi_ciolib_setdoorway(int enable);
@@ -575,6 +586,8 @@ CIOLIBEXPORT void ansi_ciolib_setdoorway(int enable);
 	#define setwinsize(a,b)			ciolib_setwinsize(a,b)
 	#define setwinposition(a,b)		ciolib_setwinposition(a,b)
 	#define getcodepage()			ciolib_getcodepage()
+	#define setscaling_type(a)		ciolib_setscaling_type(a)
+	#define getscaling_type()		ciolib_getscaling_type()
 #endif
 
 #ifdef WITH_SDL
diff --git a/src/conio/sdl_con.c b/src/conio/sdl_con.c
index 8faf7dccd2..e3b11026c0 100644
--- a/src/conio/sdl_con.c
+++ b/src/conio/sdl_con.c
@@ -329,25 +329,6 @@ void sdl_flush(void)
 	sdl_user_func(SDL_USEREVENT_FLUSH);
 }
 
-/*
- * Returns true if the specified width/height can use the
- * internal scaler
- *
- * vstat lock must be held
- */
-static bool
-window_can_scale_internally(struct video_stats *vs)
-{
-	double ival;
-	double fval = modf(vstat.scaling, &ival);
-
-	// TODO: Add toggle for software scaling
-	return true;
-	if (fval == 0.0)
-		return true;
-	return false;
-}
-
 static int sdl_init_mode(int mode, bool init)
 {
 	int w, h;
@@ -374,10 +355,12 @@ static int sdl_init_mode(int mode, bool init)
 		h = 0;
 	}
 	bitmap_drv_init_mode(mode, &bitmap_width, &bitmap_height, w, h);
-	if (init && ciolib_initial_scaling) {
-		bitmap_get_scaled_win_size(ciolib_initial_scaling, &vstat.winwidth, &vstat.winheight, 0, 0);
+	if (init) {
+		internal_scaling = (ciolib_initial_scaling_type == CIOLIB_SCALING_INTERNAL);
+		if (ciolib_initial_scaling) {
+			bitmap_get_scaled_win_size(ciolib_initial_scaling, &vstat.winwidth, &vstat.winheight, 0, 0);
+		}
 	}
-	internal_scaling = window_can_scale_internally(&vstat);
 	pthread_mutex_lock(&sdl_mode_mutex);
 	sdl_mode = true;
 	pthread_mutex_unlock(&sdl_mode_mutex);
@@ -409,7 +392,7 @@ int sdl_init(int mode)
 #ifdef _WIN32
 		FreeConsole();
 #endif
-		cio_api.options |= CONIO_OPT_PALETTE_SETTING | CONIO_OPT_SET_TITLE | CONIO_OPT_SET_NAME | CONIO_OPT_SET_ICON;
+		cio_api.options |= CONIO_OPT_PALETTE_SETTING | CONIO_OPT_SET_TITLE | CONIO_OPT_SET_NAME | CONIO_OPT_SET_ICON | CONIO_OPT_EXTERNAL_SCALING;
 		return(0);
 	}
 
@@ -461,7 +444,6 @@ static void internal_setwinsize(struct video_stats *vs, bool force)
 		vstat.scaling = sdl_getscaling();
 	}
 	pthread_mutex_unlock(&vstatlock);
-	internal_scaling = window_can_scale_internally(vs);
 	if (changed)
 		setup_surfaces(vs);
 }
@@ -610,7 +592,6 @@ static void setup_surfaces(struct video_stats *vs)
 	pthread_mutex_lock(&win_mutex);
 	idealw = vs->winwidth;
 	idealh = vs->winheight;
-	internal_scaling = window_can_scale_internally(vs);
 	sdl.SetHint(SDL_HINT_RENDER_SCALE_QUALITY, internal_scaling ? "0" : "2");
 
 	if (win == NULL) {
@@ -620,10 +601,12 @@ static void setup_surfaces(struct video_stats *vs)
 			vs->winwidth = idealw;
 			vs->winheight = idealh;
 			sdl.RenderClear(renderer);
-			if (internal_scaling)
+			if (internal_scaling) {
 				newtexture = sdl.CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, idealw, idealh);
-			else
+			}
+			else {
 				newtexture = sdl.CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, vs->scrnwidth, vs->scrnheight);
+			}
 
 			if (texture)
 				sdl.DestroyTexture(texture);
@@ -640,10 +623,12 @@ static void setup_surfaces(struct video_stats *vs)
 		sdl.GetWindowSize(win, &idealw, &idealh);
 		vs->winwidth = idealw;
 		vs->winheight = idealh;
-		if (internal_scaling)
+		if (internal_scaling) {
 			newtexture = sdl.CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, idealw, idealh);
-		else
+		}
+		else {
 			newtexture = sdl.CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, vs->scrnwidth, vs->scrnheight);
+		}
 		sdl.RenderClear(renderer);
 		if (texture)
 			sdl.DestroyTexture(texture);
@@ -1279,3 +1264,34 @@ sdl_setscaling(double newval)
 	pthread_mutex_unlock(&vstatlock);
 	sdl_setwinsize(w, h);
 }
+
+enum ciolib_scaling
+sdl_getscaling_type(void)
+{
+	enum ciolib_scaling ret;
+
+	pthread_mutex_lock(&vstatlock);
+	ret = (internal_scaling ? CIOLIB_SCALING_INTERNAL : CIOLIB_SCALING_EXTERNAL);
+	pthread_mutex_unlock(&vstatlock);
+	return ret;
+}
+
+void
+sdl_setscaling_type(enum ciolib_scaling newval)
+{
+	struct video_stats cvstat = vstat;
+	int w, h;
+
+	update_cvstat(&cvstat);
+	pthread_mutex_lock(&vstatlock);
+	if ((newval == CIOLIB_SCALING_INTERNAL) != internal_scaling) {
+		internal_scaling = (newval == CIOLIB_SCALING_INTERNAL);
+		w = vstat.winwidth;
+		h = vstat.winheight;
+		pthread_mutex_unlock(&vstatlock);
+		sdl_user_func_ret(SDL_USEREVENT_SETVIDMODE, w, h);
+	}
+	else {
+		pthread_mutex_unlock(&vstatlock);
+	}
+}
diff --git a/src/conio/sdl_con.h b/src/conio/sdl_con.h
index 56d6a5c974..569391c9cf 100644
--- a/src/conio/sdl_con.h
+++ b/src/conio/sdl_con.h
@@ -40,6 +40,8 @@ void sdl_beep(void);
 int sdl_mousepointer(enum ciolib_mouse_ptr type);
 double sdl_getscaling(void);
 void sdl_setscaling(double newval);
+enum ciolib_scaling sdl_getscaling_type(void);
+void sdl_setscaling_type(enum ciolib_scaling newval);
 
 #if defined(__DARWIN__)
 void sdl_init_darwin(void *args);
diff --git a/src/conio/win32gdi.c b/src/conio/win32gdi.c
index ad1900f9df..c2ad288ce5 100644
--- a/src/conio/win32gdi.c
+++ b/src/conio/win32gdi.c
@@ -24,6 +24,7 @@ static int xoff, yoff;
 static int dwidth = 640;
 static int dheight = 480;
 static bool init_success;
+static enum ciolib_scaling stype;
 
 #define WM_USER_INVALIDATE WM_USER
 #define WM_USER_SETSIZE (WM_USER + 1)
@@ -57,6 +58,7 @@ static pthread_mutex_t gdi_headlock;
 static pthread_mutex_t winpos_lock;
 static pthread_mutex_t rect_lock;
 static pthread_mutex_t off_lock;
+static pthread_mutex_t stypelock;
 
 // Internal implementation
 
@@ -251,17 +253,16 @@ gdi_handle_wm_paint(HWND hwnd)
 	static HDC memDC = NULL;
 	static HBITMAP di = NULL;
 	static int diw = -1, dih = -1;
-	static int sww = -1, swh = -1;
-	static int lww = -1, lwh = -1;
 
 	PAINTSTRUCT ps;
 	struct rectlist *list;
 	HDC winDC;
 	int w,h;
 	int sw,sh;
-	int xscale, yscale;
+	int vsw,vsh;
 	struct graphics_buffer *gb;
 	void *data;
+	enum ciolib_scaling st;
 
 	list = get_rect();
 	if (list == NULL)
@@ -269,32 +270,54 @@ gdi_handle_wm_paint(HWND hwnd)
 	pthread_mutex_lock(&vstatlock);
 	w = vstat.winwidth;
 	h = vstat.winheight;
+	vsw = vstat.scrnwidth;
+	vsh = vstat.scrnheight;
 	bitmap_get_scaled_win_size(vstat.scaling, &sw, &sh, vstat.winwidth, vstat.winheight);
 	pthread_mutex_unlock(&vstatlock);
-	gb = do_scale(list, sw, sh);
-	if (di == NULL || diw != gb->w || dih != gb->h) {
-		lww = -1;
-		if (di != NULL) {
-			DeleteObject(di);
-			di = NULL;
+	pthread_mutex_lock(&off_lock);
+	dwidth = sw;
+	dheight = sh;
+	pthread_mutex_unlock(&off_lock);
+	pthread_mutex_lock(&stypelock);
+	st = stype;
+	pthread_mutex_unlock(&stypelock);
+	if (st == CIOLIB_SCALING_INTERNAL) {
+		gb = do_scale(list, sw, sh);
+		if (di == NULL || diw != gb->w || dih != gb->h) {
+			if (di != NULL) {
+				DeleteObject(di);
+				di = NULL;
+			}
+			diw = gb->w;
+			b5hdr.bV5Width = gb->w;
+			dih = gb->h;
+			b5hdr.bV5Height = -gb->h;
+			b5hdr.bV5SizeImage = gb->w * gb->h * 4;
 		}
-		diw = gb->w;
-		b5hdr.bV5Width = gb->w;
-		dih = gb->h;
-		b5hdr.bV5Height = -gb->h;
-		b5hdr.bV5SizeImage = gb->w * gb->h * 4;
-		pthread_mutex_lock(&off_lock);
-		dwidth = diw;
-		dheight = dih;
-		pthread_mutex_unlock(&off_lock);
+		data = gb->data;
+	}
+	else {
+		if (di == NULL || diw != vsw || dih != vsh) {
+			if (di != NULL) {
+				DeleteObject(di);
+				di = NULL;
+			}
+			diw = vsw;
+			b5hdr.bV5Width = vsw;
+			dih = vsh;
+			b5hdr.bV5Height = -vsh;
+			b5hdr.bV5SizeImage = vsw * vsh * 4;
+		}
+		sw = vsw;
+		sh = vsh;
+		data = list->data;
 	}
 	pthread_mutex_lock(&off_lock);
 	if (maximized) {
-		xoff = (w - diw) / 2;
-		yoff = (h - dih) / 2;
+		xoff = (w - dwidth) / 2;
+		yoff = (h - dheight) / 2;
 	}
 	pthread_mutex_unlock(&off_lock);
-	data = gb->data;
 	winDC = BeginPaint(hwnd, &ps);
 	if (memDC == NULL) {
 		memDC = CreateCompatibleDC(winDC);
@@ -303,10 +326,13 @@ gdi_handle_wm_paint(HWND hwnd)
 		di = CreateDIBitmap(winDC, (BITMAPINFOHEADER *)&b5hdr, CBM_INIT, data, (BITMAPINFO *)&b5hdr, DIB_RGB_COLORS);
 	else
 		SetDIBits(winDC, di, 0, dih, data, (BITMAPINFO *)&b5hdr, DIB_RGB_COLORS);
-	// Clear to black first
 	di = SelectObject(memDC, di);
 	pthread_mutex_lock(&off_lock);
-	BitBlt(winDC, xoff, yoff, dwidth, dheight, memDC, 0, 0, SRCCOPY);
+	if (st == CIOLIB_SCALING_INTERNAL)
+		BitBlt(winDC, xoff, yoff, dwidth, dheight, memDC, 0, 0, SRCCOPY);
+	else
+		StretchBlt(winDC, xoff, yoff, dwidth, dheight, memDC, 0, 0, sw, sh, SRCCOPY);
+	// Clear around image
 	if (xoff > 0) {
 		BitBlt(winDC, 0, 0, xoff - 1, h, memDC, 0, 0, BLACKNESS);
 		BitBlt(winDC, xoff + dwidth, 0, w, h, memDC, 0, 0, BLACKNESS);
@@ -326,7 +352,8 @@ gdi_handle_wm_paint(HWND hwnd)
 	pthread_mutex_unlock(&off_lock);
 	EndPaint(hwnd, &ps);
 	di = SelectObject(memDC, di);
-	release_buffer(gb);
+	if (st == CIOLIB_SCALING_INTERNAL)
+		release_buffer(gb);
 	return 0;
 }
 
@@ -725,6 +752,7 @@ gdi_thread(void *arg)
 	if (ciolib_initial_scaling != 0) {
 		bitmap_get_scaled_win_size(ciolib_initial_scaling, &vstat.winwidth, &vstat.winheight, 0, 0);
 	}
+	stype = ciolib_initial_scaling_type;
 	// Now make the inside of the window the size we want (sigh)
 	r.left = r.top = 0;
 	r.right = vstat.winwidth;
@@ -1019,6 +1047,7 @@ gdi_initciolib(int mode)
 	pthread_mutex_init(&winpos_lock, NULL);
 	pthread_mutex_init(&rect_lock, NULL);
 	pthread_mutex_init(&off_lock, NULL);
+	pthread_mutex_init(&stypelock, NULL);
 	init_sem = CreateSemaphore(NULL, 0, INT_MAX, NULL);
 
 	return(gdi_init(mode));
@@ -1108,3 +1137,26 @@ gdi_setscaling(double newval)
 	pthread_mutex_unlock(&vstatlock);
 	gdi_setwinsize(w, h);
 }
+
+enum ciolib_scaling
+gdi_getscaling_type(void)
+{
+	enum ciolib_scaling ret;
+
+	// TODO: I hate having nested locks like this. :(
+	pthread_mutex_lock(&stypelock);
+	ret = stype;
+	pthread_mutex_unlock(&stypelock);
+	return ret;
+}
+
+void
+gdi_setscaling_type(enum ciolib_scaling newtype)
+{
+	int w, h;
+
+	pthread_mutex_lock(&stypelock);
+	stype = newtype;
+	pthread_mutex_unlock(&stypelock);
+
+}
diff --git a/src/conio/win32gdi.h b/src/conio/win32gdi.h
index 0c8c8f4492..ea26a29929 100644
--- a/src/conio/win32gdi.h
+++ b/src/conio/win32gdi.h
@@ -21,5 +21,7 @@ void gdi_setwinposition(int x, int y);
 void gdi_setwinsize(int w, int h);
 double gdi_getscaling(void);
 void gdi_setscaling(double newval);
+enum ciolib_scaling gdi_getscaling_type(void);
+void gdi_setscaling_type(enum ciolib_scaling newtype);
 
 #endif
diff --git a/src/syncterm/bbslist.c b/src/syncterm/bbslist.c
index d6419d0014..40be50b624 100644
--- a/src/syncterm/bbslist.c
+++ b/src/syncterm/bbslist.c
@@ -267,6 +267,8 @@ static char *conn_type_help = "`Connection Type`\n\n"
 
 static char *YesNo[3] = {"Yes", "No", ""};
 
+static char *scaling_names[4] = {"Blocky", "Pointy", "External"};
+
 ini_style_t  ini_style = {
         /* key_len */
 	15,
@@ -1757,6 +1759,27 @@ custom_mode_adjusted(int *cur, char **opt)
 	    opt);
 }
 
+static int
+settings_to_scale(void)
+{
+	int i = 0;
+
+	if (!settings.blocky)
+		i |= 1;
+	if (settings.extern_scale)
+		i |= 2;
+	if (i > 2)
+		i = 2;
+	return i;
+}
+
+static void
+scale_to_settings(int i)
+{
+	settings.blocky = (i & 1) ? false : true;
+	settings.extern_scale = (i & 2) ? true : false;
+}
+
 void
 change_settings(int connected)
 {
@@ -1807,8 +1830,8 @@ change_settings(int connected)
 		    "        The complete path to the user's BBS list.\n\n"
 		    "~ TERM For Shell ~\n"
 		    "        The value to set the TERM envirnonment variable to goes here.\n\n"
-		    "~ Blocky Scaling ~\n"
-		    "        Toggle \"blocky\" scaling.\n\n"
+		    "~ Scaling ~\n"
+		    "        Cycle scaling type.\n\n"
 		    "~ Custom Screen Mode ~\n"
 		    "        Configure the Custom screen mode.\n\n";
 		SAFEPRINTF(opts[0], "Confirm Program Exit    %s", settings.confirm_close ? "Yes" : "No");
@@ -1826,7 +1849,7 @@ change_settings(int connected)
 		SAFEPRINTF(opts[8], "Modem Dial String       %s", settings.mdm.dial_string);
 		SAFEPRINTF(opts[9], "List Path               %s", settings.list_path);
 		SAFEPRINTF(opts[10], "TERM For Shell          %s", settings.TERM);
-		sprintf(opts[11], "Blocky Scaling          %s", settings.blocky ? "On" : "Off");
+		sprintf(opts[11],   "Scaling                 %s", scaling_names[settings_to_scale()]);
 		if (connected)
 			opt[12] = NULL;
 		else
@@ -2091,12 +2114,19 @@ change_settings(int connected)
 					check_exit(false);
 				break;
 			case 11:
-				settings.blocky = !settings.blocky;
+				i = settings_to_scale();
+				i++;
+				if (i == 3)
+					i = 0;
+				scale_to_settings(i);
+
 				iniSetBool(&inicontents, "SyncTERM", "BlockyScaling", settings.blocky, &ini_style);
+				iniSetBool(&inicontents, "SyncTERM", "ExternalScaling", settings.extern_scale, &ini_style);
 				if (settings.blocky)
 					cio_api.options |= CONIO_OPT_BLOCKY_SCALING;
 				else
 					cio_api.options &= ~CONIO_OPT_BLOCKY_SCALING;
+				setscaling_type(settings.extern_scale ? CIOLIB_SCALING_EXTERNAL : CIOLIB_SCALING_INTERNAL);
 				break;
 			case 12:
 				uifc.helpbuf = "`Custom Screen Mode`\n\n"
diff --git a/src/syncterm/syncterm.c b/src/syncterm/syncterm.c
index d9db4fea58..b47e999d2a 100644
--- a/src/syncterm/syncterm.c
+++ b/src/syncterm/syncterm.c
@@ -1330,6 +1330,7 @@ load_settings(struct syncterm_settings *set)
 	iniReadString(inifile, "SyncTERM", "ListPath", set->list_path, set->list_path);
 	set->scaling_factor = iniReadFloat(inifile, "SyncTERM", "ScalingFactor", 0);
 	set->blocky = iniReadBool(inifile, "SyncTERM", "BlockyScaling", true);
+	set->extern_scale = iniReadBool(inifile, "SyncTERM", "ExternalScaling", false);
 
         // TODO: Add this to the UI somewhere.
 	set->left_just = iniReadBool(inifile, "SyncTERM", "LeftJustify", false);
@@ -1703,6 +1704,7 @@ main(int argc, char **argv)
 		cio_api.options |= CONIO_OPT_BLOCKY_SCALING;
 	else
 		cio_api.options &= ~CONIO_OPT_BLOCKY_SCALING;
+	ciolib_initial_scaling_type = (settings.extern_scale ? CIOLIB_SCALING_EXTERNAL : CIOLIB_SCALING_INTERNAL);
 #ifdef HAS_BITMAP
 	ciolib_r2yptr = r2y;
 	ciolib_y2rptr = y2r;
diff --git a/src/syncterm/syncterm.h b/src/syncterm/syncterm.h
index b91201c600..4f60317434 100644
--- a/src/syncterm/syncterm.h
+++ b/src/syncterm/syncterm.h
@@ -76,6 +76,7 @@ struct syncterm_settings {
 	int                   window_height;
 	int                   left_just;
 	int                   blocky;
+	int                   extern_scale;
 };
 
 extern char                    *inpath;
-- 
GitLab