From b4ce023c8bc4ea2d936bbd2ae7d8beb7df983db0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Thu, 4 May 2023 16:13:33 -0400
Subject: [PATCH] Overhaul of X support

Create initial window at final size to avoid extra resize at start
- Load vmode before initializing the window
Track scaling whenever the X window is adjusted
Replace x11_window_height/x11_window_width with vstat.winwidth/vstat.winheight
Resize zim from init_mode_internal() to ensure it tracks current size on initial expose
Honour ciolib_initial_scaling
Check xim validity in local_draw_rect() in case of race
Add check to prevent divide-by-zero in handle_resize_event()
Use vstat for current on-screen info, and x_cvstat for desired info
- This allows using vstat.scaling in place of old_scaling
Have scaling set use a new mutex-protected variable
Fill in local_draw_rect() so we don't miss expose
Set max window size based on user area (or root window size if not available)
The WM Cyan uses may hate this...

This absolutely *must* fix the issue in SF ticket 103.
---
 src/conio/x_cio.c    |  28 ++-
 src/conio/x_cio.h    |   5 +
 src/conio/x_events.c | 397 ++++++++++++++++++++++++++-----------------
 src/conio/x_events.h |   4 +-
 4 files changed, 270 insertions(+), 164 deletions(-)

diff --git a/src/conio/x_cio.c b/src/conio/x_cio.c
index 1146a65d4a..52c5034d54 100644
--- a/src/conio/x_cio.c
+++ b/src/conio/x_cio.c
@@ -56,6 +56,9 @@
 #define BITMAP_CIOLIB_DRIVER
 #include "bitmap_con.h"
 
