Linux: снова вопрос про аналог conio.h (неблокирующий ввод с клавиатуры под linux)

Господа,

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

Я хочу иметь возможность в программе на C спросить "верни мне, пожалуйста, код нажатой клавиши, а если ничего не нажато - верни -1".

Под виндой именно такую функциональность даёт include <conio.h> ... int kb=getch();.

Конечно, я прочитал вот этот и вот этот топики и поэкспериментировал с теми способами, котрые там советуют.

У них есть один недостаток: они "ломают вывод на экран" при помощи простого printf().

То есть, по меткому выражению одного из комментаторов, используя ncurses вы получаете "всё или ничего". То есть, если Вы подключили ncurses - нужно забыть про printf и использовать специфичные для ncurses способы вывода на экран.

С советом, в котором приводится код для getch()

    int getch()
    {
        int r;
        unsigned char c;
        if ((r = read(0, &c, sizeof(c))) < 0) {
            return r;
        } else {
            return c;
        }
    }

(это кусочек из полного кода, который доступен по первой ссылке) всё немного лучше, но всё равно, при попытке в цикле вводить симолы и печатаь их коды на экране возникает вот такая картина:

код main'а: (выше мейна написан целиком тот код, который советуют в самом популярном ответе по первой ссылке)

    int main(int argc, char *argv[]) {
        printf("Hello again!\n");
        set_conio_terminal_mode();

        while (!kbhit()) {
            /* do some work */
        }

        for(int i=0; i<5; ++i){
            int kb = getch(); /* consume the character */
            printf ("kb=%d\n", kb); 
        }

        printf ("Bye!\n");
    }

картинка на экране:

как съезжает каретка при каждой печати

Ну, то есть, при выполнении каждого printf положение "виртуальной каретки" смещается.

Нельзя ли с этим как то побороться? может, есть способ, как вводить символы с клавиатуры и при этом нормально пользоваться printf?


Ответы (1 шт):

Автор решения: avp

Вы меня устыдили и я быстренько попробовал вспомнить.

У меня

avp@avp-desktop:~/avp/hashcode$ uname -a
Linux avp-desktop 5.4.0-70-generic #78~18.04.1-Ubuntu SMP Sat Mar 20 14:10:07 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
avp@avp-desktop:~/avp/hashcode$ cat /etc/issue
Linux Mint 19.3 Tricia \n \l

avp@avp-desktop:~/avp/hashcode$ echo $TERM
xterm-256color
avp@avp-desktop:~/avp/hashcode$ 

Вот программка для печати кодов вводимых символов

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

int main()
{
  struct termios t, old;

  tcgetattr(0, &t);
  old = t;
  t.c_lflag &= ~(ISIG | ICANON | ECHO);
  t.c_cc[VMIN] = 1;
  t.c_cc[VTIME] = 0;
  tcsetattr(0, TCSANOW, &t);

  char c;
  while (read(0, &c, 1) == 1) {
    printf("'%c' is %d 0%03o 0x%02x\n",
       c, c, c, c);
    if (c == 4) // ^D is EOF as usual
      break;
  }
    

  tcsetattr(0, TCSANOW, &old);
  
  return puts("End") == EOF;
}

Я немного подолбил по клавишам и кажется, что у меня она вполне работает:

avp@avp-desktop:~/avp/hashcode$ gcc ttt.c && ./a.out
'1' is 49 0061 0x31
'2' is 50 0062 0x32
'3' is 51 0063 0x33
'
' is 10 0012 0x0a
'
' is 10 0012 0x0a
'
' is 10 0012 0x0a
'
 ' is 11 0013 0x0b
'q' is 113 0161 0x71
'w' is 119 0167 0x77
'e' is 101 0145 0x65
'' is 127 0177 0x7f
'!' is 33 0041 0x21
'@' is 64 0100 0x40
'$' is 36 0044 0x24
'' is 4 0004 0x04
avp@avp-desktop:~/avp/hashcode$ 
→ Ссылка