Написание «настоящих» интерактивных терминальных программ, таких как vim, htop, ... на C / C ++ без ncurses - PullRequest
45 голосов
/ 12 декабря 2011

Нет, я не хочу использовать ncurses , потому что я хочу узнать, как Терминал работает и весело программирует его самостоятельно. :) Это не должен быть переносимым, он должен работать только на эмуляторах терминала на основе Linux xterm.

Что я хочу сделать, так это программировать интерактивное терминальное приложение, такое как htop и vim. Я имею в виду не вывод символов, которые выглядят как поля или настройки цветов, это тривиально; также, чтобы содержимое соответствовало размеру окна. Что мне нужно, это

  1. как получить взаимодействие с мышью , например, щелкнув по символу и прокрутив колесо мыши (когда мышь находится у определенного символа), чтобы реализовать прокрутку [ РЕДАКТИРОВАТЬ: в эмуляторе терминала конечно ] и

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

Я действительно не хочу использовать ncurses. Но, конечно, если вы знаете, какая часть ncurses отвечает за эти задачи, вы можете сообщить мне, где в исходном коде я могу найти его, поэтому я изучу его.

Ответы [ 3 ]

18 голосов
/ 12 декабря 2011

Для управления терминалом необходимо использовать последовательность управления .К сожалению, эти коды зависят от конкретного терминала, который вы используете.Вот почему terminfo (ранее termcap) существует в первую очередь.

Вы не говорите, хотите ли вы использовать terminfo или нет.Итак:

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

Как вы хотите, для целей обучения, я подробно остановлюсь на втором.

Вы можете обнаружитьтип терминала, который вы используете из переменной окружения $TERM.В Linux наиболее обычными являются xterm для эмуляторов терминалов (XTerm, gnome-terminal, konsole) и linux для виртуальных терминалов (те, когда X не работает).

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

$ TERM=xterm tput clear | hd
00000000  1b 5b 48 1b 5b 32 4a                              |.[H.[2J|

$ TERM=linux tput clear | hd
00000000  1b 5b 48 1b 5b 4a                                 |.[H.[J|

То есть, чтобы очистить экран в xterm у вас естьдля вывода ESC [ H ESC [ 2J в xterm, но ESC [ H ESC [ J в терминале linux.

О конкретных командах, о которых вы спрашиваете, вы должны внимательно прочитать man 5 terminfo.Там много информации.

7 голосов
/ 21 февраля 2016

Несмотря на то, что это старый вопрос, я подумал, что должен поделиться кратким примером того, как это сделать без использования ncurses, это не сложно, но я уверен, что он не будет таким переносимым.

Этот код устанавливает стандартный ввод в необработанном режиме, переключается на экран альтернативного буфера (который сохраняет состояние терминала перед его запуском), включает отслеживание мыши и печатает кнопку и координаты, когда пользователь щелкает где-то.После выхода с помощью Ctrl + C программа возвращает конфигурацию терминала.

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

int main (void)
{
    unsigned char buff [6];
    unsigned int x, y, btn;
    struct termios original, raw;

    // Save original serial communication configuration for stdin
    tcgetattr( STDIN_FILENO, &original);

    // Put stdin in raw mode so keys get through directly without
    // requiring pressing enter.
    cfmakeraw (&raw);
    tcsetattr (STDIN_FILENO, TCSANOW, &raw);

    // Switch to the alternate buffer screen
    write (STDOUT_FILENO, "\e[?47h", 6);

    // Enable mouse tracking
    write (STDOUT_FILENO, "\e[?9h", 5);
    while (1) {
        read (STDIN_FILENO, &buff, 1);
        if (buff[0] == 3) {
            // User pressd Ctr+C
            break;
        } else if (buff[0] == '\x1B') {
            // We assume all escape sequences received 
            // are mouse coordinates
            read (STDIN_FILENO, &buff, 5);
            btn = buff[2] - 32;
            x = buff[3] - 32;
            y = buff[4] - 32;
            printf ("button:%u\n\rx:%u\n\ry:%u\n\n\r", btn, x, y);
        }
    }

    // Revert the terminal back to its original state
    write (STDOUT_FILENO, "\e[?9l", 5);
    write (STDOUT_FILENO, "\e[?47l", 6);
    tcsetattr (STDIN_FILENO, TCSANOW, &original);
    return 0;
}

Примечание. Это не будет работать должным образом для терминалов с более чем 255 столбцами.

Лучшие ссылки на escape-последовательности, которые я нашел, это this и this one.

3 голосов
/ 12 декабря 2011

Я немного растерялся.Вы говорите о «терминальном приложении», как vim;терминальные приложения не получают события мыши и не реагируют на мышь.

Если вы говорите о реальных терминальных приложениях, которые запускаются в xterm, важно отметить, что многиеиз проблем переносимости касаются терминала, а не ОС.Терминал управляется отправкой различных escape-последовательностей.Какие из них делают то, что зависит от терминала;управляющие коды ANSI в настоящее время довольно широко распространены, однако см. http://en.wikipedia.org/wiki/ANSI_escape_code. Они обычно понимаются, например, xterm.

Возможно, вам придется вывести дополнительную последовательность в начале и в начале.конец, чтобы войти и выйти из режима «полный экран»;это необходимо для xterm.

Наконец, вам нужно будет сделать что-то особенное на уровне ввода / вывода, чтобы убедиться, что ваш драйвер вывода не добавляет никаких символов (например, преобразовать простой LF вCRLF) и убедитесь, что входные данные не отражаются, прозрачны и возвращаются немедленно.Под Linux это делается с помощью ioctl.(Опять же, не забудьте восстановить его, когда закончите.)

...