+pthread_mutex_t scalinglock;
+int newscaling;
+
 int x_kbhit(void)
 {
 	fd_set	rfd;
@@ -179,10 +182,12 @@ char *x_getcliptext(void)
 
 int x_get_window_info(int *width, int *height, int *xpos, int *ypos)
 {
+	pthread_mutex_lock(&vstatlock);
 	if(width)
-		*width=x11_window_width;
+		*width=vstat.winwidth;
 	if(height)
-		*height=x11_window_height;
+		*height=vstat.winheight;
+	pthread_mutex_unlock(&vstatlock);
 	if(xpos)
 		*xpos=x11_window_xpos;
 	if(ypos)
@@ -214,6 +219,8 @@ int x_init(void)
 	if(x11_initialized)
 		return(0);
 
+	pthread_mutex_init(&scalinglock, NULL);
+
 	/* Set up the pipe for local events */
 	if(pipe(local_pipe))
 		return(-1);
@@ -459,6 +466,10 @@ int x_init(void)
 		xp_dlclose(dl);
 		return(-1);
 	}
+	if((x11.XGetGeometry=xp_dlsym(dl,XGetGeometry))==NULL) {
+		xp_dlclose(dl);
+		return(-1);
+	}
 	setlocale(LC_ALL, "");
 	x11.XSetLocaleModifiers("@im=none");
 
@@ -534,14 +545,19 @@ void x_setscaling(int newval)
 {
 	if (newval < 1)
 		newval = 1;
-	pthread_mutex_lock(&vstatlock);
-	x_cvstat.scaling = vstat.scaling = newval;
-	pthread_mutex_unlock(&vstatlock);
+	pthread_mutex_lock(&scalinglock);
+	newscaling = newval;
+	pthread_mutex_unlock(&scalinglock);
 }
 
 int x_getscaling(void)
 {
-	return x_cvstat.scaling;
+	int ret;
+
+	pthread_mutex_lock(&vstatlock);
+	ret = vstat.scaling;
+	pthread_mutex_unlock(&vstatlock);
+	return ret;
 }
 
 int x_mousepointer(enum ciolib_mouse_ptr type)
diff --git a/src/conio/x_cio.h b/src/conio/x_cio.h
index 95dade7470..cc1755f1ed 100644
--- a/src/conio/x_cio.h
+++ b/src/conio/x_cio.h
@@ -39,6 +39,8 @@
  #undef CIOLIB_EXPORTS
 #endif
 
+#include <threadwrap.h>
+
 #include "ciolib.h"
 #include "bitmap_con.h"
 #include "x_events.h"
@@ -46,6 +48,9 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+extern pthread_mutex_t scalinglock;
+extern int newscaling;
+
 int x_puttext(int sx, int sy, int ex, int ey, void *fill);
 int x_gettext(int sx, int sy, int ex, int ey, void *fill);
 void x_textattr(int attr);
diff --git a/src/conio/x_events.c b/src/conio/x_events.c
index a7de3d588b..adf196140c 100644
--- a/src/conio/x_events.c
+++ b/src/conio/x_events.c
@@ -51,8 +51,6 @@ sem_t	init_complete;
 sem_t	mode_set;
 int x11_window_xpos;
 int x11_window_ypos;
-int x11_window_width;
-int x11_window_height;
 int x11_initialized=0;
 sem_t	event_thread_complete;
 int	terminate = 0;
@@ -79,9 +77,6 @@ static unsigned int depth=0;
 static int xfd;
 static unsigned long black;
 static unsigned long white;
-static int bitmap_width=0;
-static int bitmap_height=0;
-static int old_scaling = 0;
 struct video_stats x_cvstat;
 static unsigned long base_pixel;
 static int r_shift;
@@ -208,12 +203,72 @@ static struct {
     {	0x8600, 0x5888, 0x8a00, 0x8c00 }, /* key 88 - F12 */
 };
 
+static bool
+x11_get_maxsize(int *w, int *h)
+{
+	int i;
+	long offset;
+	int maxw = 0, maxh = 0;
+	Atom atr;
+	int afr;
+	long *ret;
+	unsigned long nir;
+	unsigned long bytes_left = 4;
+	Window root = DefaultRootWindow(dpy);
+	unsigned int rw, rh;
+	uint64_t dummy;
+	unsigned char *prop;
+
+	if (dpy == NULL)
+		return false;
+	// First, try to get _NET_WORKAREA...
+	if (x11.workarea == None) {
+		x11.workarea = x11.XInternAtom(dpy, "_NET_WORKAREA", True);
+	}
+	if (x11.workarea != None) {
+		for (i = 0, offset = 0; bytes_left; i++) {
+			if (x11.XGetWindowProperty(dpy, root, x11.workarea, offset, 4, False, XA_CARDINAL, &atr, &afr, &nir, &bytes_left, &prop) != Success)
+				break;
+			if (atr != XA_CARDINAL)
+				break;
+			ret = (long *)prop;
+			if (nir >= 3) {
+				if (ret[2] > maxw)
+					maxw = ret[2];
+			}
+			if (nir >= 4) {
+				if (ret[3] > maxh)
+					maxh = ret[3];
+			}
+			x11.XFree(prop);
+		}
+	}
+
+	// Couldn't get work area, get size of root window instead. :(
+	if (maxw == 0 || maxh == 0) {
+		if (x11.XGetGeometry(dpy, root, (void *)&dummy, (void *)&dummy, (void *)&dummy, &rw, &rh, (void *)&dummy, (void *)&dummy)) {
+			if (rw > maxw)
+				maxw = rw;
+			if (rh > maxh)
+				maxh = rh;
+		}
+	}
+	if (maxw != 0 && maxh != 0) {
+		*w = maxw;
+		*h = maxh;
+		return true;
+	}
+	return false;
+}
+
 static void resize_xim(void)
 {
-	int width = bitmap_width * x_cvstat.scaling;
-	int height = bitmap_height * x_cvstat.scaling;
+	int width, height;
+
+	pthread_mutex_lock(&vstatlock);
+	bitmap_get_scaled_win_size(x_cvstat.scaling, &width, &height, 0, 0);
+	pthread_mutex_unlock(&vstatlock);
 
-	aspect_correct(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
 	if (xim) {
 		if (width == xim->width
 		    && height == xim->height) {
@@ -221,7 +276,7 @@ static void resize_xim(void)
 				release_buffer(last);
 				last = NULL;
 			}
-			x11.XFillRectangle(dpy, win, gc, 0, 0, x11_window_width, x11_window_height);
+			x11.XFillRectangle(dpy, win, gc, 0, 0, width, height);
 			return;
 		}
 #ifdef XDestroyImage
@@ -236,7 +291,7 @@ static void resize_xim(void)
 	}
 	xim = x11.XCreateImage(dpy, &visual, depth, ZPixmap, 0, NULL, width, height, 32, 0);
 	xim->data=(char *)calloc(1, xim->bytes_per_line*xim->height);
-	x11.XFillRectangle(dpy, win, gc, 0, 0, x11_window_width, x11_window_height);
+	x11.XFillRectangle(dpy, win, gc, 0, 0, width, height);
 }
 
 /* Swiped from FreeBSD libc */
@@ -252,6 +307,50 @@ my_fls(unsigned long mask)
         return (bit);
 }
 
