Подтвердить что ты не робот

Написание "реальной" интерактивной терминальной программы, такой как vim, htop,... в C/С++ без ncurses

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Чтобы управлять терминалом, вы должны использовать контрольные последовательности. К сожалению, эти коды зависят от конкретного терминала, который вы используете. Вот почему 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. Там много информации.

Ответ 3

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

Этот код устанавливает stdin в необработанном режиме, переключается на экран альтернативного буфера (который сохраняет состояние терминала перед его запуском), позволяет отслеживать мышью и печатать кнопку и координаты, когда пользователь нажимает где-то. После выхода с помощью 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 и это один.