Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #2784. CursesDriver throws System.StackOverflowException on console resizing. #2786

Merged
49 changes: 46 additions & 3 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ public override void Refresh ()
Curses.raw ();
Curses.noecho ();
Curses.refresh ();
ProcessWinChange ();
}

private void ProcessWinChange ()
Expand All @@ -161,6 +160,10 @@ public override void End ()
StopReportingMouseMoves ();
SetCursorVisibility (CursorVisibility.Default);

// throws away any typeahead that has been typed by
// the user and has not yet been read by the program.
Curses.flushinp ();

Curses.endwin ();
}

Expand Down Expand Up @@ -337,8 +340,12 @@ void ProcessInput ()
Key k = Key.Null;

if (code == Curses.KEY_CODE_YES) {
if (wch == Curses.KeyResize) {
while (code == Curses.KEY_CODE_YES && wch == Curses.KeyResize) {
ProcessWinChange ();
code = Curses.get_wch (out wch);
}
if (wch == 0) {
return;
}
if (wch == Curses.KeyMouse) {
int wch2 = wch;
Expand Down Expand Up @@ -492,8 +499,44 @@ void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref Con
}
}

MouseFlags lastMouseFlags;
BDisp marked this conversation as resolved.
Show resolved Hide resolved

void ProcessMouseEvent (MouseFlags mouseFlag, Point pos)
{
bool WasButtonReleased (MouseFlags flag)
{
return flag.HasFlag (MouseFlags.Button1Released) ||
flag.HasFlag (MouseFlags.Button2Released) ||
flag.HasFlag (MouseFlags.Button3Released) ||
flag.HasFlag (MouseFlags.Button4Released);
}

bool IsButtonNotPressed (MouseFlags flag)
{
return !flag.HasFlag (MouseFlags.Button1Pressed) &&
!flag.HasFlag (MouseFlags.Button2Pressed) &&
!flag.HasFlag (MouseFlags.Button3Pressed) &&
!flag.HasFlag (MouseFlags.Button4Pressed);
}

bool IsButtonClickedOrDoubleClicked (MouseFlags flag)
{
return flag.HasFlag (MouseFlags.Button1Clicked) ||
flag.HasFlag (MouseFlags.Button2Clicked) ||
flag.HasFlag (MouseFlags.Button3Clicked) ||
flag.HasFlag (MouseFlags.Button4Clicked) ||
flag.HasFlag (MouseFlags.Button1DoubleClicked) ||
flag.HasFlag (MouseFlags.Button2DoubleClicked) ||
flag.HasFlag (MouseFlags.Button3DoubleClicked) ||
flag.HasFlag (MouseFlags.Button4DoubleClicked);
}

if ((WasButtonReleased (mouseFlag) && IsButtonNotPressed (lastMouseFlags)) ||
(IsButtonClickedOrDoubleClicked (mouseFlag) && lastMouseFlags == 0)) {
return;
}

lastMouseFlags = mouseFlag;
var me = new MouseEvent () {
Flags = mouseFlag,
X = pos.X,
Expand Down Expand Up @@ -529,7 +572,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandle
});

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

Expand Down
4 changes: 2 additions & 2 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ void IMainLoopDriver.Setup (MainLoop mainLoop)
{
this.mainLoop = mainLoop;
pipe (wakeupPipes);
AddWatch (wakeupPipes [0], Condition.PollIn, ml => {
read (wakeupPipes [0], ignore, readHandle);
AddWatch (wakeupPipes [1], Condition.PollIn, ml => {
read (wakeupPipes [1], ignore, readHandle);
return true;
});
}
Expand Down
5 changes: 4 additions & 1 deletion Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,10 @@ public static bool CheckWinChange ()

console_sharp_get_dims (out l, out c);

if (l == 1 || l != lines || c != cols) {
if (l < 1) {
l = 1;
}
if (l != lines || c != cols) {
lines = l;
cols = c;
return true;
Expand Down
64 changes: 33 additions & 31 deletions UnitTests/Drivers/ConsoleDriverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ public void Left_And_Top_Is_Always_Zero (Type driverType)

Application.Shutdown ();
}


[Fact, AutoInitShutdown]
public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space ()
{
Expand Down Expand Up @@ -311,54 +311,56 @@ public void MakePrintable_Does_Not_Convert_Ansi_Chars_To_Unicode (uint code)
}

private static object packetLock = new object ();

/// <summary>
/// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'.
/// These are indicated with the wVirtualKeyCode of 231. When we see this code
/// then we need to look to the unicode character (UnicodeChar) instead of the key
/// when telling the rest of the framework what button was pressed. For full details
/// see: https://github.com/gui-cs/Terminal.Gui/issues/2008
/// </summary>
[Theory, AutoInitShutdown]
[Theory]
[ClassData (typeof (PacketTest))]
public void TestVKPacket (uint unicodeCharacter, bool shift, bool alt, bool control, uint initialVirtualKey, uint initialScanCode, Key expectedRemapping, uint expectedVirtualKey, uint expectedScanCode)
{
var modifiers = new ConsoleModifiers ();
if (shift) modifiers |= ConsoleModifiers.Shift;
if (alt) modifiers |= ConsoleModifiers.Alt;
if (control) modifiers |= ConsoleModifiers.Control;
var mappedConsoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode, out uint outputChar);
lock (packetLock) {
Application.ForceFakeConsole = true;
Application.Init ();

if ((scanCode > 0 || mappedConsoleKey == 0) && mappedConsoleKey == initialVirtualKey) Assert.Equal (mappedConsoleKey, initialVirtualKey);
else Assert.Equal (mappedConsoleKey, outputChar < 0xff ? outputChar & 0xff | 0xff << 8 : outputChar);
Assert.Equal (scanCode, initialScanCode);
var modifiers = new ConsoleModifiers ();
if (shift) modifiers |= ConsoleModifiers.Shift;
if (alt) modifiers |= ConsoleModifiers.Alt;
if (control) modifiers |= ConsoleModifiers.Control;
var mappedConsoleKey = ConsoleKeyMapping.GetConsoleKeyFromKey (unicodeCharacter, modifiers, out uint scanCode, out uint outputChar);

var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (mappedConsoleKey, modifiers, out uint consoleKey, out scanCode);
if ((scanCode > 0 || mappedConsoleKey == 0) && mappedConsoleKey == initialVirtualKey) Assert.Equal (mappedConsoleKey, initialVirtualKey);
else Assert.Equal (mappedConsoleKey, outputChar < 0xff ? outputChar & 0xff | 0xff << 8 : outputChar);
Assert.Equal (scanCode, initialScanCode);

//if (scanCode > 0 && consoleKey == keyChar && consoleKey > 48 && consoleKey > 57 && consoleKey < 65 && consoleKey > 91) {
if (scanCode > 0 && keyChar == 0 && consoleKey == mappedConsoleKey) Assert.Equal (0, (double)keyChar);
else Assert.Equal (keyChar, unicodeCharacter);
Assert.Equal (consoleKey, expectedVirtualKey);
Assert.Equal (scanCode, expectedScanCode);
var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (mappedConsoleKey, modifiers, out uint consoleKey, out scanCode);

var top = Application.Top;
//if (scanCode > 0 && consoleKey == keyChar && consoleKey > 48 && consoleKey > 57 && consoleKey < 65 && consoleKey > 91) {
if (scanCode > 0 && keyChar == 0 && consoleKey == mappedConsoleKey) Assert.Equal (0, (double)keyChar);
else Assert.Equal (keyChar, unicodeCharacter);
Assert.Equal (consoleKey, expectedVirtualKey);
Assert.Equal (scanCode, expectedScanCode);

top.KeyPress += (e) => {
var after = ShortcutHelper.GetModifiersKey (e.KeyEvent);
Assert.Equal (expectedRemapping, after);
e.Handled = true;
Application.RequestStop ();
};
var top = Application.Top;

var iterations = -1;
top.KeyPress += (e) => {
var after = ShortcutHelper.GetModifiersKey (e.KeyEvent);
Assert.Equal (expectedRemapping, after);
e.Handled = true;
Application.RequestStop ();
};

Application.Iteration += () => {
iterations++;
if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control);
};
var iterations = -1;

Application.Iteration += () => {
iterations++;
if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control);
};

lock (packetLock) {
Application.Run ();
Application.Shutdown ();
}
Expand Down
Loading