Книга: Основы программирования в Linux

Обнаружение нажатий клавиш

Пользователи, программировавшие в ОС MS-DOS, часто ищут в ОС Linux эквивалент функции kbhit, которая определяет, была ли нажата клавиша, без реального ее считывания. К сожалению, их поиски оказываются безуспешными, поскольку прямого аналога нет. Программисты в среде UNIX не ощущают этого отсутствия, т.к. UNIX запрограммирована так, что программы очень редко (если когда-либо вообще) озабочены ожиданием события. Поскольку это обычный способ применения kbhit, ее нехватка редко ощущается в системах UNIX и Linux.

Однако, когда вы переносите программы из MS-DOS, часто удобно эмулировать функцию kbhit, которую можно применять на деле в неканоническом режиме ввода (упражнение 5.7).

Упражнение 5.7. Исключительно ваша собственная kbhit

1. Начните со стандартной заголовочной информации и пары структур для установки параметров терминала. peek_character применяется для проверки нажатия клавиши. Далее описываются функции, которые будут использоваться позже:

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <term.h>
#include <curses.h>
#include <unistd.h>
static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard();
void close_keyboard();
int kbhit();
int readch();

2. Функция main вызывает функцию init_keyboard для настройки терминала, затем выполняет цикл один раз в секунду, каждый раз вызывая в нем функцию kbhit. Если нажата клавиша <q>, функция close_keyboard восстанавливает нормальный режим и программа завершается:

int main() {
 int ch = 0;
 init_keyboard();
 while (ch != 'q') {
  printf("loopingn");
  sleep(1);
  if (kbhit()) {
   ch = readch();
   printf("you hit %cn", ch);
  }
 }
 close_keyboard();
 exit(0);
}

3. Функции init_keyboard и close_keyboard настраивают терминал в начале и конце программы:

void init_keyboard() {
 tcgetattr(0, &initial_settings);
 new_settings = initial_settings;
 new_settings.c_lflag &= ~ICANON;
 new_settings.c_lflag &= ~ECHO;
 new_settings.c_lflag &= ~ISIG;
 new_settings.c_cc[VMIN] = 1;
 new_settings.c_cc[VTIME] = 0;
 tcsetattr(0, TCSANOW, &new_settings);
}
void close_keyboard() {
 tcsetattr(0, TCSANOW, &initial_settings);
}

4. Теперь функция, проверяющая нажатие клавиши:

int kbhit() {
 char ch;
 int nread;
 if (peek_character != -1) return 1;
 new_settings.c_cc[VMIN] = 0;
 tcsetattr(0, TCSANOW, &new_settings);
 nread = read(0, sch, 1);
 newrsettings.c_cc[VMIN] = 1;
 tcsetattr(0, TCSANOW, &new_settings);
 if (nread == 1) {
  peek_character = ch;
  return 1;
 }
 return 0;
}

5. Нажатый символ считывается следующей функцией readch, которая затем восстанавливает значение -1 переменной peek_character для выполнения следующего цикла:

int readch() {
 char ch;
 if (peek_character != -1) {
  ch = peek_character;
  peek_character = -1;
  return ch;
 }
 read(0, &ch, 1);
 return ch;
}

Когда вы выполните программу (kbhit.c), то получите следующий вывод:

$ ./kbhit
looping
looping
looping
you hit h
looping
looping
looping
you hit d
looping
you hit q
$

Как это работает

Терминал настраивается в функции init_keyboard на считывание одного символа (MIN=1, TIME=0). Функция kbhit изменяет это поведение на проверку ввода и его немедленный возврат (MIN=0, TIME=0) и затем восстанавливает исходные установки перед завершением.

Обратите внимание на то, что вы должны считать символ нажатой клавиши, но сохраняете его локально, готовые вернуть символ в вызывающую программу по ее требованию.

Оглавление книги


Генерация: 1.299. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз