Getting unbuffered keyboard input (e.g., Arrow keys)

[size=150][color=darkred]Hello QNX experts and afficionados!

I’m new to QNX (and C++) and I’m trying to read Unbuffered single keystrokes from the keyboard (e.g., F1 to F12, arrow keys, Esc, etc.) WITHOUT waiting for the Enter key to be pressed (Linebuffered input).

This is rather simple to do in x86 Assembly language using a PC BIOS but it seems to be rather challenging in Neutrino using C++.

I would appreciate it if someone could point me in the right direction…

Thanks,

Kevin Jackson[/size]

That’s look simpler then assembly code to me :wink:

struct termios old_termios;

static int raw( int fd )
{
struct termios termdef;

if( tcgetattr( fd, &old_termios ) )
    return( -1 );

termdef = old_termios;

termdef_p.c_lflag &= ~( ECHO|ICANON|ISIG|
          ECHOE|ECHOK|ECHONL );

return( tcsetattr( fd, TCSADRAIN, &termdef ) );

}

Old_termios is used to restore thing to their original state upon program termination.

[size=150][color=green]Mario:

Thanks for the quick reply on a Sunday!

Being a QNX Neutrino (and C++ novice) it has taken me a bit of time to get this to compile, but it’s not yet working for me.

A couple of questions remain:

  1. Where does the File Descriptor ( int fd ) that I pass to raw() come from? I’ve opened “/dev/con1” and passed the return from the open() function as the argument to raw() but I’m not sure if that’s correct.

  2. In the statement where bits are cleared I changed termdef_p.c_lflag to termdef.c_lflag so it would compile. I presume termdef_p was just a typo…

  3. After calling raw() I’ve tried to read keystrokes using both cin.get(ch) and ch=fgetchar() but there is still no response until the Enter key is hit. Should I be using some OTHER function to read unbuffered raw keystrokes after calling raw()?

Finally, perhaps it’s just me since I’m still inexperienced enough to find C and C++ syntax to be pretty arcane, but in x86 Assembly language it’s only TWO lines of code to read a keystroke using the PC’s BIOS:

 mov     ah,0x10
 int        0x16

Of course that’s x86/BIOS hardware specific and most Operating Systems REALLY object to executing BIOS level functions in applications, but it’s rather simple and straightforward and it continues to drive me a bit batty that I’m still only 1/100th as productive in C++ as I am in ASM…[/size]

I’m addicted to this…

You can use 0 (stdin) or better fileno( stdin ). That way the program will work from telnet, pterm, and be 100% portable.

Yes sorry.

Try read(). I’m not sure about cin though since it probably has its own buffering. Same for fgetchar which by definition is buffered.

See there is two level here, there is buffering perform by the device itself and there is buffering done by the fonctionally of the C / C++ library. The raw function turn the device buffering off. However there is still the issue of the C/C++ library, simplest way is to use functions that do not buffer data: read/write.

As I said read() should do it.

Imagine yourself not knowOMG ASM or BIOSes. I think you’d find them pretty arcane and criptic :wink:

Then stick to ASM :wink:

dear sir,
please see my code:

// set terminal to unbuffered input. Input Keys are not shown and immediately
// returned to kernel without ENTER key.
int unbuffered_input( int fd )
{
struct termios termios_p;

if( tcgetattr( fd, &termios_p ) != 0 )
{
	printf("ERROR: Unable to get terminal settings (%s)\n", strerror(errno));
  	return( -1 );
}

termios_p.c_cc[VMIN] = 1;
termios_p.c_cc[VTIME] = 0;
termios_p.c_lflag &= ~ICANON;
termios_p.c_lflag &= ~ECHO;

return( tcsetattr( fd, TCSADRAIN, &termios_p ) );

}

