www.mikrocontroller.net

Forum: PC-Programmierung linux glib: wie Tastendruck (1 Zeichen) von Konsole erkennen


Autor: pq (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich will unter Linux eine Konsolenanwendung erstellen und auf einzelne 
Zeichen von stdin kommend reagieren. (zb um bei einem "q" das Programm 
zu beenden).

Dazu habe ich die mit glib eine Hauptschleife erzeugt und richte mir für 
Daten, die über stdin kommen einen Event-Handler ein.

Tut auch. Aber es wird erst dann ein Event erzeugt, wenn ich Text 
eingeben und dann die ENTER Taste drücke. Aber nicht schon beim ersten 
Zeichen.

Liegt das eventuell daran, dass bereits stdin die Zeichen bis zum 
Zeilenumbruch "sammelt" ?
 
  /* für Daten auf stdin einen Event-Handler einrichten  */
  gio_read = g_io_channel_unix_new (fileno(stdin));
  g_io_add_watch (gio_read, G_IO_IN, my_callback_funktion, NULL));
  
  /* glib main loop */
  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);


pq

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Siehe setbuf().

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
pq schrieb:
> Liegt das eventuell daran, dass bereits stdin die Zeichen bis zum
> Zeilenumbruch "sammelt" ?
Ja, die Konsole ist normalerweise im 'kanonischen' Modus, d.h., sie 
puffert bis zum Zeilenende.

Microsoft hatte damals mal so ein 'getch()' erfunden, was da 
herumschiffte, für Unix gibts dafür tcsetattr(). Damit kannst du ICANON 
(Zeile-für-Zeile) und ggf. noch ECHO (getippte Zeichen terminalseitig 
direkt anzeigen) abstellen.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hatte mir dafür vor Jahren die folgenden beiden Dateien gebastelt.

Es werden die Funktionen
- _getch() (liest ein Zeichen, ohne zu warten),
- _kbhit() (sagt, ob ein Zeichen eingegegebn wurde und geholt
  werden kann, ohne zu warten)
- Sleep() (wartet die übergebene Anzahl Millisekunden)
nachgebaut.

Die Headerdatei
//////////////////////////////////////////////////////////////////////
//
// Time-stamp: "28.12.09 09:29 getch.h klaus?wachtler.de"
//
// der folgende Teil kann komplett fuer sich als Ersatz von
// Sleep(), _kbhit() und _getch() verwendet werden, wie es unter
// NT oder allen DOS-C-Compilern verwendet wird.
//

/* Sleep() wartet die angegebene Anzahl Millisekunden (im Gegensatz
 * zu ANSI-sleep(), das kennt nur ganze Sekunden).
 */

#ifdef __cplusplus
extern "C"
#endif
void Sleep( int msec );

// Diese Funktion macht beim ersten Aufruf folgendes:
// - Merken der Originalkonfiguration von stdin (file id=0)
// - Installieren von _exit_conio() mit atexit()
// Bei jedem Aufruf zudem:
// - Ausschalten von ECHO und ICANON (u.a. Zeilenpufferung)
#ifdef __cplusplus
extern "C"
#endif
void _init_conio( void );

// _kbhit() liefert true, wenn mindestens ein Zeichen im
// Tastaturpuffer steht
#ifdef __cplusplus
extern "C"
#endif
int _kbhit( void );

// liefert ein Zeichen von der Tastatur (oder EOF bei Fehler)
#ifdef __cplusplus
extern "C"
#endif
int _getch( void );

//
// Ende von Sleep(), _kbhit(), und _getch() fuer __unix__.
//
//////////////////////////////////////////////////////////////////////

Kann als getch.c oder getch.cpp verwendet werden:
//////////////////////////////////////////////////////////////////////
//
// Time-stamp: "28.12.09 09:34 getch.cpp klaus?wachtler.de"
//
// der folgende Teil kann komplett fuer sich als Ersatz von
// Sleep(), _kbhit() und _getch() verwendet werden, wie es unter
// NT oder allen DOS-C-Compilern verwendet wird.
//

#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include "getch.h"


/* Sleep() wartet die angegebene Anzahl Millisekunden (im Gegensatz
 * zu ANSI-sleep(), das kennt nur ganze Sekunden).
 */

