Janis Papanagnou wrote:
> To provide some detail about my own tries, I used cfmakeraw() to
> obtain the character-wise I/O (which somehow did not work). It is
> described as
>
> cfmakeraw() sets the terminal to something like the
> "raw" mode of the old Version 7 terminal driver: input
> is available character by character, echoing is dis‐
> abled, and all special processing of terminal input and
> output characters is disabled. [...]
>
> so it at least looks like it should do what I wanted.
There are two potential layers of buffering involved. One is buffering
at the terminal, which can be disabled in a number of ways. IIRC,
cfmakeraw doesn't actually change the terminal settings, it simply
modifies an existing set of terminal settings to represent a raw-mode
version of those settings and leaves it up to you to apply them (with a
system call like tcsetattr). Note that it's probably a good idea to
remember the old terminal settings too (available via tcgetattr),
because if you don't restore the original terminal settings as your
program exits, the terminal will act weirdly after your program exits.
The other potential layer of buffering is stdio buffering, which is
involved in calls like putchar/getchar and printf/scanf. You can avoid
this either by exclusively using the kernel level system calls for
reading and writing (read/write), or by using setvbuf to entirely
disable stdio buffering on the file descriptors in question.
As a reference for this sort of terminal configuration, here's the code
that NetHack4 currently uses (omitting, for copyright-related reasons,
a small tweak required to make it work on BSD) to set up a terminal
(you'll need to adapt this for your own purposes, e.g. "fileno(ifile)"
and "fileno(ofile)" are likely to become hardcoded numbers, and "ti"
and "ti_orig" will need to be declared somewhere, with "ti_orig"
restored at the end of the program):
{{{
/* Set up the terminal control flags. Use whichever of ifile and ofile
happens to be a terminal. */
if (tcgetattr(fileno(ifile), &ti_orig) == -1)
tcgetattr(fileno(ofile), &ti_orig);
/*
* Terminal control flags to turn off:
*
* Input-related:
* ISTRIP bit 8 stripping (incompatible with Unicode)
* INPCK parity checking (ditto)
* PARENB more parity checking (ditto)
* IGNCR ignore CR (means there's a key we can't read)
* INLCR translate NL to CR (ditto)
* ICRNL translate CR to NL (ditto)
* IXON insert XON/XOFF into the stream (confuses the client)
* IXOFF respect XON/XOFF (causes the process to lock up on ^S)
* ICANON read a line at a time (means we can't read keys)
* ECHO echo typed characters (confuses our view of the screen)
* We also set ISIG based on raw_isig, and VMIN to 1, VTIME to 0,
* which gives select() the correct semantics; and we turn on CS8,
* to be able to read Unicode (although on Unicode systems it seems
* to be impossible to turn it off anyway).
*
* Output-related:
* OPOST general output postprocessing (portability nightmare)
* OCRNL map CR to NL (we need to stay on the same line with CR)
* ONLRET delete CR (we want to be able to use CR)
* OFILL delay using padding (only works on physical terminals)
*/
if (tcgetattr(fileno(ifile), &ti) == -1)
tcgetattr(fileno(ofile), &ti);
ti.c_iflag &= ~(ISTRIP | INPCK | IGNCR | INLCR | ICRNL | IXON | IXOFF);
ti.c_cflag &= ~PARENB;
ti.c_cflag |= CS8;
ti.c_lflag &= ~(ISIG | ICANON | ECHO);
ti.c_cc[VMIN] = 1;
ti.c_cc[VTIME] = 0;
if (!raw_isig)
ti.c_lflag |= ISIG;
ti.c_oflag &= ~(OPOST | OCRNL | ONLRET | OFILL);
if (tcsetattr(fileno(ifile), TCSADRAIN, &ti) == -1)
tcsetattr(fileno(ofile), TCSADRAIN, &ti);
}}}
--
ais523