Создание текстового редактора. Чтение символов из терминала в режиме raw

#c #linux #terminal

#c #linux #терминал

Вопрос:

Я следую руководству по созданию собственного текстового редактора о том, как запрограммировать свой собственный текстовый редактор.

В настоящее время я нахожусь на низкоуровневом чтении нажатий клавиш и сопоставляю его с операциями редактора (раздел: Рефакторинг ввода с клавиатуры). Но когда я реализую то же самое в своем коде и запускаю его, мой терминал просто зависает, и мне приходится перезапускать, чтобы вернуться к нормальной жизни. Я использую Parrot OS (если это имеет значение). Вот соответствующий раздел моего исходного кода:

 char editorReadKey() {
    int nread;
    char c;
    while ((nread = read(STDIN_FILENO, amp;c, 1) != 1)) {
        if (nread == -1 amp;amp; errno != EAGAIN)
            die("read");
        /*if (iscntrl(c))
            printf("%drn", c);
        else
            printf("%d('%c')rn", c, c);
        */
    }
    return c;

}
void editorProcessKeypress() {
    char c = editorReadKey();
    switch (c) {
        case CTRL_KEY('q') : exit(0);
                             break;
    }
}
int main(void) {
    enableRawMode();
    while (1) {
            editorProcessKeypress();
    }
    return 0;
  
  

Функции enableRawMode и disableRawMode не отличаются и более или менее являются точной реализацией руководства.

 void disableRawMode() {
    if(tcsetattr(STDIN_FILENO, TCSAFLUSH, amp;orig_termios) == -1)
        die("tcsetattr");
}
void enableRawMode() {
    if(tcgetattr(STDIN_FILENO, amp;orig_termios) == -1)
        die("tcgetattr");
    atexit(disableRawMode);
    struct termios raw = orig_termios;
    tcgetattr(STDIN_FILENO, amp;raw);
    //turns off echoing the command on the screen
    //turn off cannonical mode
    raw.c_iflag amp;= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    raw.c_oflag amp;= ~(OPOST);
    raw.c_cflag |= (CS8);
    raw.c_lflag amp;= ~(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 0;
    raw.c_cc[VTIME] = 1;
    if(tcsetattr(STDIN_FILENO, TCSAFLUSH, amp;raw) == -1)
        die("tcsetattr");
}
  

Какие-либо причины, по которым это может происходить? Или я делаю что-то не так?

РЕДАКТИРОВАТЬ: добавлено возвращаемое значение, к editorReadKey() которому я как-то пропустил его здесь.

Комментарии:

1. Начните с int c; и, вероятно, вы используете termcap или curses: по крайней мере, добавьте соответствующие заголовки.

2. @wildplasser int c; — не в этот раз

3. Итак, что вы имели в виду под «зависанием терминала»? У вас там есть клавиша ctrl q выхода, ты нажал ctrl q ?!

4. @wildplasser уже пробовал то же самое. Проблема по-прежнему сохраняется.

5. editorReadKey необходимо вернуть значение.

Ответ №1:

Ctrl-S и Ctrl-Qиспользуются для приостановки и возобновления вывода на консоль в готовом режиме, что может объяснить ваши наблюдения, хотя вы отключаете этот режим, отключив IXON raw.c_iflag его . Настройки VMIN and VTIME также могут быть неверными: установка VMIN нуля кажется неправильной.

В Quick Emacs я использую этот код для настройки режима raw:

     tcgetattr(fileno(s->STDIN), amp;tty);
    ts->oldtty = tty;

    /* input modes: no break, no CR to NL, no parity check, no strip char,
     * no start/stop output control. */
    tty.c_iflag amp;= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON |
                     IGNBRK | PARMRK | INLCR | IGNCR);
    /* output modes - enable post processing? */
    tty.c_oflag amp;= ~(OPOST);
    /* control modes - set 8 bit chars, no parity checking */
    tty.c_cflag amp;= ~(CSIZE | PARENB);
    tty.c_cflag |= CS8;
    /* local modes - echoing off, canonical off, no extended functions,
     * no signal chars (^Z,^C) */
    tty.c_lflag amp;= ~(ECHO | ICANON | IEXTEN | ISIG | ECHONL);
    /* control chars - set return condition: min number of bytes and timer.
     * We want read to return every single byte, without timeout. */
    tty.c_cc[VMIN] = 1;   /* 1 byte */
    tty.c_cc[VTIME] = 0;  /* no timer */

    tcsetattr(fileno(s->STDIN), TCSANOW, amp;tty);
  

Подробное объяснение флагов можно найти на странице руководства Linux для termios.

Комментарии:

1. Я ценю объяснение. Но я не понимаю, как VMIN = 0 здесь может быть неправильно. Я установил его равным нулю, потому что я хочу read() , чтобы он возвращался, как только ввод считывается. Я должен добавить, что код работал отлично, пока я не дошел до Refactor Keyboard Input части руководства, а затем он начал вести себя странно. Я не понимаю, почему это может происходить.