#ifdef __cplusplus
extern "C"
#endif
void Sleep( int msec )
{
  struct timeval
    timeout;

  timeout.tv_sec  = msec/1000;
  timeout.tv_usec = msec%1000;

  select( 0, NULL, NULL, NULL, &timeout );
}

// In einer struct termios wird eine Terminalkonfiguration
// gespeichert.
// Davon brauchen wir zwei: eine fuer den Originalzustand,
// und eine zum aendern.
static struct termios     term_neu,
                          term_original;

// Darin wird die Aenderung der Terminaleinstellungen gemerkt,
// damit sie nur einmal stattfinden:
static int                _conio_initialisiert = 0;

// Bei Programmende soll automatisch die Originalkonfiguration
// wiederhergestellt werden:
static void _exit_conio( void )
{
  if( _conio_initialisiert )
    {
      tcsetattr( 0, TCSANOW, &term_original );
      _conio_initialisiert = 0;
    }
}

// Diese Funktion macht beim ersten Aufruf folgendes:
// - Merken der Originalkonfiguration von stdin (file id=0)
// - Installieren von _exit_conio() mit atexit()
// Bei jedem Aufruf zudem:
// - Ausschalten von ECHO und ICANON (u.a. Zeilenpufferung)
#ifdef __cplusplus
extern "C"
#endif
void _init_conio( void )
{
  if( !_conio_initialisiert )
    {
      //  Dies passiert nur beim ersten Aufruf

      // Originalkonfiguration holen:
      tcgetattr( 0, &term_original );  // 0 ist stdin
      // komplett kopieren:
      term_neu = term_original;
      // ICANON und ECHO ausschalten:
      term_neu.c_lflag = term_neu.c_lflag & ~(ICANON | ECHO);
      // Bei Programmende wieder aufraeumen lassen:
      atexit( _exit_conio );
      // In _conio_initialisiert merken, dass wir schon etwas getan
      // haben:
      _conio_initialisiert++;
    }

  // bei jedem Aufruf die eigene Terminalkonfiguration
  // fuer stdin (file id = 0) passend setzen:
  tcsetattr( 0, TCSANOW, &term_neu );
}

// _kbhit() liefert true, wenn mindestens ein Zeichen im
// Tastaturpuffer steht
#ifdef __cplusplus
extern "C"
#endif
int _kbhit( void )
{
  fd_set
    lesen_set;

  struct timeval
    timeout;

  _init_conio();

  FD_ZERO( &lesen_set );
  FD_SET( 0, &lesen_set ); // 0 ist std-Eingabe

  timeout.tv_sec  = 0;
  timeout.tv_usec = 0;

  if( select( 1,
              &lesen_set,  // set fuer "Lesen"
              NULL,        // kein set fuer "Schreiben"
              NULL,        // kein set fuer "out of band data"
              &timeout
              )
      ==-1
      )
      {
        // select macht Bloedsinn!
        return 0;
      }
  else
    {
      // in lesen_set steht jetzt an der Stelle [0], ob gelesen
      // werden kann:
      return FD_ISSET( 0, &lesen_set );
    }
}

// liefert ein Zeichen von der Tastatur (oder EOF bei Fehler)
#ifdef __cplusplus
extern "C"
#endif
int _getch( void )
{
  char c;

  _init_conio();

  return ( read( 0, &c, 1 )==1 ) ? (int)(unsigned char)c : EOF;
}
//
// Ende von Sleep(), _kbhit(), und _getch() fuer __unix__.
//
//////////////////////////////////////////////////////////////////////


kleines Testprogramm dazu:
#include <stdio.h>
#include <ctype.h>

#include "getch.h"


int main( int nargs, char **args )
{
  int   c;

  setvbuf( stdin, NULL, _IONBF, 0 );

  while( ( c = _getch())!='q' )
  {
    printf( "c = %3d (%c)\n", c, ( isprint(c) ? c : '?') );
  }


  return 0;
}

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: das setvbuf() im Testprogramm kann raus; das
stand noch drin, weil ich es erst damit probiert hatte.
Stört aber auch nicht...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.