Come posso catturare sequenze di tasti singole in un'app console unix senza bloccare?
Domanda
Ho un server TCP molto semplice scritto in C. Funziona indefinitamente, in attesa di connessioni. Su Windows, utilizzo select
per verificare l'attività sul socket, e se non ce n'è uno, ho il seguente codice per consentirmi di uscire premendo 'q' sulla tastiera:
if( kbhit() ) {
char c = getch();
if( c == 'q' ) break;
}
Questo non funziona su unix, poiché kbhit
non esiste e getch
funziona in modo diverso. Ho trovato alcuni codice di esempio che utilizza tcsetattr
per modificare le impostazioni del terminale e consentire l'inserimento carattere per carattere. Dopo aver chiamato la funzione init, apro / dev / stdin (con O_NONBLOCK
) e leggo un carattere, ma read (f, & amp; c, 1)
si blocca fino a quando un carattere è colpito.
Suppongo di poter generare un thread separato e di farlo attendere indefinitamente e quindi segnalare il primo thread se l'utente preme 'q', ma sembra un po 'pesante. Sicuramente c'è un modo più semplice?
Soluzione
Aggiungi stdin al tuo elenco di handle selezionati e, se contiene dati, chiama read per leggere un carattere da esso.
Altri suggerimenti
Piuttosto, aggiungi " f " dal tuo
read( f, &c, 1 )
per selezionare la chiamata. Quando f è pronto per la lettura, è stato premuto un carattere e read () non si bloccherà.
In Unix, sia sulla console di sistema che in una finestra del terminale X, l'I / O della tastiera passa attraverso un terminale virtuale. Device / dev / tty è il solito modo, in questi giorni, di accedere al terminale di controllo di un processo. Manipolazioni del dispositivo diverse da open / close / read / write sono tutte gestite dalla chiamata di sistema ioctl (2) per quel dispositivo specifico. L'idea generale di ciò che vuoi fare è
Apri il terminale di controllo (che può essere o meno stdin)
Cambia la modalità operativa su quel terminale per tornare senza aspettare una linea piena di input (che è il normale default)
Continua con il resto del programma, sapendo che le letture da quel terminale (che potrebbe essere stdin) possono restituire righe parziali o addirittura zero caratteri senza che si tratti di un errore o di una condizione di terminazione.
Una risposta dettagliata su come eseguire il secondo passaggio è disponibile nella Domande frequenti sulla programmazione in C . Sottolineano inoltre che si tratta di una domanda del sistema operativo, non di una lingua. Forniscono nove possibilità, ma le tre principali pertinenti a questa domanda sono
- Usa la libreria curses (3)
- Vecchi sistemi in stile BSD, utilizzare sgttyb per impostare CBREAK o RAW
- Posix o vecchi sistemi in stile System V, utilizzare TCGETAW / TCSETAW per impostare c_cc [VMIN] su 1 e c_cc [VTIME] su 0.
Seguire un paio di riferimenti nelle FAQ C potrebbe portare a questa pagina di frammenti di codice di kbhit .