Skip to content

Commit

Permalink
Fixes #2865. Unknown character sequence while resizing terminal. (#2951)
Browse files Browse the repository at this point in the history
* 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 <tig@users.noreply.github.com>
  • Loading branch information
BDisp and tig committed Dec 19, 2023
1 parent 5eafea0 commit b57b1f3
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 20 deletions.
80 changes: 72 additions & 8 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ void ProcessInput ()
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);
Expand Down Expand Up @@ -379,6 +380,25 @@ void ProcessInput ()
} 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)));
Expand All @@ -388,8 +408,6 @@ void ProcessInput ()

// 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) {
Expand Down Expand Up @@ -425,6 +443,56 @@ void ProcessInput ()
} 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;
Expand Down Expand Up @@ -559,7 +627,6 @@ void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
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;
Expand All @@ -572,9 +639,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandle
return true;
});

mLoop.WinChanged += () => {
ProcessInput ();
};
mLoop.WinChanged += () => ProcessWinChange ();
}

public override void Init (Action terminalResized)
Expand All @@ -585,6 +650,7 @@ public override void Init (Action terminalResized)
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}");
}
Expand Down Expand Up @@ -673,13 +739,11 @@ public override void Init (Action terminalResized)

ResizeScreen ();
UpdateOffScreen ();

}

public override void ResizeScreen ()
{
Clip = new Rect (0, 0, Cols, Rows);
Curses.refresh ();
}

public override void UpdateOffScreen ()
Expand Down
56 changes: 44 additions & 12 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -77,10 +81,11 @@ public struct MouseEvent {

[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" };
Expand Down Expand Up @@ -227,9 +232,32 @@ internal static IntPtr console_sharp_get_curscr ()

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;
Expand Down Expand Up @@ -347,6 +375,7 @@ static public int IsAlt (int key)
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.
Expand Down Expand Up @@ -424,6 +453,7 @@ internal class Delegates {
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 {
Expand Down Expand Up @@ -498,6 +528,7 @@ internal class NativeMethods {
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)
Expand Down Expand Up @@ -574,6 +605,7 @@ public NativeMethods (UnmanagedLibrary lib)
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
Expand Down
1 change: 1 addition & 0 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public enum Event : long {
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);
Expand Down
101 changes: 101 additions & 0 deletions UICatalog/Scenarios/MainLoopTimeouts.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}

0 comments on commit b57b1f3

Please sign in to comment.