Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28631 Discussions

Exiting an active loop with a keypress

dboggs
New Contributor I
1,170 Views
My program uses an infinite loop to print various things to a text window. I need it to stop whenever the user presses a key.

I thought this would be a simple matter using the function GETCHARQQ () or INCHARQQ (), but these will halt execution until the key is pressed. The solution would besomething like PEEKCHARQQ (), which looks to see if there is a character in the keyboard buffer and then continues. But this is only available in a Console application. I need something comparable in a Quickwin application--maybe some API call, if it's not a headache to implement.

Any ideas?
0 Kudos
6 Replies
anthonyrichards
New Contributor III
1,170 Views
Create a simple dialog box, DLG.
Display it using DLGMODELESS. It doesn't have to be visible (e.g. DLGMODELESS(DLG,SW_HIDE) ).
Subclass the dialog.
In the subclass dialog procedure, handle the WM_SYSCOMMAND message.
This will be issued and detected whenever you press ALT+ another key (but will not tell you what the other key is).
To stop your loop, press ALT+ another key.
When the dialog procedure detects the WM_SYSCOMMAND message, get it to set a module variable accessible by your infinite loop to .TRUE. (or .FALSE. depending on your logic preference), e.g. stopprint=.TRUE.

eg

Module Loopmodule
logical stopprint
end module
...

USE LOOPMODULE

stopprint=.false.

Do While(.not.stopprint)

end do
...

subroutine Subclassdlgproc(DLG, mesg, wparam, lparam)
...
select case (mesg)
case(wm_syscommand)
stopprint=.true.
subclassdlgproc=0
case default
subclassdlgproc = DefDlgProc (hwnd, msg, wParam, lParam)
end select
return
end sub

and it will cause your loop to exit.

close the dialog when you no longer need it.
0 Kudos
anthonyrichards
New Contributor III
1,170 Views
Create a simple dialog, DLG
Display it using DLGMODELESS. You can even make it invisible, eg.

DLGMODELSS(DLG, SW_HIDE)

This dialog requires a message loop, e.g.

...
DO WHILE( GetMessage (MESG, NULL, 0, 0) )
! Note that DlgIsDlgMessage must be called in order to give
! the dialog box first chance at the message.
IF ( DlgIsDlgMessage(MESG) .EQV. .FALSE. ) THEN
LRET = TranslateMessage( MESG )
RET = DispatchMessage( MESG )
END IF
END DO
CALL DLGUNINIT(DLG) ! Release the dialog resources
...

Use a dialog call-back subroutine to Subclass the dialog when it is created but before it is displayed.
In the subclassed procedure, handle the WM_SYSCOMMAND which is issued whenever you press ALT+ another key (but does not tell you what the other key is).

When this message is detected, use the fact to set a module variable that is accessible by the code driving your printing loop and use that variable to stop the loop.

eg.

Module Loopvariable
logical stopprint
end module

Program myprogram
use loopvariable
...
stopprint=.false.
do while(.NOT.stopprint)
...
end do

end program
....
subroutine Subclassproc(hwnd, mesg, wparam, lparam)
use loopvariable
...
select case (mesg)

case(WM_SYSCOMMAND)
stopprint=.true.
Subclassproc=0

case default
Subclassproc = DefDlgProc (hwnd, msg, wParam, lParam)
end select
return
end sub

Start the dialog when you start the print, then you can stop the print any time by pressing ALT+ another key.
0 Kudos
anthonyrichards
New Contributor III
1,170 Views
Suggestion#2:

Add a '&Stop Print' menu item somewhere in your application's menu bar.
Assign an accelerator key combination to it (e.g. ALT+S).
In the call-back routine assigned to it, set the stop print module variable as described in suggestion#1 above.
When the accelerator key combination is pressed, the module variable will be set by your callback and your loop should be stopped.

P.S. Ignore, because having tried it, it doesn't work.
0 Kudos
dboggs
New Contributor I
1,170 Views
Thanks for the ideas Anthony. Not sure I want to pursue it though. I had hoped the solution would be much simpler. Surely there's an easily-implemented API call that willtell whether the keyboard buffer is empty or if there's a keystroke in it? (how else could they have implemented such a Fortran function for console apps?) That's all I need.
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,170 Views
>>how else could they have implemented such a Fortran function for console apps?)

A console app does not use the Windows message system (from the perspective of the console app).
If you place the window in focus (e.g. click on title bar or corner), then hit a key, the Window is going to receive a message containing the key stroke (and the key is consumed). What you are looking for is something similar to kbhit(); that senses the keystroke without consuming the key.

What you might be able to do is:

a) set a flag in your program that states "In Keystroke interruptable section"
b) Modify your message loop to detect when Ctrl, Alt, or Shift is pressed
if flag a) is set then also set exit loop flag

The Ctrl, Alt or Shift will not get eaten by the message loop (the message will get eaten, but the key state will be re-sent along with the key code).

An alternate method would be to use Ctrl-Left-Mouse-Click or other wierd combination.
The titlebar menu selection, as mentioned earlier is also viable.

You still have an issue of keystroke pressed while your app window is not in focus but your eyes are focused on the window. IOW key(s) entered into different window. Using a benign method might be something to consider.

Jim Dempsey
0 Kudos
IanH
Honored Contributor II
1,170 Views
Having something that works off the main message queue like Anthony suggests would be more characteristic (process a WM_COMMAND from an accelerator or a WM_CHAR, etc - set a flag - processing code checks that flag every iteration or so). I don't know how easy that is to set up in quick-win.

You can use the PeekMessage API to see if there are messages in the message queue for a thread (~ keyboard buffer equivalent) that correspond to keyboard events. You can also use the GetKeyState API to check the "logical" state of a particular key (consistent with historical message processing by the thread) or the GetAsyncKeyState API to check (more or less) the physical state.

Whether these are easily relevant to you depends on the organisation of threads in quick-win - I'm not familiar with quickwin to really be commenting, but I suspect that the thread handling input in a quickwin application is not going to be the thread running your fortran code. If so, you may need to AttachThreadInput or similar - this is out of my domain.

The console subsystem provides a keyboard buffer service for console applications. That buffer can be checked using (Read|Peek)ConsoleInput.
0 Kudos
Reply