// set terminal to standard buffered input. Input Keys are displayed on screen
// and terminal waits for ENTER key.
int buffered_input( int fd)
{
struct termios termios_p;

if( tcgetattr( fd, &termios_p ) != 0)
{  
	printf("ERROR: Unable to get terminal settings (%s)\n", strerror(errno));
	return( -1 );
}

termios_p.c_cc[VMIN] = 1;
termios_p.c_cc[VTIME] = 0;
termios_p.c_lflag |= ICANON;
termios_p.c_lflag |= ECHO;

return( tcsetattr( fd, TCSADRAIN, &termios_p ) );

}

regards,
tuyndie

mario, You are a C/C++ QNX Master, but KevinJackson - not (and he mentioned that)
:slight_smile:

ok, Kevin - use secret link “search” on openqnx.com and/or google :slight_smile:.
shortly: the guilty is… POSIX: en.wikipedia.org/wiki/POSIX

You can do this (read keypress without delay) in two ways:

  • method #1 - use ncurses lib - the easiest, portable & really works :slight_smile:, but has has some disadvantages
    or
  • method #2 - use select() or ionotify() + MsgReceive() - the most powerful, but a ‘little’ bit complicated :slight_smile:

— method #1 - use ncurses lib —

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <ncurses.h>
//don’t forget to link ncurses lib while builing exe (add -lncurses):
// cc -c your_code.cc
// cc -M -o your_exe_name your_code.o -lncurses

//exemplary key codes for ncurses getch():
#define K_ENTER 0x0A
#define K_ENTER2 0x0D //ncurses only (and very strange :neutral_face:)
#define K_ESC 0x1B
#define K_DOWN 0x102
#define K_UP 0x103
#define K_LEFT 0x104
#define K_RIGHT 0x105

char *get_progress_str(int *pprogress_val = NULL, int val2add = 1)
{
static int progress_val = 0;
static char progress_str[8];
char progress_chars[] = “-\|/”;
int pstr_len = strlen(progress_chars);

if (pprogress_val == NULL)
	pprogress_val = &progress_val;
*pprogress_val = *pprogress_val % pstr_len;
sprintf( progress_str, "[%c]", progress_chars[*pprogress_val] );
*pprogress_val = *pprogress_val + val2add;

return progress_str;

}

int main(int argc, char *argv[])
{
int key;

initscr();
cbreak();
noecho();
keypad( stdscr, TRUE );
move( 0, 0 );

for (;;)
{
	key = getch();

	mvprintw( 0, 0, "%s key = %d = 0x%X", get_progress_str(), key, key );
	clrtoeol();
	refresh();
	
	if ( key == ERR ) //getch() reported an error
		break;

	if ( key == K_ESC ) //<Esc> was pressed
		break;
}

refresh();
keypad(stdscr, FALSE);
endwin();

return EOK;

}

— method #2 - use select() == ionotify() + MsgReceive() —
// → next post :slight_smile:

how read keypress without delay;
part 2 of 2;

— method #2 - use select() == ionotify() + MsgReceive() —

#include “term_keyb.h”
//…add here other *.h files

// key codes (term raw read):
#define K_ENTER 0x0A
#define K_ESC 0x1B
#define K_DOWN 0x00425B1B
#define K_UP 0x00415B1B
#define K_LEFT 0x00445B1B
#define K_RIGHT 0x00435B1B

int main(int argc, char *argv[])
{
int key, res;

init_term_keyb();

res = wait_for_keypress( &key, -1 );
if( !res )
	key = 0;

if ( key == K_ESC ) //<Esc> was pressed
	break; 

close_term_keyb();

}

// — term_keyb.h —

#ifndef __TERM_KEYB_H
#define __TERM_KEYB_H

int init_term_keyb(void);
int close_term_keyb(void);
int wait_for_keypress_break(void);
bool wait_for_keypress( int *key_code, int tout = -1 );

#endif //__TERM_KEYB_H

// — term_keyb.cc —

#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <sys/neutrino.h>
#include <sys/netmgr.h> //ND_LOCAL_NODE
#include <sys/iomsg.h>
#include <sys/select.h>

#include “term_keyb.h”

#define IONOTIFY_PULSE_CODE _PULSE_CODE_MINAVAIL+0
#define PVAL_ARMED 0
#define PVAL_NOTARMED 1 //some chars in keyb buff
#define PVAL_TIMEOUT 2
#define PVAL_BREAK 3

