Christian Seiler: C-Programmierung: conio.h unter Linux für getch()

Beitrag lesen

Hallo,

in der Schule haben wir heute angefangen, C zu lernen. Leider ist das alles auf Windows zugeschnitten. Hier zuhause, ich besitze nur Linux und kaufe mir kein Windows, funktioniert schon unser erstes Hello-World-Programm nicht:

#include <stdio.h>
#include <conio.h>

main()
{
printf("Hello\nWorld!");
getch();
}

  
Du solltest Deinen Lehrer übrigens schlagen, dass er schlechten Stil lehrt. Zumindest einen Rückgabetyp sollte er der Main-Funktion geben (und am besten gleich auch noch einen Rückgabewert) - und die Parameter der Funktion (selbst wenn sie nicht verwendet werden) sollten durchaus explizit angegeben werden, d.h. besserer Stil wäre:  
  
~~~c
#include <stdio.h>  
// sonstiger Code (siehe unten)  
  
int main (int argc, char **argv) {  
  printf ("Hello\nWorld!");  
  getch();  
  return 0;  
}

Der gcc motzt, es gäbe keine conio.h. Tja, der muss es wissen, also habe ich im Internet nach der Linux-Variante von conio.h geschaut, fand immer nur "ncurses". Ist das die richtige Header-Datei für getch()?

Es gibt unter Linux keine Funktion getch(). getch() macht unter DOS ja nichts anderes, als (ohne das Zeichen auf die Konsole auszugeben) auf die eingabe eines beliebigen Zeichens zu warten. Dafür gibt's unter ANSI C keine Funktion - d.h. portabel bekommst Du das nicht hin.

getchar() (ANSI C, eingegebene Zeichen werden angezeigt) dagegen reagiert unter Linux jedoch so, dass es erst ein Zeichen zurückliefert, sobald vom Terminal Zeichen gekommen sind - das ist *nicht* notwendigerweise dann, sobald Zeichen eingegeben werden. Im Normalfall ist das Terminal nämlich im zeilenbasierten Modus - d.h. das Programm bekommt die Eingabe erst, sobald eine Zeile komplett eingegeben wurde.

Wenn Du unter Linux die Funktionalität von getch() nutzen willst, musst Du folgendes tun:

  1. Den Zeilenmodus des Terminals deaktivieren.
  2. Den Echo-Modus (automatische Ausgabe von eingegebenen Zeichen) des Terminals deaktivieren
  3. Das Zeichen per getchar() einlesen
  4. Den Echo-Modus wieder aktivieren
  5. Den Zeilenmodus wieder aktivieren

Da das ganze ne ganze Menge mit POSIX-Systemprogrammierung zu tun hat und zum C-Lernen eigentlich ein völlig ungeeigneter Start ist, habe ich die Funktion getch() mal kurzfristig für POSIX-Betriebsysteme (hab's allerdings nur unter Linux getestet - nicht schlagen, wenn's unter FreeBSD o.ä. knallt) nachprogrammiert, wenn Du das in Dein Hello-World-Programm reinkopierst oder in eine externe .c-Datei kopierst und dann dagegen linkst, wird Dein Hello-World-Programm genauso funktionieren, wie unter Windows.

#include <stdio.h>  
#include <termios.h>  
#include <unistd.h>  
#include <string.h>  
  
// Nachimplementierung der getch()-Funktion aus DOS-conio.h  
// für POSIX-Betriebsysteme  
// Lizenz: Public domain  
int getch () {  
 int ch;  
 struct termios old_t, tmp_t;  
  
 // alte Attribute holen  
 if (tcgetattr (STDIN_FILENO, &old_t)) {  
  // moeglicherweise kein Terminal  
  // moeglicherweise anderer Fehler  
  return -1;  
 }  
 // struktur kopieren  
 memcpy (&tmp_t, &old_t, sizeof (old_t));  
 // Zeilenmodus sowie Spezialzeichen deaktivieren,  
 // Bildschirmausgabe von eingegebenen Zeichen  
 // deaktivieren  
 tmp_t.c_lflag &= ~ICANON & ~ECHO;  
 // Neue Flags setzen  
 if (tcsetattr (STDIN_FILENO, TCSANOW, (const struct termios *)&tmp_t)) {  
  // moeglicherweise irgend ein Fehler  
  return -1;  
 }  
 // zeichen einlesen (darauf verlassen,  
 // STDIN_FILENO blockierend ist)  
 ch = getchar ();  
 // Alte Flags zuruecksetzen  
 // Rueckgabewert ignorieren, da das Terminal bei Fehler sowieso  
 // nicht korrekt reagieren wird, wir dagegen aber nichts tun  
 // koennen  
 tcsetattr (STDIN_FILENO, TCSANOW, (const struct termios *)&old_t);  
 // Zeichen zurueckgeben  
 return ch;  
}

Viele Grüße,
Christian

--
"I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone." - Bjarne Stroustrup