using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using static ConsoleLib.NativeMethods; namespace RobvanderWoude { class Mause { static string progver = "1.06"; const string defaultprompt = "Press any key or mouse button or wheel to continue . . ."; const int defaulttimeout = 0; static IntPtr inHandle = IntPtr.Zero; static uint initialmode = 0; static string prompt = defaultprompt; static int timeout = defaulttimeout; static bool flushkeyboardbuffer = true; private static System.Timers.Timer timer; static void Main( string[] args ) { foreach ( string arg in args ) { if ( arg == "/?" ) { ShowHelp( ); } if ( arg.Length > 3 && arg.ToUpper( ).Substring( 0, 3 ) == "/T:" ) { if ( timeout != defaulttimeout ) { ShowHelp( "Duplicate timeout switch /T" ); } try { timeout = Convert.ToInt32( arg.Substring( 3 ) ); if ( timeout < 1 || timeout > 3600 ) { ShowHelp( "Specified timeout out of 1..3600 seconds range" ); } } catch { ShowHelp( "Invalid timeout \"{0}\"", arg ); } } else if ( arg.ToUpper( ) == "/N" ) { if ( !flushkeyboardbuffer ) { ShowHelp( "Duplicate timeout switch /N" ); } flushkeyboardbuffer = false; } else { if ( prompt != defaultprompt ) { ShowHelp( "Too many command line arguments" ); } prompt = arg; } } // Flush keyboard buffer unless /N switch is used if ( flushkeyboardbuffer ) { FlushKeyboardBuffer( ); } // Temporarily enable mouse input and disable Quick-Edit mode inHandle = GetStdHandle( STD_INPUT_HANDLE ); GetConsoleMode( inHandle, ref initialmode ); uint tempmode = initialmode | ENABLE_EXTENDED_FLAGS; // enable tempmode |= ENABLE_MOUSE_INPUT; // enable tempmode &= ~ENABLE_QUICK_EDIT_MODE; // disable tempmode &= ~ENABLE_LINE_INPUT; // disable tempmode &= ~ENABLE_ECHO_INPUT; // disable tempmode &= ~ENABLE_WINDOW_INPUT; // disable SetConsoleMode( inHandle, tempmode ); // Set event listeners for keyboard and mouse input ConsoleLib.ConsoleListener.MouseEvent += ConsoleListener_MouseEvent; ConsoleLib.ConsoleListener.KeyEvent += ConsoleListener_KeyEvent; ConsoleLib.ConsoleListener.Start( ); // Start timeout timer if requested if ( timeout > 0 ) { timer = new System.Timers.Timer( ); timer.Interval = 1000 * timeout; timer.Elapsed += Timer_Elapsed; timer.Start( ); } // Show the prompt if ( !string.IsNullOrWhiteSpace( prompt ) ) { Console.Write( prompt ); } // Temporarily hide the cursor Console.CursorVisible = false; } static void FlushKeyboardBuffer( ) { // Code to flush keyboard buffer by gandjustas // https://stackoverflow.com/a/3769828 bool hidekey = true; while ( Console.KeyAvailable ) { Console.ReadKey( hidekey ); } } static void Quit( int rc ) { // If a prompt was shown, append a newline if ( !string.IsNullOrWhiteSpace( prompt ) ) { Console.WriteLine( ); } // Stop the timeout timer, if active if ( timeout > 0 ) { timer.Stop( ); timer.Dispose( ); } // Restore the cursor Console.CursorVisible = true; // Restore the console's initial Quick-Edit mode SetConsoleMode( inHandle, initialmode ); // Exit with appropriate return code Environment.Exit( rc ); } private static void ConsoleListener_KeyEvent( KEY_EVENT_RECORD r ) { // Keyboard input Quit( 1 ); } private static void ConsoleListener_MouseEvent( MOUSE_EVENT_RECORD r ) { if ( r.dwEventFlags != 1 ) // ignore mouse movement events { // Mouse input Quit( 2 ); } } private static void Timer_Elapsed( object sender, System.Timers.ElapsedEventArgs e ) { // Timeout elapsed Quit( 3 ); } #region Error handling static void ShowHelp( params string[] errmsg ) { #region Error Message if ( errmsg.Length > 0 ) { List errargs = new List( errmsg ); errargs.RemoveAt( 0 ); Console.Error.WriteLine( ); Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write( "ERROR:\t" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) ); Console.ResetColor( ); } #endregion Error Message #region Help Text /* Mause.exe, Version 1.06 Mouse enabled pAUSE command Usage: Mause.exe [ prompt ] [ /T:seconds ] [ /N ] Where: prompt is the prompt to be displayed (default: "Press any key or mouse button or wheel to continue . . ."; use "" to show no prompt) /N do Not flush the keyboard buffer (default: flush keyboard buffer when program is started) /T:seconds timeout in seconds (1..3600; default: wait forever) Notes: Return code 1 for key, 2 for mouse, 3 for timeout or -1 for errors. While Mause is running, the console's Quick-Edit mode will be temporarily disabled; before exiting, the console's initial Quick-Edit state will be restored. Credits: ConsoleListener class by SWdV on StackOverflow.com https://stackoverflow.com/a/29971246 Code to flush keyboard buffer by gandjustas on StackOverflow.com https://stackoverflow.com/a/3769828 Written by Rob van der Woude https://www.robvanderwoude.com */ #endregion Help Text #region Display Help Text Console.Error.WriteLine( ); Console.Error.WriteLine( "Mause.exe, Version {0}", progver ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "M" ); Console.ResetColor( ); Console.Error.Write( "ouse enabled p" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "AUSE" ); Console.ResetColor( ); Console.Error.WriteLine( " command" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "Mause.exe [ prompt ] [ /T:timeout ] [ /N ]" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Where: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "prompt" ); Console.ResetColor( ); Console.Error.WriteLine( " is the prompt to be displayed (default: \"Press any key" ); Console.Error.WriteLine( " or mouse button or wheel to continue . . .\"; use \"\" to" ); Console.Error.WriteLine( " show no prompt)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /N" ); Console.ResetColor( ); Console.Error.Write( " do " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "N" ); Console.ResetColor( ); Console.Error.WriteLine( "ot flush the keyboard buffer" ); Console.Error.WriteLine( " (default: flush keyboard buffer when program is started)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /T:seconds" ); Console.ResetColor( ); Console.Error.WriteLine( " timeout in seconds (1..3600; default: wait forever)" ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Notes: Return code 1 for key, 2 for mouse, 3 for timeout or -1 for errors." ); Console.Error.WriteLine( " While Mause is running, the console's Quick-Edit mode will be" ); Console.Error.WriteLine( " temporarily disabled; before exiting, the console's initial" ); Console.Error.WriteLine( " Quick-Edit state will be restored." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Credits: ConsoleListener class by SWdV on StackOverflow.com" ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( " https://stackoverflow.com/a/29971246" ); Console.ResetColor( ); Console.Error.WriteLine( " Code to flush keyboard buffer by gandjustas on StackOverflow.com" ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( " https://stackoverflow.com/a/3769828" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.WriteLine( "https://www.robvanderwoude.com" ); #endregion Display Help Text Environment.Exit( -1 ); } #endregion Error handling } } // ConsoleListener class by SWdV on StackOverflow.com // https://stackoverflow.com/a/29971246 namespace ConsoleLib { public static class ConsoleListener { public static event ConsoleMouseEvent MouseEvent; public static event ConsoleKeyEvent KeyEvent; public static event ConsoleWindowBufferSizeEvent WindowBufferSizeEvent; private static bool Run = false; public static void Start( ) { if ( !Run ) { Run = true; IntPtr handleIn = GetStdHandle( STD_INPUT_HANDLE ); new Thread( ( ) => { while ( true ) { uint numRead = 0; INPUT_RECORD[] record = new INPUT_RECORD[1]; record[0] = new INPUT_RECORD( ); ReadConsoleInput( handleIn, record, 1, ref numRead ); if ( Run ) switch ( record[0].EventType ) { case INPUT_RECORD.MOUSE_EVENT: MouseEvent?.Invoke( record[0].MouseEvent ); break; case INPUT_RECORD.KEY_EVENT: KeyEvent?.Invoke( record[0].KeyEvent ); break; case INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT: WindowBufferSizeEvent?.Invoke( record[0].WindowBufferSizeEvent ); break; } else { uint numWritten = 0; WriteConsoleInput( handleIn, record, 1, ref numWritten ); return; } } } ).Start( ); } } public static void Stop( ) => Run = false; public delegate void ConsoleMouseEvent( MOUSE_EVENT_RECORD r ); public delegate void ConsoleKeyEvent( KEY_EVENT_RECORD r ); public delegate void ConsoleWindowBufferSizeEvent( WINDOW_BUFFER_SIZE_RECORD r ); } public static class NativeMethods { public struct COORD { public short X; public short Y; public COORD( short x, short y ) { X = x; Y = y; } } [StructLayout( LayoutKind.Explicit )] public struct INPUT_RECORD { public const ushort KEY_EVENT = 0x0001, MOUSE_EVENT = 0x0002, WINDOW_BUFFER_SIZE_EVENT = 0x0004; //more [FieldOffset( 0 )] public ushort EventType; [FieldOffset( 4 )] public KEY_EVENT_RECORD KeyEvent; [FieldOffset( 4 )] public MOUSE_EVENT_RECORD MouseEvent; [FieldOffset( 4 )] public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; /* and: MENU_EVENT_RECORD MenuEvent; FOCUS_EVENT_RECORD FocusEvent; */ } public struct MOUSE_EVENT_RECORD { public COORD dwMousePosition; public const uint FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001, FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004, FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008, FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010, RIGHTMOST_BUTTON_PRESSED = 0x0002; public uint dwButtonState; public const int CAPSLOCK_ON = 0x0080, ENHANCED_KEY = 0x0100, LEFT_ALT_PRESSED = 0x0002, LEFT_CTRL_PRESSED = 0x0008, NUMLOCK_ON = 0x0020, RIGHT_ALT_PRESSED = 0x0001, RIGHT_CTRL_PRESSED = 0x0004, SCROLLLOCK_ON = 0x0040, SHIFT_PRESSED = 0x0010; public uint dwControlKeyState; public const int DOUBLE_CLICK = 0x0002, MOUSE_HWHEELED = 0x0008, MOUSE_MOVED = 0x0001, MOUSE_WHEELED = 0x0004; public uint dwEventFlags; } [StructLayout( LayoutKind.Explicit, CharSet = CharSet.Unicode )] public struct KEY_EVENT_RECORD { [FieldOffset( 0 )] public bool bKeyDown; [FieldOffset( 4 )] public ushort wRepeatCount; [FieldOffset( 6 )] public ushort wVirtualKeyCode; [FieldOffset( 8 )] public ushort wVirtualScanCode; [FieldOffset( 10 )] public char UnicodeChar; [FieldOffset( 10 )] public byte AsciiChar; public const int CAPSLOCK_ON = 0x0080, ENHANCED_KEY = 0x0100, LEFT_ALT_PRESSED = 0x0002, LEFT_CTRL_PRESSED = 0x0008, NUMLOCK_ON = 0x0020, RIGHT_ALT_PRESSED = 0x0001, RIGHT_CTRL_PRESSED = 0x0004, SCROLLLOCK_ON = 0x0040, SHIFT_PRESSED = 0x0010; [FieldOffset( 12 )] public uint dwControlKeyState; } public struct WINDOW_BUFFER_SIZE_RECORD { public COORD dwSize; } public const uint STD_INPUT_HANDLE = unchecked((uint) -10), STD_OUTPUT_HANDLE = unchecked((uint) -11), STD_ERROR_HANDLE = unchecked((uint) -12); [DllImport( "kernel32.dll" )] public static extern IntPtr GetStdHandle( uint nStdHandle ); public const uint ENABLE_MOUSE_INPUT = 0x0010, ENABLE_QUICK_EDIT_MODE = 0x0040, ENABLE_EXTENDED_FLAGS = 0x0080, ENABLE_ECHO_INPUT = 0x0004, ENABLE_WINDOW_INPUT = 0x0008, ENABLE_INSERT_MODE = 0x0020, ENABLE_LINE_INPUT = 0x0002, ENABLE_PROCESSED_INPUT = 0x0001, ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200; [DllImportAttribute( "kernel32.dll" )] public static extern bool GetConsoleMode( IntPtr hConsoleInput, ref uint lpMode ); [DllImportAttribute( "kernel32.dll" )] public static extern bool SetConsoleMode( IntPtr hConsoleInput, uint dwMode ); [DllImportAttribute( "kernel32.dll", CharSet = CharSet.Unicode )] public static extern bool ReadConsoleInput( IntPtr hConsoleInput, [Out] INPUT_RECORD[] lpBuffer, uint nLength, ref uint lpNumberOfEventsRead ); [DllImportAttribute( "kernel32.dll", CharSet = CharSet.Unicode )] public static extern bool WriteConsoleInput( IntPtr hConsoleInput, INPUT_RECORD[] lpBuffer, uint nLength, ref uint lpNumberOfEventsWritten ); } }