Windows: How to sleep until key is pressed

D

Dilbert

I have a perl program (under windows vista) that records every
scancode / keystroke I make (it even records when I hit the Ctrl-key,
the Shift-key, F1, F2, etc....

So the program works ok for me, but it has a "do{...}until" loop that
better should be replaced by a sleep command. My concern is that I
waste a lot of CPU cycles in the do{...}until loop, that could be
avoided if I replace that loop by a sleep command

How can I achieve this ? does such a command exist under windows ?
(just in case you might ask: I already tried each and every option in
Term::Readkey, it definitely does not record when I hit the Ctrl-key,
Shift-key, etc...) Fortunately, Win32::Console does the job quite
well, it is just the do{...}until loop that bothers me because it
wastes CPU cycles.

Here is the program:

use strict;
use warnings;

use Win32::Console;

my $CONS_INP = Win32::Console->new(STD_INPUT_HANDLE);

while (1) {

# I want to sleep here until a key is pressed...
# How can I achieve this under Windows... ???

my @event;
do {
@event = $CONS_INP->Input() if $CONS_INP->GetEvents();
} until @event;

local $" = "', '";
print "event = ('@event')\n";

last if $event[5] == 27; # Escape key
}

Is there a perl module for windows that allows me to sleep (or wait ?)
until a key is pressed (even if it is the Ctrl-key, or the Shift-key)
 
C

C.DeRykus

I have a perl program (under windows vista) that records every
scancode / keystroke I make (it even records when I hit the Ctrl-key,
the Shift-key, F1, F2, etc....

So the program works ok for me, but it has a "do{...}until" loop that
better should be replaced by a sleep command. My concern is that I
waste a lot of CPU cycles in the do{...}until loop, that could be
avoided if I replace that loop by a sleep command

How can I achieve this ? does such a command exist under windows ?
(just in case you might ask: I already tried each and every option in
Term::Readkey, it definitely does not record when I hit the Ctrl-key,
Shift-key, etc...) Fortunately, Win32::Console does the job quite
well, it is just the do{...}until loop that bothers me because it
wastes CPU cycles.

Here is the program:

use strict;
use warnings;

use Win32::Console;

my $CONS_INP = Win32::Console->new(STD_INPUT_HANDLE);

while (1) {

    # I want to sleep here until a key is pressed...
    # How can I achieve this under Windows... ???

    my @event;
    do {
        @event = $CONS_INP->Input() if $CONS_INP->GetEvents();
    } until @event;

    local $" = "', '";
    print "event = ('@event')\n";

    last if $event[5] == 27; # Escape key

}

Is there a perl module for windows that allows me to sleep (or wait ?)
until a key is pressed (even if it is the Ctrl-key, or the Shift-key)

Win32 5.10.1 seems to understand sleep();

do {
@event = ...
sleep 1;
} until @event;

At least, hitting Ctrl-C interrupts the above. Don't know if
this'll do everything you need though.
 
D

Dilbert

So the program works ok for me, but it has a "do{...}until" loop that
better should be replaced by a sleep command. My concern is that I
waste a lot of CPU cycles in the do{...}until loop, that could be
avoided if I replace that loop by a sleep command
[snip]

Win32 5.10.1 seems to understand sleep();

    do {
        @event = ...
        sleep 1;
    } until @event;

At least, hitting Ctrl-C interrupts the above. Don't know if
this'll do everything you need though.

Thanks to Charles DeRykus for his solution. This is much better for
the CPU cycles.

However, the sleep command as it works currently has one disadvantage:
it does not respond quickly enough to normal keystrokes -- it takes a
second to recognise my keystrokes (except for Ctrl-C, which is
recognised immediately, but this doesn't help because I don't use Ctrl-
C very much)

Is there an advanced sleep command for Windows that sleeps for 1
second *maximum* ?, i.e. if any key was hit during the sleep of 1
second (let's say, for example, I hit the letter 'A' after 0.5
seconds), then the "advanced" sleep should be aborted immediately
(before the 1 second is over) so that the perl program can resumes its
normal processing after 0.5 seconds.

Something like "advanced_sleep_until_keypress_max_seconds(1)" ???

Is there a Win32 module to that effect ???
 
J

Jürgen Exner

C.DeRykus said:
Win32 5.10.1 seems to understand sleep();

Well, older versions do, too. For a long time, actually.
do {
@event = ...
sleep 1;
} until @event;

Yikes. That is still busy waiting. Maybe not quite as bad as the OP's
attempt, but still bad.

No, sorry, I don't have a better solution myself, unfortunately. That is
an area I haven't ventured in so far. But I could imagine that setting
up a signal handler for a keypress event may be a solution.

jue
 
P

Peter J. Holzer

I don't think you understand the definition of "sleep" here...

"sleep" means "do not do _anything_".

Monitoring the keyboard is "something", so it cannot be done while sleeping.

That is, if it could notice that something happened on the keyboard,
then it was not truly sleeping.

If it could notice that time has passed, then it was not truly sleeping
;-)

"Sleeping" means that the process has told the OS to suspend it until a
given amount of time has passed or until the process needs to be resumed
(or terminated) for another reason.

There is in fact one Unix system call (and corresponding Perl builtin
function) which has exactly the semantics Dilbert wants:

select

It tells the OS:

Suspend me until IO is possible on one of these file descriptors
or until x seconds have passed, whichever happens earlier.

hp
 
S

sln

AFAICT from the MSDN docs, ->Input should block until there is something
to read. That is, replace the whole do/until loop with

my @event = $CONS_INP->Input();
That could probably be tested with a print statement
and fingers on Ctrl-C.
do {
@event = $CONS_INP->Input() if $CONS_INP->GetEvents();
print ".";
} until @event;

I wouldn't know why if $CONS_INP->GetEvents() would be needed
unless he is managing his own queue, otherwise the perl thread
running this program would be blocked. And you can PeekMessage
I guess.

-sln
 
S

sln

Unfortunately select doesn't generally work on Win32, since it doesn't
have a proper unified fd model. The perl builtin calls a version that
only works on sockets.

The native Win32 replacement for select(2) is WaitForMultipleObjects,
which will wait for any HANDLE. This is wrapped by the Win32::IPC
module.

Arguably perl's select on Win32 ought to use WFMO to provide semantics
more like Unix' select(2), but it doesn't.

Ben

Probably in its simplest form all thats necessary is this:

#include <Windows.h>
#include <Winbase.h>
#include <Wincon.h>

HANDLE hStdin;
DWORD ret;

if (hStdin = GetStdHandle( STD_INPUT_HANDLE ))
{
while (1)
{
// Wait for state of a console input buffer handle to be signaled
ret = WaitForSingleObject( hStdin, INFINITE );
switch (ret)
{
// process low level buffer events
case WAIT_OBJECT_0:
if ( ReadConsoleInput( hStdin, ...) ) {
switch() {
...
}
}
else {
ExitError( "ReadConsole" );
}
break;

// Not waiting for timeouts
default:
ExitError( "WaitForSingleObject" );
break;
}
}
ExitError( "GetStdHandle" );

I'm sure Win32::IPC has the WFSO but can it get the
console handle via either CreateFile(..CONIN$..) or
GetStdHandle(STD_INPUT_HANDLE)? Then a module has to
do a low level input buffer read. Maybe thats what
the Win32::Console does and could do the async waiting
as well because I would think the module wouldn't just
be doing busy waiting (polling).

-sln
 
D

Dilbert

Unfortunately select doesn't generally work on Win32, since it doesn't
have a proper unified fd model. The perl builtin calls a version that
only works on sockets.

The native Win32 replacement for select(2) is WaitForMultipleObjects,
which will wait for any HANDLE. This is wrapped by the Win32::IPC
module.

Arguably perl's select on Win32 ought to use WFMO to provide semantics
more like Unix' select(2), but it doesn't.

Hurray !
I have resolved my problem - Thanks to everybody who responded

I am now using wait_any() of Win32::IPC, and it works perfectly.

Here is the complete program:

use strict;
use warnings;

use Win32::Console;
use Win32::IPC qw(wait_any);

my $CONS_INP = Win32::Console->new(STD_INPUT_HANDLE);
my @CONS_INL = ($CONS_INP);

LOOP1: while (1) {
# I want to sleep here until a key is pressed...
# How can I achieve this under Windows... ???
# use Win32::IPC does the trick.
# WaitForMultipleObjects([$CONS_INP]); # this works, but is
deprecated.

wait_any(@CONS_INL); # this works and is not deprecated

while ($CONS_INP->GetEvents) {
my @event = $CONS_INP->Input;

local $" = "', '";
print "event = ('@event')\n";

last LOOP1 if $event[5] and $event[5] == 27; # Escape key
}
}
 
S

sln

[snip]
Hurray !
I have resolved my problem - Thanks to everybody who responded

I am now using wait_any() of Win32::IPC, and it works perfectly.

Here is the complete program:

use strict;
use warnings;

use Win32::Console;
use Win32::IPC qw(wait_any);

my $CONS_INP = Win32::Console->new(STD_INPUT_HANDLE);
my @CONS_INL = ($CONS_INP);

LOOP1: while (1) {
# I want to sleep here until a key is pressed...
# How can I achieve this under Windows... ???
# use Win32::IPC does the trick.
# WaitForMultipleObjects([$CONS_INP]); # this works, but is
deprecated.

wait_any(@CONS_INL); # this works and is not deprecated

while ($CONS_INP->GetEvents) {
my @event = $CONS_INP->Input;

local $" = "', '";
print "event = ('@event')\n";

last LOOP1 if $event[5] and $event[5] == 27; # Escape key
}
}

Thats great. I'm suprised they had 'WaitForMultipleObjects'
as a method. Its not depricated in vc 2005, thats for sure.

You can also get/set the input console mode like this:
----------------------
my %val2mode = (
'0004' => 'ENABLE_ECHO_INPUT',
'0032' => 'ENABLE_INSERT_MODE',
'0002' => 'ENABLE_LINE_INPUT',
'0016' => 'ENABLE_MOUSE_INPUT',
'0001' => 'ENABLE_PROCESSED_INPUT',
'0064' => 'ENABLE_QUICK_EDIT_MODE',
'0008' => 'ENABLE_WINDOW_INPUT',
'0128' => 'UNKNOWN',
'0256' => 'UNKNOWN'
);
my %mode2val = reverse %val2mode;
my $curmode = $CONS_INP->Mode();

print "Current modes ($curmode):\n";
for my $val (map {1<<$_} 0 .. 10) {
if ($val & $curmode) {
if (exists $val2mode{ $val=sprintf ("%04s", $val) } ) {
print " $val - $val2mode{ $val }\n";
}
}
}
#$CONS_INP->Mode( $curmode | $mode2bit{ENABLE_MOUSE_INPUT}); ...
------------------

Good job!

-sln

Just a side note .., your getting key-down events.
Key-down events are almost never used except in games.
They generate a constant event stream when keys are held down.
I don't know about Win32::Console, but in the bowels of win32
you can filter out key-down events (not control state) before
it ever gets to the event queue (ie: the win32 input event buffer).

Key-up events is what is of normal interest. Its a little
more meaningfull if they can filtered.
Note the control key (states) with each key-up event as well.

Example:
---------------
while ($CONS_INP->GetEvents) {
my @event = $CONS_INP->Input;
if (@event) # no event mean error
{
# KEYS ...
if ($event[0] == 1) {
next unless $event[1]==0; # Only process key-up events

local $" = "', '";
print "event = ('@event')\n";
last LOOP1 if $event[5] == 27; # Escape key
}
# MOUSE ... (if ENABLE_MOUSE_INPUT)
if ($event[0] == 2) {
#
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top