Работа с терминалом; пример простого проекта
Терминал
TODO перенести в первую лекцию (отчасти уже)
- В/В байтов
- Управление со стороны ОС (сигналы с клавиатуры),
stty
- Управление выводом со стороны приложения, ESC-последовательности
- Немного истории
- изменение атрибутов текста
xterm:
$ echo -e '\e[44;36m'; cat; echo -e '\e[m'
В конце команды — Enter + ^D
- позиционирование курсора
xterm:
$ echo -e '\e[2J\e[10;10HЭто 10:10\e[2;20HЭто 2:20\e[20;5HЭто 20:5'
- дополнительные устройства ввода (например, мышь) — имитация ввода с клавиатуры (обычно включается особой ESC-последовательностью)
xterm:
$ echo -e '\e[?1002h'; cat; echo -e '\e[?1002l' …
или 1003
terminfo(/termcap); tput/infocmp
- Унификация управляющих последовательностей
Проблемы с неправильным $TERM
Неожиданный бонус Tektronix_4010:
если вы используете xterm, просто скачайте этот файл и скажите ему cat:
$ cat map.tek.xterm
Хуже не будет
Для пользователей xterm в дистрибутивах линейки ALT (возможно, и для других тоже) — сценарий на shell, который показывает все .tek-файлы в заданном каталоге (по умолчанию — в документации по xterm).
Curses
(кому интересно) Учебник на русском
(кому интересно) Учебник на английском
(кому интересно) Учебник от автора ncurses
NCurses — свободная (на сегодняшний день, главная) реализация Curses
- Curses: библиотека работы с произвольным терминалом
- Независимость от типа терминала
- Экран = матрица символов (а не телетайп)
Важно — функции вывода заполняют эту воображаемую матрицу (а не настоящий экран), экран обновляется отдельной командой.
- Простой пример:
Сборка: cc Hello.c -lcurses -o Hello
initscr() / endwin() — инициализация и останов движка curses
move() — перемещение курсора, LINES/COLS — размер экрана
addstr() — аналог puts()
(никогда больше не используем -lcurses)
- Если надо по-русски, программа должна установить локаль:
Сборка — с libncursesw: cc Hello.c -lncursesw -o Hello
- Пример с окном
1 #include <ncurses.h> 2 3 #define DX 3 4 5 void main() { 6 WINDOW *win; 7 int c = 0; 8 9 initscr(); 10 noecho(); 11 cbreak(); 12 printw("Window:"); 13 refresh(); 14 15 win = newwin(LINES-2*DX, COLS-2*DX, DX, DX); 16 keypad(win, TRUE); 17 scrollok (win, TRUE); 18 box(win, 0, 0); 19 wmove(win, 1, 0); 20 while((c = wgetch(win)) != 27) { 21 wprintw(win, " Key: %d, Name: %s\n", c, keyname(c)); 22 box(win, 0, 0); 23 wrefresh(win); 24 } 25 endwin(); 26 }
Поэкспериментируйте: попробуйте закомментировать refresh(), вместо noecho() вызвать echo(), вместо cbreak() — nocbreak(), а в keypad() и scrollok() указать FALSE. Что делают эти функции?
В виртуалке практикума есть man-страницы на всё))
Функция wrefresh(win) в этом коде не нужна, потому что curses обновляет содержимое соответствующего окна перед тем, как вводить из него
Примитивный Makefile:
- Make (очень коротко): цели и рецепты
Синтаксис: обязательный символ табуляции в рецептах
- Задача — запуск команд пересборки, когда это надо
- Цели могут быть, а могут не быть файлами
- Пример:
- Использование:
1 $ make 2 cc Window.c -o Window -lncursesw 3 $ ls 4 a.out Hello.c~ Makefile~ win.c Window.c WindowEx.c 5 Hello.c Makefile simple.c Window Window.c~ 6 $ make 7 make: «Window» не требует обновления. 8 $ make clean 9 rm -f *~ *.o Window a.out 10 $ ls 11 Hello.c Makefile simple.c win.c Window.c WindowEx.c 12 $ make 13 cc Window.c -o Window -lncursesw 14 $ ls 15 Hello.c Makefile simple.c win.c Window Window.c WindowEx.c 16
Tab hell. Again.
- Что такое символ табуляции
- Как распознать табуляцию
hexdump -C файл
vim :set list
mcedit
Если ничего не помогает, в Makefile можно переопределить символ, с которого начинаются строчки с «рецептами» (но, к сожалению, не в пробел ☹)
.RECIPEPREFIX=: Window: Window.c : cc Window.c -o Window -lncursesw clean: : rm -f *~ *.o Window a.out
Д/З
Предполагается, что на спецкурс вы записались
Сделать в репозитории подкаталог, совпадающий с именем данной страницы (02_TerminalProject, и поместить в него решение следующей задачи.
С помощью интернета и здравого смысла написать на ncurses программу Show.c, которая постранично просматривает файл (слишком длинные строки усекаются или переносятся — как вам удобнее).
- Имя файла передавать параметром командной строки.
- Файл должен показываться в окне, а в первой строке экрана должно содержаться его имя.
Весь файл допустимо хранить в памяти, например, одним куском или в виде списка строк.
При нажатии пробела файл прокручивается дальше (если есть такая возможность), при нажатии ESC программа завершается.
Имеет смысл очищать окно при помощи werase(), а потом всё заново на нём выводить.
- Многобайтовую кодировку можно не поддерживать
Сделать Makefile и добиться работоспособности make (обратите внимание на использование табуляций; понадобится на следующей лекции)
- Не забыть опубликовать решение в репозитории!
Дополнительно, для тех, кому стало интересно:
- При нажатии стрелки вправо выводить строки файла, начиная со следующей колонки (так можно посмотреть урезанные длинные строки; начала строк при этом не видны). Стрелку влево обработать так же.
Реализовать обработку PgUp, PgDown и стрелок вверх/вниз — прокрутка на один экран вперёд/назад, на одну строку вперёд-назад, остальные клавиши игнорировать
- … (далее везде)
Вот так как-то это выглядело год назад
А вот так это выглядит сейчас (я сделал поддержку wide characters и цвет)