From 2d9aa3f7ed0b653c72347469d4861e5b21a23c28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Tue, 6 Jun 2023 02:26:47 -0400
Subject: [PATCH] Add support for starting X11 mode fullscreen.

---
 src/conio/ciolib.c      |  3 +-
 src/conio/ciolib.h      |  1 +
 src/conio/win32gdi.c    |  2 +-
 src/conio/x_cio.c       |  6 ++--
 src/conio/x_cio.h       |  3 +-
 src/conio/x_events.c    | 68 ++++++++++++++++++++++++++++++-----------
 src/syncterm/bbslist.c  |  2 ++
 src/syncterm/syncterm.c | 20 +++++++++---
 8 files changed, 77 insertions(+), 28 deletions(-)

diff --git a/src/conio/ciolib.c b/src/conio/ciolib.c
index f125994810..220144ffb9 100644
--- a/src/conio/ciolib.c
+++ b/src/conio/ciolib.c
@@ -281,7 +281,7 @@ static int try_x_init(int mode)
 	}
 #endif
 
-	if(!x_init()) {
+	if(!x_initciolib(mode)) {
 		cio_api.mode=CIOLIB_MODE_X;
 		cio_api.mouse=1;
 		cio_api.puttext=bitmap_puttext;
@@ -502,6 +502,7 @@ CIOLIBEXPORT int initciolib(int mode)
 			break;
 
 		case CIOLIB_MODE_X:
+		case CIOLIB_MODE_X_FULLSCREEN:
 #ifndef NO_X
 			try_x_init(mode);
 #endif
diff --git a/src/conio/ciolib.h b/src/conio/ciolib.h
index c4f7442160..9e4e2d2fa5 100644
--- a/src/conio/ciolib.h
+++ b/src/conio/ciolib.h
@@ -74,6 +74,7 @@ enum {
 	,CIOLIB_MODE_CURSES_ASCII
 	,CIOLIB_MODE_ANSI
 	,CIOLIB_MODE_X
+	,CIOLIB_MODE_X_FULLSCREEN
 	,CIOLIB_MODE_CONIO
 	,CIOLIB_MODE_CONIO_FULLSCREEN
 	,CIOLIB_MODE_SDL
diff --git a/src/conio/win32gdi.c b/src/conio/win32gdi.c
index c3a55e4bd7..c2a0b0a62e 100644
--- a/src/conio/win32gdi.c
+++ b/src/conio/win32gdi.c
@@ -1077,7 +1077,7 @@ gdi_init(int mode)
 
 		cio_api.mode=CIOLIB_MODE_GDI;
 		FreeConsole();
-		cio_api.options |= CONIO_OPT_PALETTE_SETTING | CONIO_OPT_SET_TITLE | CONIO_OPT_SET_NAME | CONIO_OPT_SET_ICON;
+		cio_api.options |= CONIO_OPT_SET_TITLE | CONIO_OPT_SET_NAME | CONIO_OPT_SET_ICON | CONIO_OPT_EXTERNAL_SCALING;
 		return(0);
 	}
 	CloseHandle(rch);
diff --git a/src/conio/x_cio.c b/src/conio/x_cio.c
index d0fed32f67..19ead83a0d 100644
--- a/src/conio/x_cio.c
+++ b/src/conio/x_cio.c
@@ -211,7 +211,7 @@ void x11_mouse_thread(void *data)
 	}
 }
 
-int x_init(void)
+int x_initciolib(int mode)
 {
 	dll_handle	dl;
 	const char *libnames[]={"X11",NULL};
@@ -716,7 +716,7 @@ int x_init(void)
 		return(-1);
 	}
 
-	_beginthread(x11_event_thread,1<<16,NULL);
+	_beginthread(x11_event_thread,1<<16,(void *)(intptr_t)mode);
 	sem_wait(&init_complete);
 	if(!x11_initialized) {
 		xp_dlclose(dl);
@@ -743,7 +743,7 @@ int x_init(void)
 		return(-1);
 	}
 	_beginthread(x11_mouse_thread,1<<16,NULL);
