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


von pq (Gast)


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" ?
1
 
2
  /* für Daten auf stdin einen Event-Handler einrichten  */
3
  gio_read = g_io_channel_unix_new (fileno(stdin));
4
  g_io_add_watch (gio_read, G_IO_IN, my_callback_funktion, NULL));
5
  
6
  /* glib main loop */
7
  loop = g_main_loop_new (NULL, FALSE);
8
  g_main_loop_run (loop);

pq

von Hc Z. (mizch)


Lesenswert?

Siehe setbuf().

von Sven P. (Gast)


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.

von Klaus W. (mfgkw)


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
1
//////////////////////////////////////////////////////////////////////
2
//
3
// Time-stamp: "28.12.09 09:29 getch.h klaus?wachtler.de"
4
//
5
// der folgende Teil kann komplett fuer sich als Ersatz von
6
// Sleep(), _kbhit() und _getch() verwendet werden, wie es unter
7
// NT oder allen DOS-C-Compilern verwendet wird.
8
//
9
10
/* Sleep() wartet die angegebene Anzahl Millisekunden (im Gegensatz
11
 * zu ANSI-sleep(), das kennt nur ganze Sekunden).
12
 */
13
14
#ifdef __cplusplus
15
extern "C"
16
#endif
17
void Sleep( int msec );
18
19
// Diese Funktion macht beim ersten Aufruf folgendes:
20
// - Merken der Originalkonfiguration von stdin (file id=0)
21
// - Installieren von _exit_conio() mit atexit()
22
// Bei jedem Aufruf zudem:
23
// - Ausschalten von ECHO und ICANON (u.a. Zeilenpufferung)
24
#ifdef __cplusplus
25
extern "C"
26
#endif
27
void _init_conio( void );
28
29
// _kbhit() liefert true, wenn mindestens ein Zeichen im
30
// Tastaturpuffer steht
31
#ifdef __cplusplus
32
extern "C"
33
#endif
34
int _kbhit( void );
35
36
// liefert ein Zeichen von der Tastatur (oder EOF bei Fehler)
37
#ifdef __cplusplus
38
extern "C"
39
#endif
40
int _getch( void );
41
42
//
43
// Ende von Sleep(), _kbhit(), und _getch() fuer __unix__.
44
//
45
//////////////////////////////////////////////////////////////////////

Kann als getch.c oder getch.cpp verwendet werden:
1
//////////////////////////////////////////////////////////////////////
2
//
3
// Time-stamp: "28.12.09 09:34 getch.cpp klaus?wachtler.de"
4
//
5
// der folgende Teil kann komplett fuer sich als Ersatz von
6
// Sleep(), _kbhit() und _getch() verwendet werden, wie es unter
7
// NT oder allen DOS-C-Compilern verwendet wird.
8
//
9
10
#include <sys/time.h>
11
#include <sys/types.h>
12
#include <termios.h>
13
#include <unistd.h>
14
#include <stdio.h>
15
#include <stdlib.h>
16
17
#include "getch.h"
18
19
20
/* Sleep() wartet die angegebene Anzahl Millisekunden (im Gegensatz
21
 * zu ANSI-sleep(), das kennt nur ganze Sekunden).
22
 */
23
24
#ifdef __cplusplus
25
extern "C"
26
#endif
27
void Sleep( int msec )
28
{
29
  struct timeval
30
    timeout;
31
32
  timeout.tv_sec  = msec/1000;
33
  timeout.tv_usec = msec%1000;
34
35
  select( 0, NULL, NULL, NULL, &timeout );
36
}
37
38
// In einer struct termios wird eine Terminalkonfiguration
39
// gespeichert.
40
// Davon brauchen wir zwei: eine fuer den Originalzustand,
41
// und eine zum aendern.
42
static struct termios     term_neu,
43
                          term_original;
