http://qs321.pair.com?node_id=11143431


in reply to Unable to capture mouse events in Win32::Console

Thanks again for the feedback. My latest attempt is the following. I've added the sleep function call back in again to see if it will solve the excessive CPU time problem.

# This program displays details about console events that occur in +the window in # wnich this program is running. This program creates a file calle +d "event_log.txt" # in the current directory. # # References: # # https://docs.microsoft.com/en-us/windows/console/reading-i +nput-buffer-events # # Definitions and values of constants such as ENABLE_* are avail +able at the # following URL: # # https://docs.microsoft.com/en-us/windows/console/setconsol +emode # BEGIN { select STDERR; $|++; # do not buffer STDERR select STDOUT; $|++; # do not buffer STDOUT system ("cls"); print "\n\n\tInitializing the environment ...\n\n\t"; }; use strict; use warnings; use Time::HiRes qw (sleep); # The following package provides access to basic information about t +he runtime environment # such as the release of Windows under which this program is running use Win32; # The following package provides access to console events such as ke +y presses and mouse clicks use Win32::Console; my ($IN, $OUT); my @console_event; my @info; my $log_file = "event_log.txt"; my $fh_log_file; unless (open ($fh_log_file, ">", $log_file)) { print STDERR "\nCould not open $log_file: $!\n\n"; die; } unless ($IN = Win32::Console -> new (STD_INPUT_HANDLE)) { print STDERR "\nSomething has gone wrong with the Win32::Conso +le constructor: $!\n\n"; die; } unless ($OUT = Win32::Console -> new (STD_OUTPUT_HANDLE)) { print STDERR "\nSomething has gone wrong with the Win32::Conso +le constructor: $!\n\n"; die; } $IN -> Flush (); my $starting_console_mode_setting = $IN -> Mode; END {$IN -> Mode ($starting_console_mode_setting)}; $IN -> Mode ((($starting_console_mode_setting | 0x0010 ) & ~0x0040 ) & + ~ENABLE_PROCESSED_INPUT); print "Perl version $^V running on ", join (" ", Win32::GetOSName), ". +\n\n"; print $fh_log_file "Perl version $^V running on ", join (" ", Win32::G +etOSName), ".\n\n"; # The following is to demonstrate that the $console environment has +been set up print "Your mouse has ", $IN->MouseButtons(), " buttons.\n\n"; print $fh_log_file "Your mouse has ", $IN->MouseButtons(), " buttons.\ +n\n"; print "Enter keyboard activity or mouse events ...\n\n"; print $fh_log_file "Enter keyboard activity or mouse events ...\n\n"; my $start_time = time (); my $when_to_stop_listening = $start_time + 15; while (time () < $when_to_stop_listening) { if ($IN -> GetEvents ()) # This may be unnecessary, can si +mply invoke Input () # and check if it has return +ed anything { @console_event = $IN -> Input (); print "A console event has been detected. Its attribu +tes are the following:\n\n"; print $fh_log_file "A console event has been detected. + Its attributes are the following:\n\n"; print "Time of event: ", time (), "\n"; print $fh_log_file "Time of event: ", time (), "\ +n"; print "Type of event: "; print $fh_log_file "Type of event: "; if (defined ($console_event [0])) { if ($console_event [0] == 1) { print "Keyboard event.\n"; print $fh_log_file "Keyboard event.\n" +; print "Key down: ", $console +_event[1], "\n"; print $fh_log_file "Key down: + ", $console_event[1], "\n"; print "Repeat count: ", $console +_event[2], "\n"; print $fh_log_file "Repeat count: + ", $console_event[2], "\n"; print "Virtual key code: ", $console +_event[3], "\n"; print $fh_log_file "Virtual key code: + ", $console_event[3], "\n"; print "Virtual scan code: ", $console +_event[4], "\n"; print $fh_log_file "Virtual scan code: + ", $console_event[4], "\n"; print "ASCII code: ", $console +_event[5], "\n"; print $fh_log_file "ASCII code: + ", $console_event[5], "\n"; print "ASCII code letter: ", chr $con +sole_event[5], "\n"; print $fh_log_file "ASCII code letter: + ", chr $console_event[5], "\n"; print "Control key state: ", $console +_event[6], "\n"; print $fh_log_file "Control key state: + ", $console_event[6], "\n"; } elsif ($console_event [0] == 2) { print "Mouse event.\n"; print $fh_log_file "Mouse event.\n"; print "Mouse X coord: ", $console +_event[1], "\n"; print $fh_log_file "Mouse X coord: + ", $console_event[1], "\n"; print "Mouse Y coord: ", $console +_event[2], "\n"; print $fh_log_file "Mouse Y coord: + ", $console_event[2], "\n"; print "Mouse button state: ", $console +_event[3], "\n"; print $fh_log_file "Mouse button state +: ", $console_event[3], "\n"; print "Control key state: ", $console +_event[4], "\n"; print $fh_log_file "Control key state: + ", $console_event[4], "\n"; print "Event flags: ", $console +_event[5], "\n"; print $fh_log_file "Event flags: + ", $console_event[5], "\n"; } else { print "Unknown type \"$console_event[0 +]\".\n"; print $fh_log_file "Unknown type \"$co +nsole_event[0]\".\n"; } print "\nAdditional information as of the time + of the event:\n\n"; print $fh_log_file "\nAdditional information a +s of the time of the event:\n\n"; @info = $OUT -> Info(); # @info contains the following elements: # # $info[0]: columns (X size) of the consol +e buffer. # $info[1]: rows (Y size) of the console b +uffer. # $info[2]: current column (X position) of + the cursor. # $info[3]: current row (Y position) of th +e cursor. # $info[4]: current attribute used for Wri +te. # $info[5]: left column (X of the starting + point) of the current console window. # $info[6]: top row (Y of the starting poi +nt) of the current console window. # $info[7]: right column (X of the final p +oint) of the current console window. # $info[8]: bottom row (Y of the final poi +nt) of the current console window. # $info[9]: maximum number of columns for +the console window, given the current buffer size, font and the scree +n size. # $info[10]: maximum number of rows for the + console window, given the current buffer size, font and the screen s +ize. print "\$info[0] = ", sprintf ("%6d", $info[0 +]), "\tcolumns (X size) of the console buffer\n"; print $fh_log_file "\$info[0] = ", sprintf (" +%6d", $info[0]), "\tcolumns (X size) of the console buffer\n"; print "\$info[1] = ", sprintf ("%6d", $info[1 +]), "\trows (Y size) of the console buffer\n"; print $fh_log_file "\$info[1] = ", sprintf (" +%6d", $info[1]), "\trows (Y size) of the console buffer\n"; print "\$info[2] = ", sprintf ("%6d", $info[2 +]), "\tcurrent column (X position) of the cursor\n"; print $fh_log_file "\$info[2] = ", sprintf (" +%6d", $info[2]), "\tcurrent column (X position) of the cursor\n"; print "\$info[3] = ", sprintf ("%6d", $info[3 +]), "\tcurrent row (Y position) of the cursor\n"; print $fh_log_file "\$info[3] = ", sprintf (" +%6d", $info[3]), "\tcurrent row (Y position) of the cursor\n"; print "\$info[4] = ", sprintf ("%6d", $info[4 +]), "\tcurrent attribute used for Write\n"; print $fh_log_file "\$info[4] = ", sprintf (" +%6d", $info[4]), "\tcurrent attribute used for Write\n"; print "\$info[6] = ", sprintf ("%6d", $info[5 +]), "\tX of the starting point of the current console window\n"; print $fh_log_file "\$info[6] = ", sprintf (" +%6d", $info[5]), "\tX of the starting point of the current console wi +ndow\n"; print "\$info[6] = ", sprintf ("%6d", $info[6 +]), "\tY of the starting point of the current console window\n"; print $fh_log_file "\$info[6] = ", sprintf (" +%6d", $info[6]), "\tY of the starting point of the current console wi +ndow\n"; print "\$info[7] = ", sprintf ("%6d", $info[7 +]), "\tX of the final point of the current console window\n"; print $fh_log_file "\$info[7] = ", sprintf (" +%6d", $info[7]), "\tX of the final point of the current console windo +w\n"; print "\$info[8] = ", sprintf ("%6d", $info[8 +]), "\tY of the final point of the current console window\n"; print $fh_log_file "\$info[8] = ", sprintf (" +%6d", $info[8]), "\tY of the final point of the current console windo +w\n"; print "\$info[9] = ", sprintf ("%6d", $info[9 +]), "\tmaximum number of columns for the console window\n"; print $fh_log_file "\$info[9] = ", sprintf (" +%6d", $info[9]), "\tmaximum number of columns for the console window\ +n"; print "\$info[10] = ", sprintf ("%6d", $info[1 +0]), "\tmaximum number of rows for the console window\n"; print $fh_log_file "\$info[10] = ", sprintf (" +%6d", $info[10]), "\tmaximum number of rows for the console window\n" +; print "\n"; print $fh_log_file "\n"; } else { print "undefined event type.\n"; print $fh_log_file "undefined event type.\n"; } print "\n"; print $fh_log_file "\n"; } sleep 0.01; } print "The time to stop listening has been reached. Program is ending +.\n\n"; print $fh_log_file "The time to stop listening has been reached. Prog +ram is ending.\n\n";

