C nieblokujące wejście klawiatury
Próbuję napisać program w języku C (na Linuksie), który zapętla się, dopóki użytkownik nie naciśnie klawisza, ale nie powinien wymagać naciśnięcia klawisza, aby kontynuować każdą pętlę.
Czy jest na to prosty sposób? Myślę, że mógłbym to zrobić zselect()
, ale to wygląda na dużo pracy.
Alternatywnie, czy istnieje sposób, aby złapać ctrl-C nacisnąć klawisz do czyszczenia przed zamknięciem programu zamiast nieblokującego io?
10 answers
Jak już wspomniano, możesz użyć sigaction
do pułapki ctrl-C lub select
do pułapki dowolnego standardowego wejścia.
Zauważ jednak, że w tej ostatniej metodzie musisz również ustawić TTY tak, aby był w trybie znak-w-czasie, a nie w trybie linia-w-czasie. Ta ostatnia jest domyślna - jeśli wpiszesz wiersz tekstu, nie zostanie on wysłany na stdin uruchomionego programu, dopóki nie naciśniesz klawisza enter.
Musisz użyć funkcji tcsetattr()
, aby wyłączyć tryb ICANON i prawdopodobnie również wyłączyć ECHO. Od pamięci, musisz również ustawić terminal z powrotem w trybie ICANON po zakończeniu programu!
Dla kompletności, oto jakiś kod, który właśnie zapukałem (nb: bez sprawdzania błędów!), który ustawia uniksowy TTY i emuluje funkcje DOS <conio.h>
kbhit()
i getch()
:
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
int getch()
{
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
int main(int argc, char *argv[])
{
set_conio_terminal_mode();
while (!kbhit()) {
/* do some work */
}
(void)getch(); /* consume the character */
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-30 08:29:10
select()
jest trochę za niski poziom dla wygody. Sugeruję użycie biblioteki ncurses
, aby umieścić terminal w trybie cbreak i delay, a następnie wywołać getch()
, które zwróci ERR
, Jeśli żaden znak nie jest gotowy:
WINDOW *w = initscr();
cbreak();
nodelay(w, TRUE);
W tym momencie możesz zadzwonić getch
bez blokowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-16 01:03:13
W systemach uniksowych można użyć wywołania sigaction
, aby zarejestrować obsługę sygnału dla sygnału SIGINT
, który reprezentuje sekwencję klawiszy Control+C. Obsługa sygnału może ustawić flagę, która będzie sprawdzana w pętli, co spowoduje odpowiednie przerwanie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-15 23:31:33
Prawdopodobnie chcesz kbhit();
//Example will loop until a key is pressed
#include <conio.h>
#include <iostream>
using namespace std;
int main()
{
while(1)
{
if(kbhit())
{
break;
}
}
}
To może nie działać we wszystkich środowiskach. Przenośnym sposobem byłoby utworzenie wątku monitorującego i ustawienie flagi na getch();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-15 14:30:09
Innym sposobem uzyskania nieblokującego wejścia z klawiatury jest otwarcie pliku urządzenia i odczytanie go!
Musisz znać plik urządzenia, którego szukasz, jeden z /dev/input/event*. Możesz uruchomić cat / proc/bus/input / devices, aby znaleźć żądane urządzenie.
Ten kod działa dla mnie (Uruchom jako administrator).
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
int main(int argc, char** argv)
{
int fd, bytes;
struct input_event data;
const char *pDevice = "/dev/input/event2";
// Open Keyboard
fd = open(pDevice, O_RDONLY | O_NONBLOCK);
if(fd == -1)
{
printf("ERROR Opening %s\n", pDevice);
return -1;
}
while(1)
{
// Read Keyboard Data
bytes = read(fd, &data, sizeof(data));
if(bytes > 0)
{
printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code);
}
else
{
// Nothing read
sleep(1);
}
}
return 0;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-24 21:36:07
Biblioteka curses może być używana do tego celu. Oczywiście, select()
i Signal handlers mogą być również używane do pewnego stopnia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-12 05:35:59
Jeśli jesteś szczęśliwy, że złapałeś Control-C, to załatwione. Jeśli naprawdę chcesz nieblokujące wejścia / wyjścia, ale nie chcesz biblioteki curses, inną alternatywą jest przeniesienie lock, stock i barrel do AT&T sfio
. Ładna biblioteka wzorowana na C stdio
, ale bardziej elastyczna, bezpieczna dla wątków i działa lepiej. (SFIO oznacza bezpieczne, szybkie I / O.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-27 21:05:45
Nie ma na to przenośnego sposobu, ale select() może być dobrym sposobem. Zobacz http://c-faq.com/osdep/readavail.html aby uzyskać więcej możliwych rozwiązań.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-15 23:33:07
Oto funkcja, aby to zrobić dla Ciebie. Potrzebujesz termios.h
, który jest dostarczany z systemami POSIX.
#include <termios.h>
void stdin_set(int cmd)
{
struct termios t;
tcgetattr(1,&t);
switch (cmd) {
case 1:
t.c_lflag &= ~ICANON;
break;
default:
t.c_lflag |= ICANON;
break;
}
tcsetattr(1,0,&t);
}
Dzieląc to: tcgetattr
pobiera bieżące informacje o terminalu i przechowuje je w t
. Jeśli cmd
wynosi 1, znacznik wejścia lokalnego w {[3] } jest ustawiony na wejście nieblokujące. W przeciwnym razie jest resetowany. Następnie tcsetattr
zmienia standardowe wejście na t
.
Jeśli nie zresetujesz standardowego wejścia na końcu programu, będziesz miał problemy w powłoce.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-05 01:00:31
Możesz to zrobić za pomocą select w następujący sposób:
int nfds = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(0, &readfds); /* set the stdin in the set of file descriptors to be selected */
while(1)
{
/* Do what you want */
int count = select(nfds, &readfds, NULL, NULL, NULL);
if (count > 0) {
if (FD_ISSET(0, &readfds)) {
/* If a character was pressed then we get it and exit */
getchar();
break;
}
}
}
Nie za dużo pracy: D
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-04 08:10:43