Linux graphics in console

Терминальная графика

Когда данных становится слишком много, бывает не хватает стандартного вывода printf в консольной программе. Особенно если различных событий много и различные данные превращаются в безумный листинг. Эти данные могут поступать от контроллера через UART, и тут нечего и думать о какой-то gui-программе. Может так же быть и обычный bash-скрипт, к которому хочется прикрутить какой-никакой псевдографический интерфейс.

Скажу сразу, может поначалу показаться это изобретением велосипеда, ведь есть прекрасная библиотека ncurses. Ничуть не умаляю её достоинств, но часто её возможности бывают слишком избыточны. Плюс она слабо переносима в контроллеры и там уж точно избыточна. А вот пару макросов и функций для работы с псевдографикой можно набросать и самим.

Постановка задачи

Те кто занимался программированием контроллеров часто для вывода информации при отладке используют консоль аки UART или УАПП по-русски. Раньше я давал описание данного интерфейса в своих постах на хабре. При достаточно большом количестве разнообразной информации отлавливать её становится совершенно невозможно. Получается многостраничный лог, особенно если непрерывно идут данные с каких-то аналоговых датчиков, плюс надо контролировать состояние портов и т.п. В результате люди начинают писать свои обработчики на стороне компьютера и решение становится совсем не переносимым, а главное тратится впустую куча времени. Но, на самом деле можно всю разнообразную информацию с контроллера уместить в одном окне терминала и удобно её использовать, без написания стороннего софта. В результате наше решение становится переносимым (нужна любая терминалка), и экономится куча программисто-часов на отладку.

Методический пример

Данный пример будет скорее учебный, методический, но вы его легко сможете адаптировать к своему реальному устройство на любом контроллере. Так же по аналогии это всё будет работать на bash, pyton и прочем.

Пусть у нас есть контроллер, где к 8-ми разрядному порту подключены контактные датчики, которые могут принимать состояние ВКЛ и ВЫКЛ, есть аналоговый трёхосевой датчик, и мы должны выводить их состояние, плюс в контроллере есть время, дата, и мы хотим контролировать правильность отображения времени; и наконец там может случится событие (например сбой по питанию), где мы должны срочно информировать оператора. Подытожим:

  • Состояние 8-ми пинов порта (on/off)
  • Трёхосевой датчик (x, y, z) — отдельный вывод каждой оси
  • Дата и время
  • Сообщение оператору об ошибке

Рамочки я скопировал тупо из mc. Те, кто хочет красоты, могут посмотреть какие есть рамочки в unicode. Сверху цифры написаны специально для координат.

Вроде как идея понятна, но как это реализовать в терминале? А вот тут начинается настоящая магия! «А теперь надо обязательно дунуть! Если не дунуть, никакого чуда не произойдет!» Амаяк Акопян

Управляющие последовательности

Изначально планировал в этой главе развернуть лекцию про управление терминалом с помощью ESC-последовательностей. Но тогда бы статья разрослась просто до неприличных размеров, поэтому я дам лишь некоторые понятия, а остальное можно найти в интернете или в мануалах.
Терминал — это устройство ввода-вывода, которое позволяет вводить и отображать данные. Раньше терминал представлял из себя монитор и клавиатуру, подключаемые по СОМ-порту к ЭВМ, а в более ранних версиях даже телетайп.

Читайте также:  What is working directory in linux

Телетайп Model 33 (картинка с википедии)

Сегодня терминал представляет собой программу, которая может быть виртуальным терминалом, как в linux или терминалом, которая работает с СОМ-портом. Все эти терминалы, даже классический HyperTerminal отвечают некоторым стандартам. Как не сложно догадаться, такими терминалами нужно управлять. Есть обычные ASCII символы, которые отображаются на экране, а есть специальные последовательности символов, которые позволяют задавать координаты курсора, очищать экран, задавать цвет и т.п. В своих статьях на хабре и гиктаймс я уже неоднократно касался тем ESC-последовательностей. Мы их применяли в управлении дисплеем при написание драйверов под Linux (описание в спойлере, который никто не читал), и в статье про Дисплей покупателя для wifi-радио, который так же управляется ESC-последовательностями.

