Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,7 @@ MouseEvent ToDriverMouse (Curses.MouseEvent cev)
};

if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
Application.MainLoop.AddIdle (() => {
Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
return false;
});
Application.MainLoop.Invoke (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
}


Expand Down Expand Up @@ -1500,6 +1497,7 @@ protected override void SetClipboardDataImpl (string text)
}
}) {
powershell.Start ();
powershell.WaitForExit ();
if (!powershell.DoubleWaitForExit ()) {
var timeoutError = $@"Process timed out. Command line: bash {powershell.StartInfo.Arguments}.
Output: {powershell.StandardOutput.ReadToEnd ()}
Expand Down
30 changes: 9 additions & 21 deletions Terminal.Gui/ConsoleDrivers/NetDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -713,10 +713,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki)
//System.Diagnostics.Debug.WriteLine ($"ButtonState: {mouseEvent.ButtonState} X: {mouseEvent.Position.X} Y: {mouseEvent.Position.Y}");

if (isButtonDoubleClicked) {
Application.MainLoop.AddIdle (() => {
Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
return false;
});
Application.MainLoop.Invoke (async () => await ProcessButtonDoubleClickedAsync ());
}

if ((buttonState & MouseButtonState.Button1Pressed) != 0
Expand Down Expand Up @@ -782,12 +779,9 @@ void GetMouseEvent (ConsoleKeyInfo [] cki)
isButtonClicked = false;
isButtonDoubleClicked = true;
ProcessButtonDoubleClicked (mouseEvent);
Application.MainLoop.AddIdle (() => {
Task.Run (async () => {
await Task.Delay (600);
isButtonDoubleClicked = false;
});
return false;
Application.MainLoop.Invoke (async () => {
await Task.Delay (600);
isButtonDoubleClicked = false;
});
inputReady.Set ();
return;
Expand Down Expand Up @@ -829,12 +823,9 @@ void GetMouseEvent (ConsoleKeyInfo [] cki)
|| (buttonState & MouseButtonState.Button3Released) != 0)) {
isButtonClicked = true;
ProcessButtonClicked (mouseEvent);
Application.MainLoop.AddIdle (() => {
Task.Run (async () => {
await Task.Delay (300);
isButtonClicked = false;
});
return false;
Application.MainLoop.Invoke (async () => {
await Task.Delay (300);
isButtonClicked = false;
});
inputReady.Set ();
return;
Expand All @@ -852,10 +843,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki)
};
}
if ((buttonState & MouseButtonState.ReportMousePosition) == 0) {
Application.MainLoop.AddIdle (() => {
Task.Run (async () => await ProcessContinuousButtonPressedAsync ());
return false;
});
Application.MainLoop.Invoke (async () => await ProcessContinuousButtonPressedAsync ());
}
}

Expand Down Expand Up @@ -951,7 +939,7 @@ async Task ProcessContinuousButtonPressedAsync ()
if (view == null) {
break;
}
if (isButtonPressed && (lastMouseEvent.ButtonState & MouseButtonState.ReportMousePosition) == 0) {
if (isButtonPressed) {
inputResultQueue.Enqueue (new InputResult () {
EventType = EventType.Mouse,
MouseEvent = lastMouseEvent
Expand Down
10 changes: 2 additions & 8 deletions Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -914,10 +914,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
// $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}");

if (isButtonDoubleClicked || isOneFingerDoubleClicked) {
Application.MainLoop.AddIdle (() => {
Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
return false;
});
Application.MainLoop.Invoke (async () => await ProcessButtonDoubleClickedAsync ());
}

// The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
Expand Down Expand Up @@ -1025,10 +1022,7 @@ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
isButtonPressed = true;

if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
Application.MainLoop.AddIdle (() => {
Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
return false;
});
Application.MainLoop.Invoke (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
}

} else if (lastMouseButtonPressed != null && mouseEvent.EventFlags == 0
Expand Down
3 changes: 2 additions & 1 deletion Terminal.Gui/Core/Clipboard/ClipboardBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ public bool TrySetClipboardData (string text)
try {
SetClipboardDataImpl (text);
return true;
} catch (Exception) {
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine ($"TrySetClipboardData: {ex.Message}");
return false;
}
}
Expand Down
10 changes: 3 additions & 7 deletions Terminal.Gui/Core/ShortcutHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,20 +244,16 @@ public static bool PostShortcutValidation (Key key)
public static bool FindAndOpenByShortcut (KeyEvent kb, View view = null)
{
if (view == null) {
return false; }
return false;
}

var key = kb.KeyValue;
var keys = GetModifiersKey (kb);
key |= (int)keys;
foreach (var v in view.Subviews) {
if (v.Shortcut != Key.Null && v.Shortcut == (Key)key) {
var action = v.ShortcutAction;
if (action != null) {
Application.MainLoop.AddIdle (() => {
action ();
return false;
});
}
Application.MainLoop.Invoke (() => action?.Invoke ());
return true;
}
if (FindAndOpenByShortcut (kb, v)) {
Expand Down
17 changes: 3 additions & 14 deletions Terminal.Gui/Views/Menu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,7 @@ public void Run (Action action)
host.CloseAllMenus ();
Application.Refresh ();

Application.MainLoop.AddIdle (() => {
action ();
return false;
});
Application.MainLoop.Invoke (() => action?.Invoke ());
}

public override bool OnLeave (View view)
Expand Down Expand Up @@ -1089,10 +1086,7 @@ void Selected (MenuItem item)
CloseAllMenus ();
Application.Refresh ();

Application.MainLoop.AddIdle (() => {
action ();
return false;
});
Application.MainLoop.Invoke (() => action?.Invoke ());
}

/// <summary>
Expand Down Expand Up @@ -1635,12 +1629,7 @@ internal bool FindAndOpenMenuByShortcut (KeyEvent kb, MenuItem [] children = nul
}
if ((!(mi is MenuBarItem mbiTopLevel) || mbiTopLevel.IsTopLevel) && mi.Shortcut != Key.Null && mi.Shortcut == (Key)key) {
var action = mi.Action;
if (action != null) {
Application.MainLoop.AddIdle (() => {
action ();
return false;
});
}
Application.MainLoop.Invoke (() => action?.Invoke ());
return true;
}
if (mi is MenuBarItem menuBarItem && !menuBarItem.IsTopLevel && FindAndOpenMenuByShortcut (kb, menuBarItem.Children)) {
Expand Down
5 changes: 1 addition & 4 deletions Terminal.Gui/Views/StatusBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,7 @@ void Run (Action action)
if (action == null)
return;

Application.MainLoop.AddIdle (() => {
action ();
return false;
});
Application.MainLoop.Invoke (() => action?.Invoke ());
}

/// <inheritdoc/>
Expand Down
1 change: 1 addition & 0 deletions Terminal.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\dotnet-core.yml = .github\workflows\dotnet-core.yml
.github\workflows\publish.yml = .github\workflows\publish.yml
README.md = README.md
testenvironments.json = testenvironments.json
EndProjectSection
EndProject
Global
Expand Down
185 changes: 185 additions & 0 deletions UnitTests/MainLoopTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -591,5 +591,190 @@ public async Task InvokeLeakTest ()

Assert.Equal ((numIncrements * numPasses), tbCounter);
}

private static int total;
private static Button btn;
private static string clickMe;
private static string cancel;
private static string pewPew;
private static int zero;
private static int one;
private static int two;
private static int three;
private static int four;

[Theory, AutoInitShutdown]
[MemberData (nameof (TestAddIdle))]
public void Mainloop_Invoke_Or_AddIdle_Should_Not_Be_Used_For_Events_Or_Actions (Action action, string pclickMe, string pcancel, string ppewPew, int pzero, int pone, int ptwo, int pthree, int pfour)
{
total = 0;
btn = null;
clickMe = pclickMe;
cancel = pcancel;
pewPew = ppewPew;
zero = pzero;
one = pone;
two = ptwo;
three = pthree;
four = pfour;

var btnLaunch = new Button ("Open Window");

btnLaunch.Clicked += () => action ();

Application.Top.Add (btnLaunch);

var iterations = -1;

Application.Iteration += () => {
iterations++;
if (iterations == 0) {
Assert.Null (btn);
Assert.Equal (zero, total);
Assert.True (btnLaunch.ProcessKey (new KeyEvent (Key.Enter, null)));
if (btn == null) {
Assert.Null (btn);
Assert.Equal (zero, total);
} else {
Assert.Equal (clickMe, btn.Text);
Assert.Equal (four, total);
}
} else if (iterations == 1) {
Assert.Equal (clickMe, btn.Text);
Assert.Equal (zero, total);
Assert.True (btn.ProcessKey (new KeyEvent (Key.Enter, null)));
Assert.Equal (cancel, btn.Text);
Assert.Equal (one, total);
} else if (iterations == 20000) {
if (total == one) {
Assert.Equal (cancel, btn.Text);
Assert.Equal (one, total);

Application.RequestStop ();
}
}
};

Application.Run ();

if (four == 4) {
Assert.Equal (clickMe, btn.Text);
Assert.Equal (four, total);
} else {
Assert.Equal (cancel, btn.Text);
Assert.Equal (four, total);
}

Application.Shutdown ();

}

public static IEnumerable<object []> TestAddIdle {
get {
// Goes fine
Action a1 = StartWindow;
yield return new object [] { a1, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 };

// Goes badly
Action a2 = () => Application.MainLoop.Invoke (StartWindow);
yield return new object [] { a2, "Click Me", "Cancel", "Cancel", 0, 1, 1, 1, 1 };
}
}

private static void StartWindow ()
{
var startWindow = new Window {
Modal = true
};

btn = new Button {
Text = "Click Me"
};

btn.Clicked += RunAsyncTest;

var totalbtn = new Button () {
X = Pos.Right (btn),
Text = "total"
};

totalbtn.Clicked += () => {
MessageBox.Query ("Count", $"Count is {total}", "Ok");
};

startWindow.Add (btn);
startWindow.Add (totalbtn);

Application.Run (startWindow);

if (four == 4) {
Assert.Equal (clickMe, btn.Text);
Assert.Equal (four, total);
} else {
Assert.Equal (cancel, btn.Text);
Assert.Equal (four, total);
}

Application.RequestStop ();
}

private static void RunAsyncTest ()
{
Assert.Equal (clickMe, btn.Text);
Assert.Equal (zero, total);

btn.Text = "Cancel";
Interlocked.Increment (ref total);
btn.SetNeedsDisplay ();

var task = Task.Run (() => {
try {
Assert.Equal (cancel, btn.Text);
Assert.Equal (one, total);

RunSql ();
} finally {
SetReadyToRun ();
}
}).ContinueWith ((s, e) => {

Assert.Equal (clickMe, btn.Text);
Assert.Equal (three, total);

Interlocked.Increment (ref total);

Assert.Equal (clickMe, btn.Text);
Assert.Equal (four, total);

Application.RequestStop ();

}, TaskScheduler.FromCurrentSynchronizationContext ());
}

private static void RunSql ()
{
Thread.Sleep (100);
Assert.Equal (cancel, btn.Text);
Assert.Equal (one, total);

Application.MainLoop.Invoke (() => {
btn.Text = "Pew Pew";
Interlocked.Increment (ref total);
btn.SetNeedsDisplay ();
});
}

private static void SetReadyToRun ()
{
Thread.Sleep (100);
Assert.Equal (pewPew, btn.Text);
Assert.Equal (two, total);

Application.MainLoop.Invoke (() => {
btn.Text = "Click Me";
Interlocked.Increment (ref total);
btn.SetNeedsDisplay ();
});
}
}
}
Loading