Unfortunately, what you are asking for is not possible in plain ISO C. However, most platforms offer platform-specific extensions which provide the functionality that you require.
Since you stated that your question applies to Microsoft Windows, the two functions that you are looking for are WaitForSingleObject
and ReadConsoleInput
. You won't be able to use the function scanf
or C-style streams (FILE*
) for this.
The function WaitForSingleObject
allows you to wait until a specific object is in a signalled state. It also allows you to set a timeout (which should be 10 seconds in your case).
One type of object that WaitForSingleObject
can wait for is a console handle. It will be in a signalled state when there is new input available to be read by ReadConsoleInput
. According to my tests, it won't work reliably with ReadConsole
though, because even if WaitForSingleObject
indicates that input is waiting, the function ReadConsole
will still sometimes block. This is because in contrast to ReadConsoleInput
, the function ReadConsole
will filter some events. Therefore, attempting to read one event with ReadConsole
may actually attempt to read more than one raw event, which will cause the function to block if there are no non-filtered raw events available. The function ReadConsoleInput
does not have this problem, as it works with raw events directly and does not filter any.
Here is my program which uses the functions mentioned above, and does exactly what you asked for.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void DoDefaultTask()
{
printf( "Doing default task.\n" );
}
void DoTask1()
{
printf( "Doing task #1.\n" );
}
void DoTask2()
{
printf( "Doing task #2.\n" );
}
int main(void)
{
INPUT_RECORD ir;
HANDLE hConInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwReadCount;
bool should_prompt = true, quit = false;
while ( !quit )
{
//prompt user for input
if ( should_prompt )
{
printf( "Please select an option: " );
should_prompt = false;
}
//flush output
fflush( stdout );
switch ( WaitForSingleObject( hConInput, 10000 ) )
{
case WAIT_OBJECT_0:
//attempt to read input
if ( !ReadConsoleInput( hConInput, &ir, 1, &dwReadCount ) || dwReadCount != 1 )
{
fprintf( stderr, "Unexpected input error!\n" );
exit( EXIT_FAILURE );
}
//only handle key-down events
if ( ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown )
break;
//echo output, if character is printable
if ( isprint( (unsigned char)ir.Event.KeyEvent.uChar.AsciiChar ) )
{
printf( "%c", ir.Event.KeyEvent.uChar.AsciiChar );
}
printf( "\n" );
switch ( ir.Event.KeyEvent.uChar.AsciiChar )
{
case '1':
DoTask1();
break;
case '2':
DoTask2();
break;
case 'q':
printf( "Quitting program...\n" );
quit = true;
break;
default:
printf( "Unknown command.\n" );
}
should_prompt = true;
break;
case WAIT_TIMEOUT:
printf( "Timeout!\n" );
DoDefaultTask();
should_prompt = true;
break;
default:
fprintf( stderr, "unexpected error!" );
exit( EXIT_FAILURE );
}
}
return 0;
}
This program has the following behavior:
Please select an option: 1
Doing task #1.
Please select an option: 2
Doing task #2.
Please select an option: 3
Unknown command.
Please select an option: 4
Unknown command.
Please select an option: Timeout!
Doing default task.
Please select an option: Timeout!
Doing default task.
Please select an option: 1
Doing task #1.
Please select an option: 3
Unknown command.
Please select an option: 2
Doing task #2.
Please select an option: 1
Doing task #1.
Please select an option: Timeout!
Doing default task.
Please select an option: q
Quitting program...