Replies are listed 'Best First'.
Re^2: Unable to capture mouse events in Win32::Console
by vr (Curate) on Apr 29, 2022 at 18:45 UTC

    I feel partly responsible for confusion and code re-writes caused by my words that "GetEvents and sleep are not required". I don't know if you indeed tried to exclude both to find timeout never occurs if user is inactive; then restored just "peeking" into the queue (GetEvents) to detrimental effect, and finally restored the original. Maybe this timeout is crucial to your application. Maybe the above never happened, it's just the (wrong) idea of

    GetEvents returned just a single scalar ... would be more efficient

    Maybe you tried to timeout through alarm (I think it is somewhere in FAQ) to find it doesn't work with blocking I/O on Windows. Maybe I'm partially or totally wrong, you are not interested in "things async", peeking/blocking, etc. Code finally working as desired is important (which is absolutely right!), goal achieved. Well, so it's to my amusement and sleep-not-really-OK bias I found that timed-out Win32::Console::Input can be done like this:

    use strict; use warnings; use feature 'say'; use Win32::Console; use Win32::IPC 'wait_any'; use Time::HiRes 'time'; my $IN = Win32::Console-> new( STD_INPUT_HANDLE ); my $max = 15; my $count = 0; my $t = time + $max; while ( wait_any @{[ $IN ]}, 1000 * ( $t - time )) { my @ev = $IN-> Input; say "Ah ah ah, you didn't press the magic key <--- @ev"; ++ $count } say $count ? "Enough, I'm tired! $count events in $max seconds!" : "You failed to press ANY key in $max seconds! Read my FAX!!!";
