Написание "реальной" интерактивной терминальной программы, такой как vim, htop,... в C/С++ без ncurses
Нет, я не хочу использовать ncurses, потому что хочу узнать, как терминал работает и развлекается программированием самостоятельно.:) Это не должны быть переносимыми, он должен работать только с эмуляторами на терминалах linux xterm.
Что я хочу сделать, так это программирование интерактивного терминального приложения, такого как htop и vim. Я имею в виду не вывод символов, которые выглядят как коробки или цвета, это тривиально; также чтобы содержимое соответствовало размеру окна. Мне нужно
-
как получить взаимодействие с мышью, например, щелкнуть по символу и прокрутить колесико мыши (когда указатель мыши находится на определенном символе) для реализации прокрутки [ EDIT: в терминале эмулятор, конечно), и
-
как полностью сохранить и восстановить вывод родительского процесса и отделить мою печать от его вывода, поэтому после выхода из моего приложения ничего, кроме команды, которую я ввел в оболочку, должно быть там, например, при запуске htop и его повторении: ничего больше не видно из этого приложения.
Я действительно не хочу использовать ncurses. Но, конечно, если вы знаете, какая часть ncurses отвечает за эти задачи, вы можете сообщить мне, где в исходном коде я могу ее найти, поэтому я буду изучать его.
Ответы
Ответ 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 и это один.