diff --git a/.github/workflows/continuous_integration_other.yml b/.github/workflows/continuous_integration_other.yml
index a0d14782e35aa753c1d1f9a15f27261451a1faba..06708903cfbc558dd4641d109cbf1678d55c6f2a 100644
--- a/.github/workflows/continuous_integration_other.yml
+++ b/.github/workflows/continuous_integration_other.yml
@@ -28,7 +28,7 @@ jobs:
           make mac
         elif [[ "${{ runner.os }}" == 'Linux' ]]; then
           sudo apt-get update
-          sudo apt install mesa-common-dev
+          sudo apt install mesa-common-dev libxfixes-dev
           make linux
         fi
 
diff --git a/Help/e_curvelinedefs.html b/Help/e_curvelinedefs.html
index b0e3ca3a9b73b566a06e1d3e972ea9f6b53df3ba..be1138d4e9a02ae8ad8be742426f2f1e2bc6948b 100755
--- a/Help/e_curvelinedefs.html
+++ b/Help/e_curvelinedefs.html
@@ -32,6 +32,22 @@
 			<td valign="top"><b>Escape</b></td>
 			<td>Discard the changes and return to the previous mode.</td>
 		</tr>
+		<tr>
+			<td valign="top"><b>Ctrl + Mousewheel</b></td>
+			<td>Increase/decrease the number of dividing vertices.</td>
+		</tr>
+		<tr>
+			<td valign="top"><b>LMB + Drag</b></td>
+			<td>Change the distance of the curve (when Fixed Circular Curve is not set).</td>
+		</tr>
+		<tr>
+			<td valign="top"><b>RMB + Drag</b></td>
+			<td>Change the angle of the curve.</td>
+		</tr>
+		<tr>
+			<td valign="top"><b>Ctrl + Alt + RMB + Drag</b></td>
+			<td>When Fixed Circlar Curve is set, increase/decrease the dividing vertices together with the angle of the curve to maintain a consistent circular curve</td>
+		</tr>
 		</table>
 	</p>
 	</div>