-	cio_api.options |= CONIO_OPT_SET_TITLE | CONIO_OPT_SET_NAME | CONIO_OPT_SET_ICON;
+	cio_api.options |= CONIO_OPT_SET_TITLE | CONIO_OPT_SET_NAME | CONIO_OPT_SET_ICON | CONIO_OPT_EXTERNAL_SCALING;
 	return(0);
 }
 
diff --git a/src/conio/x_cio.h b/src/conio/x_cio.h
index 7ea95e80b8..2a259d519a 100644
--- a/src/conio/x_cio.h
+++ b/src/conio/x_cio.h
@@ -60,8 +60,7 @@ int x_wherey(void);
 int x_wherex(void);
 int x_putch(int ch);
 void x_gotoxy(int x, int y);
-int x_init(void);
-void x_initciolib(int inmode);
+int x_initciolib(int mode);
 void x_gettextinfo(struct text_info *info);
 void x_setcursortype(int type);
 int x_getch(void);
diff --git a/src/conio/x_events.c b/src/conio/x_events.c
index d52965fc53..5f5b632c5c 100644
--- a/src/conio/x_events.c
+++ b/src/conio/x_events.c
@@ -33,6 +33,9 @@
 #include "x_cio.h"
 #include "utf8_codepages.h"
 
+static bool send_fullscreen(bool set);
+static void resize_window();
+
 /*
  * Exported variables 
  */
@@ -175,6 +178,8 @@ static Window parent;
 static Window root;
 static char *wm_wm_name;
 static Atom copy_paste_selection = XA_PRIMARY;
+static bool map_pending = true;
+static int pending_width, pending_height, pending_xpos, pending_ypos;
 
 /* Array of Graphics Contexts */
 static GC gc;
