Прерывание по нажатию клавиши (Прерывание по нажатию клавиши)

Модератор: Модераторы разделов

serzz
Сообщения: 6

Прерывание по нажатию клавиши

Сообщение serzz »

Пишется прогамма для запуска в терминальном режиме. Она должна непрерывно работать, а по нажатию любой (или не любой) клавиши выходить из цикла в некоторый новый режим. Что-то оператора подходящего не нашел. Кто знает, как выйти из цикла по нажатию клавиши? Под ОС/2 цикл выглядел так

while(!kbhit())
{

....

}


А под Линукс что? kbhit отсутствует
Спасибо сказали:
Ananas
Сообщения: 64

Re: Прерывание по нажатию клавиши

Сообщение Ananas »

man getch
Спасибо сказали:
Zmoukie
Сообщения: 29

Re: Прерывание по нажатию клавиши

Сообщение Zmoukie »

#include <conio.h>
..................
while (!_getch())
{
//нажали
}
Спасибо сказали:
serzz
Сообщения: 6

Re: Прерывание по нажатию клавиши

Сообщение serzz »

(Zmoukie @ Вторник, 30 Ноября 2004, 20:52) писал(а):#include <conio.h>
..................
while (!_getch())
{
        //нажали
}


так ведь #include <conio.h> в линуксе отсутсвует, или я чего не понимаю?
serzz добавил в 30.11.2004 23:13
(Ananas @ Вторник, 30 Ноября 2004, 13:39) писал(а):man getch


Пробуем написать test.c

#include <curses.h>
int q;
int main()
{
q = getch();
return q;
}

компилируем gcc -o qq test.c
Запускаем ./qq
Получаем
/tmp/cc8dspXP.o(.text+0x15): In function `main':
: undefined reference to `stdscr'
/tmp/cc8dspXP.o(.text+0x1a): In function `main':
: undefined reference to `wgetch'
collect2: ld returned 1 exit status

Где грабли?
Спасибо сказали:
Аватара пользователя
nercus
Сообщения: 150

Re: Прерывание по нажатию клавиши

Сообщение nercus »

может стоит посмотреть в сторону select(2)?
2.6.14-gentoo-r5
kde-3.5.0 | openbox-3.2
Deep Purple | Rob Zombie | Led Zeppelin | ДДТ
Спасибо сказали:
Ananas
Сообщения: 64

Re: Прерывание по нажатию клавиши

Сообщение Ananas »

gcc -lncurses test.c
Спасибо сказали:
Аватара пользователя
nercus
Сообщения: 150

Re: Прерывание по нажатию клавиши

Сообщение nercus »

Не, ncurses это конечно хорошо, но нафига ее тянуть за собой, если надо только клавишу считать... Тем более в man 2 select приведен пример, практически полностью соответствующий задаче - только таймаут поставить не 5 секунд, конечно, и в цикл завернуть.
2.6.14-gentoo-r5
kde-3.5.0 | openbox-3.2
Deep Purple | Rob Zombie | Led Zeppelin | ДДТ
Спасибо сказали:
serzz
Сообщения: 6

Re: Прерывание по нажатию клавиши

Сообщение serzz »

(Ananas @ Среда, 01 Декабря 2004, 14:38) писал(а):gcc -lncurses test.c



Ну да, правильно. Делаю, запускаю вышеозначенную программу, получаю

Segmentation fault

Где грабли на этот раз?

serzz добавил в 01.12.2004 19:26
(nercus @ Среда, 01 Декабря 2004, 18:38) писал(а):Не, ncurses это конечно хорошо, но нафига ее тянуть за собой, если надо только клавишу считать... Тем более в man 2 select приведен пример, практически полностью соответствующий задаче - только таймаут поставить не 5 секунд, конечно, и в цикл завернуть.


Так вроде бы select останавливается на заданное время и ожидает нажатие. Т.е. если его встроить в цикл, в котором много других дел делается, то возникают потери времени на остановку и придется успеть нажать во время остановки, иначе опять в цикл вернемся. Так?
Спасибо сказали:
Аватара пользователя
brazhe
Сообщения: 89

Re: Прерывание по нажатию клавиши

Сообщение brazhe »

А если задача ещё сложнее? Например, программа получает поток данных через stdin, выводит куда-нибудь, а нужна возможность её по нажатию клавиши остановить, при этом правильно освободив занятую память. Такое возможно?
Спасибо сказали:
Аватара пользователя
nercus
Сообщения: 150

Re: Прерывание по нажатию клавиши

Сообщение nercus »

(serzz @ Среда, 01 Декабря 2004, 19:26) писал(а):Так вроде бы select останавливается на заданное время и ожидает нажатие. Т.е. если его встроить в цикл, в котором много других дел делается, то возникают потери времени на остановку и придется успеть нажать во время остановки, иначе опять в цикл вернемся. Так?

Во-первых, "потери на остановку" - afaik можно поставить нулевой таймаут. Во-вторых, "успеть нажать" не надо - ключевое слово: "буферизованный io".
К сожалению, из-за этого самого буферизованного io , работать это будет не совсем так, как надо. То есть, чтение ввода будет только после <CR>.

