LXF92:Unix API
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- Unix API Настоящее программирование для Unix – без прикрас и библиотек-«оберток»
С окнами на «ты»
- ЧАСТЬ 12 В заключительной статье цикла о программировании для Unix Андрей Боровский расскажет об использовании цветов и поддержке мыши... в консоли!
М
ы продолжаем знакомство с ncurses. В прошлый раз мы научились создавать окна. На этом уроке мы рассмотрим другие важные возможности ncurses, такие, как управле-
ние цветом и поддержка мыши. Управление цветом Принципы работы с цветом в ncurses могут оказаться неожиданны- ми для тех, кто привык работать с цветами в растровых графических системах (и для тех, кто имеет опыт работы с текстовым режимом DOS/Windows). Библиотека ncurses инициализирует восемь базовых цветов: черный, красный, зеленый, желтый, синий (blue), ярко-крас- ный (magenta), голубой (cyan) и белый (базовыми называются цвета с обычным уровнем яркости). Поскольку к каждому базовому цве- ту можно применить атрибут повышенной яркости A_BOLD, всего мы получаем 16 цветов (в результате применения атрибута A_BOLD к черному цвету получается темно-серый цвет). Базовым цветам соответствуют константы COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN и COLOR_WHITE (для черного, красного, зеленого, желтого, синего, ярко-красного, голубого и белого цветов соответственно). Следует отметить, что фактические цвета в окне терминала зависят, прежде всего, от настроек самого терминала. Например, базовый желтый цвет (COLOR_YELLLOW) будет выглядеть скорее как коричневый, а для того, чтобы он стал собственно желтым, ему необходимо придать атрибут повышенной яркости. Библиотека ncurses позволяет опреде- лять собственные цвета с помощью функции init_color, но эта воз- можность поддерживается не всеми консолями. Позволяет ли консоль определять собственные цвета, можно выяснить с помощью функции can_change_color(). Цвета в ncurses объединяются в пары – цвет сим- волов (foreground) и цвет фона (background). Перед тем как печатать цветной текст, необходимо определить соответствующую цветовую пару и установить ее в качестве атрибута текста (так же, как устанавли- вается атрибут мигания или подчеркивания). Изменить цвет фона или символов независимо друг от друга нельзя, необходимо определять новую пару. Система управления цветами ncurses инициализирует две переменные – COLORS (количество базовых цветов) и COLOR_PAIRS (максимальное количество цветовых пар, которые можно определить одновременно). При работе с терминалом konsole эти переменные при- нимают значения 8 и 64 соответственно.
Рассмотрим управление цветом на примере программы cursedcolors
(на диске – файл cursedcolors.c)
#include <termios.h> #include <sys/ioctl.h> #include <signal.h> #include <stdlib.h> #include <curses.h> void sig_winch(int signo) { struct winsize size; ioctl(fileno(stdout), TIOCGWINSZ, (char *) &size); resizeterm(size.ws_row, size.ws_col); } int main(int argc, char ** argv) { WINDOW * wnd; WINDOW * subwnd; initscr(); signal(SIGWINCH, sig_winch); curs_set(FALSE); start_color(); refresh(); init_pair(1, COLOR_BLUE, COLOR_GREEN); init_pair(2, COLOR_YELLOW, COLOR_BLUE); wnd = newwin(5, 18, 2, 4); wattron(wnd, COLOR_PAIR(1)); box(wnd, ‘|’, ‘-’); subwnd = derwin(wnd, 3, 16, 1, 1); wbkgd(subwnd, COLOR_PAIR(2)); wattron(subwnd, A_BOLD); wprintw(subwnd, “Hello, brave new curses world!\n”); wrefresh(subwnd); wrefresh(wnd); delwin(subwnd); delwin(wnd); wmove(stdscr, 8, 1); printw(“Press any key to continue...”); refresh(); getch(); endwin(); exit(EXIT_SUCCESS); } Эта программа основана на программе cursedwindows из преды-
дущей статьи, так что многие ее части должны быть вам знакомы. Функция start_color() инициализирует управление цветом ncurses. Остальные функции, связанные с цветом, можно использовать только после вызова start_color(). Новые цветовые пары создаются с помо- щью функции init_pair(). Первым параметром init_pair() должен быть один из допустимых номеров пары (от 1 до COLOR_PAIRS-1). Вторым параметром функции init_pair() должна быть константа, обознача- ющая базовый цвет символа, а третьим – константа, обозначающая базовый цвет фона. Цветовая пара с номером 0 определена в ncurses как «белый на черном», и изменить ее нельзя. Мы создаем две пары цветов – «синие символы на зеленом фоне» под номером 1 и желтые символы на синем фоне (любимое сочетание цветов небезызвестного Питера Нортона) под номером 2. Номер цветовой пары служит ее иден- тификатором. Для того чтобы сделать выбранную цветовую пару атри- бутом выводимого текста, необходимо установить с помощью функции attron/wattron атрибут COLOR_PAIR(X), где X – номер цветовой пары. Атрибут COLOR_PAIR(X) можно комбинировать с другими, например, с атрибутом A_BOLD, который влияет на яркость цвета символов (но не на яркость цвета фона). Для того чтобы изменить яркость фона, необ- ходимо скомбинировать этот атрибут с A_REVERSE.
Вызов функции wattron(wnd, COLOR_PAIR(1));
устанавливает цвет фона и символов (цветовую пару 1) для «внешне- го» окна wnd, содержащего рамку. Теперь функция box() напечатает символы рамки с учетом заданных атрибутов цвета. Функция wbkgd() позволяет нам заполнить структуру данных, соответствующую массиву символов окна, различными атрибутами текста. Вызов
wbkgd(subwnd, COLOR_PAIR(2));
заполняет окно subwnd фоновым цветом из цветовой пары 2 и уста- навливает соответствующий цвет символов в окне. Помимо атрибута COLOR_PAIR(), этой функции можно передавать все те же комбина- ции атрибутов, что и wattron(). Атрибуты затем будут применены к тексту, выводимому в окне по умолчанию. Для того чтобы сделать цвет шрифта в окне subwnd ярким, мы вызываем функцию wattron() с атрибутом A_BOLD. Заметьте, что в функции wattron(), вызванной для окна subwnd, мы не указываем цветовую пару, поскольку в этом нет необходимости. Функция wbkgd() уже заполнила символьный массив окна subwnd нужными атрибутами цвета, и нам остается только указать атрибут яркости. В принципе, мы могли бы обойтись и без wattron(), если бы вызов wbkgd() выглядел так:
wbkgd(subwnd, COLOR_PAIR(2)|A_BOLD); Теперь мы можем распечатать текст в окне, что и делается с помо-
щью функции wprintw(). Для того чтобы символы, напечатанные в окне, стали видимыми, необходимо вызвать функцию wrefresh(). Теперь окно с текстом и обрамляющая его рамка сияют разными цветами (Рис. 1). Вы могли заметить, что если в обычном режиме окно терминала было, например, белым, то во время работы программы cursedcolors оно ста- новится черным. Это происходит потому, что по умолчанию при ини- циализации цвета окно stdscr заполняется атрибутами цветовой пары 0, как если бы была вызвана функция
wbkgd(stdscr, COLOR_PAIR(0)); В результате надпись “Press any key to continue...”, которую мы
печатаем в окне stdscr, выводится белым шрифтом на черном фоне.
При работе с цветом в ncurses следует помнить о том, что массивы
данных окон хранят только номера цветовых пар, применяемых к каж- дой ячейке, а не сами значения цветов. Из этого следует, что цвета уже напечатанного текста зависят от определения цветовых пар. Допустим, вы определили цветовую пару 1 как «желтый на синем» и напечатали какой-нибудь текст, используя эту пару в качестве атрибута. Если затем вы переопределите цветовую пару 1 как «красный на белом», цвета шрифта и фона в уже напечатанном тексте изменятся соответственно новому определению цветовой пары. Ввод данных в окнах С одной из функций ввода данных – getch(), мы уже познакомились. Мы также знаем, что этой функции соответствует «оконная» функция wgetch(). Обе они позволяют считывать отдельные символы. В отличие от них, семейство функций getstr()/getnstr/wgetstr()/wgetnstr() позволя- ет считывать целые строки. Буква n перед str в именах функций свиде- тельствует о том, что эти варианты функций позволяют указать макси- мальную длину строки-буфера и, тем самым, избежать его переполне- ния при вводе. Программа cursedinput (ее исходные тексты вы найдете в файле cursedinput.c) позволяет пользователю вводить строку текста и, затем, распечатывает введенную строку.
#include <termios.h> #include <sys/ioctl.h> #include <signal.h> #include <stdlib.h> #include <curses.h> #define MAX_NAME_LEN 15 void sig_winch(int signo) { struct winsize size; ioctl(fileno(stdout), TIOCGWINSZ, (char *) &size); resizeterm(size.ws_row, size.ws_col); } int main(int argc, char ** argv) { WINDOW * wnd; char name[MAX_NAME_LEN + 1]; initscr(); signal(SIGWINCH, sig_winch); curs_set(TRUE); start_color(); refresh(); init_pair(1, COLOR_YELLOW, COLOR_BLUE); wnd = newwin(5, 23, 2, 2); wbkgd(wnd, COLOR_PAIR(1)); wattron(wnd, A_BOLD); wprintw(wnd, “Enter your name...\n”); wgetnstr(wnd, name, MAX_NAME_LEN); name[MAX_NAME_LEN] = 0; wprintw(wnd, “Hello, %s!”, name); wrefresh(wnd); delwin(wnd); curs_set(FALSE); move(8, 4); printw(“Press any key to continue...”); refresh(); getch(); endwin(); exit(EXIT_SUCCESS); } Поскольку в программе cursedinput пользователь
должен вводить данные, нам удобно сделать кур- сор видимым, что мы и делаем с помощью вызова curs_set(TRUE). Собственно ввод строки выполняется с помощью wgetnstr(). Первый параметр функции – иден- тификатор окна, в котором вводятся данные (то есть отображаются вводимые символы и курсор), второй параметр – строка-буфер, в которую записываются вве- денные символы, а третьим аргументом является дли- на буфера. При работе с программой вы увидите, что нельзя ввести число символов, превышающее MAX_NAME_LEN. Перед выводом строки “Press any key to continue...” мы снова прячем курсор.
Режим работы терминала, в котором была запущена наша програм-
ма (она наследует его от программы-родителя), может повлиять на поведение некоторых функций ввода данных. В одной из предыдущих статей мы уже упоминали о каноническом и неканоническом режиме работы терминала. В каноническом режиме терминал буферизует вво- димые данные и передает их программе только после того, как поль- зователь нажмет Enter. В неканоническом режиме вводимые символы передаются программе немедленно. Режим работы терминала мож- но изменить с помощью функций cbreak() и nocbreak(). В результате вызова cbreak() терминал переходит в режим, в котором введенные символы предаются программе, не дожидаясь нажатия Enter, а клави- ша BackSpace игнорируется. Терминал выводится из режима cbreak() с помощью вызова функции nocbreak().
Работая с программой cursedinput, вы, конечно, заметили, что
функция wgetnstr() допускает редактирование вводимой строки с помощью клавиши BackSpace. Поведение этой функции не зависит от режима cbreak()/nocbreak(), но поведение других функций, в частно- сти, getch(), зависит. В режиме nocbreak() функция getch() возвращает управление программе только после того, как пользователь нажмет Enter. Все наши программы (как и большинство программ ncurses) устанавливают режим cbreak(). Функция для ввода пароля Чтобы лучше изучить возможности ввода текста в библиотеке ncurses, напишем функцию, предназначенную для ввода пароля. Вместо вво- димых символов наша функция будет печатать на экране звездочки (Рис. 2). Cтроку можно будет редактировать с помощью клавиши BackSpace. Мы перепишем программу cursedinput так, чтобы вмес- то своего имени пользователь вводил пароль, кото- рый затем сверяется с константой, заданной в про- грамме, и в зависимости от того, совпадут они или нет, будет выводиться сообщение о предоставлении доступа или отказе в нем. Исходный текст програм- мы вы найдете в файле cursedpassword.c. Мы при- водим только фрагмент, изменившийся по сравнению с программой cursedinput.
keypad(wnd, TRUE); wprintw(wnd, “Enter password...\n”); get_password(wnd, password, MAX_LEN); wattron(wnd, A_BLINK); if (strcmp(password, RIGHT_PASSWORD) == 0) wprintw(wnd, “ACCESS GRANTED!”); else wprintw(wnd, “ACCESS DENIED!”); Определенная нами функция get_password() принимает три пара-
метра – идентификатор окна, в котором выполняется ввод, адрес буфера, в который записываются вводимые символы и число, ука- зывающее длину буфера (вместе с завершающим нулем). Прототип функции strcmp(), которую мы используем для сравнения пере- данной пользователем строки и пароля, находится в файле string.h. Рассмотрим теперь саму функцию get_password():
void get_password(WINDOW * win, char * password, int max_len) { int i = 0; int ch; while (((ch = wgetch(win)) != 10) && (i < max_len-1)) { if (ch == KEY_BACKSPACE) { int x, y; if (i==0) continue; getyx(win, y, x); mvwaddch(win, y, x-1, ‘ ‘); wrefresh(win); wmove(win, y, x-1); i--; continue; } password[i++] = ch; wechochar(win, ‘*’); } password[i] = 0; wechochar(win, ‘\n’); } Функция считывает символы из входного потока с помощью функ-
ции wgetch() до тех пор, пока пользователь не нажмет Enter, или пока длина введенной строки не сравняется с максимально допустимой. Для вывода отдельных символов в окно можно применить функцию waddch(), однако мы используем функцию wechochar(), которая экви- валентна вызову waddch() с последующим вызовом wrefresh(). Самая сложная часть функции get_password() связана с обработкой нажатия клавиши BackSpace. Прежде всего, необходимо получить код этой специальной клавиши. По умолчанию при нажатии на специальные клавиши, такие как стрелки, клавиши F1-F12 или BackSpace, терминал генерирует последовательность кодов, представляющих так называе- мую Esc-последовательность клавиши. Для того чтобы заменить Esc- последовательность одним специальным кодом, необходимо вызвать функцию keypad() с ненулевым вторым параметром (что мы и делаем в главной функции программы). Первым параметром keypad() должен быть идентификатор окна (в нашем случае – wnd).
Вызов keypad() с ненулевым вторым параметром приводит к тому,
что клавиши F1-F12 генерируют коды KEY_F1-KEY_F12, клавиши со стрелками – коды KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, а клавиша BackSpace – код KEY_BACKSPACE. Описание других кодов специальных клавиш и событий вы найдете на странице man функции getch(). Получив в потоке ввода код KEY_BACKSPACE, мы должны выполнить несколько операций, прежде всего – стереть только что напечатанную звездочку. Для этого нужно получить текущие координаты курсора, сдвинуть его на одну позицию влево и напечатать пробел. Затем курсор снова нужно сдвинуть на одну позицию влево. Получить текущие координаты курсо- ра в окне можно с помощью макроса getyx(). Его первым параметром является идентификатор окна, вторым – переменная, в которой макрос сохранит значение строки курсора, третьим – переменная, в которой будет сохранен столбец курсора. Именно потому, что getyx() – макрос, мы передаем для получения значений переменные, а не указатели на них. Функция mvwaddch() сочетает перемещение курсора и вывод сим- вола. Первый параметр функции – идентификатор окна. За ним следуют новые координаты курсора – строка и столбец. Последним параметром функции является символ, который нужно напечатать. После того, как мы привели в порядок экран, мы уменьшаем на единицу счетчик вве- денных символов (переменная i). Если переменная i равна нулю, никаких действий не выполняется. Наша функция get_password() будет работать правильно только в режиме cbreak(). Следует отметить, что функция не будет корректно работать с клавишей BackSpace, если при вводе пароля произошел перенос строки. Окна и мыши Еще одной полезной возможностью, которую ncurses предостав- ляет программистам, является поддержка мыши в окне терминала. Рассмотрим программу cursedmouse (на диске – файл cursedmouse.c), которая регистрирует щелчки левой кнопкой мыши, сделанные пользо- вателем в окне терминала, и распечатывает координаты курсора мыши в момент щелчка. Ради простоты мы не создаем в этой программе никаких окон (кроме окна stdscr, которое создается автоматически).
#include <termios.h> #include <sys/ioctl.h> #include <signal.h> #include <stdlib.h> #include <curses.h> void sig_winch(int signo) { struct winsize size; ioctl(fileno(stdout), TIOCGWINSZ, (char *) &size); resizeterm(size.ws_row, size.ws_col); nodelay(stdscr, 1); while (wgetch(stdscr) != ERR); nodelay(stdscr, 0); } int main(int argc, char ** argv) { initscr(); signal(SIGWINCH, sig_winch); keypad(stdscr, 1); mousemask(BUTTON1_CLICKED, NULL); move(2,2); printw(“Press the left mouse button to test mouse\n”); printw(“Press any key to quit...\n”); refresh(); while (wgetch(stdscr) == KEY_MOUSE) { MEVENT event; getmouse(&event); move(0, 0); printw(“Mouse button pressed at %i, %i\n”, event.x, event.y); refresh(); move(event.y, event.x); } endwin(); exit(EXIT_SUCCESS); } Поддержка мыши в ncurses инициализируется с помощью функции
mousemask(). Первым параметром этой функции должна быть маска событий мыши, которые следует обрабатывать в программе, вторым параметром может быть указатель на переменную, в которой функция сохранит прежнюю маску событий, или NULL, если прежняя маска нам не нужна. Каждому событию мыши в ncurses соответствует своя кон- станта. Если мы хотим обрабатывать несколько событий мыши, при вызове функции mousemask() мы должны объединить соответствую- щие константы операцией «ИЛИ» (|). Повторный вызов mousemask() приведет к установке новой маски событий (вызов mousemask() с пер- вым аргументом, равным 0, отключает поддержку мыши).
Рассмотрим некоторые константы, определяющие события мыши.
Константа BUTTON1_CLICKED соответствует щелчку левой кнопкой мыши (точнее говоря – щелчку первой кнопкой; будет ли пер- вая кнопка левой кнопкой мыши, зависит от настроек). Константа BUTTON2_PRESSED указывает, что программа должна реагировать на нажатие пользователем второй (обычно – правой) кнопки мыши. Константа REPORT_MOUSE_POSITION указывает, что мы хотим отсле- живать движение указателя мыши, а константа ALL_MOUSE_EVENTS заставляет программу реагировать на все события мыши. Более пол- ное описание констант событий вы найдете на man-странице функ- ции mousemask(3x). В качестве результирующего значения функция mousemask() возвращает маску из выбранных нами событий, которые фактически могут быть обработаны. Если функция возвращает 0, зна- чит, работа с мышью в консоли не поддерживается.
Каждый раз, когда в системе происходит одно из «наблюдаемых»
событий мыши, в потоке ввода программы появляется специальный символ KEY_MOUSE. Точнее говоря, по умолчанию, в потоке ввода программы Linux появляется Esc-последовательность, соответствую- щая этому символу, так что в программе cursedmouse мы тоже должны вызвать функцию keypad() с ненулевым вторым параметром.
После того, как мы считали из потока ввода специальный символ
KEY_MOUSE, мы можем получить более подробную информацию о вызвавшем его событии мыши. Делается это с помощью функции getmouse(). Аргументом функции getmouse() должен быть указатель на структуру MEVENT. Определение структуры MEVENT выглядит сле- дующим образом:
typedef struct { short id; /* идентификатор для различения нескольких устройств */ int x, y, z; /* координаты указателя в момент события */ mmask_t bstate; /* маска событий */ } MEVENT; Координаты указателя возвращаются в формате строка (y), стол-
бец (x). Поле bstate содержит один-единственный бит, соответствую- щий константе события.
В программе cursedmouse мы считываем поступающие во входной
поток символы в цикле. Если во входном потоке появляется символ KEY_MOUSE, мы, с помощью функции getmouse(), определяем коор- динаты указателя мыши в момент возникновения события и распеча- тываем их в левом верхнем углу экрана, а затем переводим курсор туда, куда указывала мышь в момент возникновения события. Появление в потоке ввода символа, отличного от KEY_MOUSE, приводит к заверше- нию программы.
Осталось обратить внимание читателя на обработку сигнала
SIGWINCH в программе cursedmouse. Изменение размеров экрана при включенной поддержке мыши приведет к появлению в потоке ввода символов Esc-последовательности специального символа KEY_ RESIZE (это еще один способ предупредить программу о том, что размеры экрана изменились). В программе cursedmouse появление в потоке ввода каких-либо кодов, отличных от KEY_MOUSE, приводит к завершению программы. Для того чтобы избежать этого, в обра- ботчике сигнала SIGWINCH мы опустошаем поток ввода с помощью функции flushinp(). Естественно, этот способ спасения программы от досрочного завершения годится далеко не всегда, ведь в момент изменения размеров окна терминала поток ввода может содержать важную информацию. Все это лишний раз демонстрирует, насколь- ко нетривиальной является обработка изменения размеров экрана в программах ncurses.
На этом я заканчиваю (честное слово!) серию статей, посвященную
Unix API. Я благодарю вас за внимание, проявленное к этой серии, и надеюсь, что с помощью моих статей вы получили некоторое общее представление о низкоуровневом программировании в Linux/Unix, а самое главное, смогли ответить на вопрос – нужно ли вам все это. LXF