int						fd = -1;
int                     channel_id = -1;
int 					connection_id = -1;
struct termios			termios_old;

int raw_term( int fd, struct termios *termios_old )
{
struct termios termios_new;

if( tcgetattr(fd, termios_old) != 0 )
	return -1;

memcpy( &termios_new, termios_old, sizeof(termios_new) );

termios_new.c_cc[VMIN]  =  1;
termios_new.c_cc[VTIME] =  0;
termios_new.c_lflag &= ~( ECHO|ICANON|ISIG|ECHOE|ECHOK|ECHONL );
termios_new.c_oflag &= ~( OPOST );

return( tcsetattr(fd, TCSANOW, &termios_new) );

}

int unraw_term( int fd, struct termios *termios_old )
{
return( tcsetattr(fd, TCSADRAIN, termios_old) );
}

int raw_read_term( int fd, int *key_code )
{
int res;

//raw_term( fd, &termios_old );
*key_code = 0;
res = read( fd, key_code, 4 );
//unraw_term( fd, &termios_old );

return res;

}

int init_term_keyb(void)
{
fd = -1;
channel_id = -1;
connection_id = -1;

fd = open( ttyname(STDIN_FILENO), O_RDONLY | O_NONBLOCK );
if( fd != -1 )
{
	channel_id = ChannelCreate( 0 );
	if( channel_id != -1 )
	{
		connection_id = ConnectAttach( ND_LOCAL_NODE, 0, channel_id, _NTO_SIDE_CHANNEL, 0 );
		if( connection_id != -1 )
		{
			raw_term( fd, &termios_old );
		}
		else
			close_term_keyb();
	}
	else
		close_term_keyb();
}

return connection_id;

}

int close_term_keyb(void)
{
if( connection_id != -1 )
{
wait_for_keypress_break();
unraw_term( fd, &termios_old );

	ConnectDetach( connection_id );
	connection_id = -1;
}

if( channel_id != -1 )
{
	ChannelDestroy( channel_id );
	channel_id = -1;
}

if( fd != -1 )
	close( fd );
	
return 0;

}

int wait_for_keypress_break(void)
{
struct sigevent exit_sigevent;
int res;

if( connection_id != -1 )
{
	SIGEV_PULSE_INIT( &exit_sigevent, connection_id, SIGEV_PULSE_PRIO_INHERIT, IONOTIFY_PULSE_CODE, PVAL_BREAK );
	res = MsgSendPulse( exit_sigevent.__sigev_un1.__sigev_coid,
		exit_sigevent.__sigev_un2.__st.__sigev_priority, exit_sigevent.__sigev_un2.__st.__sigev_code,
		exit_sigevent.sigev_value.sival_int ); //PVAL_BREAK
	//if (res == -1) log_error()..;
	return res;
}
else
	return connection_id;

}