Если кто-то из вас застал времена BBS, то вы помните какие «красивые» были эти доски: цветные, у которых были определённые поля ввода-вывода информации и т.п. Вся эта радость выводилась с помощью таких Управляющих символов. Символ считается управляющим, если (до преобразования согласно таблице перекодировки) он содержит один из 14-и кодов: 00 (NUL), 0x07 (BEL), 0x08 (BS), 0x09 (HT), 0x0a (LF), 0x0b (VT), 0x0c (FF), 0x0d (CR), 0x0e (SO), 0x0f (SI), 0x18 (CAN), 0x1a (SUB), 0x1b (ESC), 0x7f (DEL). Нас интересует в первую очередь символ ESC=0x1b, ‘\033’ или же ‘\e’. Подробнее о этих последовательностях можно прочитать в мануалах man console_codes, либо в интернете на русском.

Кроме управления курсором, можно ещё раскрашивать терминал в разные цвета (если он, конечно, цветной). Проверить цвета вашего терминала можно простейшим BASH-скриптом:

for fgbg in 38 48 ; do #Foreground/Background for color in ; do #Colors #Display the color echo -en "\e[$;5;$m $\t\e[0m" #Display 10 colors per lines if [ $((($color + 1) % 10)) == 0 ] ; then echo #New line fi done echo #New line done exit 0 

Результат выполнения будет примерно такой:

Более детально почитать о цветах и шрифтах в консоли (и любых терминалах) с примерами можно вот тут.

Вот, вы уже владеете необходимой информацией, чтобы сделать весёлые пасхалки в BASH-скриптах, как например картинка, вынесенная в заголовок поста.

Более детально, как я делал такую пасхалку на bash, можно почитать у меня в ЖЖ.

Оформим всё на си

Данный пример я даю на своих мастер-классах по программированию и приведу его здесь. Сделаем макросы установления атрибутов текста, очистки экрана, перемещения курсора в верхний угол и перемещения курсора в заданную позицию:

#define home() printf(ESC "[H") //Move cursor to the indicated row, column (origin at 1,1) #define clrscr() printf(ESC "[2J") //lear the screen, move to (1,1) #define gotoxy(x,y) printf(ESC "[%d;%dH", y, x); #define visible_cursor() printf(ESC "[?251"); //Set Display Attribute Mode [;. ;m #define resetcolor() printf(ESC "[0m") #define set_display_atrib(color) printf(ESC "[%dm",color) 

По возможности, особенно на медленных системах лучше не использовать printf, так как его работа весьма медленная. Лучше заменять более легковесными функциями вывода на дисплей (COM-порт).

Читайте также:  Pid текущего процесса linux

Сами атрибуты цветов закинем в отдельный заголовочный файл в виде отдельных макросов.

#ifndef __TERM_EXAMPLE__ #define __TERM_EXAMPLE__ #define ESC "\033" //Format text #define RESET 0 #define BRIGHT 1 #define DIM 2 #define UNDERSCORE 3 #define BLINK 4 #define REVERSE 5 #define HIDDEN 6 //Foreground Colours (text) #define F_BLACK 30 #define F_RED 31 #define F_GREEN 32 #define F_YELLOW 33 #define F_BLUE 34 #define F_MAGENTA 35 #define F_CYAN 36 #define F_WHITE 37 //Background Colours #define B_BLACK 40 #define B_RED 41 #define B_GREEN 42 #define B_YELLOW 44 #define B_BLUE 44 #define B_MAGENTA 45 #define B_CYAN 46 #define B_WHITE 47 #endif /*__TERM_EXAMPLE__*/ 

Обратите внимание, что Foreground — раскрашивает сам текст, а Background подложку текста.

Сделаем небольшую програмку, демонстрирующую работу всех макросов.

Обращаю ваше внимание, что атрибуты текста следует сбрасывать, иначе они наследуются!

Симуляция данных выводимых контроллером

Дело стало за малым, набросать программу, которая будет отвечать нашему ТЗ: выводить состояние пинов порта, акселерометр, дата и время, сообщение об ошибке. Код написан таким образом, что легко может быть перенесён на любой контроллер. Не буду загромождать текст кусками кода, тем более что можно его легко посмотреть на github, остановлюсь только на некоторых моментах.

В функции main мы переводим курсор в левый верхний угол, затем очищаем экран и рисуем рамку с помощью функции frame_draw ();. Рамку данных мы рисуем только один раз, потом все данные выводим просто каждую в своей позиции. После передаём управление функции controller_emulator ();.

В функции controller_emulator (); мы каждые 30 секунд меняем сообщение пользователю с хорошо на ошибку, генерируем данные в порт (там реализован «бегущий» бит) и генерируем случайные числа на акселерометре. Вы можете легко переписать эту функцию под свои задачи. Выводим все данные с помощью трёх функций:

 print_accelerometer(a); print_port_bits(port); print_time_date(tm_info);