diff --git a/Makefile b/Makefile
index 410144606654cfc0bcf1dd2a841924607b243dc6..cdf9308c1b391d19fdaefce0b39d5d6ec2d5ab57 100644
--- a/Makefile
+++ b/Makefile
@@ -75,6 +75,6 @@ nativemac:
 	$(CXX) -std=c++14 -O2 --shared -g3 -o Build/libBuilderNative.so -fPIC -I Source/Native Source/Native/*.cpp Source/Native/OpenGL/*.cpp Source/Native/OpenGL/gl_load/*.c -ldl
 
 native:
-	$(CXX) -std=c++14 -O2 --shared -g3 -o Build/libBuilderNative.so -fPIC -I Source/Native Source/Native/*.cpp Source/Native/OpenGL/*.cpp Source/Native/OpenGL/gl_load/*.c -lX11 -ldl
+	$(CXX) -std=c++14 -O2 --shared -g3 -o Build/libBuilderNative.so -fPIC -I Source/Native Source/Native/*.cpp Source/Native/OpenGL/*.cpp Source/Native/OpenGL/gl_load/*.c -DUDB_LINUX=1 -lX11 -lXfixes -ldl
 
 -include $(DEPS)
diff --git a/README.md b/README.md
old mode 100755
new mode 100644
index 3beba4a744a2df531752985dd88810c4c4835ee0..6632a525cf69d86f76120caff4294a0bb46edeae
--- a/README.md
+++ b/README.md
@@ -8,15 +8,15 @@
 
 **Building on Linux:**
 
-These instructions are for Debian-based distros and were tested with Debian 10 and Ubuntu 18.04. For others it should be similar.
+These instructions are for Debian-based distros and were tested with Ubuntu 24.04 LTS. For others it should be similar.
 
 __Note:__ this is experimental. None of the developers are using Linux as a desktop OS, so you're pretty much on your own if you encounter any problems with running the application.
 
 - Install Mono. The `mono-complete` package from the Debian repo doesn't include `msbuild`, so you have to install `mono-complete` by following the instructions on the Mono project's website: https://www.mono-project.com/download/stable/#download-lin
-- Install additional required packages: `sudo apt install make g++ git libx11-dev mesa-common-dev`
-- Go to a directory of your choice and clone the repository (it'll automatically create an `UltimateZoneBuilder` directory in the current directory): `git clone https://git.do.srb2.org/STJr/UltimateZoneBuilder.git`
-- Compile UDB: `cd UltimateZoneBuilder && make`
-- Run UDB: `cd Build && ./builder`
+- Install additional required packages: `sudo apt install make g++ git libx11-dev libxfixes-dev mesa-common-dev`
+- Go to a directory of your choice and clone the repository (it'll automatically create an `UltimateZoneBuilder` directory in the current directory): `git clone https://github.com/jewalky/UltimateDoomBuilder.git`
+- Compile UZB: `cd UltimateZoneBuilder && make`
+- Run UZB: `cd Build && ./builder`
 
 **Links:**
 - [SRB2MB thread](https://mb.srb2.org/addons/ultimate-zone-builder.6126/)
diff --git a/Source/Core/Actions/MouseInput.cs b/Source/Core/Actions/MouseInput.cs
index 1b6bb64ee418de5012577e295d17ca000a97dca6..8855872679b6cf56f64494b1eec4aec162def405 100755
--- a/Source/Core/Actions/MouseInput.cs
+++ b/Source/Core/Actions/MouseInput.cs
@@ -55,6 +55,10 @@ namespace CodeImp.DoomBuilder.Actions
 				mouse = null;
 			}
 
+			#if MONO_WINFORMS
+			MouseInput_ShowCursor(false);
+			#endif
+
 			// We have no destructor
 			GC.SuppressFinalize(this);
 		}
@@ -67,6 +71,10 @@ namespace CodeImp.DoomBuilder.Actions
 				mouse.Dispose();
 				mouse = null;
 			}
+
+			#if MONO_WINFORMS
+			MouseInput_ShowCursor(true);
+			#endif
 		}
 
 		#endregion
@@ -113,6 +121,11 @@ namespace CodeImp.DoomBuilder.Actions
 		}
 
 		#endregion
+
+		#if MONO_WINFORMS
+		[DllImport("BuilderNative.dll", CallingConvention = CallingConvention.Cdecl)]
+		private static extern void MouseInput_ShowCursor(bool show);
+		#endif
 	}
 
 	public struct MouseState
diff --git a/Source/Core/Controls/ImageSelectorPanel.cs b/Source/Core/Controls/ImageSelectorPanel.cs
index 82f0315da16c47ed78f629882b620c1441d3015a..bdd2148431d2988d8eb9c808ddcd87bc8608697d 100755
--- a/Source/Core/Controls/ImageSelectorPanel.cs
+++ b/Source/Core/Controls/ImageSelectorPanel.cs
@@ -408,6 +408,9 @@ namespace CodeImp.DoomBuilder.Controls
 		protected override void OnMouseDoubleClick(MouseEventArgs e)
 		{
 			base.OnMouseDoubleClick(e);
+			//TODO: testing this on Windows, it looks like General.Interface.CtrlState and 
+			//  General.Interface.ShiftState are always false, because as the main window
+			//  doesn't have focus and won't update these states
 			if(General.Interface.CtrlState || General.Interface.ShiftState || selection.Count != 1)
 				return;
 
diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs
index d7aa048dbee49a823ddfdd57f64bb4c42231984c..4c06c435f6dc1a311b6f318ca5e83cce27310eb5 100755
--- a/Source/Core/Data/DataManager.cs
+++ b/Source/Core/Data/DataManager.cs
@@ -1573,7 +1573,7 @@ namespace CodeImp.DoomBuilder.Data
 			foreach(string spritefile in files)
 			{
 				ImageData img = new FileImage(Path.GetFileNameWithoutExtension(spritefile).ToLowerInvariant(), spritefile);
-				img.LoadImageNow();
+				img.LoadImageNow(false);
 				img.AllowUnload = false;
 				name = INTERNAL_PREFIX + img.Name;
 				long hash = Lump.MakeLongName(name, true); //mxd
diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs
index 9ca7342a0a8f0d33b721cb5ea9479ac41f52739d..7e9c68eeab38568cab514291e54c5cb049221254 100755
--- a/Source/Core/Windows/MainForm.cs
+++ b/Source/Core/Windows/MainForm.cs
@@ -1264,24 +1264,6 @@ namespace CodeImp.DoomBuilder.Windows
 				Cursor.Position = display.PointToScreen(new Point(display.ClientSize.Width / 2, display.ClientSize.Height / 2)); //mxd
 				Cursor.Clip = display.RectangleToScreen(display.ClientRectangle);
 				Cursor.Hide();
-				
-				#if MONO_WINFORMS
-				// A beautiful transparent cursor, just for you mono!
-				string emptycursor =
-					"AAACAAEAICACAAAAAAAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAgAAAAAAAAAAAAAAAAgAA" +
-					"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
-					"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
-					"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////////////////////////////////" +
-					"////////////////////////////////////////////////////////////////////////////" +
-					"//////////////////////////////////////////////////////8=";
-				using (var stream = new MemoryStream(System.Convert.FromBase64String(emptycursor)))
-				{
-					var cursor = new Cursor(stream);
-					Cursor.Current = cursor;
-					display.Cursor = cursor;
-				}
-				Application.DoEvents();
-				#endif
 			}
 		}
 
@@ -1298,11 +1280,6 @@ namespace CodeImp.DoomBuilder.Windows
 				// Release and show the mouse
 				Cursor.Clip = Rectangle.Empty;
 				Cursor.Position = display.PointToScreen(new Point(display.ClientSize.Width / 2, display.ClientSize.Height / 2));
-				#if MONO_WINFORMS
-				Cursor.Current = Cursors.Default;
-				display.Cursor = Cursors.Default;
-				Application.DoEvents();
-				#endif
 				Cursor.Show();
 			}
 		}
@@ -4256,14 +4233,41 @@ namespace CodeImp.DoomBuilder.Windows
 		// Returns the new texture name or the same texture name when cancelled
 		public string BrowseTexture(IWin32Window owner, string initialvalue)
 		{
-			return TextureBrowserForm.Browse(owner, initialvalue, false);//mxd
+
+			DisableProcessing();
+			#if MONO_WINFORMS
+			//Mono's Winforms treat dialogs a little differently
+			//  they don't implicitly take focus from the parent window
+			//  and keyboard input from focus window isn't reset when the dialog takes focus
+			BreakExclusiveMouseInput();
+			ReleaseAllKeys();
+			#endif
+			string tex = TextureBrowserForm.Browse(owner, initialvalue, false);//mxd
+			#if MONO_WINFORMS
+			ResumeExclusiveMouseInput();
+			#endif
+			EnableProcessing();
+			return tex;
 		}
 
 		// This browses for a flat
 		// Returns the new flat name or the same flat name when cancelled
 		public string BrowseFlat(IWin32Window owner, string initialvalue)
 		{
-			return TextureBrowserForm.Browse(owner, initialvalue, true); //mxd. was FlatBrowserForm
+			DisableProcessing();
+			#if MONO_WINFORMS
+			//Mono's Winforms treat dialogs a little differently
+			//  they don't implicitly take focus from the parent window
+			//  and keyboard input from focus window isn't reset when the dialog takes focus
+			BreakExclusiveMouseInput();
+			ReleaseAllKeys();
+			#endif
+			string tex = TextureBrowserForm.Browse(owner, initialvalue, true); //mxd. was FlatBrowserForm
+			#if MONO_WINFORMS
+			ResumeExclusiveMouseInput();
+			#endif
+			EnableProcessing();
+			return tex;
 		}
 		
 		// This browses the lindef types
diff --git a/Source/Native/RawMouse.cpp b/Source/Native/RawMouse.cpp
index 5cdc0b4e491bb37136f3f5baede84462b7f58309..c0a45a9a3382edd0ad00d47ca8d9f07073455403 100644
--- a/Source/Native/RawMouse.cpp
+++ b/Source/Native/RawMouse.cpp
@@ -172,7 +172,7 @@ extern "C"
 
 RawMouse* RawMouse_New(void* hwnd)
 {
-#ifdef _WIN32
+#if defined(WIN32)
 	return new RawMouse(hwnd);
 #else
 	return nullptr;
@@ -194,4 +194,28 @@ float RawMouse_GetY(RawMouse* mouse)
 	return mouse->GetY();
 }
 
+#ifdef UDB_LINUX
+#include <X11/extensions/Xfixes.h>
+
+static Display *display = NULL;
+#endif
+
+void MouseInput_ShowCursor(bool show)
+{
+#ifdef UDB_LINUX
+	if (display == NULL)
+	{
+		display = XOpenDisplay(NULL);
+		if (display == NULL)
+			return;
+    }
+
+	if (show)
+		XFixesShowCursor(display, DefaultRootWindow(display));
+	else
+		XFixesHideCursor(display, DefaultRootWindow(display));
+	XSync(display, True);
+#endif
+}
+
 }
diff --git a/Source/Native/exports.def b/Source/Native/exports.def
index 8f364f8c2ba7f9fe19f6ba23cbb04ea8869af702..d7ed8f0cbd6fb30121ab3129bcdeea87770de0aa 100644
--- a/Source/Native/exports.def
+++ b/Source/Native/exports.def
@@ -50,6 +50,7 @@ EXPORTS
 	RawMouse_Delete
 	RawMouse_GetX
 	RawMouse_GetY
+	MouseInput_ShowCursor
 	Matrix_Null
 	Matrix_Identity
 	Matrix_Translation
diff --git a/Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs
index 62b10977e355836d0ca5c74041a15f65f743475f..b7d7fd662a9f99c351d3f8f5cefc9089d29e95f8 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/CurveLinedefsMode.cs
@@ -496,6 +496,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			else if(editpressed && prevoffset != 0)
 			{
 				int newangle = 0;
+				int newvertices = panel.Vertices;
 				if(panel.FixedCurve)
 				{
 					// Flip required?
@@ -513,7 +514,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					//TODO: there surely is a way to get new angle without iteration...
 					double targetoffset = radius.GetLength() * u;
 					double prevdiff = double.MaxValue;
-					int increment = (clampvalue ? panel.AngleIncrement : 1);
+					bool clampToKeyEllipseSegments = General.Interface.CtrlState && General.Interface.AltState;
+
+					int increment = (clampToKeyEllipseSegments ? 15 : clampvalue ? panel.AngleIncrement : 1);
 					for(int i = 1; i < panel.MaximumAngle; i += increment)
 					{
 						// Calculate diameter for current angle...
@@ -530,7 +533,19 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					}
 
 					// Clamp to 5 deg increments
-					if(clampvalue) newangle = (newangle / panel.AngleIncrement) * panel.AngleIncrement; 
+					if(clampvalue) newangle = (newangle / increment) * increment; 
+					
+					if(clampToKeyEllipseSegments)
+					{
+						newvertices = Math.Abs(newangle) / increment - 1;
+
+						if(newvertices < 1)
+							newvertices = 1;
+						if(newangle < 30)
+						{
+							newangle = 0;
+						}
+					}
 				}
 				else
 				{
@@ -543,7 +558,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				}
 
 				// Set new angle without triggering the update...
-				panel.SetValues(panel.Vertices, panel.Distance, newangle, panel.FixedCurve, panel.FixedCurveOutwards);
+				panel.SetValues(newvertices, panel.Distance, newangle, panel.FixedCurve, panel.FixedCurveOutwards);
 
 				// Update hint text
 				hintlabel.Text = "Angle: " + panel.Angle;
diff --git a/Source/Plugins/BuilderModes/Resources/Hints.cfg b/Source/Plugins/BuilderModes/Resources/Hints.cfg
index 85317da910094b10758836eb5f90144d58aeb79a..ca70331117aab20389213e11747cdd147d7bb2e3 100755
--- a/Source/Plugins/BuilderModes/Resources/Hints.cfg
+++ b/Source/Plugins/BuilderModes/Resources/Hints.cfg
@@ -242,5 +242,6 @@ group general
 "<b>LMB-drag</b> or use <k>buildermodes_increasebevel</k> and <k>buildermodes_decreasebevel</k> to change curve depth. Hold <b>Shift</b> while dragging to change by 1 map unit" 
 "<b>RMB-drag</b> or use <k>buildermodes_rotateclockwise</k> and <k>buildermodes_rotatecounterclockwise</k> to change curve bulginess. Hold <b>Shift</b> while dragging to change by 1 degree"
 "<b>LMB+RMB-drag</b> or use <k>buildermodes_increasesubdivlevel</k> and <k>buildermodes_decreasesubdivlevel</k> to change the number of points in the curve"
+"When <b>CTRL+ALT+RMB-drag</b> When Fixed Circlar Curve is set, increase/decrease the dividing vertices together with the angle of the curve to maintain a consistent circular curve."
 "Press <k>builder_acceptmode</k> to accept"
-"Press <k>builder_cancelmode</k> or <k>builder_classicedit</k> to cancel"
\ No newline at end of file
+"Press <k>builder_cancelmode</k> or <k>builder_classicedit</k> to cancel"