You couldn't read eight posts? You do know the OP said he's using Linux?
Sorry I didn't see this before.
Jsmith's answer is the best answer so far (meaning it works and it works for the right reasons --it just requires direct read()s).
There is no need to spawn extra threads just for input handling. Idling a child thread hurts execution just as much as idling the main thread.
NCurses
If you can afford to get input somewhere other than the standard streams (cin), then you should seriously consider using
NCurses (
http://www.gnu.org/software/ncurses/ ) or some other keyboard input library. Chances are you will have to use your package manager to download and install the proper libraries:
sudo apt-get install ncurses-dev
ought to do it (or something similar).
Terminal Settings
If you are required to stick with the standard I/O streams, you will want to play with the input stream directly. And yes, it is a whole can of worms.
Keep in mind that the following code snippit only works on modern POSIX systems (like Unix: AIX, Sun OS, etc and Linux and Mac OS X). You can do the same things on Windows... it just has to be done a different way.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
#include <cstdio>
#include <iostream>
#include <limits>
#include <string>
#include <unistd.h>
#include <termios.h>
#include <poll.h>
static bool initialized = false;
static struct termios initial_settings;
//---------------------------------------------------------------------------
bool initialize()
{
if (!initialized)
{
initialized = (bool)isatty( STDIN_FILENO );
if (initialized)
initialized = (0 == tcgetattr( STDIN_FILENO, &initial_settings ));
if (initialized)
std::cin.sync_with_stdio();
}
return initialized;
}
//---------------------------------------------------------------------------
void finalize()
{
if (initialized)
tcsetattr( STDIN_FILENO, TCSANOW, &initial_settings );
}
//---------------------------------------------------------------------------
bool linebuffered( bool on = true )
{
struct termios settings;
if (!initialized) return false;
if (tcgetattr( STDIN_FILENO, &settings )) return false;
if (on) settings.c_lflag |= ICANON;
else settings.c_lflag &= ~(ICANON);
if (tcsetattr( STDIN_FILENO, TCSANOW, &settings )) return false;
if (on) setlinebuf( stdin );
else setbuf( stdin, NULL );
return true;
}
//---------------------------------------------------------------------------
bool echo( bool on = true )
{
struct termios settings;
if (!initialized) return false;
if (tcgetattr( STDIN_FILENO, &settings )) return false;
if (on) settings.c_lflag |= ECHO;
else settings.c_lflag &= ~(ECHO);
return 0 == tcsetattr( STDIN_FILENO, TCSANOW, &settings );
}
//---------------------------------------------------------------------------
#define INFINITE (-1)
bool iskeypressed( unsigned timeout_ms = 0 )
{
if (!initialized) return false;
struct pollfd pls[ 1 ];
pls[ 0 ].fd = STDIN_FILENO;
pls[ 0 ].events = POLLIN | POLLPRI;
return poll( pls, 1, timeout_ms ) > 0;
}
//---------------------------------------------------------------------------
using namespace std;
int main()
{
if (!initialize())
{
cout << "You must be a human to use this program.\n";
return 1;
}
try {
string name, password;
linebuffered( false );
echo( false );
cout << "Press any key to wake me up.\n";
while (true)
{
cout << "Zzz..." << flush;
if (iskeypressed( 500 )) break;
}
if (cin.get() == '\n') cout << "\nHmm... Not much of an 'any' key"
" test when you press ENTER...\n";
else cout << "\nThanks!\n";
echo();
linebuffered();
cout << "\nPlease enter your name> " << flush;
getline( cin, name );
cout << "Please enter a (fake) password> " << flush;
echo( false );
getline( cin, password );
echo();
cout << "\nSo, your password is \"" << password << "\", hmm?\n";
cout << "\nPress ENTER to quit> " << flush;
cin.ignore( numeric_limits <streamsize> ::max(), '\n' );
cout << "\nGood-bye " << name << ".\n";
}
catch (...) { }
finalize();
return 0;
}
|
Again, the poll() function is available on most modern systems. If it doesn't have poll(), you can use the old BSD select() as
jsmith did above.
The above leaves the terminal in a relatively "cooked" state: you can still use Ctrl-C to kill the program, Ctrl-S/Q to stop/start, Ctrl-Z; pressing ENTER will still be understood as '\n', etc. If you need to handle things like arrow keys and the like, that is a deeper can of worms: use NCurses.
Final Notes
you
must call finalize() before terminating or your users will hate you. If this is homework then the above should be good enough (and your professor
will know that you got this code from the internet, so own-up to it right up front). If this is a professional application, then you should already have some signal handlers (with sigaction()) in there to catch abnormal program termination. Be sure to finalize() from them too.
Hmm, I don't think I've forgotten anything...
Hope this helps.
[edit] Added INFINITE timeout, and a default timeout of zero milliseconds. (Sorry I forgot that...)