Browse Source

First implementation of mouse events on windows

Nick Van Dyck 7 years ago
parent
commit
8eea18b8e8

+ 4 - 6
Terminal.Gui/Core.cs

@@ -1590,14 +1590,12 @@ namespace Terminal.Gui {
 			if (Top != null)
 			if (Top != null)
 				return;
 				return;
 
 
-			if (!UseSystemConsole) {
-				var p = Environment.OSVersion.Platform;
-				if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
-					UseSystemConsole = true;
-			}
-			//UseSystemConsole = true;
+			var p = Environment.OSVersion.Platform;
+
 			if (UseSystemConsole)
 			if (UseSystemConsole)
 				Driver = new NetDriver ();
 				Driver = new NetDriver ();
+			else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
+				Driver = new WindowsDriver();
 			else
 			else
 				Driver = new CursesDriver ();
 				Driver = new CursesDriver ();
 			Driver.Init (TerminalResized);
 			Driver.Init (TerminalResized);

+ 1 - 1
Terminal.Gui/Drivers/NetDriver.cs

@@ -238,7 +238,7 @@ namespace Terminal.Gui {
 			currentAttribute = c.value;
 			currentAttribute = c.value;
 		}
 		}
 
 
-		Key MapKey (ConsoleKeyInfo keyInfo)
+		public Key MapKey (ConsoleKeyInfo keyInfo)
 		{
 		{
 			switch (keyInfo.Key) {
 			switch (keyInfo.Key) {
 			case ConsoleKey.Escape:
 			case ConsoleKey.Escape:

+ 145 - 14
Terminal.Gui/Drivers/WindowsDriver.cs

@@ -1,5 +1,7 @@
 using System;
 using System;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Mono.Terminal;
 
 
 namespace Terminal.Gui {
 namespace Terminal.Gui {
 
 
@@ -8,9 +10,6 @@ namespace Terminal.Gui {
 		public const int STD_INPUT_HANDLE = -10;
 		public const int STD_INPUT_HANDLE = -10;
 		public const int STD_ERROR_HANDLE = -12;
 		public const int STD_ERROR_HANDLE = -12;
 
 
-		[DllImport ("kernel32.dll", SetLastError = true)]
-		static extern IntPtr GetStdHandle (int nStdHandle);
-
 		IntPtr inputHandle, outputHandle;
 		IntPtr inputHandle, outputHandle;
 
 
 		public WindowsConsole ()
 		public WindowsConsole ()
@@ -19,6 +18,15 @@ namespace Terminal.Gui {
 			outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
 			outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
 		}
 		}
 
 
+		[Flags]
+		public enum ConsoleModes : uint
+		{
+			EnableMouseInput = 16,
+			EnableQuickEditMode = 64,
+			EnableExtendedFlags = 128,
+		}
+
+
 		[StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
 		[StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
 		public struct KeyEventRecord {
 		public struct KeyEventRecord {
 			[FieldOffset (0), MarshalAs (UnmanagedType.Bool)]
 			[FieldOffset (0), MarshalAs (UnmanagedType.Bool)]
@@ -160,6 +168,57 @@ namespace Terminal.Gui {
 			}
 			}
 		};
 		};
 
 
+		public void PollEvents(Action<InputRecord> inputEventHandler)
+		{
+			if (OriginalConsoleMode != 0)
+				return;
+
+			OriginalConsoleMode = ConsoleMode;
+
+			ConsoleMode |= (uint)ConsoleModes.EnableMouseInput;
+			ConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
+			ConsoleMode |= (uint)ConsoleModes.EnableExtendedFlags;
+
+			Task.Run(() =>
+			{
+				uint numberEventsRead = 0;
+				uint length = 1;
+				InputRecord[] records = new InputRecord[length];
+
+				while (
+					ContinueListeningForConsoleEvents &&
+					ReadConsoleInput(inputHandle, records, length, out numberEventsRead) &&
+					numberEventsRead > 0
+				)
+				{
+					inputEventHandler(records[0]);
+				}
+			});
+		}
+
+		public void Cleanup()
+		{
+			ContinueListeningForConsoleEvents = false;
+			ConsoleMode = OriginalConsoleMode;
+			OriginalConsoleMode = 0;
+		}
+
+		private bool ContinueListeningForConsoleEvents = true;
+
+		private uint OriginalConsoleMode = 0;
+
+		public uint ConsoleMode {
+			get {
+				uint v;
+				GetConsoleMode (inputHandle, out v);
+				return v;
+			}
+
+			set {
+				SetConsoleMode (inputHandle, value);
+			}
+		}
+
 		[DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
 		[DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
 		public static extern bool ReadConsoleInput (
 		public static extern bool ReadConsoleInput (
 			IntPtr hConsoleInput,
 			IntPtr hConsoleInput,
@@ -173,21 +232,93 @@ namespace Terminal.Gui {
 		[DllImport ("kernel32.dll")]
 		[DllImport ("kernel32.dll")]
 		static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
 		static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
 
 
-		public uint ConsoleMode {
-			get {
-				uint v;
-				GetConsoleMode (inputHandle, out v);
-				return v;
-			}
+		[DllImport ("kernel32.dll", SetLastError = true)]
+		static extern IntPtr GetStdHandle (int nStdHandle);
 
 
-			set {
-				SetConsoleMode (inputHandle, value);
+	}
+
+	internal class WindowsDriver : NetDriver {
+
+		WindowsConsole WinConsole;
+
+		public WindowsDriver ()
+		{
+			WinConsole = new WindowsConsole();
+		}
+
+		private MouseEvent ToDriverMouse(WindowsConsole.MouseEventRecord mouseEvent)
+		{
+			MouseFlags mouseFlag = MouseFlags.AllEvents;
+
+			if (mouseEvent.EventFlags == 0)
+			{
+				switch (mouseEvent.ButtonState)
+				{
+				case WindowsConsole.ButtonState.Button1Pressed:
+					mouseFlag = MouseFlags.Button1Clicked;
+					break;
+
+				case WindowsConsole.ButtonState.Button2Pressed:
+					mouseFlag = MouseFlags.Button2Clicked;
+					break;
+
+				case WindowsConsole.ButtonState.Button3Pressed:
+					mouseFlag = MouseFlags.Button3Clicked;
+					break;
+				}
 			}
 			}
+			else if(mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved)
+			{
+				mouseFlag = MouseFlags.ReportMousePosition;
+			}
+
+
+			return new MouseEvent () {
+				X = mouseEvent.MousePosition.X,
+				Y = mouseEvent.MousePosition.Y,
+				Flags = mouseFlag
+			};
 		}
 		}
-	}	
-	public class WindowsDriver {
-		public WindowsDriver ()
+
+		private ConsoleKeyInfo ToConsoleKeyInfo(WindowsConsole.KeyEventRecord keyEvent)
 		{
 		{
+			var state = keyEvent.dwControlKeyState;
+
+			bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0;
+			bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0;
+			bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0;
+
+			return new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
 		}
 		}
+
+		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
+		{
+			WinConsole.PollEvents (inputEvent =>
+			{
+				switch(inputEvent.EventType)
+				{
+					case WindowsConsole.EventType.Key:
+						if (inputEvent.KeyEvent.bKeyDown == false)
+							return;
+						var map = MapKey (ToConsoleKeyInfo (inputEvent.KeyEvent));
+						if (map == (Key) 0xffffffff)
+							return;
+						keyHandler (new KeyEvent (map));
+						break;
+
+					case WindowsConsole.EventType.Mouse:
+						mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
+						break;
+
+				}
+			});
+		}
+
+		public override void End()
+		{
+			WinConsole.Cleanup();
+			base.End();
+		}
+
 	}
 	}
 }
 }

+ 6 - 3
Terminal.Gui/MonoCurses/mainloop.cs

@@ -128,10 +128,13 @@ namespace Mono.Terminal {
 					read (wakeupPipes [0], ignore, (IntPtr)1);
 					read (wakeupPipes [0], ignore, (IntPtr)1);
 					return true;
 					return true;
 				});
 				});
-			} else {
-				Thread readThread = new Thread (WindowsKeyReader);
-				readThread.Start ();
 			}
 			}
+			// Using this results in buggy behavior when we are hooking into ReadConsoleInput api ourselves
+			// Because if left in both custom code and this one tries to read from the consoleInput
+			// else {
+			// 	Thread readThread = new Thread (WindowsKeyReader);
+			// 	readThread.Start ();
+			// }
 		}
 		}
 
 
 		void Wakeup ()
 		void Wakeup ()