+/*
+ * Actually maps (shows) the window
+ */
+static void map_window()
+{
+	XSizeHints *sh;
+	int minwidth = x_cvstat.scrnwidth;
+	int minheight = x_cvstat.scrnheight;
+
+	sh = x11.XAllocSizeHints();
+	if (sh == NULL) {
+		fprintf(stderr, "Could not get XSizeHints structure");
+		exit(1);
+	}
+
+	sh->base_width = x_cvstat.scrnwidth * x_cvstat.scaling;
+	sh->base_height = x_cvstat.scrnheight * x_cvstat.scaling;
+
+	int mw, mh;
+	if (x11_get_maxsize(&mw,&mh)) {
+		pthread_mutex_lock(&vstatlock);
+		bitmap_get_scaled_win_size(bitmap_largest_mult_inside(mw, mh), &sh->max_width, &sh->max_height, mw, mh);
+		pthread_mutex_unlock(&vstatlock);
+	}
+
+	aspect_correct(&sh->base_width, &sh->base_height, x_cvstat.aspect_width, x_cvstat.aspect_height);
+	aspect_correct(&minwidth, &minheight, x_cvstat.aspect_width, x_cvstat.aspect_height);
+
+	sh->min_width = sh->width_inc = sh->min_aspect.x = sh->max_aspect.x = minwidth;
+	sh->min_height = sh->height_inc = sh->min_aspect.y = sh->max_aspect.y = minheight;
+
+	sh->flags = USSize | PMinSize | PSize | PResizeInc | PAspect | PMaxSize;
+
+	x11.XSetWMNormalHints(dpy, win, sh);
+	pthread_mutex_lock(&vstatlock);
+	vstat.scaling = x_cvstat.scaling;
+	pthread_mutex_unlock(&vstatlock);
+	x11.XMapWindow(dpy, win);
+
+	x11.XFree(sh);
+
+	return;
+}
+
 /* Get a connection to the X server and create the window. */
 static int init_window()
 {
@@ -265,6 +364,8 @@ static int init_window()
 	int best_cmap=0;
 	XVisualInfo template = {0};
 	XVisualInfo *vi;
+	int w, h;
+	int mw, mh;
 
 	dpy = x11.XOpenDisplay(NULL);
 	if (dpy == NULL) {
@@ -273,6 +374,7 @@ static int init_window()
 	xfd = ConnectionNumber(dpy);
 	x11.utf8 = x11.XInternAtom(dpy, "UTF8_STRING", False);
 	x11.targets = x11.XInternAtom(dpy, "TARGETS", False);
+	x11.workarea = x11.XInternAtom(dpy, "_NET_WORKAREA", True);
 
 	template.screen = DefaultScreen(dpy);
 	template.class = TrueColor;
@@ -317,8 +419,15 @@ static int init_window()
 	wa.background_pixel = black;
 	wa.border_pixel = black;
 	depth = best_depth;
-    win = x11.XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0,
-			      640*x_cvstat.scaling, 400*x_cvstat.scaling, 2, depth, InputOutput, &visual, CWColormap | CWBorderPixel | CWBackPixel, &wa);
+	x11_get_maxsize(&mw, &mh);
+	pthread_mutex_lock(&vstatlock);
+	bitmap_get_scaled_win_size(x_cvstat.scaling, &w, &h, mw, mh);
+	vstat.winwidth = x_cvstat.winwidth = w;
+	vstat.winwidth = x_cvstat.winheight = h;
+	vstat.scaling = x_cvstat.scaling;
+	pthread_mutex_unlock(&vstatlock);
+	win = x11.XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0,
+	    w, h, 2, depth, InputOutput, &visual, CWColormap | CWBorderPixel | CWBackPixel, &wa);
 
 	classhints=x11.XAllocClassHint();
 	if (classhints)
@@ -359,47 +468,23 @@ static int init_window()
 	return(0);
 }
 