@@ -756,7 +761,8 @@ static void map_window()
 
 	if (x11_get_maxsize(&sh->max_width,&sh->max_height)) {
 		pthread_mutex_lock(&vstatlock);
-		bitmap_get_scaled_win_size(bitmap_double_mult_inside(sh->max_width, sh->max_height), &sh->max_width, &sh->max_height, sh->max_width, sh->max_height);
+		if (!fullscreen)
+			bitmap_get_scaled_win_size(bitmap_double_mult_inside(sh->max_width, sh->max_height), &sh->max_width, &sh->max_height, sh->max_width, sh->max_height);
 	}
 	else {
 		pthread_mutex_lock(&vstatlock);
@@ -784,7 +790,8 @@ static void map_window()
 	pthread_mutex_lock(&vstatlock);
 	vstat.scaling = x_cvstat.scaling;
 	pthread_mutex_unlock(&vstatlock);
-	x11.XMapWindow(dpy, win);
+	if (map_pending)
+		x11.XMapWindow(dpy, win);
 
 	x11.XFree(sh);
 
@@ -991,6 +998,9 @@ static int init_window()
 	a = A(_NET_WM_WINDOW_TYPE_NORMAL);
 	if (a != None)
 		set_win_property(ATOM__NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, &a, 1);
+	a = A(_NET_WM_STATE_FULLSCREEN);
+	if (a != None)
+		set_win_property(ATOM__NET_WM_STATE, XA_ATOM, 32, PropModeReplace, &a, 1);
 
 	im = x11.XOpenIM(dpy, NULL, "CIOLIB", "CIOLIB");
 	if (im != NULL) {
@@ -1069,12 +1079,15 @@ static void resize_window()
 
 	pthread_mutex_lock(&vstatlock);
 	if (fullscreen && send_fullscreen(true)) {
+		cio_api.mode = CIOLIB_MODE_X_FULLSCREEN;
 	}
 	else {
 		send_fullscreen(false);
 		fullscreen = false;
+		cio_api.mode = CIOLIB_MODE_X;
 		new_scaling = x_cvstat.scaling;
 		bitmap_get_scaled_win_size(new_scaling, &width, &height, 0, 0);
+		// TODO: If we're in fullscreen mode, we don't get the decoration sizes...
 		if (x11_get_maxsize(&max_width, &max_height)) {
 			if (width > max_width || height > max_height) {
 				new_scaling = bitmap_double_mult_inside(max_width, max_height);
@@ -1184,7 +1197,6 @@ static int video_init()
 	bitmap_drv_init_mode(vstat.mode, NULL, NULL, 0, 0);
 	x_cvstat = vstat;
 	pthread_mutex_unlock(&vstatlock);
-	init_mode_internal(x_cvstat.mode);
 
 	return(0);
 }
@@ -1370,12 +1382,13 @@ local_draw_rect(struct rectlist *rect)
 	last = source;
 }
 
-static void handle_resize_event(int width, int height)
+static void handle_resize_event(int width, int height, bool map)
 {
 	pthread_mutex_lock(&vstatlock);
-	if (fullscreen) {
+	if (fullscreen && !map) {
 		if ((width != x_cvstat.winwidth || height != x_cvstat.winheight)) {
 			fullscreen = false;
+			cio_api.mode = CIOLIB_MODE_X;
   		}
 	}
 	x_cvstat.winwidth = vstat.winwidth = width;
@@ -1518,6 +1531,23 @@ is_maximized(void)
 	return is;
 }
 
+static void
+handle_configuration(int x, int y, int w, int h, bool map)
+{
+	bool resize = false;
+
+	if ((x11_window_xpos != x || x11_window_ypos != y)) {
+		x11_window_xpos = x;
+		x11_window_ypos = y;
+	}
+	pthread_mutex_lock(&vstatlock);
+	if (w != vstat.winwidth || h != vstat.winheight)
+		resize = true;
+	pthread_mutex_unlock(&vstatlock);
+	if (resize)
+		handle_resize_event(w, h, map);
+}
+
 static int x11_event(XEvent *ev)
 {
 	if (x11.XFilterEvent(ev, win))
@@ -1545,7 +1575,6 @@ static int x11_event(XEvent *ev)
 			break;
 		/* Graphics related events */
 		case ConfigureNotify: {
-			bool resize = false;
 			int ax, ay;
 			Window cr;
 
@@ -1558,22 +1587,24 @@ static int x11_event(XEvent *ev)
 					ax = ev->xconfigure.x;
 					ay = ev->xconfigure.y;
 				}
-				if ((x11_window_xpos != ax || x11_window_ypos != ay)) {
-					x11_window_xpos = ax;
-					x11_window_ypos = ay;
+				if (map_pending) {
+					pending_xpos = ax;
+					pending_ypos = ay;
+					pending_width = ev->xconfigure.width;
+					pending_height = ev->xconfigure.height;
 				}
-				pthread_mutex_lock(&vstatlock);
-				if (ev->xconfigure.width != vstat.winwidth || ev->xconfigure.height != vstat.winheight) {
-					// XWayland on ChromeOS appears to send a 1x1 resize event early on for unknown reasons... inore it.
-					if (ev->xconfigure.width != 1 && ev->xconfigure.height != 1)
-						resize = true;
+				else {
+					handle_configuration(ax, ay, ev->xconfigure.width, ev->xconfigure.height, false);
 				}
-				pthread_mutex_unlock(&vstatlock);
-				if (resize)
-					handle_resize_event(ev->xconfigure.width, ev->xconfigure.height);
 			}
 			break;
 		}
+		case MapNotify:
+			if (map_pending) {
+				map_pending = false;
+				handle_configuration(pending_xpos, pending_ypos, pending_width, pending_height, true);
+			}
+			break;
 		case NoExpose:
 			break;
 		case GraphicsExpose:
@@ -2040,8 +2071,11 @@ void x11_event_thread(void *args)
 	fd_set fdset;
 	XEvent ev;
 	static struct timeval tv;
+	int mode = (int)(intptr_t)args;
 
 	SetThreadName("X11 Events");
+	if (mode == CIOLIB_MODE_X_FULLSCREEN)
+		fullscreen = true;
 	if(video_init()) {
 		sem_post(&init_complete);
 		return;
diff --git a/src/syncterm/bbslist.c b/src/syncterm/bbslist.c
index 40be50b624..830c8db17c 100644
--- a/src/syncterm/bbslist.c
+++ b/src/syncterm/bbslist.c
@@ -1927,6 +1927,8 @@ change_settings(int connected)
 				    "        Uses the Xlib library directly for graphical output.  This is\n"
 				    "        the graphical mode most likely to work when using X11.  This\n"
 				    "        mode supports font changes.\n\n"
+				    "~ X11 Fullscreen ~\n"
+				    "        As above, but starts in full-screen mode rather than a window\n\n"
 #endif
 #ifdef _WIN32
 				    "~ Win32 Console ~\n"
diff --git a/src/syncterm/syncterm.c b/src/syncterm/syncterm.c
index 259f765801..fc4cf96992 100644
--- a/src/syncterm/syncterm.c
+++ b/src/syncterm/syncterm.c
@@ -103,7 +103,7 @@ char *usage =
     "-iX =  set interface mode to X (default=auto) where X is one of:\n"
     "       S[W|F] = SDL surface mode W for windowed and F for fullscreen\n"
 #ifdef __unix__
-    "       X = X11 mode\n"
+    "       X[W|F] = X11 mode W for windowed and F for fullscreen\n"
     "       C = Curses mode\n"
     "       I = Curses mode with forced ASCII charset\n"
     "       F = Curses mode with forced IBM charset\n"
@@ -759,14 +759,15 @@ char *output_types[] = {
 	, "ANSI"
 #if defined(__unix__) && !defined(NO_X)
 	, "X11"
+	, "X11 Fullscreen"
 #endif
 #ifdef _WIN32
 	, "Win32 Console",
 	"Win32 Console Fullscreen"
 #endif
 #if defined(WITH_SDL) || defined(WITH_SDL_AUDIO)
-	, "SDL",
-	"SDL Fullscreen"
+	, "SDL"
+	, "SDL Fullscreen"
 #endif
 #if defined(WITH_GDI)
 	, "GDI"
@@ -783,6 +784,7 @@ int   output_map[] = {
 	, CIOLIB_MODE_ANSI
 #if defined(__unix__) && !defined(NO_X)
 	, CIOLIB_MODE_X
+	, CIOLIB_MODE_X_FULLSCREEN
 #endif
 #ifdef _WIN32
 	, CIOLIB_MODE_CONIO,
@@ -805,6 +807,7 @@ char *output_descrs[] = {
 	"Curses using US-ASCII",
 	"ANSI",
 	"X11",
+	"X11 Fullscreen",
 	"Win32 Console",
 	"Win32 Console Fullscreen",
 	"SDL",
@@ -820,6 +823,7 @@ char *output_enum[] = {
 	"CursesAscii",
 	"ANSI",
 	"X11",
+	"X11Fullscreen",
 	"WinConsole",
 	"WinConsoleFullscreen",
 	"SDL",
@@ -1628,7 +1632,15 @@ main(int argc, char **argv)
 							ciolib_mode = CIOLIB_MODE_CURSES_ASCII;
 							break;
 						case 'X':
-							ciolib_mode = CIOLIB_MODE_X;
+							switch (toupper(argv[i][3])) {
+								case 0:
+								case 'W':
+									ciolib_mode = CIOLIB_MODE_X;
+									break;
+								case 'F':
+									ciolib_mode = CIOLIB_MODE_X_FULLSCREEN;
+									break;
+							}
 							break;
 						case 'W':
 							ciolib_mode = CIOLIB_MODE_CONIO;
-- 
GitLab