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

Does https://metacpan.org/release/JDB/Win32-Console-0.10/source/eg/sample.pl work for you? 1 second sleep is too long
  • Comment on Re: Unable to capture mouse events in Win32::Console

Replies are listed 'Best First'.
Re^2: Unable to capture mouse events in Win32::Console
by fireblood (Scribe) on Apr 26, 2022 at 03:50 UTC

    Hi Anonymous, thanks for replying so quickly. Actually, I had seen that sample code previously, and had downloaded and run it, and found that although it displayed amazing graphics, it too did not respond to my mouse clicks. I also noticed that it did not have a $console -> Alloc () instruction anywhere, which I initially thought might be why neither it nor my program responded to mouse events, because I did not have a $console -> Alloc either. However, when I added a $console -> Alloc () instruction to my code, the window in which my program was running would instantly vanish as soon as the $console -> Alloc instruction was reached. So I fiddled with $console -> Free () with it for a while, to no avail, and finally removed the $console -> Alloc instruction again.

    I have now revised my code to eliminate the 1-second sleep. By commenting one instruction or not, I can make it sleep only a hundredth of a second or not sleep at all, with the results being the same, viz. no mouse events are detected. This is the revised code:

    use strict; use warnings; use Time::HiRes qw (usleep); use Win32; use Win32::Console; my $console; my @console_event; undef $/; unless ($console = new Win32::Console STD_INPUT_HANDLE) { print STDERR "\nSomething has gone wrong with the Win32::Conso +le constructor: $!\n\n"; die; } $console -> Mode (ENABLE_MOUSE_INPUT); print "Perl version $^V running on ", join (" ", Win32::GetOSName), ". +\n\n"; print "Your mouse has ", $console->MouseButtons(), " buttons.\n\n"; print "Go ahead, make my day ...\n\n"; my $start_time = time (); my $when_to_stop_waiting = $start_time + 15; while (time () < $when_to_stop_waiting) { if ($console -> GetEvents ()) { @console_event = $console -> Input (); print "A console event has been detected. Its attribu +tes are the following:\n\n"; print "$_\n" for @console_event; print "\nGood job.\n"; exit; # exit the program, do not fall through to t +hat final print instruction } else { usleep 100; # sleep for 10 milliseconds, i.e. a +hundredth of a second, to prevent # this program from getting into a r +eally tight loop and crowding out # everyone else, but even without th +is usleep instruction the mouse # clicks are still not detected } } print "The time to stop waiting has been reached. Your input was not d +etected.\n"; print "Better luck after seeking the wisdom of the monks.\n";

    When I run this revised version and press any key, the following is generated:

    Go ahead, make my day ... A console event has been detected. Its attributes are the following: 1 1 1 70 33 102 32 Good job.

    But as before, I can click on the mouse all night, and this is what is generated:

    H:\sandbox>perl capture_mouse_events.pl Perl version v5.32.0 running on Win10 Build 18363 (64-bit). Your mouse has 16 buttons. Go ahead, make my day ... The time to stop waiting has been reached. Your input was not detected +. Better luck after seeking the wisdom of the monks. H:\sandbox>

    I've thought about re-writing my application in Win32::GUI but it would involve writing a lot more code creating and managing the window entirely within the program, not just hooking into a pre-existing console as with Win32::Console.

      replacing your Mode call with the following works for me.

      my $save = $console-> Mode; END { $console-> Mode( $save )}; $console-> Mode(( $save | 0x0010 ) & ~0x0040 ); # +MOUSE, -QUICK_EDIT

      Edit. Looks like either ENABLE_INSERT_MODE or ENABLE_EXTENDED_FLAGS are necessary in the mask (i.e. passing 0x30 or 0x90 as argument would work). Without them console continues to behave as if ENABLE_QUICK_EDIT_MODE is still on i.e. mouse selects text.

        Hi vr, This is really excellent. I made the changes that you recommended and now I am capturing mouse events in addition to keyboard events. I have one remaining question. I recall that there was a way to specify that mouse movements would not be captured, but everything else that was mouse-related, in particular mouse clicks, would be captured. I checked https://docs.microsoft.com/en-us/windows/console/setconsolemode but could not find it. I know that programmatically I can filter out mouse movement events, but then there would be a huge amount of CPU time spent in doing so. Is there a value that can be "or"ed or "and"ed to the values already being sent to the mode () method that prevents mouse movement events from being detected? Thanks again for your solution.
Re^2: Unable to capture mouse events in Win32::Console
by Marshall (Canon) on Apr 26, 2022 at 03:47 UTC
    Interestingly enough, this sample.pl does not work as advertised on my Windows 10 machine.
    When it starts, "TEST SUITE" is flashing. It says, "Press any key or mouse button to proceed". However, no mouse click will work, although pressing any other key will work.

    I believe that the 1 second sleep idea is a "red herring" -> doesn't matter. All that means is that it will take at most one second for the keyboard event to be seen. A normal assumption would be that if you don't poll fast enough, you miss a character because apparently (not sure) that the event stack is only one character deep?

    I did play around with the Properties of my standard command window. I turned off any of the normal fancy CTL-V or CTL-C off. But I haven't found the "magic formula" yet. In my testing with short programs, I am seeing that a right click causes the following program to "hang". When the program starts, hitting a normal key produces expected results, but a mouse click gets you nowhere!

    I have seen other test programs fail to see the Right-Click. When the test program exits, I get the normal CTL-V behavior. Somehow the Mouse Click is not getting to the running test program and is buffered and presented to the command shell when the test program exits. The correct voo-doo for Console should be able to get this character (mouse click).

    I am curious as to what others find. The sample code is complex and I am just trying to find a simple subset that works. This does not.

    # https://docs.microsoft.com/en-us/windows/console/reading-input-buffe +r-events\ # reference Perl Monks: https://www.perlmonks.org/?node_id=1212227 use strict; use warnings; use Win32::Console; my $OUT = new Win32::Console(STD_OUTPUT_HANDLE); my $IN = new Win32::Console(STD_INPUT_HANDLE); $OUT->Write("Perl version $^V \n"); $OUT->Write("more text could go here\n\n"); my $n_buttons = $IN->MouseButtons(); $OUT->Write("Your mouse has $n_buttons buttons\n"); $IN->Mode(ENABLE_MOUSE_INPUT); my $counter = 0; while ($counter++ < 5) #5x time out for testing... { if (my @console_events = $IN -> Input ()) { $OUT->Write ("calling input method!\n"); # my @console_events = $IN -> Input (); ##not needed goof (COR +RECTION) $OUT->Write ("An Event was detected! number= ".@console_events. +"\n"); foreach my $event (@console_events) { $OUT->Write("event: $event\n"); } exit; # exit the program, do not fall through to that final + print instruction } else { sleep 1; #should be fine to poll 1x per second for testing... } } $OUT->Write("Timed out! Bummer!\n");
      I played briefly with Marshall's adaptation of the script (though I upped to 60 instead of 5 loops, and before the exit I added a sleep(10);).

      If I run cmd.exe, and from there run perl pm.pl, when I try to do any mouse, it is consumed by my cmd.exe > properties > options, so the script doesn't show any events. If I go to cmd.exe > properties > options, and turn off all the edit and text selection options, then run the script, it still grabs my mouse clicks -- and when I look, I see that it turned back on Quick Edit Mode and Insert Mode! If I do the sequence of run the script, then turn off all the edit options, if I click or wiggle, I do see a mouse event (#2):

      C:\Users\peter.jones\Downloads\TempData\perl>perl pm.pl Perl version v5.30.0 more text could go here Your mouse has 5 buttons calling input method! An Event was detected! number= 6 event: 2 event: 42 event: 48 event: 1 event: 32 event: 0

      Similarly, if I run from Explorer using my double-click association, so that it opens a new cmd.exe window (which doesn't inherit my default options with edit options enabled), I get events. (This was the reason I added the sleep(10): so that the created cmd.exe window wouldn't go away immediately after processing the mouse event(s).

      update: As an aside, if I add the mode setting from vr's post (which I hadn't read yet when I created this post), modified to use Marshall's $IN console, then I find I don't need to futz with the cmd.exe properties manually. Thanks!
      my $save = $IN-> Mode; END { $IN-> Mode( $save )}; $IN-> Mode(( $save | 0x0010 ) & ~0x0040 ); # +MOUSE, -QUICK_EDIT

      When looking at the adapted code, I have a nitpick and a query.

      Nitpick: (this originated in fireblood's original): per Indirect Object Syntax, one should avoid the indirect-object style new Win32::Console(...) syntax, and instead use Win32::Console->new(...) syntax (or even the Win32::Console::->new(...) syntax; there are some circumstances where perl can interpret it ambiguously without both the :: and ->). It's not critical in this example, and I know that Win32::Console still recommends the old notation, but one can learn and use best practices even when the documentation/examples don't use it.

      Query to Marshall: why are you throwing away the first @console_events ? You are actually getting two events, and only reporting the first. I can see this if I change the "calling input method!" print to $OUT->Write ("calling input method!" . @console_events . "(@console_events)\n");, at which point the above in-cmd.exe example becomes

      C:\Users\peter.jones\Downloads\TempData\perl>perl pm.pl Perl version v5.30.0 more text could go here Your mouse has 5 buttons calling input method!6(2 42 47 0 32 1) An Event was detected! number= 6 event: 2 event: 42 event: 48 event: 1 event: 32 event: 0

      For the if condition, did you really want to PeekInput of Win32::Console instead, or stick with GetEvents of Win32::Console as shown in the original, so that it polls whether there was an event, but doesn't remove the event from the queue? Or was there a specific reason for throwing away the first event?

      Regarding the two mys, I am actually surprised there isn't a warning, because the my @console_events in the if condition is then masked by the my @console_events inside the block, but the use warnings isn't saying anything about it. (which it normally does, like in perl -le "use strict; use warnings; my $x; my $x" (warning: windows quote syntax, given the windows context of Win32::Console))

      TL;DR

      Back to the main point: if your cmd.exe window is not trapping the mouse inputs with its edit options, Win32::Console can see mouse events. But sometimes, when you think it won't trap, cmd.exe re-enables at least a couple of the traps, so you have to re-disable them after the program has started (edit: or by using the Mode command).
        Your testing agrees with what I was seeing. I also saw Quick edit mode get turned back on also, but was so tired that I didn't really believe my own eyes!

        I have no idea why my erroneous extra console_events didn't trigger a warning. I went through numerous versions of this thing, trying different stuff. At the time of this version I think I was trying to figure out if actually getting the console events was a blocking or non-blocking call. One other version of the code had a check to see if there was an event at all inside the if statement, then the actual read of the event was the second (now superfluous) one.

      Hi Marshall, thanks for modifying it and testing it with the two different consoles. That looked promising. Also, after writing what I wrote about the sample code and my own code both not working, my next thought was that if the problem appears in two different programs written by two different people, then maybe the problem was in my mouse, and that I should try re-running it using a better/newer mouse. But since you reproduced the same problem on your academic computer that I was having, I'm thinking now that the mouse is not the problem.

      But both of us are running Windows 10, and the Win32::Console module has been around for a lot longer than Windows 10 has. I wonder if it's a Windows 10 problem? An Internet search of Win32::Console reveals that other languages have versions of it as well. I wonder if they are encountering problems when running programs written in those other languages on Windows 10 machines? No way to pin that down since this is a Perl forum. I did notice that one discussion about this module on Perl in particular -- http://computer-programming-forum.com/53-perl/1fb16f663d489dbe.htm -- goes back to June of 1902, so maybe it is only a Windows 10 problem. But that date makes me even more impressed with the magnitude of what Larry has accomplished beyond just the brilliant design of Perl.

        HI fireblood,
        I did spend some hours messing with this. On the surface, it appeared simple. But I found out that it was not. I do kick myself in the behind because at one point, I did get the "2" for mouse instead of "1" for keyboard and then without thinking, changed so many things at once that I am unable to re-create that result. The false "ahh-ha" syndrome! Hey, it happens!

        I am sure that there is a way to make Win32::Console do what you want, I just don't know how. I think this is a case where the documentation is just not very good. However, Perl Tk GUI's work great. There will be more support here for Tk GUI than for low level console stuff. I commented just this day about the Tk TableMatrix object in another thread.

        It could be that this is what is called an "X Y" problem. You are asking how to do X, but what you really need to know is how to do Y. There are various formulations of this situation. Since you are talking about mouse clicks, some more detail about how you envision your interface working would be helpful. there are all kinds of zoomy GUI things like drop down context sensitive things (i.e. right click ala Windows menus) that are possible.

        It could even perhaps be that your code becomes more simple and no polling loop is required.

        Out of curiosity, where does this 16 buttons come from? I've never seen a Windows mouse with more than 3, but perhaps that exists with some sort of special thing for a gaming control?