From eb37e776e86e9afde4bae40f2bc97bfff4c27ba8 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 17 May 2020 18:15:43 +0100 Subject: [PATCH] Added mouse features in the Unix version. Supports xterm-1006. --- Terminal.Gui/Drivers/CursesDriver.cs | 203 +++++++++++++++++++++++++-- Terminal.Gui/MonoCurses/constants.cs | 59 ++++---- 2 files changed, 226 insertions(+), 36 deletions(-) diff --git a/Terminal.Gui/Drivers/CursesDriver.cs b/Terminal.Gui/Drivers/CursesDriver.cs index c4ac1223d..fa51c4990 100644 --- a/Terminal.Gui/Drivers/CursesDriver.cs +++ b/Terminal.Gui/Drivers/CursesDriver.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Mono.Terminal; using NStack; using Unix.Terminal; @@ -182,12 +183,185 @@ namespace Terminal.Gui { } } - static MouseEvent ToDriverMouse (Curses.MouseEvent cev) + Curses.Event? LastMouseButtonPressed = null; + bool IsButtonPressed = false; + bool cancelButtonClicked = false; + Point point; + + MouseEvent ToDriverMouse (Curses.MouseEvent cev) + { + MouseFlags mouseFlag = MouseFlags.AllEvents; + + if (LastMouseButtonPressed != null && cev.ButtonState != Curses.Event.ReportMousePosition) { + LastMouseButtonPressed = null; + IsButtonPressed = false; + } + + + if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked || + cev.ButtonState == Curses.Event.Button3Clicked) && + LastMouseButtonPressed == null) { + + IsButtonPressed = false; + mouseFlag = ProcessButtonClickedEvent (cev, mouseFlag); + + } else if (((cev.ButtonState == Curses.Event.Button1Pressed || cev.ButtonState == Curses.Event.Button2Pressed || + cev.ButtonState == Curses.Event.Button3Pressed) && LastMouseButtonPressed == null) || + IsButtonPressed && cev.ButtonState == Curses.Event.ReportMousePosition) { + + mouseFlag = (MouseFlags)cev.ButtonState; + if (cev.ButtonState != Curses.Event.ReportMousePosition) + LastMouseButtonPressed = cev.ButtonState; + IsButtonPressed = true; + + if (cev.ButtonState == Curses.Event.ReportMousePosition) { + mouseFlag = (MouseFlags)LastMouseButtonPressed | MouseFlags.ReportMousePosition; + point = new Point (); + cancelButtonClicked = true; + } else { + point = new Point () { + X = cev.X, + Y = cev.Y + }; + } + + if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Task.Run (async () => { + while (IsButtonPressed && LastMouseButtonPressed != null) { + await Task.Delay (200); + var me = new MouseEvent () { + X = cev.X, + Y = cev.Y, + Flags = mouseFlag + }; + + var view = Application.wantContinuousButtonPressedView; + if (view == null) + break; + if (IsButtonPressed && LastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + mouseHandler (me); + mainLoop.Driver.Wakeup (); + } + } + }); + } + + + } else if ((cev.ButtonState == Curses.Event.Button1Released || cev.ButtonState == Curses.Event.Button2Released || + cev.ButtonState == Curses.Event.Button3Released)) { + + mouseFlag = ProcessButtonReleasedEvent (cev, mouseFlag); + IsButtonPressed = false; + + } else if (cev.ButtonState == Curses.Event.Button4Pressed) { + + mouseFlag = MouseFlags.WheeledUp; + + } else if (cev.ButtonState == Curses.Event.ReportMousePosition && cev.X == point.X && cev.Y == point.Y) { + + mouseFlag = MouseFlags.WheeledDown; + + } + else if (cev.ButtonState == Curses.Event.ReportMousePosition) { + + mouseFlag = MouseFlags.ReportMousePosition; + } else { + mouseFlag = (MouseFlags)cev.ButtonState; + } + + point = new Point () { + X = cev.X, + Y = cev.Y + }; + + + + if (cev.ID != 0) + mouseFlag = MouseFlags.WheeledDown; + + return new MouseEvent () { + X = cev.X, + Y = cev.Y, + //Flags = (MouseFlags)cev.ButtonState + Flags = mouseFlag + }; + } + + private MouseFlags ProcessButtonClickedEvent (Curses.MouseEvent cev, MouseFlags mf) + { + LastMouseButtonPressed = cev.ButtonState; + mf = GetButtonState (cev, true); + mouseHandler (ProcessButtonState (cev, mf)); + if (LastMouseButtonPressed != null && LastMouseButtonPressed == cev.ButtonState) { + mf = GetButtonState (cev, false); + mouseHandler (ProcessButtonState (cev, mf)); + if (LastMouseButtonPressed != null && LastMouseButtonPressed == cev.ButtonState) { + mf = (MouseFlags)cev.ButtonState; + } + } + LastMouseButtonPressed = null; + return mf; + } + + private MouseFlags ProcessButtonReleasedEvent (Curses.MouseEvent cev, MouseFlags mf) + { + mf = (MouseFlags)cev.ButtonState; + mouseHandler (ProcessButtonState (cev, mf)); + if (!cancelButtonClicked && LastMouseButtonPressed == null) + mf = GetButtonState (cev); + else + cancelButtonClicked = false; + return mf; + } + + MouseFlags GetButtonState (Curses.MouseEvent cev, bool pressed = false) + { + MouseFlags mf = default; + switch (cev.ButtonState) { + case Curses.Event.Button1Clicked: + if (pressed) + mf = MouseFlags.Button1Pressed; + else + mf = MouseFlags.Button1Released; + break; + + case Curses.Event.Button2Clicked: + if (pressed) + mf = MouseFlags.Button2Pressed; + else + mf = MouseFlags.Button2Released; + break; + + case Curses.Event.Button3Clicked: + if (pressed) + mf = MouseFlags.Button3Pressed; + else + mf = MouseFlags.Button3Released; + break; + + case Curses.Event.Button1Released: + mf = MouseFlags.Button1Clicked; + break; + + case Curses.Event.Button2Released: + mf = MouseFlags.Button2Clicked; + break; + + case Curses.Event.Button3Released: + mf = MouseFlags.Button3Clicked; + break; + + + } + return mf; + } + + MouseEvent ProcessButtonState (Curses.MouseEvent cev, MouseFlags mf) { return new MouseEvent () { X = cev.X, Y = cev.Y, - Flags = (MouseFlags)cev.ButtonState + Flags = mf }; } @@ -245,12 +419,17 @@ namespace Terminal.Gui { keyHandler (new KeyEvent ((Key)wch)); } + Action mouseHandler; + MainLoop mainLoop; + public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { // Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called - Curses.timeout (-1); + Curses.timeout (0); + this.mouseHandler = mouseHandler; + this.mainLoop = mainLoop; - (mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => { + (mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => { ProcessInput (keyHandler, mouseHandler); return true; }); @@ -418,21 +597,21 @@ namespace Terminal.Gui { Console.Out.Flush (); } - int lastMouseInterval; - bool mouseGrabbed; + //int lastMouseInterval; + //bool mouseGrabbed; public override void UncookMouse () { - if (mouseGrabbed) - return; - lastMouseInterval = Curses.mouseinterval (0); - mouseGrabbed = true; + //if (mouseGrabbed) + // return; + //lastMouseInterval = Curses.mouseinterval (0); + //mouseGrabbed = true; } public override void CookMouse () { - mouseGrabbed = false; - Curses.mouseinterval (lastMouseInterval); + //mouseGrabbed = false; + //Curses.mouseinterval (lastMouseInterval); } } diff --git a/Terminal.Gui/MonoCurses/constants.cs b/Terminal.Gui/MonoCurses/constants.cs index c2da74157..a29f8d55c 100644 --- a/Terminal.Gui/MonoCurses/constants.cs +++ b/Terminal.Gui/MonoCurses/constants.cs @@ -2,6 +2,8 @@ * This file is autogenerated by the attrib.c program, do not edit */ +#define XTERM1006 + using System; namespace Unix.Terminal { @@ -76,6 +78,15 @@ namespace Unix.Terminal { ReportMousePosition = unchecked((int)0x8000000), AllEvents = unchecked((int)0x7ffffff), } +#if XTERM1006 + public const int LeftRightUpNPagePPage= unchecked((int)0x8); + public const int DownEnd = unchecked((int)0x6); + public const int Home = unchecked((int)0x7); +#else + public const int LeftRightUpNPagePPage= unchecked((int)0x0); + public const int DownEnd = unchecked((int)0x0); + public const int Home = unchecked((int)0x0); +#endif public const int ERR = unchecked((int)0xffffffff); public const int KeyBackspace = unchecked((int)0x107); public const int KeyUp = unchecked((int)0x103); @@ -109,30 +120,30 @@ namespace Unix.Terminal { public const int ShiftKeyPPage = unchecked((int)0x18e); public const int ShiftKeyHome = unchecked((int)0x187); public const int ShiftKeyEnd = unchecked((int)0x182); - public const int AltKeyUp = unchecked((int)0x234); - public const int AltKeyDown = unchecked((int)0x20b); - public const int AltKeyLeft = unchecked((int)0x21f); - public const int AltKeyRight = unchecked((int)0x22e); - public const int AltKeyNPage = unchecked((int)0x224); - public const int AltKeyPPage = unchecked((int)0x229); - public const int AltKeyHome = unchecked((int)0x215); - public const int AltKeyEnd = unchecked((int)0x210); - public const int CtrlKeyUp = unchecked((int)0x236); - public const int CtrlKeyDown = unchecked((int)0x20d); - public const int CtrlKeyLeft = unchecked((int)0x221); - public const int CtrlKeyRight = unchecked((int)0x230); - public const int CtrlKeyNPage = unchecked((int)0x226); - public const int CtrlKeyPPage = unchecked((int)0x22b); - public const int CtrlKeyHome = unchecked((int)0x217); - public const int CtrlKeyEnd = unchecked((int)0x212); - public const int ShiftCtrlKeyUp = unchecked((int)0x237); - public const int ShiftCtrlKeyDown = unchecked((int)0x20e); - public const int ShiftCtrlKeyLeft = unchecked((int)0x222); - public const int ShiftCtrlKeyRight = unchecked((int)0x231); - public const int ShiftCtrlKeyNPage = unchecked((int)0x227); - public const int ShiftCtrlKeyPPage = unchecked((int)0x22c); - public const int ShiftCtrlKeyHome = unchecked((int)0x218); - public const int ShiftCtrlKeyEnd = unchecked((int)0x213); + public const int AltKeyUp = unchecked((int)0x234 + LeftRightUpNPagePPage); + public const int AltKeyDown = unchecked((int)0x20b + DownEnd); + public const int AltKeyLeft = unchecked((int)0x21f + LeftRightUpNPagePPage); + public const int AltKeyRight = unchecked((int)0x22e + LeftRightUpNPagePPage); + public const int AltKeyNPage = unchecked((int)0x224 + LeftRightUpNPagePPage); + public const int AltKeyPPage = unchecked((int)0x229 + LeftRightUpNPagePPage); + public const int AltKeyHome = unchecked((int)0x215 + Home); + public const int AltKeyEnd = unchecked((int)0x210 + DownEnd); + public const int CtrlKeyUp = unchecked((int)0x236 + LeftRightUpNPagePPage); + public const int CtrlKeyDown = unchecked((int)0x20d + DownEnd); + public const int CtrlKeyLeft = unchecked((int)0x221 + LeftRightUpNPagePPage); + public const int CtrlKeyRight = unchecked((int)0x230 + LeftRightUpNPagePPage); + public const int CtrlKeyNPage = unchecked((int)0x226 + LeftRightUpNPagePPage); + public const int CtrlKeyPPage = unchecked((int)0x22b + LeftRightUpNPagePPage); + public const int CtrlKeyHome = unchecked((int)0x217 + Home); + public const int CtrlKeyEnd = unchecked((int)0x212 + DownEnd); + public const int ShiftCtrlKeyUp = unchecked((int)0x237 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyDown = unchecked((int)0x20e + DownEnd); + public const int ShiftCtrlKeyLeft = unchecked((int)0x222 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyRight = unchecked((int)0x231 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyNPage = unchecked((int)0x227 + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyPPage = unchecked((int)0x22c + LeftRightUpNPagePPage); + public const int ShiftCtrlKeyHome = unchecked((int)0x218 + Home); + public const int ShiftCtrlKeyEnd = unchecked((int)0x213 + DownEnd); public const int LC_ALL = 6; static public int ColorPair(int n){