bool wait_for_keypress( int *key_code, int tout = -1 )
{
timer_t timer_id;
int timer_created;
struct itimerspec timer_specs;
struct sigevent ionotify_sigevent;
int rcvid;
struct _pulse pulse;
bool is_armed;
int res; //ionotify_res;
int fkey_code;
int fkey_code_size;

if( connection_id == -1 ) return false;
fkey_code_size = -1; //0

if( tout == 0 )
{
	fkey_code_size = raw_read_term( fd, &fkey_code );
}
else
{
	SIGEV_PULSE_INIT( &ionotify_sigevent, connection_id, SIGEV_PULSE_PRIO_INHERIT, IONOTIFY_PULSE_CODE, PVAL_ARMED );
	res = ionotify( fd, _NOTIFY_ACTION_POLLARM, _NOTIFY_COND_INPUT, &ionotify_sigevent );
	if( res != -1 )
	{
		is_armed = ( (res & _NOTIFY_COND_INPUT) == 0 );
		if( !is_armed )
		{
			fkey_code_size = raw_read_term( fd, &fkey_code );
		}
		else
		{
			if( tout > 0 )
			{
				SIGEV_PULSE_INIT( &ionotify_sigevent, connection_id, SIGEV_PULSE_PRIO_INHERIT, IONOTIFY_PULSE_CODE, PVAL_TIMEOUT);
				timer_created = ( timer_create( CLOCK_REALTIME, &ionotify_sigevent, &timer_id ) == 0 );
				if( timer_created )
				{
					set_timer_specs( &timer_specs, tout, 0 );
					timer_settime( timer_id, 0, &timer_specs, NULL );
				}
			}
			else
				timer_created = false;
			
			rcvid = MsgReceive( channel_id, &pulse, sizeof(pulse), NULL );
			if( rcvid == 0 ) //pulse received
				if( pulse.code == IONOTIFY_PULSE_CODE )
					if( pulse.value.sival_int == PVAL_ARMED )
						fkey_code_size = raw_read_term( fd, &fkey_code );
			
			if( timer_created )
			{
				timer_delete( timer_id );   
				timer_created = false;
			}
		}
	}
}

if( fkey_code_size > 0 )
{
	if( key_code != NULL )
		*key_code = fkey_code;
	return true;
}
else
{
	return false;
}

}

// code end.

I was too fast; It should be:

int main(int argc, char *argv[])
{
int key, res;

init_term_keyb();

for( ; ; )
{
res = wait_for_keypress( &key, -1 );

 if( !res ) 
    key = 0;
 if ( key == K_ESC ) //<Esc> was pressed
    break;

}

close_term_keyb();

return 0;
}

// Variation on Mario’s code; replacement for getchar() that returns as soon
// as key is pressed.

char get_one_char(){
struct termios termios_p;
char c=0;

if(tcgetattr(STDIN_FILENO, &termios_p ) ){
return(0);
}
termios_p.c_cc[VMIN] = 1;
termios_p.c_cc[VTIME] = 0;
termios_p.c_lflag &= ~(ECHO|ICANON|ECHOE|ECHOK|ECHONL);
termios_p.c_oflag &= ~OPOST;
tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_p );
tcflush(STDIN_FILENO, TCIFLUSH);
c = getchar();
termios_p.c_lflag |= (ECHO|ICANON|ISIG|ECHOE|ECHOK|ECHONL);
termios_p.c_oflag |= OPOST;
tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_p );
return c;
}

I wonder what happen if the key is pressed while outside this function.

Mario, tuyndie, oink and ingraham:

Thank you all VERY much for the time you have all taken to give me some direction on this. I’ve tried the Mario, tuyndie, and ingraham approaches without success. The source code follows in case anyone wants to compile it and run it to see what I’m seeing. It appears that ECHO never turns off and neither does line buffering.

When I use read(fileno(stdin), charbuff, 1); after calling Mario’s raw(fileno(stdin)) function the program never even displays the starting banner until after the Enter key is hit which is pretty much the opposite of what I would expect to happen so I commented it out for now.

At any rate I’m sure it’s probably something simple I’m doing wrong (or MORE than one simple thing) due to my lack of experience with C++ and/or Neutrino and I would appreciate any direction anyone can offer. Regarding sticking with ASM, I wish I could because C++ makes me feel like an amateur despite 20 years of software engineering experience but trying to call Operating System functions and a bunch of libraries of C/C++ functions from ASM is probably at least as challenging as learning the C++ language itself so I’m determined to persevere despite the challenges and a critical looming deadline on my current project…

Oink, you’re apparently a very prolific coder! I’m going to dive into the POSIX link and the source code you sent me and give it a try as well but it’s a rather intimidating and massive chunk of code so I’ve saved it for last…

Warmest Regards,

Kevin Jackson

The source code follows…

#include
#include

#include <termios.h> //~~~~ Required by termios structure, tcgetattr and tcsetattr
#include <fcntl.h> //~~~~ Required by open() and close()

using namespace std;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

struct termios termios_Backup; 		//~~~~ termios_Backup is used to restore things to their original state upon program termination. 

