|
@@ -1,4 +1,5 @@
|
|
|
-//
|
|
|
+//#define PROCESS_REQUEST
|
|
|
+//
|
|
|
// NetDriver.cs: The System.Console-based .NET driver, works on Windows and Unix, but is not particularly efficient.
|
|
|
//
|
|
|
// Authors:
|
|
@@ -107,24 +108,40 @@ namespace Terminal.Gui {
|
|
|
internal class NetEvents {
|
|
|
ManualResetEventSlim inputReady = new ManualResetEventSlim (false);
|
|
|
ManualResetEventSlim waitForStart = new ManualResetEventSlim (false);
|
|
|
+ ManualResetEventSlim winChange = new ManualResetEventSlim (false);
|
|
|
Queue<InputResult?> inputResultQueue = new Queue<InputResult?> ();
|
|
|
-
|
|
|
+ ConsoleDriver consoleDriver;
|
|
|
+ int lastWindowHeight;
|
|
|
+ int largestWindowHeight;
|
|
|
+#if PROCESS_REQUEST
|
|
|
+ bool neededProcessRequest;
|
|
|
+#endif
|
|
|
public int NumberOfCSI { get; }
|
|
|
|
|
|
- public NetEvents (int numberOfCSI = 1)
|
|
|
+ public NetEvents (ConsoleDriver consoleDriver, int numberOfCSI = 1)
|
|
|
{
|
|
|
+ if (consoleDriver == null) {
|
|
|
+ throw new ArgumentNullException ("Console driver instance must be provided.");
|
|
|
+ }
|
|
|
+ this.consoleDriver = consoleDriver;
|
|
|
NumberOfCSI = numberOfCSI;
|
|
|
Task.Run (ProcessInputResultQueue);
|
|
|
+ Task.Run (CheckWinChange);
|
|
|
}
|
|
|
|
|
|
public InputResult? ReadConsoleInput ()
|
|
|
{
|
|
|
while (true) {
|
|
|
waitForStart.Set ();
|
|
|
+ winChange.Set ();
|
|
|
+
|
|
|
if (inputResultQueue.Count == 0) {
|
|
|
inputReady.Wait ();
|
|
|
inputReady.Reset ();
|
|
|
}
|
|
|
+#if PROCESS_REQUEST
|
|
|
+ neededProcessRequest = false;
|
|
|
+#endif
|
|
|
if (inputResultQueue.Count > 0) {
|
|
|
return inputResultQueue.Dequeue ();
|
|
|
}
|
|
@@ -145,6 +162,67 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ void CheckWinChange ()
|
|
|
+ {
|
|
|
+ while (true) {
|
|
|
+ winChange.Wait ();
|
|
|
+ winChange.Reset ();
|
|
|
+ WaitWinChange ();
|
|
|
+ inputReady.Set ();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void WaitWinChange ()
|
|
|
+ {
|
|
|
+ while (true) {
|
|
|
+ if (!consoleDriver.HeightAsBuffer) {
|
|
|
+ if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) {
|
|
|
+ var w = Math.Max (Console.WindowWidth, 0);
|
|
|
+ var h = Math.Max (Console.WindowHeight, 0);
|
|
|
+ GetWindowSizeEvent (new Size (w, h));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight);
|
|
|
+ if (Console.BufferWidth != consoleDriver.Cols || largestWindowHeight != consoleDriver.Rows
|
|
|
+ || Console.WindowHeight != lastWindowHeight) {
|
|
|
+ lastWindowHeight = Console.WindowHeight;
|
|
|
+ GetWindowSizeEvent (new Size (Console.BufferWidth, lastWindowHeight));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (Console.WindowTop != consoleDriver.Top) {
|
|
|
+ // Top only working on Windows.
|
|
|
+ var winPositionEv = new WindowPositionEvent () {
|
|
|
+ Top = Console.WindowTop
|
|
|
+ };
|
|
|
+ inputResultQueue.Enqueue (new InputResult () {
|
|
|
+ EventType = EventType.WindowPosition,
|
|
|
+ WindowPositionEvent = winPositionEv
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+#if PROCESS_REQUEST
|
|
|
+ if (!neededProcessRequest) {
|
|
|
+ Console.Out.Write ("\x1b[6n");
|
|
|
+ neededProcessRequest = true;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void GetWindowSizeEvent (Size size)
|
|
|
+ {
|
|
|
+ WindowSizeEvent windowSizeEvent = new WindowSizeEvent () {
|
|
|
+ Size = size
|
|
|
+ };
|
|
|
+
|
|
|
+ inputResultQueue.Enqueue (new InputResult () {
|
|
|
+ EventType = EventType.WindowSize,
|
|
|
+ WindowSizeEvent = windowSizeEvent
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo)
|
|
|
{
|
|
|
InputResult inputResult = new InputResult {
|
|
@@ -174,6 +252,7 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
break;
|
|
|
case 27:
|
|
|
+ case 91:
|
|
|
ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { consoleKeyInfo };
|
|
|
ConsoleModifiers mod = consoleKeyInfo.Modifiers;
|
|
|
while (Console.KeyAvailable) {
|
|
@@ -207,30 +286,71 @@ namespace Terminal.Gui {
|
|
|
ConsoleKeyInfo [] splitedCki = new ConsoleKeyInfo [] { };
|
|
|
int length = 0;
|
|
|
var kChar = GetKeyCharArray (cki);
|
|
|
- var nCSI = kChar.Where (val => val == '\x1b').ToArray ().Length;
|
|
|
+ var nCSI = GetNumberOfCSI (kChar);
|
|
|
int curCSI = 0;
|
|
|
+ char previousKChar = '\0';
|
|
|
if (nCSI > 1) {
|
|
|
- foreach (var ck in cki) {
|
|
|
+ for (int i = 0; i < cki.Length; i++) {
|
|
|
+ var ck = cki [i];
|
|
|
if (NumberOfCSI > 0 && nCSI - curCSI > NumberOfCSI) {
|
|
|
- if (ck.KeyChar == '\x1b') {
|
|
|
+ if (cki [i + 1].KeyChar == '\x1b' && previousKChar != '\0') {
|
|
|
curCSI++;
|
|
|
+ previousKChar = '\0';
|
|
|
+ } else {
|
|
|
+ previousKChar = ck.KeyChar;
|
|
|
}
|
|
|
continue;
|
|
|
}
|
|
|
if (ck.KeyChar == '\x1b') {
|
|
|
- if (splitedCki.Length > 0) {
|
|
|
+ if (ck.KeyChar == 'R') {
|
|
|
+ ResizeArray (ck);
|
|
|
+ }
|
|
|
+ if (splitedCki.Length > 1) {
|
|
|
DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, splitedCki, ref mod);
|
|
|
}
|
|
|
splitedCki = new ConsoleKeyInfo [] { };
|
|
|
length = 0;
|
|
|
}
|
|
|
- length++;
|
|
|
- Array.Resize (ref splitedCki, length);
|
|
|
- splitedCki [length - 1] = ck;
|
|
|
+ ResizeArray (ck);
|
|
|
+ if (i == cki.Length - 1 && splitedCki.Length > 0) {
|
|
|
+ DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, splitedCki, ref mod);
|
|
|
+ }
|
|
|
}
|
|
|
} else {
|
|
|
DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, cki, ref mod);
|
|
|
}
|
|
|
+
|
|
|
+ void ResizeArray (ConsoleKeyInfo ck)
|
|
|
+ {
|
|
|
+ length++;
|
|
|
+ Array.Resize (ref splitedCki, length);
|
|
|
+ splitedCki [length - 1] = ck;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ char [] GetKeyCharArray (ConsoleKeyInfo [] cki)
|
|
|
+ {
|
|
|
+ char [] kChar = new char [] { };
|
|
|
+ var length = 0;
|
|
|
+ foreach (var kc in cki) {
|
|
|
+ length++;
|
|
|
+ Array.Resize (ref kChar, length);
|
|
|
+ kChar [length - 1] = kc.KeyChar;
|
|
|
+ }
|
|
|
+
|
|
|
+ return kChar;
|
|
|
+ }
|
|
|
+
|
|
|
+ int GetNumberOfCSI (char [] csi)
|
|
|
+ {
|
|
|
+ int nCSI = 0;
|
|
|
+ for (int i = 0; i < csi.Length; i++) {
|
|
|
+ if (csi [i] == '\x1b' || (csi [i] == '[' && (i == 0 || (i > 0 && csi [i - 1] != '\x1b')))) {
|
|
|
+ nCSI++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nCSI;
|
|
|
}
|
|
|
|
|
|
void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ref MouseEvent mouseEvent, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod)
|
|
@@ -306,7 +426,8 @@ namespace Terminal.Gui {
|
|
|
(mod & ConsoleModifiers.Control) != 0);
|
|
|
break;
|
|
|
case 7:
|
|
|
- throw new NotImplementedException ("Condition not yet detected!");
|
|
|
+ GetRequestEvent (GetKeyCharArray (cki));
|
|
|
+ return;
|
|
|
case int n when n >= 8:
|
|
|
GetMouseEvent (cki);
|
|
|
return;
|
|
@@ -320,12 +441,63 @@ namespace Terminal.Gui {
|
|
|
inputResultQueue.Enqueue (inputResult);
|
|
|
}
|
|
|
|
|
|
+ Point lastCursorPosition;
|
|
|
+
|
|
|
+ void GetRequestEvent (char [] kChar)
|
|
|
+ {
|
|
|
+ EventType eventType = new EventType ();
|
|
|
+ Point point = new Point ();
|
|
|
+ int foundPoint = 0;
|
|
|
+ string value = "";
|
|
|
+ for (int i = 0; i < kChar.Length; i++) {
|
|
|
+ var c = kChar [i];
|
|
|
+ if (c == '[') {
|
|
|
+ foundPoint++;
|
|
|
+ } else if (foundPoint == 1 && c != ';') {
|
|
|
+ value += c.ToString ();
|
|
|
+ } else if (c == ';') {
|
|
|
+ if (foundPoint == 1) {
|
|
|
+ point.Y = int.Parse (value) - 1;
|
|
|
+ }
|
|
|
+ value = "";
|
|
|
+ foundPoint++;
|
|
|
+ } else if (foundPoint > 0 && i < kChar.Length - 1) {
|
|
|
+ value += c.ToString ();
|
|
|
+ } else if (i == kChar.Length - 1) {
|
|
|
+ point.X = int.Parse (value) + Console.WindowTop - 1;
|
|
|
+
|
|
|
+ switch (c) {
|
|
|
+ case 'R':
|
|
|
+ if (lastCursorPosition.Y != point.Y) {
|
|
|
+ lastCursorPosition = point;
|
|
|
+ eventType = EventType.WindowPosition;
|
|
|
+ var winPositionEv = new WindowPositionEvent () {
|
|
|
+ CursorPosition = point
|
|
|
+ };
|
|
|
+ inputResultQueue.Enqueue (new InputResult () {
|
|
|
+ EventType = eventType,
|
|
|
+ WindowPositionEvent = winPositionEv
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new NotImplementedException ();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ inputReady.Set ();
|
|
|
+ }
|
|
|
+
|
|
|
MouseEvent lastMouseEvent;
|
|
|
bool isButtonPressed;
|
|
|
bool isButtonClicked;
|
|
|
bool isButtonDoubleClicked;
|
|
|
bool isButtonTripleClicked;
|
|
|
bool isProcContBtnPressedRuning;
|
|
|
+ bool isButtonReleased;
|
|
|
|
|
|
void GetMouseEvent (ConsoleKeyInfo [] cki)
|
|
|
{
|
|
@@ -358,36 +530,64 @@ namespace Terminal.Gui {
|
|
|
} else if (c == 'm' || c == 'M') {
|
|
|
point.Y = int.Parse (value) + Console.WindowTop - 1;
|
|
|
|
|
|
- if (c == 'M') {
|
|
|
- isButtonPressed = true;
|
|
|
- } else if (c == 'm') {
|
|
|
- isButtonPressed = false;
|
|
|
- }
|
|
|
+ //if (c == 'M') {
|
|
|
+ // isButtonPressed = true;
|
|
|
+ //} else if (c == 'm') {
|
|
|
+ // isButtonPressed = false;
|
|
|
+ //}
|
|
|
|
|
|
switch (buttonCode) {
|
|
|
case 0:
|
|
|
case 8:
|
|
|
case 16:
|
|
|
+ case 24:
|
|
|
case 32:
|
|
|
+ case 36:
|
|
|
+ case 40:
|
|
|
+ case 48:
|
|
|
+ case 56:
|
|
|
buttonState = c == 'M' ? MouseButtonState.Button1Pressed
|
|
|
: MouseButtonState.Button1Released;
|
|
|
break;
|
|
|
case 1:
|
|
|
case 9:
|
|
|
case 17:
|
|
|
+ case 25:
|
|
|
case 33:
|
|
|
+ case 37:
|
|
|
+ case 41:
|
|
|
+ case 45:
|
|
|
+ case 49:
|
|
|
+ case 53:
|
|
|
+ case 57:
|
|
|
+ case 61:
|
|
|
buttonState = c == 'M' ? MouseButtonState.Button2Pressed
|
|
|
: MouseButtonState.Button2Released;
|
|
|
break;
|
|
|
case 2:
|
|
|
case 10:
|
|
|
+ case 14:
|
|
|
case 18:
|
|
|
+ case 22:
|
|
|
+ case 26:
|
|
|
+ case 30:
|
|
|
case 34:
|
|
|
+ case 42:
|
|
|
+ case 46:
|
|
|
+ case 50:
|
|
|
+ case 54:
|
|
|
+ case 58:
|
|
|
+ case 62:
|
|
|
buttonState = c == 'M' ? MouseButtonState.Button3Pressed
|
|
|
: MouseButtonState.Button3Released;
|
|
|
break;
|
|
|
case 35:
|
|
|
+ case 39:
|
|
|
case 43:
|
|
|
+ case 47:
|
|
|
+ case 55:
|
|
|
+ case 59:
|
|
|
+ case 63:
|
|
|
buttonState = MouseButtonState.ReportMousePosition;
|
|
|
break;
|
|
|
case 64:
|
|
@@ -396,11 +596,15 @@ namespace Terminal.Gui {
|
|
|
case 65:
|
|
|
buttonState = MouseButtonState.ButtonWheeledDown;
|
|
|
break;
|
|
|
+ case 68:
|
|
|
case 72:
|
|
|
- buttonState = MouseButtonState.ButtonWheeledLeft; // Ctrl+ButtonWheeledUp
|
|
|
+ case 80:
|
|
|
+ buttonState = MouseButtonState.ButtonWheeledLeft; // Shift/Ctrl+ButtonWheeledUp
|
|
|
break;
|
|
|
+ case 69:
|
|
|
case 73:
|
|
|
- buttonState = MouseButtonState.ButtonWheeledRight; // Ctrl+ButtonWheeledDown
|
|
|
+ case 81:
|
|
|
+ buttonState = MouseButtonState.ButtonWheeledRight; // Shift/Ctrl+ButtonWheeledDown
|
|
|
break;
|
|
|
}
|
|
|
// Modifiers.
|
|
@@ -408,17 +612,74 @@ namespace Terminal.Gui {
|
|
|
case 8:
|
|
|
case 9:
|
|
|
case 10:
|
|
|
+ case 43:
|
|
|
+ buttonState |= MouseButtonState.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 14:
|
|
|
+ case 47:
|
|
|
+ buttonState |= MouseButtonState.ButtonAlt | MouseButtonState.ButtonShift;
|
|
|
+ break;
|
|
|
case 16:
|
|
|
case 17:
|
|
|
case 18:
|
|
|
- case 43:
|
|
|
+ case 51:
|
|
|
buttonState |= MouseButtonState.ButtonCtrl;
|
|
|
break;
|
|
|
+ case 22:
|
|
|
+ case 55:
|
|
|
+ buttonState |= MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 24:
|
|
|
+ case 25:
|
|
|
+ case 26:
|
|
|
+ case 59:
|
|
|
+ buttonState |= MouseButtonState.ButtonAlt | MouseButtonState.ButtonCtrl;
|
|
|
+ break;
|
|
|
+ case 30:
|
|
|
+ case 63:
|
|
|
+ buttonState |= MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift | MouseButtonState.ButtonAlt;
|
|
|
+ break;
|
|
|
case 32:
|
|
|
case 33:
|
|
|
case 34:
|
|
|
buttonState |= MouseButtonState.ReportMousePosition;
|
|
|
break;
|
|
|
+ case 36:
|
|
|
+ case 37:
|
|
|
+ buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 39:
|
|
|
+ case 68:
|
|
|
+ case 69:
|
|
|
+ buttonState |= MouseButtonState.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 40:
|
|
|
+ case 41:
|
|
|
+ case 42:
|
|
|
+ buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 45:
|
|
|
+ case 46:
|
|
|
+ buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonAlt | MouseButtonState.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 48:
|
|
|
+ case 49:
|
|
|
+ case 50:
|
|
|
+ buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl;
|
|
|
+ break;
|
|
|
+ case 53:
|
|
|
+ case 54:
|
|
|
+ buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift;
|
|
|
+ break;
|
|
|
+ case 56:
|
|
|
+ case 57:
|
|
|
+ case 58:
|
|
|
+ buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonAlt;
|
|
|
+ break;
|
|
|
+ case 61:
|
|
|
+ case 62:
|
|
|
+ buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift | MouseButtonState.ButtonAlt;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -426,6 +687,14 @@ namespace Terminal.Gui {
|
|
|
mouseEvent.Position.Y = point.Y;
|
|
|
mouseEvent.ButtonState = buttonState;
|
|
|
|
|
|
+ if ((buttonState & MouseButtonState.Button1Pressed) != 0
|
|
|
+ || (buttonState & MouseButtonState.Button2Pressed) != 0
|
|
|
+ || (buttonState & MouseButtonState.Button3Pressed) != 0) {
|
|
|
+ isButtonPressed = true;
|
|
|
+ } else {
|
|
|
+ isButtonPressed = false;
|
|
|
+ }
|
|
|
+
|
|
|
if ((isButtonClicked || isButtonDoubleClicked || isButtonTripleClicked)
|
|
|
&& ((buttonState & MouseButtonState.Button1Released) != 0
|
|
|
|| (buttonState & MouseButtonState.Button2Released) != 0
|
|
@@ -460,6 +729,16 @@ namespace Terminal.Gui {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (!isButtonPressed && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked
|
|
|
+ && !isButtonReleased
|
|
|
+ && ((buttonState & MouseButtonState.Button1Released) == 0
|
|
|
+ && (buttonState & MouseButtonState.Button2Released) == 0
|
|
|
+ && (buttonState & MouseButtonState.Button3Released) == 0)) {
|
|
|
+ ProcessButtonReleased (lastMouseEvent);
|
|
|
+ inputReady.Set ();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
inputResultQueue.Enqueue (new InputResult () {
|
|
|
EventType = EventType.Mouse,
|
|
|
MouseEvent = mouseEvent
|
|
@@ -484,6 +763,7 @@ namespace Terminal.Gui {
|
|
|
|
|
|
lastMouseEvent = mouseEvent;
|
|
|
if (isButtonPressed && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked && !isProcContBtnPressedRuning) {
|
|
|
+ isButtonReleased = false;
|
|
|
Application.MainLoop.AddIdle (() => {
|
|
|
ProcessContinuousButtonPressedAsync ().ConfigureAwait (false);
|
|
|
return false;
|
|
@@ -499,22 +779,18 @@ namespace Terminal.Gui {
|
|
|
Position = mouseEvent.Position,
|
|
|
ButtonState = mouseEvent.ButtonState
|
|
|
};
|
|
|
- switch (mouseEvent.ButtonState) {
|
|
|
- case MouseButtonState.Button1Released:
|
|
|
+ if ((mouseEvent.ButtonState & MouseButtonState.Button1Released) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button1Released;
|
|
|
me.ButtonState |= MouseButtonState.Button1Clicked;
|
|
|
- break;
|
|
|
- case MouseButtonState.Button2Released:
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Released) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button2Released;
|
|
|
me.ButtonState |= MouseButtonState.Button2Clicked;
|
|
|
- break;
|
|
|
- case MouseButtonState.Button3Released:
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Released) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button3Released;
|
|
|
me.ButtonState |= MouseButtonState.Button3Clicked;
|
|
|
- break;
|
|
|
- default:
|
|
|
- return;
|
|
|
}
|
|
|
+ isButtonReleased = true;
|
|
|
+
|
|
|
inputResultQueue.Enqueue (new InputResult () {
|
|
|
EventType = EventType.Mouse,
|
|
|
MouseEvent = me
|
|
@@ -527,22 +803,18 @@ namespace Terminal.Gui {
|
|
|
Position = mouseEvent.Position,
|
|
|
ButtonState = mouseEvent.ButtonState
|
|
|
};
|
|
|
- switch (mouseEvent.ButtonState) {
|
|
|
- case MouseButtonState.Button1Pressed:
|
|
|
+ if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button1Pressed;
|
|
|
me.ButtonState |= MouseButtonState.Button1DoubleClicked;
|
|
|
- break;
|
|
|
- case MouseButtonState.Button2Pressed:
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button2Pressed;
|
|
|
me.ButtonState |= MouseButtonState.Button2DoubleClicked;
|
|
|
- break;
|
|
|
- case MouseButtonState.Button3Pressed:
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button3Pressed;
|
|
|
me.ButtonState |= MouseButtonState.Button3DoubleClicked;
|
|
|
- break;
|
|
|
- default:
|
|
|
- return;
|
|
|
}
|
|
|
+ isButtonReleased = true;
|
|
|
+
|
|
|
inputResultQueue.Enqueue (new InputResult () {
|
|
|
EventType = EventType.Mouse,
|
|
|
MouseEvent = me
|
|
@@ -555,22 +827,18 @@ namespace Terminal.Gui {
|
|
|
Position = mouseEvent.Position,
|
|
|
ButtonState = mouseEvent.ButtonState
|
|
|
};
|
|
|
- switch (mouseEvent.ButtonState) {
|
|
|
- case MouseButtonState.Button1Pressed:
|
|
|
+ if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button1Pressed;
|
|
|
me.ButtonState |= MouseButtonState.Button1TripleClicked;
|
|
|
- break;
|
|
|
- case MouseButtonState.Button2Pressed:
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button2Pressed;
|
|
|
me.ButtonState |= MouseButtonState.Button2TrippleClicked;
|
|
|
- break;
|
|
|
- case MouseButtonState.Button3Pressed:
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) {
|
|
|
me.ButtonState &= ~MouseButtonState.Button3Pressed;
|
|
|
me.ButtonState |= MouseButtonState.Button3TripleClicked;
|
|
|
- break;
|
|
|
- default:
|
|
|
- return;
|
|
|
}
|
|
|
+ isButtonReleased = true;
|
|
|
+
|
|
|
inputResultQueue.Enqueue (new InputResult () {
|
|
|
EventType = EventType.Mouse,
|
|
|
MouseEvent = me
|
|
@@ -585,7 +853,7 @@ namespace Terminal.Gui {
|
|
|
await Task.Delay (200);
|
|
|
var view = Application.wantContinuousButtonPressedView;
|
|
|
if (isButtonPressed && !Console.KeyAvailable
|
|
|
- && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked
|
|
|
+ && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked
|
|
|
&& (view != null || view == null && lastMouseEvent.Position != point)) {
|
|
|
point = lastMouseEvent.Position;
|
|
|
inputResultQueue.Enqueue (new InputResult () {
|
|
@@ -598,20 +866,32 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
}
|
|
|
isProcContBtnPressedRuning = false;
|
|
|
- isButtonPressed = false;
|
|
|
+ //isButtonPressed = false;
|
|
|
}
|
|
|
|
|
|
- char [] GetKeyCharArray (ConsoleKeyInfo [] cki)
|
|
|
+ void ProcessButtonReleased (MouseEvent mouseEvent)
|
|
|
{
|
|
|
- char [] kChar = new char [] { };
|
|
|
- var length = 0;
|
|
|
- foreach (var kc in cki) {
|
|
|
- length++;
|
|
|
- Array.Resize (ref kChar, length);
|
|
|
- kChar [length - 1] = kc.KeyChar;
|
|
|
- }
|
|
|
+ var me = new MouseEvent () {
|
|
|
+ Position = mouseEvent.Position,
|
|
|
+ ButtonState = mouseEvent.ButtonState
|
|
|
+ };
|
|
|
+ if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) {
|
|
|
+ me.ButtonState &= ~(MouseButtonState.Button1Pressed | MouseButtonState.ReportMousePosition);
|
|
|
+ me.ButtonState |= MouseButtonState.Button1Released;
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) {
|
|
|
+ me.ButtonState &= ~(MouseButtonState.Button2Pressed | MouseButtonState.ReportMousePosition);
|
|
|
+ me.ButtonState |= MouseButtonState.Button2Released;
|
|
|
+ } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) {
|
|
|
+ me.ButtonState &= ~(MouseButtonState.Button3Pressed | MouseButtonState.ReportMousePosition);
|
|
|
+ me.ButtonState |= MouseButtonState.Button3Released;
|
|
|
+ }
|
|
|
+ isButtonReleased = true;
|
|
|
+ lastMouseEvent = me;
|
|
|
|
|
|
- return kChar;
|
|
|
+ inputResultQueue.Enqueue (new InputResult () {
|
|
|
+ EventType = EventType.Mouse,
|
|
|
+ MouseEvent = me
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
ConsoleModifiers GetConsoleModifiers (uint keyChar)
|
|
@@ -719,7 +999,9 @@ namespace Terminal.Gui {
|
|
|
|
|
|
public enum EventType {
|
|
|
key = 1,
|
|
|
- Mouse = 2
|
|
|
+ Mouse = 2,
|
|
|
+ WindowSize = 3,
|
|
|
+ WindowPosition = 4
|
|
|
}
|
|
|
|
|
|
[Flags]
|
|
@@ -760,10 +1042,21 @@ namespace Terminal.Gui {
|
|
|
public MouseButtonState ButtonState;
|
|
|
}
|
|
|
|
|
|
+ public struct WindowSizeEvent {
|
|
|
+ public Size Size;
|
|
|
+ }
|
|
|
+
|
|
|
+ public struct WindowPositionEvent {
|
|
|
+ public int Top;
|
|
|
+ public Point CursorPosition;
|
|
|
+ }
|
|
|
+
|
|
|
public struct InputResult {
|
|
|
public EventType EventType;
|
|
|
public ConsoleKeyInfo ConsoleKeyInfo;
|
|
|
public MouseEvent MouseEvent;
|
|
|
+ public WindowSizeEvent WindowSizeEvent;
|
|
|
+ public WindowPositionEvent WindowPositionEvent;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -775,16 +1068,19 @@ namespace Terminal.Gui {
|
|
|
public override bool HeightAsBuffer { get; set; }
|
|
|
|
|
|
public NetWinVTConsole NetWinConsole { get; }
|
|
|
+ public bool IsWinPlatform { get; }
|
|
|
+ public bool AlwaysSetPosition { get; set; }
|
|
|
|
|
|
- bool isWinPlatform;
|
|
|
+ int largestWindowHeight;
|
|
|
|
|
|
public NetDriver ()
|
|
|
{
|
|
|
var p = Environment.OSVersion.Platform;
|
|
|
if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
|
|
|
- isWinPlatform = true;
|
|
|
+ IsWinPlatform = true;
|
|
|
NetWinConsole = new NetWinVTConsole ();
|
|
|
}
|
|
|
+ largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight);
|
|
|
}
|
|
|
|
|
|
// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
|
|
@@ -848,7 +1144,7 @@ namespace Terminal.Gui {
|
|
|
|
|
|
public override void End ()
|
|
|
{
|
|
|
- if (isWinPlatform) {
|
|
|
+ if (IsWinPlatform) {
|
|
|
NetWinConsole.Cleanup ();
|
|
|
}
|
|
|
|
|
@@ -861,6 +1157,8 @@ namespace Terminal.Gui {
|
|
|
{
|
|
|
if (Rows > 0) {
|
|
|
Console.Clear ();
|
|
|
+ Console.Out.Write ("\x1b[3J");
|
|
|
+ //Console.Out.Write ("\x1b[?25l");
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -934,7 +1232,7 @@ namespace Terminal.Gui {
|
|
|
// Can raise an exception while is still resizing.
|
|
|
try {
|
|
|
// Not supported on Unix.
|
|
|
- if (isWinPlatform) {
|
|
|
+ if (IsWinPlatform) {
|
|
|
#pragma warning disable CA1416
|
|
|
Console.CursorTop = 0;
|
|
|
Console.CursorLeft = 0;
|
|
@@ -954,7 +1252,7 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- if (isWinPlatform && Console.WindowHeight > 0) {
|
|
|
+ if (IsWinPlatform && Console.WindowHeight > 0) {
|
|
|
// Can raise an exception while is still resizing.
|
|
|
try {
|
|
|
#pragma warning disable CA1416
|
|
@@ -1012,11 +1310,16 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public override void Refresh ()
|
|
|
+ {
|
|
|
+ UpdateScreen ();
|
|
|
+ }
|
|
|
+
|
|
|
public override void UpdateScreen ()
|
|
|
{
|
|
|
if (winChanging || Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3
|
|
|
|| (!HeightAsBuffer && Rows != Console.WindowHeight)
|
|
|
- || (HeightAsBuffer && Rows != Console.BufferHeight)) {
|
|
|
+ || (HeightAsBuffer && Rows != largestWindowHeight)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -1024,6 +1327,7 @@ namespace Terminal.Gui {
|
|
|
int rows = Math.Min (Console.WindowHeight + top, Rows);
|
|
|
int cols = Cols;
|
|
|
|
|
|
+ Console.CursorVisible = false;
|
|
|
for (int row = top; row < rows; row++) {
|
|
|
if (!dirtyLine [row]) {
|
|
|
continue;
|
|
@@ -1033,31 +1337,35 @@ namespace Terminal.Gui {
|
|
|
if (contents [row, col, 2] != 1) {
|
|
|
continue;
|
|
|
}
|
|
|
- if (Console.WindowHeight > 0) {
|
|
|
- // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
|
|
|
- try {
|
|
|
- Console.SetCursorPosition (col, row);
|
|
|
- } catch (Exception) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (Console.WindowHeight > 0 && !SetCursorPosition (col, row)) {
|
|
|
+ return;
|
|
|
}
|
|
|
for (; col < cols && contents [row, col, 2] == 1; col++) {
|
|
|
var color = contents [row, col, 1];
|
|
|
if (color != redrawColor) {
|
|
|
SetColor (color);
|
|
|
}
|
|
|
+ if (AlwaysSetPosition && !SetCursorPosition (col, row)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
Console.Write ((char)contents [row, col, 0]);
|
|
|
contents [row, col, 2] = 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+ Console.CursorVisible = true;
|
|
|
UpdateCursor ();
|
|
|
}
|
|
|
|
|
|
- public override void Refresh ()
|
|
|
+ bool SetCursorPosition (int col, int row)
|
|
|
{
|
|
|
- UpdateScreen ();
|
|
|
+ // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
|
|
|
+ try {
|
|
|
+ Console.SetCursorPosition (col, row);
|
|
|
+ return true;
|
|
|
+ } catch (Exception) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public override void UpdateCursor ()
|
|
@@ -1230,15 +1538,42 @@ namespace Terminal.Gui {
|
|
|
|
|
|
var mLoop = mainLoop.Driver as NetMainLoop;
|
|
|
|
|
|
- // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
|
|
|
- mLoop.KeyPressed = (e) => ProcessInput (e);
|
|
|
+ // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called.
|
|
|
+ mLoop.ProcessInput = (e) => ProcessInput (e);
|
|
|
+ }
|
|
|
|
|
|
- mLoop.WinChanged = (e) => ChangeWin (e);
|
|
|
+ void ProcessInput (NetEvents.InputResult inputEvent)
|
|
|
+ {
|
|
|
+ switch (inputEvent.EventType) {
|
|
|
+ case NetEvents.EventType.key:
|
|
|
+ var map = MapKey (inputEvent.ConsoleKeyInfo);
|
|
|
+ if (map == (Key)0xffffffff) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ keyDownHandler (new KeyEvent (map, keyModifiers));
|
|
|
+ keyHandler (new KeyEvent (map, keyModifiers));
|
|
|
+ keyUpHandler (new KeyEvent (map, keyModifiers));
|
|
|
+ keyModifiers = new KeyModifiers ();
|
|
|
+ break;
|
|
|
+ case NetEvents.EventType.Mouse:
|
|
|
+ mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
|
|
|
+ break;
|
|
|
+ case NetEvents.EventType.WindowSize:
|
|
|
+ ChangeWin ();
|
|
|
+ break;
|
|
|
+ case NetEvents.EventType.WindowPosition:
|
|
|
+ var newTop = inputEvent.WindowPositionEvent.Top;
|
|
|
+ if (HeightAsBuffer && top != newTop) {
|
|
|
+ top = newTop;
|
|
|
+ Refresh ();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
bool winChanging;
|
|
|
|
|
|
- void ChangeWin (int newTop)
|
|
|
+ void ChangeWin ()
|
|
|
{
|
|
|
winChanging = true;
|
|
|
const int Min_WindowWidth = 14;
|
|
@@ -1248,8 +1583,8 @@ namespace Terminal.Gui {
|
|
|
Console.WindowHeight);
|
|
|
top = 0;
|
|
|
} else {
|
|
|
- size = new Size (Console.BufferWidth, Console.BufferHeight);
|
|
|
- top = newTop;
|
|
|
+ largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight);
|
|
|
+ size = new Size (Console.BufferWidth, largestWindowHeight);
|
|
|
}
|
|
|
cols = size.Width;
|
|
|
rows = size.Height;
|
|
@@ -1260,25 +1595,6 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void ProcessInput (NetEvents.InputResult inputEvent)
|
|
|
- {
|
|
|
- switch (inputEvent.EventType) {
|
|
|
- case NetEvents.EventType.key:
|
|
|
- var map = MapKey (inputEvent.ConsoleKeyInfo);
|
|
|
- if (map == (Key)0xffffffff) {
|
|
|
- return;
|
|
|
- }
|
|
|
- keyDownHandler (new KeyEvent (map, keyModifiers));
|
|
|
- keyHandler (new KeyEvent (map, keyModifiers));
|
|
|
- keyUpHandler (new KeyEvent (map, keyModifiers));
|
|
|
- keyModifiers = new KeyModifiers ();
|
|
|
- break;
|
|
|
- case NetEvents.EventType.Mouse:
|
|
|
- mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
|
|
|
{
|
|
|
MouseFlags mouseFlag = 0;
|
|
@@ -1416,24 +1732,15 @@ namespace Terminal.Gui {
|
|
|
internal class NetMainLoop : IMainLoopDriver {
|
|
|
ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
|
|
|
ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
|
|
|
- ManualResetEventSlim winChange = new ManualResetEventSlim (false);
|
|
|
Queue<NetEvents.InputResult?> inputResult = new Queue<NetEvents.InputResult?> ();
|
|
|
MainLoop mainLoop;
|
|
|
- ConsoleDriver consoleDriver;
|
|
|
- bool winChanged;
|
|
|
- int newTop;
|
|
|
CancellationTokenSource tokenSource = new CancellationTokenSource ();
|
|
|
NetEvents netEvents;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Invoked when a Key is pressed.
|
|
|
/// </summary>
|
|
|
- public Action<NetEvents.InputResult> KeyPressed;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Invoked when the window is changed.
|
|
|
- /// </summary>
|
|
|
- public Action<int> WinChanged;
|
|
|
+ public Action<NetEvents.InputResult> ProcessInput;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Initializes the class with the console driver.
|
|
@@ -1447,11 +1754,10 @@ namespace Terminal.Gui {
|
|
|
if (consoleDriver == null) {
|
|
|
throw new ArgumentNullException ("Console driver instance must be provided.");
|
|
|
}
|
|
|
- this.consoleDriver = consoleDriver;
|
|
|
- netEvents = new NetEvents ();
|
|
|
+ netEvents = new NetEvents (consoleDriver);
|
|
|
}
|
|
|
|
|
|
- void KeyReader ()
|
|
|
+ void NetInputHandler ()
|
|
|
{
|
|
|
while (true) {
|
|
|
waitForProbe.Wait ();
|
|
@@ -1470,43 +1776,10 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void CheckWinChange ()
|
|
|
- {
|
|
|
- while (true) {
|
|
|
- winChange.Wait ();
|
|
|
- winChange.Reset ();
|
|
|
- WaitWinChange ();
|
|
|
- winChanged = true;
|
|
|
- keyReady.Set ();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- int lastWindowHeight;
|
|
|
- void WaitWinChange ()
|
|
|
- {
|
|
|
- while (true) {
|
|
|
- if (!consoleDriver.HeightAsBuffer) {
|
|
|
- if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) {
|
|
|
- return;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows
|
|
|
- || Console.WindowTop != consoleDriver.Top
|
|
|
- || Console.WindowHeight != lastWindowHeight) {
|
|
|
- // Top only working on Windows.
|
|
|
- newTop = Console.WindowTop;
|
|
|
- lastWindowHeight = Console.WindowHeight;
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
void IMainLoopDriver.Setup (MainLoop mainLoop)
|
|
|
{
|
|
|
this.mainLoop = mainLoop;
|
|
|
- Task.Run (KeyReader);
|
|
|
- Task.Run (CheckWinChange);
|
|
|
+ Task.Run (NetInputHandler);
|
|
|
}
|
|
|
|
|
|
void IMainLoopDriver.Wakeup ()
|
|
@@ -1517,7 +1790,6 @@ namespace Terminal.Gui {
|
|
|
bool IMainLoopDriver.EventsPending (bool wait)
|
|
|
{
|
|
|
waitForProbe.Set ();
|
|
|
- winChange.Set ();
|
|
|
|
|
|
if (CheckTimers (wait, out var waitTimeout)) {
|
|
|
return true;
|
|
@@ -1534,7 +1806,7 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
|
|
|
if (!tokenSource.IsCancellationRequested) {
|
|
|
- return inputResult.Count > 0 || CheckTimers (wait, out _) || winChanged;
|
|
|
+ return inputResult.Count > 0 || CheckTimers (wait, out _);
|
|
|
}
|
|
|
|
|
|
tokenSource.Dispose ();
|
|
@@ -1568,11 +1840,7 @@ namespace Terminal.Gui {
|
|
|
void IMainLoopDriver.MainIteration ()
|
|
|
{
|
|
|
if (inputResult.Count > 0) {
|
|
|
- KeyPressed?.Invoke (inputResult.Dequeue ().Value);
|
|
|
- }
|
|
|
- if (winChanged) {
|
|
|
- winChanged = false;
|
|
|
- WinChanged?.Invoke (newTop);
|
|
|
+ ProcessInput?.Invoke (inputResult.Dequeue ().Value);
|
|
|
}
|
|
|
}
|
|
|
}
|