Replies: 7 comments 5 replies
-
I'm not ready to give up on keydown/keyup. I want to push terminals to support it over time. There's not a huge amount of complexity in simulating up. The rest makes my heart sing... although I haven't read it deeply yet. |
Beta Was this translation helpful? Give feedback.
-
I have a pretty nifty FSM library I wrote a few years ago in c++. I am fond of FSMs... |
Beta Was this translation helpful? Give feedback.
-
With the big change I'm doing in the #3768 the |
Beta Was this translation helpful? Give feedback.
-
Have started scaffolding out what this might look like in this branch: https://github.com/tznind/gui.cs/tree/v2-drivers I will tackle Net (AKA NetDriver) and Win32 (AKA WindowsDriver). Drawing in existing code as needed but starting from empty. Success would be a simple exe with just the raw events (mouse location, last click type, recent Ansi responses e.g. DAR and key) output direct to console - probably just with some basic out abstraction like there is here for in. Then will look to integrate the main Terminal.Gui architecture. Have added |
Beta Was this translation helpful? Give feedback.
-
Got output buffer and console out working enough to put something on the screen Next step is to process input buffer and get window size etc using System.Collections.Concurrent;
namespace Terminal.Gui;
public class MainLoop<T> : IMainLoop<T>
{
public ConcurrentQueue<T> InputBuffer { get; private set; } = new ();
public IOutputBuffer OutputBuffer { get; private set; } = new OutputBuffer();
public AnsiResponseParser<T> Parser
{
get;
private set;
}
public IConsoleOutput Out { get;private set; }
/// <inheritdoc />
public void Initialize (ConcurrentQueue<T> inputBuffer, AnsiResponseParser<T> parser, IConsoleOutput consoleOutput)
{
InputBuffer = inputBuffer;
Parser = parser;
Out = consoleOutput;
}
public void Run (CancellationToken token)
{
do
{
var dt = DateTime.Now;
Iteration ();
var took = DateTime.Now - dt;
var sleepFor = TimeSpan.FromMilliseconds (50) - took;
if (sleepFor.Milliseconds > 0)
{
Task.Delay (sleepFor, token).Wait (token);
}
}
while (!token.IsCancellationRequested);
}
/// <inheritdoc />
public void Iteration ()
{
Random r = new Random ();
OutputBuffer.SetWindowSize (20, 10);
OutputBuffer.CurrentAttribute = new Attribute (Color.White, Color.Black);
OutputBuffer.Move (r.Next(10), r.Next (10));
// Red
OutputBuffer.CurrentAttribute = new Attribute (new Color (255, 0, 0), Color.Black);
OutputBuffer.AddRune ('H');
// Orange
OutputBuffer.CurrentAttribute = new Attribute (new Color (255, 165, 0), Color.Black);
OutputBuffer.AddRune ('e');
// Yellow
OutputBuffer.CurrentAttribute = new Attribute (new Color (255, 255, 0), Color.Black);
OutputBuffer.AddRune ('l');
// Green
OutputBuffer.CurrentAttribute = new Attribute (new Color (0, 255, 0), Color.Black);
OutputBuffer.AddRune ('l');
// Blue
OutputBuffer.CurrentAttribute = new Attribute (new Color (100, 100, 255), Color.Black);
OutputBuffer.AddRune ('o');
// Indigo
OutputBuffer.CurrentAttribute = new Attribute (new Color (75, 0, 130), Color.Black);
OutputBuffer.AddRune (' ');
// Violet
OutputBuffer.CurrentAttribute = new Attribute (new Color (238, 130, 238), Color.Black);
OutputBuffer.AddRune ('W');
// Red
OutputBuffer.CurrentAttribute = new Attribute (new Color (255, 0, 0), Color.Black);
OutputBuffer.AddRune ('o');
// Orange
OutputBuffer.CurrentAttribute = new Attribute (new Color (255, 165, 0), Color.Black);
OutputBuffer.AddRune ('r');
// Yellow
OutputBuffer.CurrentAttribute = new Attribute (new Color (255, 255, 0), Color.Black);
OutputBuffer.AddRune ('l');
// Green
OutputBuffer.CurrentAttribute = new Attribute (new Color (0, 255, 0), Color.Black);
OutputBuffer.AddRune ('d');
// Blue
OutputBuffer.CurrentAttribute = new Attribute (new Color (100, 100, 255), Color.Black);
OutputBuffer.AddRune ('!');
Out.Write (OutputBuffer);
}
/// <inheritdoc />
public void Dispose ()
{ // TODO release managed resources here
}
} |
Beta Was this translation helpful? Give feedback.
-
I am focusing on 'NetDriver' porting for now. We have parsing and key detection sort of working: Here is the new class diagram We have a problem in the sheer number of key / input types we have:
I think best way to handle this is to make public static Key ToKey<T> (T result)
{
// TODO: Requires testing
return result switch
{
ConsoleKeyInfo keyInfo =>MapKey (keyInfo),
// TODO: how?
// WindowsConsole.InputRecord inputRecord => inputRecord.KeyEvent.UnicodeChar,
_ => throw new ArgumentException ($"Unsupported type {typeof (T).Name}")
};
}
public static char ToChar<T> (T result)
{
// TODO: Requires testing
return result switch
{
ConsoleKeyInfo keyInfo => keyInfo.KeyChar,
Key key => (char)key,
// TODO: probably not that simple
WindowsConsole.InputRecord inputRecord => inputRecord.KeyEvent.UnicodeChar,
_ => throw new ArgumentException ($"Unsupported type {typeof (T).Name}")
};
} |
Beta Was this translation helpful? Give feedback.
-
I've added mouse support to Code is massively simplified from public class MouseParser
{
// Regex patterns for button press/release, wheel scroll, and mouse position reporting
private readonly Regex _mouseEventPattern = new (@"\u001b\[<(\d+);(\d+);(\d+)(M|m)", RegexOptions.Compiled);
/// <summary>
/// Parses a mouse ansi escape sequence into a mouse event. Returns null if input
/// is not a mouse event or its syntax is not understood.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public MouseEventArgs? ProcessMouseInput (string input)
{
// Match mouse wheel events first
Match match = _mouseEventPattern.Match (input);
if (match.Success)
{
int buttonCode = int.Parse (match.Groups [1].Value);
int x = int.Parse (match.Groups [2].Value);
int y = int.Parse (match.Groups [3].Value);
char terminator = match.Groups [4].Value.Single ();
return new()
{
Position = new (x, y),
Flags = GetFlags (buttonCode, terminator)
};
}
// its some kind of odd mouse event that doesn't follow expected format?
return null;
}
private static MouseFlags GetFlags (int buttonCode, char terminator)
{
MouseFlags buttonState = 0;
switch (buttonCode)
{
case 0:
case 8:
case 16:
case 24:
case 32:
case 36:
case 40:
case 48:
case 56:
buttonState = terminator == 'M'
? MouseFlags.Button1Pressed
: MouseFlags.Button1Released;
[... the rest of this epic switch copied over ...] |
Beta Was this translation helpful? Give feedback.
-
Lets talk about how we might simplify
IMainLoop
andConsoleDriver
. I firmly believe that it really shouldn't be anywhere near as complicated as it is at the moment.Relevant issues:
ANSIDriver
- Pure "Console Virtual Terminal Sequences" driver #2940Goals:
ConcurrentQueue<T>
Drop Key Down / Key Up distinction - we only need KeyPressSuggested Design
I think we can restrict ourselves to only 2 threads
Console In
Console In is typically blocking, it needs to run in a Thread because you have to poll for keys. All drivers basically have a variation of the following:
Here are the relevant methods they use and the datatypes they work in. Each driver has its own datatype
int
orInputRecord
but fundamentally its always a sequence ofchar
. Generics should allow us to handle these disperate types consistently without having to duplicate code (see for exampleAnsiParser<T>
in #3791 ).ConsoleKeyInfo
Console.ReadKey
Console.KeyAvailable
int
int code = Curses.get_wch(out int wch)
Curses.timeout(100);
and checkcode
ifERR
(no input)InputRecord
ReadConsoleInput
PeekConsoleInput
(not currently used by TG)Main Loop
Similarly to how it is now the main loop should:
The difference is that we should not have any of those steps blocking. There should be no
Wakeup
method and noEventsPending
.When the Main Loop runs everyone looks at state and does stuff immediately then it reaches the end of its loop and does the only sleeping in the program.
We achieve this with 'state machines' - similar to how things work in video games. Maximum performance - minimum fuss. Also will become super testable as we have abstracted away the variances and split things up into state processing tasks.
Beta Was this translation helpful? Give feedback.
All reactions