static int	raw( int );								//~~~~ Function prototype
char			get_one_char();					//~~~~ Function prototype
int 			buffered_input( int );					//~~~~ Function prototype
int 			unbuffered_input( int );				//~~~~ Function prototype

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

int main(int argc, char *argv[])
{
cout << “\n";
cout << “Test of single keystroke retrieval.\n”;
cout << "
\n\n”;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	cout	<<	" STDIN_FILENO is: "	<<	STDIN_FILENO	<<	"\n";	//~~~ Making sure I understand what STDIN_FILENO is.
	cout	<<	"fileno(stdin) is: "	<<	fileno(stdin)	<<	"\n\n";	//~~~ Making sure I understand what fileno() does.

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ Test OpenQNX QNX Master Mario’s raw() function ~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

raw(fileno(stdin));					// Execute Mario's raw() function

cout	<<	"\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

char	charbuff[2]={ 0, 0 };		//~~~~ A little char array to hold the results of a one byte read()
char	keycode = '?';	                //~~~~ Set char variable to '?' to see if fgetchar() function returns anything else...

/*
read(fileno(stdin), charbuff, 1);
cout << "read(fileno(stdin),1) results: " << charbuff[0] << “\n”;
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

cout	<<	"hit a single character key to test fgetchar() after raw()...\n";
keycode = fgetchar();
cout	<< "fgetchar() AFTER buffered_input function results: " << keycode << "\n\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

keycode = '?';							//~~~~ Set char variable to '?' to see if cin.get function returns anything else...

cout	<<	"hit a single character key to test cin.get() after raw()...\n";
cin.get(keycode);
cout	<< "cin.get() AFTER buffered_input function results: " << keycode << "\n\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ Test OpenQNX ingraham’s get_one_char() function ~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

keycode = '?';							//~~~~ Set char variable to '?' to see if get_one_char() returns anything else...

cout	<<	"hit a single character key to test get_one_char()...\n";

// sleep( 2 ); //~~~~ Wait a few seconds after prompt so user has time to hit a key…

keycode = get_one_char();		// Execute ingraham's get_one_char() function
cout	<< "get_one_char() results: " << keycode << "\n\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ Test OpenQNX tuyndie’s buffered_input() function ~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

buffered_input( STDIN_FILENO );		// Execute tuyndie's buffered_input() function

keycode = '?';									//~~~~ Set char variable to '?' to see if fgetchar() function returns anything else...

cout	<<	"hit a single character key to test fgetchar() after buffered_input()...\n";
keycode = fgetchar();
cout	<< "fgetchar() AFTER buffered_input function results: " << keycode << "\n\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

keycode = '?';									//~~~~ Set char variable to '?' to see if cin.get function returns anything else...

cout	<<	"hit a single character key to test cin.get() after buffered_input()...\n";
cin.get(keycode);
cout	<< "cin.get() AFTER buffered_input function results: " << keycode << "\n\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ Test OpenQNX tuyndie’s unbuffered_input() function ~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

unbuffered_input( STDIN_FILENO );		// Execute tuyndie's unbuffered_input() function

keycode = '?';										//~~~~ Set char variable to '?' to see if fgetchar() function returns anything else...

cout	<<	"hit a single character key to test fgetchar() after unbuffered_input()...\n";
keycode = fgetchar();
cout	<< "fgetchar() AFTER unbuffered_input function results: " << keycode << "\n\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

keycode = '?';										//~~~~ Set char variable to '?' to see if cin.get function returns anything else...

cout	<<	"hit a single character key to test cin.get() after unbuffered_input()...\n";
cin.get(keycode);
cout	<< "cin.get() AFTER unbuffered_input function results: " << keycode << "\n\n";

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ Restore termios to original state and exit.~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	int		tcsetattr_return = tcsetattr( fileno(stdin), TCSADRAIN, &termios_Backup );			//~~~~ Restore original state of termios
			if	(tcsetattr_return)
				cout	<<	"Error on tcsetattr (termios restore): " << 	tcsetattr_return << "\n";
			else
				cout	<<	"tcsetattr (termios restore) successful: " << 	tcsetattr_return << "\n";

	cout	<<	"\n*** End of Get_A_Keystroke test ***\n";
	
	return EXIT_SUCCESS;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ Mario’s raw() function ~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static int raw( int fd ) 
	{ 
		struct termios termios_New; 

		int		tcgetattr_return = tcgetattr( fd, &termios_Backup );
		if		(tcgetattr_return)
				{
				cout	<<	"Error on tcgetattr: " << 	tcgetattr_return << "\n";
				return	tcgetattr_return;
				}
		else
				cout	<<	"tcgetattr successful: " << 	tcgetattr_return << "\n";

		termios_New = termios_Backup;						//~~~~ Copy structure that was filled by tcgetattr
		termios_New.c_lflag &= ~( ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL );	//~~~~ Clear bits

		int		tcsetattr_return = tcsetattr( fd, TCSADRAIN, &termios_New );
		if		(tcsetattr_return)
				{
				cout	<<	"Error on tcsetattr: " << 	tcsetattr_return << "\n";
				return	tcsetattr_return;
				}
		else
				{
				cout	<<	"tcsetattr successful: " << 	tcsetattr_return << "\n";
				return	tcsetattr_return;
				}
	}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ ingraham’s variation on Mario’s code: replacement for getchar() that returns as soon as a key is pressed. ~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

char get_one_char()
{
struct termios termios_Temp;
char ch=0;

		if ( tcgetattr ( STDIN_FILENO, &termios_Temp ) )
			{
				return(0);
			}

		termios_Temp.c_cc[VMIN] = 1;
		termios_Temp.c_cc[VTIME] = 0;
		termios_Temp.c_lflag &= ~( ICANON | ECHO | ECHOE | ECHOK | ECHONL );
		termios_Temp.c_oflag &= ~OPOST;

		tcsetattr ( STDIN_FILENO, TCSADRAIN, &termios_Temp );

		tcflush ( STDIN_FILENO, TCIFLUSH );

		ch = getchar();

		termios_Temp.c_lflag |= ( ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL );
		termios_Temp.c_oflag |= OPOST;
		tcsetattr ( STDIN_FILENO, TCSADRAIN, &termios_Temp );
		return ch;
	}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ tuyndie’s buffered_input() function. ~~~~
// Set terminal to standard buffered input.
// Input Keys are displayed on screen and terminal waits for ENTER key.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

int buffered_input( int fd)
{
struct termios termios_Temp;

		if( tcgetattr( fd, &termios_Temp ) != 0)
			{
				//	printf("ERROR: Unable to get terminal settings (%s)\n", strerror(errno));
				cout	<<	"ERROR: Unable to get terminal settings\n";
				return( -1 );
				}

		termios_Temp.c_cc[VMIN] = 1;
		termios_Temp.c_cc[VTIME] = 0;
		termios_Temp.c_lflag |= ICANON;
		termios_Temp.c_lflag |= ECHO;

		return( tcsetattr( fd, TCSADRAIN, &termios_Temp ) );
	}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~ tuyndie’s unbuffered_input() function. ~~~~
// Set terminal to unbuffered input.
// Input Keys are not shown and immediately returned to kernel without ENTER key.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

int unbuffered_input( int fd )
{
struct termios termios_Temp;

		if( tcgetattr( fd, &termios_Temp ) != 0 )
			{
				//	printf("ERROR: Unable to get terminal settings (%s)\n", strerror(errno));
				cout	<<	"ERROR: Unable to get terminal settings\n";
				return( -1 );
			}

		termios_Temp.c_cc[VMIN] = 1;
		termios_Temp.c_cc[VTIME] = 0;
		termios_Temp.c_lflag &= ~ICANON;
		termios_Temp.c_lflag &= ~ECHO;

		return( tcsetattr( fd, TCSADRAIN, &termios_Temp ) );
	}

What about:
#include <stdio.h>
#include <unistd.h>
#include <termios.h>

struct termios old_termios_p;

int raw(){

struct termios termios_p;

if(tcgetattr(STDIN_FILENO, &termios_p ) ){
	return(0);
}

old_termios_p = termios_p;

termios_p.c_lflag &= ~( ECHO|ICANON|ISIG| ECHOE|ECHOK|ECHONL );

return tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_p );

}

int unraw() {

return tcsetattr(STDIN_FILENO, TCSADRAIN, &old_termios_p );

}

int main(void)
{

char buf;

raw();

read(STDIN_FILENO, &buf, sizeof ( buf ));

printf("%d\n", buf );

unraw();
	
return 0;

}

Mario:

I copied your latest code EXACTLY (without even so much as adding a comment or a tab) and compiled and ran it. Clearly it’s neither switching the console into raw mode nor turning off ECHO since the keys that I hit show up on the console AND the printf is not executed until the Enter key has been pressed. I typed a string of six characters (which were all displayed despite turning off the ECHO bit in the termios_p structure) followed by six backspaces (which erased the characters), then pressed uppercase A and the printf output the number 65 (the decimal version of the 0x41 ASCII A) and the program terminated. This clearly indicates that the console is still in edited (or canonical) mode and is doing line buffering.

I’m surprised that something that seems like it should be so basic is causing such a problem. Maybe it works on everyone ELSE’s system but not on my particular configuration for some reason. I’m running QNX Momentics Service Pack 3 with the SMP, Adaptive Partitioning, Critical Process Monitoring and Multimedia TDKs. Momentics is hosted on a quad core Windows XP PC with 4 gig of RAM linked by a redundant gigabit ethernet link to my target PC which is a harsh environment ruggedized PC based on a COMMELL LV-677DC industrial mini ITX motherboard running a dual core T5500 x86CPU with 4 gig of RAM.

When I test out these various programs I’m running them on the target PC using the console in the Momentics IDE. I haven’t tried copying the application over TO the target PC and running it in a terminal window yet (and I’m not quite sure how to actually DO that yet) so maybe it’s something weird that only manifests in the IDE’s console…

Something that worked in your version that I wasn’t able to make work was doing a 1 byte read to a char variable. I couldn’t make that compile until I pointed it to an array instead of a char but YOUR read(STDIN_FILENO, &buf, sizeof ( buf )); compiled and worked just fine. I guess that’s why YOU get to be the Master and I’m still just a QNX zygote (not even a frickin’ embryo yet)…

Mario:

AHA!

YOUR read(STDIN_FILENO, &buf, sizeof ( buf )); worked but my version didn’t because I didn’t realize that although a char array is treated as an address when it is used as an argument, a char variable is not and I neglected the all important ampersand. That’s just one of the many anisotropic things about C++ that I’m having to commit to memory but I DO get it now.

I’m CONFIDENT I’ll graduate from C++ Kindergarten any day now. The education continues…

  • Kevin :mrgreen:

It’s no more anisotropic then assembler ;-) You must have got bitten by mov versus movsx ;-=)

If you forget the ampersand it should not have compiled, strange or at least give a warning.

[size=150][color=darkblue]Mario:

You are correct; it wouldn’t compile without the ampersand so I replaced the char variable with a char array to get it to compile. I didn’t realize what I had done wrong until I looked at YOUR use of a char variable WITH the ampersand. That makes sense to me now. Thanks for assisting with my C++ and QNX Neutrino education. :smiley:

MOV is a single operation and MOVS is a string mov operation; fourtunately they use the same syntax (other than the MOVSB, MOVSW or MOVSD depending on variable size) so I can’t say I find it confusing or illogical at this point in time. I can code in ASM as fast as I can type but so far I find that I spend about 50 times as long dealing with the grammar of C++ as I spend actually writing code. I suppose I was probably also fighting with MASM syntax in back 1987 but I’ve just forgotten about it.

Sometime in the not too distant future I HOPE to be at least semi-fluent in C++ instead of semi-incompetent as I am now. If not, I’m not going to have my vehicle ready in time for the DARPA Urban Challenge deadline and I’ll feel pretty foolish for having failued due to a 20 year aversion to forcing myself to just sit down and learn the C language. :unamused:

Now back to converting 20,000 lines of code from ASM to C++…

Cheers!

Kevin[/size]

It’s my understanding that movsx is move with sign extension which mov which doesn’t do sign extension. movs ( with no x ) is a move string, as you mention, no?

If you have lots of assembly code, and it doesn’t talk to the hardware you might be able to reuse it. I would also recommend you stick to C and not C++ if you are short on time. C++ has concepts that don’t really exists in the assembly language, constructor, destructor, polymorphism, inheritance.

You might also find that you program will be faster in C then in assembly. It has now become impossible for a normal human brain to optimize assembly code to ensure pipeline/integer unit/float point unit/branch prediction are all used to their fullest. The intel compiler is impressive in that regard.

Mario please, ridiculously tedious probably, but not impossible ;-).

No Mitch, it is impossible ;-) First of all if you look at the P4 or Core you will find no information about how much cycle an instruction takes, the reason is that there are too many factor involved. The last “24” instructions, the cache size, the memory type etc. Then there is the case some dual core CPU that shares the same cache, etc…

I am mostly talking x86. I got in touch with one of the maintainer of the OpenWatcom Compiler and he mentionned that for compiler writer it is becoming a nightware. That even Intel engineer have a hard time figuring out themselves and need to really on simulator to figure that out.

Sure there are some basic rules that you can apply, but it has become impossible to count the number of CPU cycles a set of instruction will take.

[color=indigo]Masters Mario and Mitchell:

What an interesting debate this conversation thread has evolved into! Thanks for the suggestion of C versus C++, I’ll give that some consideration. I’m not familiar with a MOVSX instruction; perhaps someone’s using the X as a variable since MOVS comes in three flavors: MOVSB (8 bit Byte), MOVSW (16 bit Word) and MOVSD (32 bit DWord). At least those are the mnemonics used by MASM and the Phar Lap Protected Mode x86 Assembler that I’m familiar with. But I digress…

Now for MY two cents worth. Although a well written compiler can probably do a great job of making instruction pipelining work well for superscalar processors, I’m not convinced that the compiler (in collusion with the OS) doesn’t throw all that performance gain away by introducing MANY other inefficiencies. Take for example the situation that started this thread. I’m simply trying to get raw (non linebuffered) keystrokes from the keyboard, yet we’re having to set up the “console” as though it’s a UART (a quaint anachronism and an abstraction), then calling cin.get(), fgetchar() or read(), any of which probably take dozens if not hundreds of CPU instructions to execute, when the PC’s BIOS could make that data readily available with a couple of instructions. Surely the console Resource Manager’s interrupt handler (devc-con?) is getting keyboard data and moving it into an interrupt input buffer on a keystroke by keystroke basis and I hope SOMEDAY to find a computationally efficient way to get to that data WITHOUT having to wait for an Enter key to be pressed but so far that technique has eluded me.

Don’t get me wrong, I find Neutrino’s architecture to be wonderfully logical, especially when compared to DOS and Windows, but after studying dozens of utilites each of which seem to have 20 or more command line arguments I’m having '70s flashbacks and feel like I should be using an ADM3a terminal connected to a Data General Nova through a 300 baud acoustic coupler to write my code! However, despite my initial frustration I DO see the light at the end of the tunnel and eagerly look forward to the day when I can spend more time writing code than I do counting curly braces and whining about anisotropic C++ syntax peculiarities.

I fully expect that, despite my misgivings about high level languages and the abstractions necessitated by multi-user, multi-tasking Operating Systems, the powerful, scalable and robust architecture offered by Neutrino (especially multi-process, multi-core/multi-processor and Qnet capabilities) will put the DOS based software I’m currently running in my DARPA robot to shame and I most EAGERLY look forward to seeing that happen someday quite soon.

Cheers and thanks for the guidance!

Kevin Jackson[color=indigo]