getch unter Unix?
Soetwas wie die DOS-Funktion getch stellt Unix nicht direkt zur
Verfügung, wohl aber die Mittel es zu emulieren.
Dabei stellen sich folgende Probleme:
- Das Terminal ist möglicherweise im kanonischen Modus:
Eingaben werden Zeile für Zeile bearbeitet. Dies ist der
Default unter Unix (was man als Benutzer nicht so recht merkt weil
Shells und Editoren als allererstes den nichtkanonischen
Modus einstellen).
- Der Echomodus ist möglicherweise eingeschaltet: Das Terminal
bzw. das Betriebssystem schreibt jedes eingegebene Zeichen gleich auf
den Bildschirm.
Derartige Terminaleingenschaften werden heutzutage mit tcsetattr
veraendert.
Es folgt eine Beispielimplementation von getch:
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#define MKFLAG(which) \
static int io_tio_set_flag_##which(int fd, int val, int on, int *old) \
{ struct termios tio; \
if (tcgetattr(fd,&tio)) return -1; \
if (old) *old=(tio.which & (val)); \
if (on) tio.which |= (val); \
else tio.which &= ~(val); \
if (tcsetattr(fd,TCSADRAIN,&tio)) return -1; \
return 0; \
} \
static int io_tio_get_flag_##which(int fd, int bit, int *value) \
{ struct termios tio; \
if (tcgetattr(fd,&tio)) return -1; \
*value=(tio.which & (bit)); \
return 0; \
}
MKFLAG(c_lflag)
static int
read_character_now_without_echo(int fd, char *c)
{
int old_ICANON,old_ECHO;
int restoreflag=0;
int e=0;
int retcode=-1;
/* some devices do not support terminal flags. Notably:
* /dev/disk|floppy
* /dev/zero
* ...
* This function is able to handle them:
*/
if (-1==io_tio_set_flag_c_lflag(fd,ICANON,0,&old_ICANON)) {
if (errno!=ENOTTY
&& errno!=EINVAL) { /* LINUX /dev/random lossage */
e=errno;
perror("Turnoff of ICANON failed");
errno=e;
return -1;
}
} else
restoreflag=1;
if (restoreflag && -1==io_tio_set_flag_c_lflag(fd,ECHO,0,&old_ECHO)) {
e=errno;
perror("Turnoff of ICANON failed");
} else {
if (restoreflag) restoreflag++;
while (1) {
retcode=read(fd,c,1);
if (retcode==1 || retcode==0) break;
if (errno==EINTR) continue;
break;
}
}
/* we do not check for errors anymore: If we were able to change
* a flag before and are unable to do that afterwards then we
* are in unsolvable trouble anyway.
* btw, it _could_ happen, in case revoke() is summoned upon
* us. Oh, well.
*/
/* set echo to old value */
if (restoreflag==2)
io_tio_set_flag_c_lflag(fd,ECHO,old_ECHO,NULL);
/* back to canonical input mode (line by line) */
if (restoreflag)
io_tio_set_flag_c_lflag(0,ICANON,old_ICANON,NULL);
errno=e;
return retcode;
}
int main(void)
{
char c;
int r;
r=read_character_now_without_echo(0,&c);
if (r==-1) exit(1);
if (r) {
if (-1==write(1,&c,1))
perror("write");
}
return (0);
}