LINUX.ORG.RU

Реакция на нажатие стрелок клавиатуры.

 , , , стрелки


0

1

Собственно, сабж. Имеется файлик с двумя числами - эти числа я хочу редактировать интерактивно (вообще-то файл будет использоваться для фейка одной проги, в котором можно будет управлять выводом). Да не просто командами, а силой мысли стрелками на клавиатуре, как в альсамиксере. Но такого мощного интерфейса мне не нужно, главное, чтобы стрелками влево-вправо можно было перемещаться между числами, а стрелками вверх-вниз уменьшать и увеличивать выбранное число на 1 и флушить файл.



Последнее исправление: nic11 (всего исправлений: 2)
Ответ на: комментарий от void

я о curses тоже думал, но для моих нужд жирновато. sfml можно посмотреть. а нет способа сделать это без библиотек, штатными линуксовыми .h?

nic11
() автор топика

При нажатии стрелочки на стандартный ввод прилетает последовательность «ESC [ X», где X это A, B, C или D. Через tcsetattr выключаешь буферизацию ввода, после по getchar вылавливаешь нажатие стрелочек.

kim-roader ★★
()
Ответ на: комментарий от nic11

костыль

C/C++ не знаю, поэтому тройной getchar() :)

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

enum
{
  KEY_UP    = 183,
  KEY_DOWN  = 184,
  KEY_LEFT  = 186,
  KEY_RIGHT = 185
};

int mygetch(void){
    struct termios oldt,newt;
    int ch;
    tcgetattr( STDIN_FILENO, &oldt );
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt );
    ch = getchar() + getchar() + getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
    return ch;
}

int main () {
    int ch = mygetch();
    switch (ch) {
        case KEY_UP: puts("UP"); break;
        case KEY_DOWN: puts("DOWN"); break;
        case KEY_LEFT: puts("LEFT"); break;
        case KEY_RIGHT: puts("RIGHT"); break;
        default: puts("OTHER");
    }
    return 0;
}

void
()
Ответ на: комментарий от nic11

хмм, я повелся на ман :D т.е. наверное, описанная там методика работает, но у меня что-то не заработало, хмм

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

nic11
() автор топика
Ответ на: костыль от void

C/C++ не знаю, поэтому тройной getchar() :

Ну вообще автору явно не нужно дёргать атрибуты терминала туда-обратно. Один раз выставил в «&= ~( ICANON | ECHO )» и всё.

Кроме этого складывать коды символов это не правильно. Может там было три символа не стрелки, а сумма совпала? Надо считывать один символ. Если это был ESC (код равен 27), то считывать второй. Если вторым символом был [ (код равен 91), то мы попали в ESC последовательность. После этого, если следующий символ это A, B, C или D, то у нас была стрелка и нужно дёргать функцию обратабывающую стрелку.

Вот пример:

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

int
main (int argc, char **argv)
{
  struct termios newt, oldt;
  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt); /* enable raw mode */

  int c = 0;
  puts("press ENTER to exit");
  for (;;)			/* read_symbols */
    {
      c = getchar();
      if (c == 27)		/* if ESC */
	{
	  c = getchar();	
	  if (c == '[')		/* ... [ */
	    {
	      c = getchar();
	      switch (c)
		{
		case 'A':
		  puts("UP"); break;
		case 'B':
		  puts("DOWN"); break;
		case 'C':
		  puts("RIGHT"); break;
		case 'D':
		  puts("LEFT"); break;
		default:
		  puts("not arrow"); break;
	      }
	    }
	  else 
	    {
	      puts("not arrow");
	    }
	}
      else if (c == 10)
	{
	  puts("Good by");
	  break;
	}
      else
	{
	  puts("not arrow");
	}
    }
  tcsetattr(STDIN_FILENO, TCSANOW, &oldt); /* return old config */
  return EXIT_SUCCESS;
}

kim-roader ★★
()
Ответ на: комментарий от kim-roader

Мне кажется, он просто привел пример и поэтому не стал уж делать проверки.. (я бы их дописал). Правда, ваш код мне кажется немного громоздким.. просто я привык писать код более компактно.. но все равно спасибо :)

nic11
() автор топика
Ответ на: комментарий от nic11

curses … для моих нужд жирновато.

когда ты откроешь для себя Qt, тебя разорвёт и забрызгает стены

anonymous
()
Ответ на: комментарий от nic11

реализация ncurses - это пара сотен килобайт (бинарник)

pacify ★★★★★
()

keyboard.h:

#ifndef KEYBOARD_H
#define KEYBOARD_H

enum terminal_mode {
    NORMAL,
    RAW
};

int keyboard_event(void);
void set_terminal_mode(enum terminal_mode mode);

#define ARROW_PREFIX_1 27 // '\ESC'
#define ARROW_PREFIX_2 91 // '['
#define ARROW_LEFT 68 // 'D'
#define ARROW_RIGHT 67 // 'C'
#define ARROW_UP 65 // 'A'
#define ARROW_DOWN 66 // 'B'

#define IS_ARROW(c)\
    (((c) == ARROW_PREFIX_1) && (getchar() == ARROW_PREFIX_2))

#endif // KEYBOARD_H

keyboard.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <sys/select.h>

#include "keyboard.h"

int keyboard_event(void)
{
    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(fileno(stdin), &rfds);
    return select(fileno(stdin) + 1, &rfds, NULL, NULL, NULL);
}

void set_terminal_mode(enum terminal_mode mode)
{
    static struct termios termios;
    struct termios new_termios;

    switch (mode) {
    case RAW:
        tcgetattr(fileno(stdin), &termios);
        memcpy(&new_termios, &termios, sizeof(struct termios));
        new_termios.c_lflag &= ~(ECHO|ICANON);
        new_termios.c_cc[VTIME] = 0;
        new_termios.c_cc[VMIN] = 0;
        tcsetattr(fileno(stdin), TCSANOW, &new_termios);
        break;
    case NORMAL:
        tcsetattr(fileno(stdin), TCSANOW, &termios);
        break;
    }
}

И потом:

#include "keyboard.h"

...

    set_terminal_mode(RAW);

    int key;

    for (;;) {
        if (keyboard_event() > 0) {
            key = getchar();
            if (IS_ARROW(key)) {
                key = getchar();
                switch (key) {
                case ARROW_LEFT:
                    ...
                    continue;
                case ARROW_RIGHT:
                    ...
                    continue;
                case ARROW_DOWN:
                    ...
                    continue;
                case ARROW_UP:
                    ...
                    continue;
                }
            }
            switch (key) {
            ...
            }
        }
    }

обратно:

    set_terminal_mode(NORMAL);

вроде как на stackoverflow.com были подробные обсуждения, но сейчас с ходу не нашёл.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

set_terminal_mode

Она плохо переключает (normal -> raw -> normal -> ... - да, normal -> raw -> raw -> normal - уже нет), нужно поправить.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

new_termios.c_cc[VTIME] = 0;
new_termios.c_cc[VMIN] = 0;

так и с keyboard_event (с select) ожидает в poll_shedule_timeout, если их убрать, то можно обойтись без keyboard_event, как у kim-roader выше, тогда будет ждать в n_tty_read.

quasimoto ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.