Чуть сложнее, но как вариант, можно так (вроде все по POSIX):

Код: Выделить всё

#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>

int
main(int argc, char **argv) {
    int c;
    struct termios tio;
    tcflag_t old_lf;
    long old_fl;

    /* fgetc() won't block */
    old_fl = (long) fcntl(0, F_GETFL);
    fcntl(0, F_SETFL, O_NONBLOCK);

    /* disable buffering by lines... and some more. see termios(2) */
    tcgetattr(0, &tio);
    old_lf = tio.c_lflag;
    tio.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(0, TCSANOW, &tio);

    while (EOF == (c = fgetc(stdin))) {
        /* do something good unless key is pressed */
    }
    printf("User pressed key 0x%02x\n", c);

    /* be a good boy and restore initial settings */
    tcgetattr(0, &tio);
    tio.c_lflag = old_lf;
    tcsetattr(0, TCSANOW, &tio);
    fcntl(0, F_SETFL, old_fl);

    return 0;
}


Хотя, конечно, если использовать ncurses, то попроще:), но это ж ее за собой тянуть надо! - а надо ли?

(brazhe @ Среда, 01 Декабря 2004, 19:47) писал(а):А если задача ещё сложнее? Например, программа получает поток данных через stdin, выводит куда-нибудь, а нужна возможность её по нажатию клавиши остановить, при этом правильно освободив занятую память. Такое возможно?

Вполне возможно. signal(2), signal(7) - см сигнал SIGINT. Только это если под нажатием клавиши понимать ^c :).
А вот если не ^c - то это либо выставлять tio.c_cc[VINTR] (tio - это в контексте приведенного выше кода) в любой понравившийся символ, либо никак :)
2.6.14-gentoo-r5
kde-3.5.0 | openbox-3.2
Deep Purple | Rob Zombie | Led Zeppelin | ДДТ
Спасибо сказали:
serzz
Сообщения: 6

Re: Прерывание по нажатию клавиши

Сообщение serzz »

Код: Выделить всё

#include <curses.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>

int
main(int argc, char **argv) {
   int c;
   struct termios tio;
   tcflag_t old_lf;
   long old_fl;

   /* fgetc() won't block */
   old_fl = (long) fcntl(0, F_GETFL);
   fcntl(0, F_SETFL, O_NONBLOCK);

   /* disable buffering by lines... and some more. see termios(2) */
   tcgetattr(0, &tio);
   old_lf = tio.c_lflag;
   tio.c_lflag &= ~(ICANON | ECHO);
   tcsetattr(0, TCSANOW, &tio);

   while (EOF == (c = fgetc(stdin))) {
       /* do something good unless key is pressed */
   }
   printf("User pressed key 0x%02x\n", c);

   /* be a good boy and restore initial settings */
   tcgetattr(0, &tio);
   tio.c_lflag = old_lf;
   tcsetattr(0, TCSANOW, &tio);
   fcntl(0, F_SETFL, old_fl);

   return 0;
}


Кудряво и затейливо (по сравнению с !kbhit()), но ведь работает, да еще и нажатую клавишу определяет, спасибо! Пойду вставлять это в свою программу. А тем временем из любви к искусству попроще кто-нибудь предложить не сможет? Если, например, нажатую клавишу анализировать не обязательно, а нажатие нужно лишь для прервания цикла.

serzz добавил в 03.12.2004 00:54

А вот попытка краткого варианта:

Код: Выделить всё

#include <curses.h>

int main() {
 while (!_getch())
 {
  //Нажали
  }
 return 0;
}


имеем ошибку на стадии компиляции

$ gcc -o qq test.c -lcurses
/tmp/ccUn34EP.o(.text+0x11): In function `main':
: undefined reference to `_getch'
collect2: ld returned 1 exit status

Наученные опытом делаем по-другому

Код: Выделить всё

#include <curses.h>

int main() {
 while (!getch())
 {
        //нажали
  }
  return 0;
}


$ gcc -o qq test.c -lcurses
$ /qq
Segmentation fault

Аналогичный результат с -lncurses
В цикл для разнообразия и заполнения пустоты вставлял sleep(1); (+ #include <unistd.h>) - не помогает. Где выход из этого густого леса в три сосны?
:helpsmilie:
Спасибо сказали:
Аватара пользователя
nercus
Сообщения: 150

Re: Прерывание по нажатию клавиши

Сообщение nercus »

2serzz:
man ncurses:

Код: Выделить всё

.........
       To  initialize the routines, the routine initscr or newterm must be called
       before any of the other routines that deal with windows  and  screens  are
       used.   The  routine endwin must be called before exiting.  To get charac-
       ter-at-a-time input without echoing  (most  interactive,  screen  oriented
       programs want this), the following sequence should be used:

             initscr(); cbreak(); noecho();
.........

... и далее по тексту
2.6.14-gentoo-r5
kde-3.5.0 | openbox-3.2
Deep Purple | Rob Zombie | Led Zeppelin | ДДТ
Спасибо сказали:
serzz
Сообщения: 6

Re: Прерывание по нажатию клавиши

Сообщение serzz »

2nercus
Спасибо! В man ncurses действительно есть ответы на все вопросы.
Спасибо сказали: