Как сделать так, чтобы клавиши со стрелками и backspace работали правильно при запросе ввода от пользователя в C-программе, используя termios.h?

Итак, у меня есть следующий код, который в основном просто читает символы, введенные пользователем, и печатает их до тех пор, пока не будет введено «q».

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

int main(void) {
    char c; 
    static struct termios oldtio, newtio;
    tcgetattr(0, &oldtio);
    newtio = oldtio;
    newtio.c_lflag &= ~ICANON;
    newtio.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &newtio);

    printf("Give text: ");
    fflush(stdout);
    while (1) {
        read(0, &c, 1);
        printf("%c", c);
        fflush(stdout);
        if (c == 'q') { break; }
    }
    printf("\n"); 
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

В начале основной функции я отключаю канонический режим, чтобы пользователь мог видеть его ввод, когда он его дает. Я также отключаю эхо, чтобы такие вещи, как «^ [[A» не появлялись, например, при нажатии клавиши со стрелкой вверх. Это работает, но я также могу переместить курсор в верхние строки в окне терминала, и это не хорошо. Есть ли способ исправить это, чтобы пользователь мог двигаться только в пределах текущей строки?

Еще одна проблема - это забой. Когда я нажимаю на нее, программа печатает странный символ (который я предполагаю 0x7f) вместо стирания символа слева от текущего местоположения курсора. Я должен как-то обработать вывод клавиши возврата в программе, но я не знаю, как это сделать, потому что это странное шестнадцатеричное число. Любые советы для этого?

Один из вариантов, о котором я тоже подумал, - это использовать канонический режим, чтобы автоматически использовались клавиши со стрелками и функции возврата. Однако канонический режим работает построчно, поэтому текст не появляется, пока пользователь не нажмет «Enter». До сих пор я не нашел способа заставить пользователя видеть свои данные при наборе текста. Это вообще возможно?

И, пожалуйста, никаких ncurses или readline предложений. Я хочу сделать это с помощью termios.h.

 n.m.28 окт. 2014 г., 19:00
Вы выключаете эхо, а затем эхом все сами. Это не удачная стратегия. Эхо обыкновенных печатных символов, обработка и интерпретация управляющих функций. Вы должны знать, каковы ваши контрольные последовательности и что они значат. Для этого вам нужен эквивалент базы данных terminfo. Так как terminfo тесно связан с ncurses, а вы не хотите ncurses, я могу только пожелать вам удачи.
 JZ55528 окт. 2014 г., 19:33
Так ты говоришь, что это даже невозможно без ncurses?
 n.m.28 окт. 2014 г., 20:32
Все возможно без всего, если вы переопределяете части всего с нуля и не возражаете отказаться от функциональности в других частях. Вы можете ограничиться одним популярным семейством терминалов, скажем VT100 и потомками, составить свой собственный список последовательностей управления и работать с ним. В противном случае вы в значительной степени должны использовать хотя бы terminfo (технически часть (n) curses).
 Nulik06 сент. 2016 г., 13:54
@ N.m. как насчет реализации других ncurses, полагающихся на кодировку UTF8? UTF8 теперь является стандартным набором символов, и его поддерживает каждый эмулятор терминала. Будет ли это сделать новую библиотеку (чтобы заменить ncurses) легче? Нам также не понадобится terminfo, верно?

Ответы на вопрос(2)

Решение Вопроса

ты заглянул в справочные страницы? (должно бытьman termios или где-нибудь посмотретьонлайн)

Там я нашелECHOE флаг, который, как говорят, имеет следующий эффект:

Если также установлен ICANON, символ ERASE стирает предыдущий входной символ, а WERASE стирает предыдущее слово.

Это должно исправить вашу проблему с возвратом?

Я также предлагаю вам взглянуть на примеры на странице руководства. Например. Вы могли бы сделать следующее:

newtio.c_lflag &= ~(ECHO | ECHOE | ICANON);

... установить более одного флага за раз только в одной строке. Я знаю, что справочные страницы трудны для чтения новичкам, но вы привыкнете к ним, и чем больше вы их используете, тем эффективнее они становятся для поиска C / POSIX-функций и т. Д. (На всякий случай, вы их не используете тем не мение).

Стрелка-ключ-проблема: Может быть, вы можете попробоватьcfmakeraw()-функции; его описание звучит многообещающе. У меня не было времени, чтобы продолжить расследование по поводу клавиш со стрелками. Однако, возможно, вы найдете что-то еще полезное на странице руководства.

КСТАТИ:termios выглядит интересно, я всегда задавался вопросом, какие функции используют определенные программы командной строки; узнал что-то по вашему вопросу, спасибо!

РЕДАКТИРОВАТЬ

Я сделал еще несколько исследований в эти выходные. «Странный» символ, напечатанный при нажатии клавиши возврата, довольно легко скрыть. Это ASCII-значение0x7f, Так что добавьте простой

if (c == 0x7f) { continue; }

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

Этот простой обходной путь не работает для клавиш со стрелками, поскольку они не являются символами ASCII: / Тем не менее, эти две темы помогли мне решить и эту проблему:тема 1 а такжетема 2, В основном нажатие клавиш со стрелками приводит к последовательности из нескольких символов, отправляемых наstdin (см. вторую ссылку для получения дополнительной информации).

Вот мой полный код, который (я думаю) работает так, как вы хотите:

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

int main(void) {
    char c;
    static struct termios oldtio, newtio;
    tcgetattr(0, &oldtio);
    newtio = oldtio;
    newtio.c_lflag &= ~ICANON;
    newtio.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &newtio);

    printf("Give text:\n");
    fflush(stdout);
    while (1) {
        c = getchar();

        // is this an escape sequence?
        if (c == 27) {
            // "throw away" next two characters which specify escape sequence
            c = getchar();
            c = getchar();
            continue;
        }

        // if backspace
        if (c == 0x7f) {
            // go one char left
            printf("\b");
            // overwrite the char with whitespace
            printf(" ");
            // go back to "now removed char position"
            printf("\b");
            continue;
        }

        if (c == 'q') {
            break;
        }
        printf("%c", c);
    }
    printf("\n");
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

Кстати, вы можете получить полные escape-последовательности с помощью следующего кода:

int main(void) {
    char c;
    while (1) {
        c = getchar();
        printf("%d", c);
    }
    return 0;
}

Я думаю, мне не нужно говорить, что эта полная штука довольно грязная, и легко забыть обработать некоторые специальные ключи. Например. в моем коде я не обрабатываюpage-up/down или жеhome ключи ... -> приведенный выше код далеко не полный, но дает вам точку для начала. Вы также должны взглянуть наterminfo который может предоставить вам много необходимой информации; это также должно помочь с более портативным решением. Как видите, эта «простая» вещь может стать довольно сложной. Так что вы можете пересмотреть свое решение против ncurses :)

 mozzbozz03 нояб. 2014 г., 13:42
@ JZ555 Добро пожаловать :)
 mozzbozz29 окт. 2014 г., 10:22
@ JZ555: Хм, хорошо, вы правы ... Попробую проверить это дома сегодня вечером (там у меня есть система Linux (-> POSIX)), может быть, я могу найти что-то еще там ... извините, я не могу помочь прямо сейчас.
 JZ55528 окт. 2014 г., 19:30
Нет, он все еще дает мне символ «0x7f» вместо стирания символа, потому что, как говорится, ICANON должен быть установлен, то есть он должен быть включен. И если я включу его, пользователь не сможет увидеть, что он печатает, пока не нажмет «Enter». Но спасибо за усилия.
 JZ55503 нояб. 2014 г., 12:47
Это именно то, что мне нужно, чтобы начать. Спасибо! :)
 mozzbozz03 нояб. 2014 г., 01:37
@ JZ555 Было немного сложнее, чем я думал, но я думаю, что нашел решение для тебя сейчас. УвидетьРЕДАКТИРОВАТЬраздел моего ответа :)

На самом деле, для обработки клавиш со стрелками, вам придется реализовать кусок ncurses хорошего размера. Есть плюсы / минусы: основным недостатком использования ncurses в приложении командной строки может быть то, что оно обычно очищает экран. Тем не менее, (п) проклятия обеспечивает функциюфильтр, В исходном коде ncurses есть пример программы «test / filter.c», который иллюстрирует это с помощью клавиши со стрелкой влево в качестве символа стирания и передает полученную строку в system () для выполнения простых команд. Пример составляет менее 100 строк кода - кажется, проще и полнее, чем в приведенных выше примерах.

Ваш ответ на вопрос