44
45
// Darin wird die Aenderung der Terminaleinstellungen gemerkt,
46
// damit sie nur einmal stattfinden:
47
static int                _conio_initialisiert = 0;
48
49
// Bei Programmende soll automatisch die Originalkonfiguration
50
// wiederhergestellt werden:
51
static void _exit_conio( void )
52
{
53
  if( _conio_initialisiert )
54
    {
55
      tcsetattr( 0, TCSANOW, &term_original );
56
      _conio_initialisiert = 0;
57
    }
58
}
59
60
// Diese Funktion macht beim ersten Aufruf folgendes:
61
// - Merken der Originalkonfiguration von stdin (file id=0)
62
// - Installieren von _exit_conio() mit atexit()
63
// Bei jedem Aufruf zudem:
64
// - Ausschalten von ECHO und ICANON (u.a. Zeilenpufferung)
65
#ifdef __cplusplus
66
extern "C"
67
#endif
68
void _init_conio( void )
69
{
70
  if( !_conio_initialisiert )
71
    {
72
      //  Dies passiert nur beim ersten Aufruf
73
74
      // Originalkonfiguration holen:
75
      tcgetattr( 0, &term_original );  // 0 ist stdin
76
      // komplett kopieren:
77
      term_neu = term_original;
78
      // ICANON und ECHO ausschalten:
79
      term_neu.c_lflag = term_neu.c_lflag & ~(ICANON | ECHO);
80
      // Bei Programmende wieder aufraeumen lassen:
81
      atexit( _exit_conio );
82
      // In _conio_initialisiert merken, dass wir schon etwas getan
83
      // haben:
84
      _conio_initialisiert++;
85
    }
86
87
  // bei jedem Aufruf die eigene Terminalkonfiguration
88
  // fuer stdin (file id = 0) passend setzen:
89
  tcsetattr( 0, TCSANOW, &term_neu );
90
}
91
92
// _kbhit() liefert true, wenn mindestens ein Zeichen im
93
// Tastaturpuffer steht
94
#ifdef __cplusplus
95
extern "C"
96
#endif
97
int _kbhit( void )
98
{
99
  fd_set
100
    lesen_set;
101
102
  struct timeval
103
    timeout;
104
105
  _init_conio();
106
107
  FD_ZERO( &lesen_set );
108
  FD_SET( 0, &lesen_set ); // 0 ist std-Eingabe
109
110
  timeout.tv_sec  = 0;
111
  timeout.tv_usec = 0;
112
113
  if( select( 1,
114
              &lesen_set,  // set fuer "Lesen"
115
              NULL,        // kein set fuer "Schreiben"
116
              NULL,        // kein set fuer "out of band data"
117
              &timeout
118
              )
119
      ==-1
120
      )
121
      {
122
        // select macht Bloedsinn!
123
        return 0;
124
      }
125
  else
126
    {
127
      // in lesen_set steht jetzt an der Stelle [0], ob gelesen
128
      // werden kann:
129
      return FD_ISSET( 0, &lesen_set );
130
    }
131
}
132
133
// liefert ein Zeichen von der Tastatur (oder EOF bei Fehler)
134
#ifdef __cplusplus
135
extern "C"
136
#endif
137
int _getch( void )
138
{
139
  char c;
140
141
  _init_conio();
142
143
  return ( read( 0, &c, 1 )==1 ) ? (int)(unsigned char)c : EOF;
144
}
145
//
146
// Ende von Sleep(), _kbhit(), und _getch() fuer __unix__.
147
//
148
//////////////////////////////////////////////////////////////////////


kleines Testprogramm dazu:
1
#include <stdio.h>
2
#include <ctype.h>
3
4
#include "getch.h"
5
6
7
int main( int nargs, char **args )
8
{
9
  int   c;
10
11
  setvbuf( stdin, NULL, _IONBF, 0 );
12
13
  while( ( c = _getch())!='q' )
14
  {
15
    printf( "c = %3d (%c)\n", c, ( isprint(c) ? c : '?') );
16
  }
17
18
19
  return 0;
20
}

von Klaus W. (mfgkw)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.