Рамка рисуется элементарно просто, одной функцией puts с предварительно заданными атрибутами цвета:

И для примера разберу функцию print_port_bits.

void print_port_bits (unsigned char port) < int i; unsigned char maxPow = 1else < set_display_atrib(F_RED); printf("pin%d= off",i); >port = port resetcolor(); > 

Она принимает без знаковый байт. Устанавливает атрибуты текста (жирный, подложка голубая) и по очереди выводит бит, каждый в свою позицию с помощью макроса gotoxy(2,2 + i); и в зависимости от того бит равен нулю или единице, окрашивает текст соответственно в красный или зелёный цвет.

Читайте также:  Linux подключение через com

Результат работы программы лучше посмотреть самостоятельно, как и код. Но тем кому лень git-ить программу и хочет просто посмотреть как это работает, я привожу гифку работы:

Что хочу отметить, что все позиции обозначайте #define и лучше всего, чтобы они рассчитывались от какой-то позиции. Таким образом, вы сможете перемещать вывод данных в нужное место, не перепахивая весь код, а просто изменив макросы в одном месте.

Итого

С помощью управляющих последовательностей можно даже получать размер окна терминала, события нажатий мыши и т.п. Специально не стал приводить эти примеры в данной статье, чтобы не загромождать её. Вы сможете сами найти данные примеры. В программе под линукс вообще доступно много всяких плюшек с управлением терминалом, отображением эха, скорости и прочим-прочим.

На мой взгляд, знать механизмы взаимодействия с терминальными программами нужно и полезно. Особенно разработчикам контроллеров и встраиваемых решений, где отладка программ чаще всего осуществляется с помощью СОМ-порта и его производных.

Источник

Графика в консоли Ubuntu

В версии Ubuntu 7.10 и выше — framebuffer выключен по умолчанию.
Для того чтобы включить его мы проделаем ниже описанное.

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

1. Открываем файл /etc/modprobe.d/blacklist-framebuffer:
nano /etc/modprobe.d/blacklist-framebuffer
закомментируем строку:
#blacklist vesafb
и сохраняем файл.
2. Открываем файл /etc/initramfs-tools/modules:
nano /etc/initramfs-tools/modules
добавляем строки:
fbcon
vesafb
и сохраняем файл.
3. Пересобираем initramfs image
update-initramfs -u
4. Редактируем GRUB2
Редактируем файл /etc/default/grub
В конец строки ‘GRUB_CMDLINE_LINUX_DEFAULT’ добавляем параметр vga=0x31b. Где «0x31b» — код нужного вам разрешения, которое можно найти одним из двух ниже приведённых вариантов.
Пример из моего grub:
GRUB_CMDLINE_LINUX_DEFAULT=«splash quiet»
я изменил на
GRUB_CMDLINE_LINUX_DEFAULT=«splash quiet vga=0x31b»
А если хотите чтобы framebuffer работало и в recovery mode, тогда измените
GRUB_CMDLINE_LINUX=»»
на
GRUB_CMDLINE_LINUX=«vga= 0x31b»
У вас в GRUB_CMDLINE_LINUX=»», в кавычках могут быть и другие параметры. В таком случае просто допишите к имеющемуся в конец.
5. Применяем изменения для Grub:
update-grub

Таблица кодов разрешений:

640×480 800×600 1024×768 1280×1024
256 0x301 0x303 0x305 0x307
32 тыс 0x310 0x313 0x316 0x319
64 тыс 0x311 0x314 0x317 0x31a
16 млн 0x312 0x315 0x318 0x31b

Если нужно другое разрешение, тогда воспользуйтесь программой hwinfo:
1. Устанавливаем программу hwinfo
apt-get install hwinfo
2. Запускаем ее с ключем —framebuffer
hwinfo —framebuffer
в появившемся списке выберите нужный Mode для вашего разрешения и добавьте его в vga=…, как описано выше, в четвертом пункте.

Осталось перезагрузить компьютер и можно запускать в консоли mplayer или links2 (это консольный браузер).
Позже напишу и про то с какими параметрами нужно запускать эти программы.
P.S.
Для ATI, всё тоже самое только в 1 и 2 пунктах, вместо vesafb, используйте radeonfb.

Источник

Оцените статью
Adblock
detector