فهرست منبع

Fixes #2865. Unknown character sequence while resizing terminal. (#2951)

* Fixes #2865. Unknown character sequence while resizing terminal.

* Added USE_IOCTL definition for toggle.

* Explaining that is a CSI (Esc[) = 27;91.

---------

Co-authored-by: Tig <[email protected]>
BDisp 1 سال پیش
والد
کامیت
b57b1f32ab

+ 72 - 8
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -341,6 +341,7 @@ namespace Terminal.Gui {
 			Key k = Key.Null;
 
 			if (code == Curses.KEY_CODE_YES) {
+				var lastWch = wch;
 				while (code == Curses.KEY_CODE_YES && wch == Curses.KeyResize) {
 					ProcessWinChange ();
 					code = Curses.get_wch (out wch);
@@ -379,6 +380,25 @@ namespace Terminal.Gui {
 				} else if (wch >= 325 && wch <= 327) { // Shift+Alt+(F1 - F3)
 					wch -= 60;
 					k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch);
+				} else {
+					code = Curses.get_wch (out wch);
+					if (code == 0) {
+						switch (wch) {
+						// Shift code.
+						case 16:
+							keyModifiers.Shift = true;
+							break;
+						default:
+							if (lastWch == Curses.KeyResize && wch == 91) {
+								// Returns this keys to the std input which is a CSI (\x1b[).
+								Curses.ungetch (91); // [
+								Curses.ungetch (27); // Esc
+								return;
+							} else {
+								throw new Exception ();
+							}
+						}
+					}
 				}
 				keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
 				keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
@@ -388,8 +408,6 @@ namespace Terminal.Gui {
 
 			// Special handling for ESC, we want to try to catch ESC+letter to simulate alt-letter as well as Alt-Fkey
 			if (wch == 27) {
-				Curses.timeout (10);
-
 				code = Curses.get_wch (out int wch2);
 
 				if (code == Curses.KEY_CODE_YES) {
@@ -425,6 +443,56 @@ namespace Terminal.Gui {
 						} else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
 							keyModifiers.Shift = true;
 							keyModifiers.Alt = true;
+						} else if (wch2 == Curses.KeySS3) {
+							while (code > -1) {
+								code = Curses.get_wch (out wch2);
+								if (code == 0) {
+									switch (wch2) {
+									case 16:
+										keyModifiers.Shift = true;
+										break;
+									case 108:
+										k = (Key)'+';
+										break;
+									case 109:
+										k = (Key)'-';
+										break;
+									case 112:
+										k = Key.InsertChar;
+										break;
+									case 113:
+										k = Key.End;
+										break;
+									case 114:
+										k = Key.CursorDown;
+										break;
+									case 115:
+										k = Key.PageDown;
+										break;
+									case 116:
+										k = Key.CursorLeft;
+										break;
+									case 117:
+										k = Key.Clear;
+										break;
+									case 118:
+										k = Key.CursorRight;
+										break;
+									case 119:
+										k = Key.Home;
+										break;
+									case 120:
+										k = Key.CursorUp;
+										break;
+									case 121:
+										k = Key.PageUp;
+										break;
+									default:
+										k = (Key)wch2;
+										break;
+									}
+								}
+							}
 						} else if (wch2 < 256) {
 							k = (Key)wch2;
 							keyModifiers.Alt = true;
@@ -559,7 +627,6 @@ namespace Terminal.Gui {
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 			// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
-			Curses.timeout (0);
 			this.keyHandler = keyHandler;
 			this.keyDownHandler = keyDownHandler;
 			this.keyUpHandler = keyUpHandler;
@@ -572,9 +639,7 @@ namespace Terminal.Gui {
 				return true;
 			});
 
-			mLoop.WinChanged += () => {
-				ProcessInput ();
-			};
+			mLoop.WinChanged += () => ProcessWinChange ();
 		}
 
 		public override void Init (Action terminalResized)
@@ -585,6 +650,7 @@ namespace Terminal.Gui {
 			try {
 				window = Curses.initscr ();
 				Curses.set_escdelay (10);
+				Curses.nodelay (window.Handle, true);
 			} catch (Exception e) {
 				throw new Exception ($"Curses failed to initialize, the exception is: {e.Message}");
 			}
@@ -673,13 +739,11 @@ namespace Terminal.Gui {
 
 			ResizeScreen ();
 			UpdateOffScreen ();
-
 		}
 
 		public override void ResizeScreen ()
 		{
 			Clip = new Rect (0, 0, Cols, Rows);
-			Curses.refresh ();
 		}
 
 		public override void UpdateOffScreen ()

+ 44 - 12
Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs

@@ -41,22 +41,26 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
+
+//#define USE_IOCTL
+
 using System;
 using System.Runtime.InteropServices;
 using Terminal.Gui;
 
 namespace Unix.Terminal {
 #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-
 	public partial class Curses {
-		//[StructLayout (LayoutKind.Sequential)]
-		//public struct winsize {
-		//	public ushort ws_row;
-		//	public ushort ws_col;
-		//	public ushort ws_xpixel;   /* unused */
-		//	public ushort ws_ypixel;   /* unused */
-		//};
+#if USE_IOCTL
 
+		[StructLayout (LayoutKind.Sequential)]
+		public struct winsize {
+			public ushort ws_row;
+			public ushort ws_col;
+			public ushort ws_xpixel;   /* unused */
+			public ushort ws_ypixel;   /* unused */
+		};
+#endif
 		[StructLayout (LayoutKind.Sequential)]
 		public struct MouseEvent {
 			public short ID;
@@ -77,10 +81,11 @@ namespace Unix.Terminal {
 
 		[DllImport ("libc")]
 		public extern static int setlocale (int cate, [MarshalAs (UnmanagedType.LPStr)] string locale);
+#if USE_IOCTL
 
-		//[DllImport ("libc")]
-		//public extern static int ioctl (int fd, int cmd, out winsize argp);
-
+		[DllImport ("libc")]
+		public extern static int ioctl (int fd, int cmd, out winsize argp);
+#endif
 		static void LoadMethods ()
 		{
 			var libs = UnmanagedLibrary.IsMacOSPlatform ? new string [] { "libncurses.dylib" } : new string [] { "libncursesw.so.6", "libncursesw.so.5" };
@@ -227,9 +232,32 @@ namespace Unix.Terminal {
 
 		internal static void console_sharp_get_dims (out int lines, out int cols)
 		{
+#if USE_IOCTL
+
+			if (UnmanagedLibrary.IsMacOSPlatform) {
+				int cmd = TIOCGWINSZ_MAC;
+
+				if (ioctl (1, cmd, out winsize ws) == 0) {
+					lines = ws.ws_row;
+					cols = ws.ws_col;
+
+					if (lines == Lines && cols == Cols) {
+						return;
+					}
+
+					resizeterm (lines, cols);
+				} else {
+					lines = Lines;
+					cols = Cols;
+				}
+			} else {
+				lines = Marshal.ReadInt32 (lines_ptr);
+				cols = Marshal.ReadInt32 (cols_ptr);
+			}
+#else
 			lines = Marshal.ReadInt32 (lines_ptr);
 			cols = Marshal.ReadInt32 (cols_ptr);
-
+#endif
 			//int cmd;
 			//if (UnmanagedLibrary.IsMacOSPlatform) {
 			//	cmd = TIOCGWINSZ_MAC;
@@ -347,6 +375,7 @@ namespace Unix.Terminal {
 		static public int savetty () => methods.savetty ();
 		static public int resetty () => methods.resetty ();
 		static public int set_escdelay (int size) => methods.set_escdelay (size);
+		static public int nodelay (IntPtr win, bool bf) => methods.nodelay (win, bf);
 	}
 
 #pragma warning disable RCS1102 // Make class static.
@@ -424,6 +453,7 @@ namespace Unix.Terminal {
 		public delegate int savetty ();
 		public delegate int resetty ();
 		public delegate int set_escdelay (int size);
+		public delegate int nodelay (IntPtr win, bool bf);
 	}
 
 	internal class NativeMethods {
@@ -498,6 +528,7 @@ namespace Unix.Terminal {
 		public readonly Delegates.savetty savetty;
 		public readonly Delegates.resetty resetty;
 		public readonly Delegates.set_escdelay set_escdelay;
+		public readonly Delegates.nodelay nodelay;
 		public UnmanagedLibrary UnmanagedLibrary;
 
 		public NativeMethods (UnmanagedLibrary lib)
@@ -574,6 +605,7 @@ namespace Unix.Terminal {
 			savetty = lib.GetNativeMethodDelegate<Delegates.savetty> ("savetty");
 			resetty = lib.GetNativeMethodDelegate<Delegates.resetty> ("resetty");
 			set_escdelay = lib.GetNativeMethodDelegate<Delegates.set_escdelay> ("set_escdelay");
+			nodelay = lib.GetNativeMethodDelegate<Delegates.nodelay> ("nodelay");
 		}
 	}
 #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member

+ 1 - 0
Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs

@@ -106,6 +106,7 @@ namespace Unix.Terminal {
 		public const int KeyHome = unchecked((int)0x106);
 		public const int KeyMouse = unchecked((int)0x199);
 		public const int KeyCSI = unchecked((int)0x5b);
+		public const int KeySS3 = unchecked((int)0x4f);
 		public const int KeyEnd = unchecked((int)0x168);
 		public const int KeyDeleteChar = unchecked((int)0x14a);
 		public const int KeyInsertChar = unchecked((int)0x14b);

+ 101 - 0
UICatalog/Scenarios/MainLoopTimeouts.cs

@@ -0,0 +1,101 @@
+#pragma warning disable format
+
+#pragma warning restore format
+using System;
+using System.Collections.Generic;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios {
+	[ScenarioMetadata (Name: "MainLoopTimeouts", Description: "MainLoop Timeouts")]
+	[ScenarioCategory ("Tests")]
+	public class MainLoopTimeouts : Scenario {
+		static readonly List<string> GlobalList = new () { "1" };
+		static readonly ListView GlobalListView = new () { Width = Dim.Fill (), Height = Dim.Fill () };
+
+		static Label CounterLabel;
+		static Label BlinkingLabel;
+
+		static int Counter = 0;
+
+		static object _listToken = null;
+		static object _blinkToken = null;
+		static object _countToken = null;
+
+		public override void Init (ColorScheme colorScheme)
+		{
+			Application.Init ();
+
+			var startButton = new Button ("Start");
+			var stopButton = new Button ("Stop") { Y = 1 };
+			var container = new View () { X = Pos.Center (), Y = Pos.Center (), Width = 8, Height = 8, ColorScheme = Colors.Error };
+
+			CounterLabel = new Label ("0") { X = Pos.X (container), Y = Pos.Y (container) - 2 };
+			BlinkingLabel = new Label ("Blink") { X = Pos.X (container), Y = Pos.Bottom (container) + 1 };
+
+			startButton.Clicked += Start;
+			stopButton.Clicked += Stop;
+
+			GlobalListView.SetSource (GlobalList);
+			container.Add (GlobalListView);
+
+			Application.Top.Add (container, CounterLabel, BlinkingLabel);
+			Application.Top.Add (startButton, stopButton);
+			Application.Run ();
+			Application.Shutdown ();
+		}
+
+		public override void Run ()
+		{
+		}
+
+		private static void Start ()
+		{
+			_listToken = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), Add);
+			_blinkToken = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (1000), Blink);
+			_countToken = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (1000), Count);
+		}
+
+		private static void Stop ()
+		{
+			Application.MainLoop.RemoveTimeout (_listToken);
+			Application.MainLoop.RemoveTimeout (_blinkToken);
+			Application.MainLoop.RemoveTimeout (_countToken);
+		}
+
+		private static bool Add (MainLoop mainLoop)
+		{
+			Application.MainLoop.Invoke (() => {
+				GlobalList.Add (new Random ().Next (100).ToString ());
+				GlobalListView.MoveDown ();
+			});
+
+			return true;
+		}
+
+		private static bool Blink (MainLoop mainLoop)
+		{
+			Application.MainLoop.Invoke (() => {
+				if (BlinkingLabel.Visible) {
+					BlinkingLabel.Visible = false;
+					System.Diagnostics.Debug.WriteLine (BlinkingLabel.Visible);
+				} else {
+					BlinkingLabel.Visible = true;
+					System.Diagnostics.Debug.WriteLine (BlinkingLabel.Visible);
+				}
+
+			});
+
+			return true;
+		}
+
+		private static bool Count (MainLoop mainLoop)
+		{
+			Application.MainLoop.Invoke (() => {
+				Counter++;
+				CounterLabel.Text = Counter.ToString ();
+			});
+
+			return true;
+		}
+	}
+}