Re^2: Unable to capture mouse events in Win32::Console
by fireblood (Scribe) on Apr 29, 2022 at 13:14 UTC

    The one aspect of this that I still find puzzling is that in the @info array, all of the Y values are usually greater than the height of my console window. Do they represent Y values within the console buffer rather than within the console window? In my case the console buffer is much taller than the console window. I was not able to find an answer at https://docs.microsoft.com/en-us/windows/console/reading-input-buffer-events

      It took me a while to figure that one out as well. It's the character-based y coordinate relative to the first (top) line of text in the console, assuming your scroll bar is at the very top. So if your window is 25 lines tall but your history is 50 characters tall total, then if you scroll to the top , it will say the y is line 0, and if you scroll to the bottom, it will say the line is 49.

      When I originally had a \n in my ->Write() string, I was confused why the y coordinate would start increasing and "never" stop, even as I moved the cursor back up the screen. So I changed it to a \r instead, so the text wouldn't scroll the screen, and it suddenly made perfect sense, as I manually scrolled or intentionally moved down a line for printing (typing an ENTER, because the keyboard events still kept \n). (I also didn't realize until I did that -- though it should have been obvious, even from the x coordinate -- that the coordinates were in characters, not in pixels.)


      update: My original reply was assuming you were talking about the mouse coordinates from @console_events. Sorry I misread. But yes, the current x and current y, in the info array as well, are still relative to the top-left of the whole buffer, so the whole size and current location can be outside the window dimensions.

        Hi pryrt, excellent! That explains it all. So to determine the actual Y-coordinate within the console window all that will be required is to subtract the Y-coordinate in the buffer from the Y-coordinate of the top of the console window in the buffer.

        Thanks much!