-/*
- * Actually maps (shows) the window
- */
-static void map_window()
-{
-	XSizeHints *sh;
-	int minwidth = bitmap_width;
-	int minheight = bitmap_height;
-
-	sh = x11.XAllocSizeHints();
-	if (sh == NULL) {
-		fprintf(stderr, "Could not get XSizeHints structure");
-		exit(1);
-	}
-
-	sh->base_width = bitmap_width * x_cvstat.scaling;
-	sh->base_height = bitmap_height * x_cvstat.scaling;
-
-	aspect_correct(&sh->base_width, &sh->base_height, x_cvstat.aspect_width, x_cvstat.aspect_height);
-	aspect_correct(&minwidth, &minheight, x_cvstat.aspect_width, x_cvstat.aspect_height);
-
-	sh->min_width = sh->width_inc = sh->min_aspect.x = sh->max_aspect.x = minwidth;
-	sh->min_height = sh->height_inc = sh->min_aspect.y = sh->max_aspect.y = minheight;
-
-	sh->flags = USSize | PMinSize | PSize | PResizeInc | PAspect;
-
-	x11.XSetWMNormalHints(dpy, win, sh);
-	x11.XMapWindow(dpy, win);
-
-	x11.XFree(sh);
-
-	return;
-}
-
 /* Resize the window. This function is called after a mode change. */
 static void resize_window()
 {
-	int width = bitmap_width * x_cvstat.scaling;
-	int height = bitmap_height * x_cvstat.scaling;
+	int width = x_cvstat.scrnwidth * x_cvstat.scaling;
+	int height = x_cvstat.scrnheight * x_cvstat.scaling;
 
 	aspect_correct(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
+	pthread_mutex_lock(&vstatlock);
+	if (width == x_cvstat.winwidth && height == x_cvstat.winheight) {
+		pthread_mutex_unlock(&vstatlock);
+		resize_xim();
+		return;
+	}
+	x_cvstat.winwidth = vstat.winwidth = width;
+	x_cvstat.winheight = vstat.winheight = height;
+	vstat.scaling = bitmap_largest_mult_inside(width, height);
+	pthread_mutex_unlock(&vstatlock);
 	x11.XResizeWindow(dpy, win, width, height);
 	resize_xim();
 
@@ -409,36 +494,37 @@ static void resize_window()
 static void init_mode_internal(int mode)
 {
 	int oldcols;
+	int mw, mh;
 
 	oldcols=x_cvstat.cols;
 
+	x11_get_maxsize(&mw, &mh);
 	pthread_mutex_lock(&vstatlock);
 	if (last) {
 		release_buffer(last);
 		last = NULL;
 	}
-	bitmap_drv_init_mode(mode, &bitmap_width, &bitmap_height, 0, 0);
-
-	/* Deal with 40 col doubling */
-	if(oldcols != vstat.cols) {
-		if(oldcols == 40)
-			vstat.scaling /= 2;
-		if(vstat.cols == 40)
-			vstat.scaling *= 2;
-	}
-	if(vstat.scaling < 1)
-		vstat.scaling = 1;
-
+	bitmap_drv_init_mode(mode, NULL, NULL, mw, mh);
 	x_cvstat = vstat;
 	pthread_mutex_unlock(&vstatlock);
+	resize_xim();
 	map_window();
 }
 
 static void check_scaling(void)
 {
-	if (old_scaling != x_cvstat.scaling) {
+	pthread_mutex_lock(&vstatlock);
+	pthread_mutex_lock(&scalinglock);
+	if (newscaling != 0 && newscaling != vstat.scaling) {
+		x_cvstat.scaling = newscaling;
+		newscaling = 0;
+		pthread_mutex_unlock(&scalinglock);
+		pthread_mutex_unlock(&vstatlock);
 		resize_window();
-		old_scaling = x_cvstat.scaling;
+	}
+	else {
+		pthread_mutex_unlock(&scalinglock);
+		pthread_mutex_unlock(&vstatlock);
 	}
 }
 
@@ -454,20 +540,29 @@ static int init_mode(int mode)
 
 static int video_init()
 {
-    /* If we are running under X, get a connection to the X server and create
-       an empty window of size (1, 1). It makes a couple of init functions a
-       lot easier. */
-	if(x_cvstat.scaling<1)
-		x_setscaling(1);
-    if(init_window())
-		return(-1);
+	/* If we are running under X, get a connection to the X server and create
+	   an empty window of size (1, 1). It makes a couple of init functions a
+	   lot easier. */
 
+	pthread_mutex_lock(&vstatlock);
+	if (x_cvstat.scaling < 1)
+		x_cvstat.scaling = vstat.scaling = 1;
+	if (ciolib_initial_scaling)
+		x_cvstat.scaling = vstat.scaling = ciolib_initial_scaling;
+	pthread_mutex_unlock(&vstatlock);
+	/* Initialize mode 3 (text, 80x25, 16 colors) */
+	if(load_vmode(&vstat, C80))
+		return(-1);
+	x_cvstat = vstat;
 	bitmap_drv_init(x11_drawrect, x11_flush);
+	pthread_mutex_lock(&vstatlock);
+	bitmap_drv_init_mode(vstat.mode, NULL, NULL, 0, 0);
+	pthread_mutex_unlock(&vstatlock);
+	if(init_window())
+		return(-1);
+	init_mode_internal(x_cvstat.mode);
 
-    /* Initialize mode 3 (text, 80x25, 16 colors) */
-    init_mode_internal(3);
-
-    return(0);
+	return(0);
 }
 
 static void
@@ -483,19 +578,13 @@ local_draw_rect(struct rectlist *rect)
 	int idx;
 	uint32_t last_pixel = 0x55555555;
 	struct graphics_buffer *source;
+	int w, h;
 
-	if (bitmap_width != rect->rect.width || bitmap_height != rect->rect.height) {
+	if (x_cvstat.scrnwidth != rect->rect.width || x_cvstat.scrnheight != rect->rect.height || xim == NULL) {
 		bitmap_drv_free_rect(rect);
 		return;
 	}
 
-	xoff = (x11_window_width - xim->width) / 2;
-	if (xoff < 0)
-		xoff = 0;
-	yoff = (x11_window_height - xim->height) / 2;
-	if (yoff < 0)
-		yoff = 0;
-
 	// Scale...
 	source = do_scale(rect, x_cvstat.scaling, x_cvstat.scaling, x_cvstat.aspect_width, x_cvstat.aspect_height);
 	bitmap_drv_free_rect(rect);
@@ -504,10 +593,14 @@ local_draw_rect(struct rectlist *rect)
 	cleft = source->w;
 	ctop = source->h;
 
-	xoff = (x11_window_width - source->w) / 2;
+	pthread_mutex_lock(&vstatlock);
+	w = vstat.winwidth;
+	h = vstat.winheight;
+	pthread_mutex_unlock(&vstatlock);
+	xoff = (w - source->w) / 2;
 	if (xoff < 0)
 		xoff = 0;
-	yoff = (x11_window_height - source->h) / 2;
+	yoff = (h - source->h) / 2;
 	if (yoff < 0)
 		yoff = 0;
 
@@ -584,6 +677,13 @@ local_draw_rect(struct rectlist *rect)
 		}
 	}
 
+	// TODO: We really only need to do this once after changing resolution...
+	if (xoff > 0 || yoff > 0) {
+		x11.XFillRectangle(dpy, win, gc, 0, 0, w, yoff);
+		x11.XFillRectangle(dpy, win, gc, 0, yoff, xoff, yoff + xim->height);
+		x11.XFillRectangle(dpy, win, gc, xoff+xim->width, yoff, w, yoff + xim->height);
+		x11.XFillRectangle(dpy, win, gc, 0, yoff + xim->height, w, h);
+	}
 	if (last == NULL)
 		x11.XPutImage(dpy, win, gc, xim, 0, 0, xoff, yoff, source->w, source->h);
 	else
@@ -593,30 +693,32 @@ local_draw_rect(struct rectlist *rect)
 
 static void handle_resize_event(int width, int height)
 {
-	int newFSH=1;
-	int newFSW=1;
-
-	aspect_fix(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
-	newFSH=width / bitmap_width;
-	newFSW=height / bitmap_height;
-	if(newFSW<1)
-		newFSW=1;
-	if(newFSH<1)
-		newFSH=1;
-	if(newFSH<newFSW)
-		x_setscaling(newFSH);
-	else
-		x_setscaling(newFSW);
-	old_scaling = x_cvstat.scaling;
-	if(x_cvstat.scaling > 16)
-		x_setscaling(16);
+	bool resize = false;
+	int new_scaling;
 
+	pthread_mutex_lock(&vstatlock);
+	vstat.winwidth = x_cvstat.winwidth = width;
+	vstat.winheight = x_cvstat.winheight = height;
+	new_scaling = bitmap_largest_mult_inside(width, height);
+	if (new_scaling != vstat.scaling) {
+		vstat.scaling = x_cvstat.scaling = new_scaling;
+		resize = true;
+	}
+	if (new_scaling > 16) {
+		new_scaling = 16;
+		pthread_mutex_lock(&scalinglock);
+		newscaling = new_scaling;
+		pthread_mutex_unlock(&scalinglock);
+		x_cvstat.scaling = new_scaling;
+		resize = true;
+	}
+	pthread_mutex_unlock(&vstatlock);
 	/*
 	 * We only need to resize if the width/height are not even multiples,
 	 * or if the two axis don't scale the same way.
 	 * Otherwise, we can simply resend everything
 	 */
-	if (newFSH != newFSW)
+	if (resize)
 		resize_window();
 	else
 		resize_xim();
@@ -627,25 +729,35 @@ static void expose_rect(int x, int y, int width, int height)
 {
 	int sx,sy,ex,ey;
 	int xoff=0, yoff=0;
+	int w, h, s;
 
-	xoff = (x11_window_width - xim->width) / 2;
+	if (xim == NULL) {
+		fprintf(stderr, "Exposing NULL xim!\n");
+		return;
+	}
+	pthread_mutex_lock(&vstatlock);
+	w = vstat.winwidth;
+	h = vstat.winheight;
+	s = vstat.scaling;
+	pthread_mutex_unlock(&vstatlock);
+	xoff = (w - xim->width) / 2;
 	if (xoff < 0)
 		xoff = 0;
-	yoff = (x11_window_height - xim->height) / 2;
+	yoff = (h - xim->height) / 2;
 	if (yoff < 0)
 		yoff = 0;
 
 	if (xoff > 0 || yoff > 0) {
 		if (x < xoff || y < yoff || x + width > xoff + xim->width || y + height > yoff + xim->height) {
-			x11.XFillRectangle(dpy, win, gc, 0, 0, x11_window_width, yoff);
+			x11.XFillRectangle(dpy, win, gc, 0, 0, w, yoff);
 			x11.XFillRectangle(dpy, win, gc, 0, yoff, xoff, yoff + xim->height);
-			x11.XFillRectangle(dpy, win, gc, xoff+xim->width, yoff, x11_window_width, yoff + xim->height);
-			x11.XFillRectangle(dpy, win, gc, 0, yoff + xim->height, x11_window_width, x11_window_height);
+			x11.XFillRectangle(dpy, win, gc, xoff+xim->width, yoff, w, yoff + xim->height);
+			x11.XFillRectangle(dpy, win, gc, 0, yoff + xim->height, w, h);
 		}
 	}
 
-	sx=(x-xoff)/x_cvstat.scaling;
-	sy=(y-yoff)/(x_cvstat.scaling);
+	sx = (x - xoff) / s;
+	sy = (y - yoff) / s;
 	if (sx < 0)
 		sx = 0;
 	if (sy < 0)
@@ -657,14 +769,14 @@ static void expose_rect(int x, int y, int width, int height)
 		ex = 0;
 	if (ey < 0)
 		ey = 0;
-	if((ex+1)%x_cvstat.scaling) {
-		ex += x_cvstat.scaling-(ex%x_cvstat.scaling);
+	if ((ex + 1) % s) {
+		ex += s - (ex % s);
 	}
-	if((ey+1)%(x_cvstat.scaling)) {
-		ey += x_cvstat.scaling-(ey%(x_cvstat.scaling));
+	if ((ey + 1) % s) {
+		ey += s - (ey % s);
 	}
-	ex=ex/x_cvstat.scaling;
-	ey=ey/(x_cvstat.scaling);
+	ex = ex / s;
+	ey = ey / s;
 
 	/* Since we're exposing, we *have* to redraw */
 	if (last) {
@@ -683,12 +795,14 @@ xlat_mouse_xy(int *x, int *y)
 {
 	int xoff, yoff;
 
-	xoff = (x11_window_width - xim->width) / 2;
+	pthread_mutex_lock(&vstatlock);
+	xoff = (vstat.winwidth - xim->width) / 2;
 	if (xoff < 0)
 		xoff = 0;
-	yoff = (x11_window_height - xim->height) / 2;
+	yoff = (vstat.winheight - xim->height) / 2;
 	if (yoff < 0)
 		yoff = 0;
+	pthread_mutex_unlock(&vstatlock);
 
 	if (*x < xoff)
 		return false;
@@ -720,47 +834,11 @@ static int x11_event(XEvent *ev)
 			break;
 		/* Graphics related events */
 		case ConfigureNotify: {
-			int width, height;
-
 			if (x11_window_xpos != ev->xconfigure.x || x11_window_ypos != ev->xconfigure.y) {
 				x11_window_xpos=ev->xconfigure.x;
 				x11_window_ypos=ev->xconfigure.y;
 			}
-			if (x11_window_width != ev->xconfigure.width || x11_window_height != ev->xconfigure.height) {
-				x11_window_width=ev->xconfigure.width;
-				x11_window_height=ev->xconfigure.height;
-				handle_resize_event(ev->xconfigure.width, ev->xconfigure.height);
-				break;
-			}
-			width = bitmap_width * x_cvstat.scaling;
-			height = bitmap_height * x_cvstat.scaling;
-
-			aspect_correct(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
-			if (ev->xconfigure.width != width || ev->xconfigure.height != height) {
-				// We can't have the size we requested... accept the size we got.
-				int newFSH=1;
-				int newFSW=1;
-
-				width = ev->xconfigure.width;
-				height = ev->xconfigure.height;
-				aspect_fix(&width, &height, x_cvstat.aspect_width, x_cvstat.aspect_height);
-				newFSH=width / bitmap_width;
-				newFSW=height / bitmap_height;
-				if(newFSW<1)
-					newFSW=1;
-				if(newFSH<1)
-					newFSH=1;
-				if(newFSH<newFSW)
-					x_setscaling(newFSH);
-				else
-					x_setscaling(newFSW);
-				old_scaling = x_cvstat.scaling;
-				if(x_cvstat.scaling > 16)
-					x_setscaling(16);
-
-				resize_xim();
-				bitmap_drv_request_pixels();
-			}
+			handle_resize_event(ev->xconfigure.width, ev->xconfigure.height);
 			break;
 		}
 		case NoExpose:
@@ -1049,7 +1127,7 @@ static int x11_event(XEvent *ev)
 							case XK_KP_Left:
 								if (ev->xkey.state & Mod1Mask) {
 									if (x_cvstat.scaling > 1)
-										x_cvstat.scaling--;
+										x_setscaling(x_cvstat.scaling - 1);
 								}
 								scan = 75;
 								goto docode;
@@ -1067,8 +1145,15 @@ static int x11_event(XEvent *ev)
 							case XK_KP_Right:
 								scan = 77;
 								if (ev->xkey.state & Mod1Mask) {
-									if (x_cvstat.scaling < 7)
-										x_cvstat.scaling++;
+									if (x_cvstat.scaling < 7) {
+										int mw, mh, ms;
+										x11_get_maxsize(&mw,&mh);
+										pthread_mutex_lock(&vstatlock);
+										ms = bitmap_largest_mult_inside(mw, mh);
+										pthread_mutex_unlock(&vstatlock);
+										if (x_cvstat.scaling < ms)
+											x_setscaling(x_cvstat.scaling + 1);
+									}
 								}
 								goto docode;
 
diff --git a/src/conio/x_events.h b/src/conio/x_events.h
index eb44f6ce2b..bca87f47a3 100644
--- a/src/conio/x_events.h
+++ b/src/conio/x_events.h
@@ -96,8 +96,10 @@ struct x11 {
 	Cursor (*XCreateFontCursor)(Display *display, unsigned int shape);
 	int (*XDefineCursor)(Display *display, Window w, Cursor cursor);
 	int (*XFreeCursor)(Display *display, Cursor cursor);
+	Status (*XGetGeometry)(Display *, Drawable, Window *, int *, int *, unsigned int *, unsigned int *, unsigned int *, unsigned int *);
 	Atom utf8;
 	Atom targets;
+	Atom workarea;
 };
 
 extern int local_pipe[2];			/* Used for passing local events */
@@ -118,8 +120,6 @@ extern sem_t	event_thread_complete;
 extern int terminate;
 extern int x11_window_xpos;
 extern int x11_window_ypos;
-extern int x11_window_width;
-extern int x11_window_height;
 extern int x11_initialized;
 extern struct video_stats x_cvstat;
 
-- 
GitLab