LXF92:Unix API
(оформление) |
|||
(не показана 1 промежуточная версия 1 участника) | |||
Строка 39: | Строка 39: | ||
(на диске – файл '''cursedcolors.c''') | (на диске – файл '''cursedcolors.c''') | ||
− | < | + | <source lang=c> |
#include <termios.h> | #include <termios.h> | ||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | ||
Строка 80: | Строка 80: | ||
exit(EXIT_SUCCESS); | exit(EXIT_SUCCESS); | ||
} | } | ||
− | </ | + | </source> |
Эта программа основана на программе ''cursedwindows'' из предыдущей статьи, так что многие ее части должны быть вам знакомы. | Эта программа основана на программе ''cursedwindows'' из предыдущей статьи, так что многие ее части должны быть вам знакомы. | ||
Строка 140: | Строка 140: | ||
[[Изображение:LXF92_api1.jpg|Рис. 1]] | [[Изображение:LXF92_api1.jpg|Рис. 1]] | ||
− | В результате надпись “Press any key to continue...”, которую мы | + | В результате надпись ''“Press any key to continue...”'', которую мы |
печатаем в окне '''stdscr''', выводится белым шрифтом на черном фоне. | печатаем в окне '''stdscr''', выводится белым шрифтом на черном фоне. | ||
Строка 156: | Строка 156: | ||
С одной из функций ввода данных – '''getch()''', мы уже познакомились. | С одной из функций ввода данных – '''getch()''', мы уже познакомились. | ||
Мы также знаем, что этой функции соответствует «оконная» функция | Мы также знаем, что этой функции соответствует «оконная» функция | ||
− | wgetch(). Обе они позволяют считывать отдельные символы. В отличие | + | '''wgetch()'''. Обе они позволяют считывать отдельные символы. В отличие |
− | от них, семейство функций getstr()/getnstr/wgetstr()/wgetnstr() | + | от них, семейство функций '''getstr()/getnstr/wgetstr()/wgetnstr()''' позволяет считывать целые строки. Буква '''n''' перед '''str''' в именах функций свидетельствует о том, что эти варианты функций позволяют указать максимальную длину строки-буфера и, тем самым, избежать его переполнения при вводе. Программа ''cursedinput'' (ее исходные тексты вы найдете |
− | + | в файле '''cursedinput.c''') позволяет пользователю вводить строку текста и, затем, распечатывает введенную строку. | |
− | + | ||
− | + | <source lang=c> | |
− | + | ||
− | в файле cursedinput.c) позволяет пользователю вводить строку текста | + | |
− | и, затем, распечатывает введенную строку. | + | |
#include <termios.h> | #include <termios.h> | ||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | ||
Строка 203: | Строка 200: | ||
exit(EXIT_SUCCESS); | exit(EXIT_SUCCESS); | ||
} | } | ||
− | + | </source> | |
− | должен вводить данные, нам удобно сделать | + | |
− | + | Поскольку в программе ''cursedinput'' пользователь | |
− | curs_set(TRUE). Собственно ввод строки выполняется с | + | должен вводить данные, нам удобно сделать курсор видимым, что мы и делаем с помощью вызова |
− | помощью wgetnstr(). Первый параметр функции – | + | '''curs_set(TRUE)'''. Собственно ввод строки выполняется с |
− | + | помощью '''wgetnstr()'''. Первый параметр функции – идентификатор окна, в котором вводятся данные (то есть | |
отображаются вводимые символы и курсор), второй | отображаются вводимые символы и курсор), второй | ||
− | параметр – строка-буфер, в которую записываются | + | параметр – строка-буфер, в которую записываются введенные символы, а третьим аргументом является длина буфера. При работе с программой вы увидите, что |
− | + | нельзя ввести число символов, превышающее '''MAX_NAME_LEN'''. Перед | |
− | + | выводом строки ''“Press any key to continue...”'' мы снова прячем курсор. | |
− | нельзя ввести число символов, превышающее MAX_NAME_LEN. Перед | + | |
− | выводом строки “Press any key to continue...” мы снова прячем курсор. | + | Режим работы терминала, в котором была запущена наша программа (она наследует его от программы-родителя), может повлиять на |
− | + | ||
− | + | ||
поведение некоторых функций ввода данных. В одной из предыдущих | поведение некоторых функций ввода данных. В одной из предыдущих | ||
статей мы уже упоминали о каноническом и неканоническом режиме | статей мы уже упоминали о каноническом и неканоническом режиме | ||
− | работы терминала. В каноническом режиме терминал буферизует | + | работы терминала. В каноническом режиме терминал буферизует вводимые данные и передает их программе только после того, как пользователь нажмет '''Enter'''. В неканоническом режиме вводимые символы |
− | + | передаются программе немедленно. Режим работы терминала можно изменить с помощью функций '''cbreak()''' и '''nocbreak()'''. В результате | |
− | + | вызова '''cbreak()''' терминал переходит в режим, в котором введенные | |
− | передаются программе немедленно. Режим работы терминала | + | символы предаются программе, не дожидаясь нажатия '''Enter''', а клавиша '''BackSpace''' игнорируется. Терминал выводится из режима '''cbreak()''' с |
− | + | помощью вызова функции '''nocbreak()'''. | |
− | вызова cbreak() терминал переходит в режим, в котором введенные | + | |
− | символы предаются программе, не дожидаясь нажатия Enter, а | + | Работая с программой ''cursedinput'', вы, конечно, заметили, что |
− | + | функция '''wgetnstr()''' допускает редактирование вводимой строки с | |
− | помощью вызова функции nocbreak(). | + | помощью клавиши '''BackSpace'''. Поведение этой функции не зависит от |
− | + | режима '''cbreak()/nocbreak()''', но поведение других функций, в частности, '''getch()''', зависит. В режиме '''nocbreak()''' функция '''getch()''' возвращает | |
− | функция wgetnstr() допускает редактирование вводимой строки с | + | |
− | помощью клавиши BackSpace. Поведение этой функции не зависит от | + | |
− | режима cbreak()/nocbreak(), но поведение других функций, в | + | |
− | + | ||
управление программе только после того, как пользователь нажмет | управление программе только после того, как пользователь нажмет | ||
− | Enter. Все наши программы (как и большинство программ ncurses) | + | '''Enter'''. Все наши программы (как и большинство программ ''ncurses'') |
− | устанавливают режим cbreak(). | + | устанавливают режим '''cbreak()'''. |
− | Функция для ввода пароля | + | |
− | Чтобы лучше изучить возможности ввода текста в библиотеке ncurses, | + | ===Функция для ввода пароля=== |
− | напишем функцию, предназначенную для ввода пароля. Вместо | + | |
− | + | Чтобы лучше изучить возможности ввода текста в библиотеке ''ncurses'', | |
+ | напишем функцию, предназначенную для ввода пароля. Вместо вводимых символов наша функция будет печатать на | ||
экране звездочки (Рис. 2). Cтроку можно будет | экране звездочки (Рис. 2). Cтроку можно будет | ||
− | редактировать с помощью клавиши BackSpace. Мы | + | редактировать с помощью клавиши '''BackSpace'''. Мы |
− | перепишем программу cursedinput так, чтобы | + | перепишем программу ''cursedinput'' так, чтобы вместо своего имени пользователь вводил пароль, который затем сверяется с константой, заданной в программе, и в зависимости от того, совпадут они или |
− | + | ||
− | + | ||
− | + | ||
нет, будет выводиться сообщение о предоставлении | нет, будет выводиться сообщение о предоставлении | ||
− | доступа или отказе в нем. Исходный текст | + | доступа или отказе в нем. Исходный текст программы вы найдете в файле '''cursedpassword.c'''. Мы приводим только фрагмент, изменившийся по сравнению с программой ''cursedinput''. |
− | + | ||
− | + | [[Изображение:LXF92_api2.jpg|Рис. 2]] | |
− | cursedinput. | + | |
+ | <source lang=c> | ||
keypad(wnd, TRUE); | keypad(wnd, TRUE); | ||
wprintw(wnd, “Enter password...\n”); | wprintw(wnd, “Enter password...\n”); | ||
Строка 259: | Строка 249: | ||
else | else | ||
wprintw(wnd, “ACCESS DENIED!”); | wprintw(wnd, “ACCESS DENIED!”); | ||
− | + | <source> | |
− | + | ||
− | буфера, в который записываются вводимые символы и число, | + | Определенная нами функция '''get_password()''' принимает три параметра – идентификатор окна, в котором выполняется ввод, адрес буфера, в который записываются вводимые символы и число, указывающее длину буфера (вместе с завершающим нулем). Прототип |
− | + | функции '''strcmp()''', которую мы используем для сравнения переданной пользователем строки и пароля, находится в файле '''string.h'''. | |
− | функции strcmp(), которую мы используем для сравнения | + | Рассмотрим теперь саму функцию '''get_password()''': |
− | + | ||
− | Рассмотрим теперь саму функцию get_password(): | + | <source lang=c> |
void get_password(WINDOW * win, char * password, int max_len) | void get_password(WINDOW * win, char * password, int max_len) | ||
{ | { | ||
Строка 287: | Строка 277: | ||
wechochar(win, ‘\n’); | wechochar(win, ‘\n’); | ||
} | } | ||
− | + | </source> | |
− | + | ||
+ | Функция считывает символы из входного потока с помощью функции '''wgetch()''' до тех пор, пока пользователь не нажмет Enter, или пока | ||
длина введенной строки не сравняется с максимально допустимой. | длина введенной строки не сравняется с максимально допустимой. | ||
Для вывода отдельных символов в окно можно применить функцию | Для вывода отдельных символов в окно можно применить функцию | ||
− | waddch(), однако мы используем функцию wechochar(), которая | + | '''waddch()''', однако мы используем функцию '''wechochar()''', которая эквивалентна вызову '''waddch()''' с последующим вызовом '''wrefresh()'''. Самая |
− | + | сложная часть функции '''get_password()''' связана с обработкой нажатия | |
− | сложная часть функции get_password() связана с обработкой нажатия | + | клавиши '''BackSpace'''. Прежде всего, необходимо получить код этой |
− | клавиши BackSpace. Прежде всего, необходимо получить код этой | + | |
специальной клавиши. По умолчанию при нажатии на специальные | специальной клавиши. По умолчанию при нажатии на специальные | ||
− | клавиши, такие как стрелки, клавиши F1-F12 или BackSpace, терминал | + | клавиши, такие как стрелки, клавиши '''F1-F12''' или '''BackSpace''', терминал |
− | генерирует последовательность кодов, представляющих так | + | генерирует последовательность кодов, представляющих так называемую Esc-последовательность клавиши. Для того чтобы заменить Esc-последовательность одним специальным кодом, необходимо вызвать |
− | + | функцию '''keypad()''' с ненулевым вторым параметром (что мы и делаем | |
− | последовательность одним специальным кодом, необходимо вызвать | + | в главной функции программы). Первым параметром '''keypad()''' должен |
− | функцию keypad() с ненулевым вторым параметром (что мы и делаем | + | быть идентификатор окна (в нашем случае – '''wnd'''). |
− | в главной функции программы). Первым параметром keypad() должен | + | |
− | быть идентификатор окна (в нашем случае – wnd). | + | Вызов '''keypad()''' с ненулевым вторым параметром приводит к тому, |
− | + | что клавиши '''F1-F12''' генерируют коды '''KEY_F1-KEY_F12''', клавиши со | |
− | что клавиши F1-F12 генерируют коды KEY_F1-KEY_F12, клавиши со | + | стрелками – коды '''KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT''', а клавиша |
− | стрелками – коды KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, а клавиша | + | '''BackSpace''' – код '''KEY_BACKSPACE'''. Описание других кодов специальных |
− | BackSpace – код KEY_BACKSPACE. Описание других кодов специальных | + | клавиш и событий вы найдете на странице ''man'' функции '''getch('''). Получив |
− | клавиш и событий вы найдете на странице man функции getch(). Получив | + | в потоке ввода код '''KEY_BACKSPACE''', мы должны выполнить несколько |
− | в потоке ввода код KEY_BACKSPACE, мы должны выполнить несколько | + | |
операций, прежде всего – стереть только что напечатанную звездочку. | операций, прежде всего – стереть только что напечатанную звездочку. | ||
Для этого нужно получить текущие координаты курсора, сдвинуть его | Для этого нужно получить текущие координаты курсора, сдвинуть его | ||
на одну позицию влево и напечатать пробел. Затем курсор снова нужно | на одну позицию влево и напечатать пробел. Затем курсор снова нужно | ||
сдвинуть на одну позицию влево. Получить текущие координаты курсо- | сдвинуть на одну позицию влево. Получить текущие координаты курсо- | ||
− | ра в окне можно с помощью макроса getyx(). Его первым параметром | + | ра в окне можно с помощью макроса '''getyx()''. Его первым параметром |
является идентификатор окна, вторым – переменная, в которой макрос | является идентификатор окна, вторым – переменная, в которой макрос | ||
сохранит значение строки курсора, третьим – переменная, в которой | сохранит значение строки курсора, третьим – переменная, в которой | ||
− | будет сохранен столбец курсора. Именно потому, что getyx() – макрос, | + | будет сохранен столбец курсора. Именно потому, что '''getyx()''' – макрос, |
мы передаем для получения значений переменные, а не указатели на | мы передаем для получения значений переменные, а не указатели на | ||
− | них. Функция mvwaddch() сочетает перемещение курсора и вывод | + | них. Функция '''mvwaddch()''' сочетает перемещение курсора и вывод символа. Первый параметр функции – идентификатор окна. За ним следуют |
− | + | ||
новые координаты курсора – строка и столбец. Последним параметром | новые координаты курсора – строка и столбец. Последним параметром | ||
функции является символ, который нужно напечатать. После того, как | функции является символ, который нужно напечатать. После того, как | ||
− | мы привели в порядок экран, мы уменьшаем на единицу счетчик | + | мы привели в порядок экран, мы уменьшаем на единицу счетчик введенных символов (переменная '''i'''). Если переменная '''i''' равна нулю, никаких |
− | + | действий не выполняется. Наша функция '''get_password()''' будет работать | |
− | действий не выполняется. Наша функция get_password() будет работать | + | правильно только в режиме '''cbreak()'''. Следует отметить, что функция не |
− | правильно только в режиме cbreak(). Следует отметить, что функция не | + | будет корректно работать с клавишей '''BackSpace''', если при вводе пароля |
− | будет корректно работать с клавишей BackSpace, если при вводе пароля | + | |
произошел перенос строки. | произошел перенос строки. | ||
− | Окна и мыши | + | |
− | Еще одной полезной возможностью, которую ncurses | + | ===Окна и мыши=== |
− | + | ||
− | Рассмотрим программу cursedmouse (на диске – файл cursedmouse.c), | + | Еще одной полезной возможностью, которую ''ncurses'' предоставляет программистам, является поддержка мыши в окне терминала. |
− | которая регистрирует щелчки левой кнопкой мыши, сделанные | + | Рассмотрим программу ''cursedmouse'' (на диске – файл '''cursedmouse.c'''), |
− | + | которая регистрирует щелчки левой кнопкой мыши, сделанные пользователем в окне терминала, и распечатывает координаты курсора мыши в момент щелчка. Ради простоты мы не создаем в этой программе | |
− | в момент щелчка. Ради простоты мы не создаем в этой программе | + | никаких окон (кроме окна '''stdscr''', которое создается автоматически). |
− | никаких окон (кроме окна stdscr, которое создается автоматически). | + | |
+ | <source lang=c> | ||
#include <termios.h> | #include <termios.h> | ||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | ||
Строка 371: | Строка 359: | ||
exit(EXIT_SUCCESS); | exit(EXIT_SUCCESS); | ||
} | } | ||
− | + | </source> | |
− | mousemask(). Первым параметром этой функции должна быть маска | + | |
+ | Поддержка мыши в ncurses инициализируется с помощью функции | ||
+ | '''mousemask()'''. Первым параметром этой функции должна быть маска | ||
событий мыши, которые следует обрабатывать в программе, вторым | событий мыши, которые следует обрабатывать в программе, вторым | ||
параметром может быть указатель на переменную, в которой функция | параметром может быть указатель на переменную, в которой функция | ||
− | сохранит прежнюю маску событий, или NULL, если прежняя маска нам | + | сохранит прежнюю маску событий, или '''NULL''', если прежняя маска нам |
− | не нужна. Каждому событию мыши в ncurses соответствует своя | + | не нужна. Каждому событию мыши в ''ncurses'' соответствует своя константа. Если мы хотим обрабатывать несколько событий мыши, при вызове функции '''mousemask()''' мы должны объединить соответствующие константы операцией «'''ИЛИ'''» ('''|'''). Повторный вызов '''mousemask()''' приведет к установке новой маски событий (вызов '''mousemask()''' с первым аргументом, равным 0, отключает поддержку мыши). |
− | + | ||
− | вызове функции mousemask() мы должны объединить | + | Рассмотрим некоторые константы, определяющие события мыши. |
− | + | Константа '''BUTTON1_CLICKED''' соответствует щелчку левой кнопкой | |
− | приведет к установке новой маски событий (вызов mousemask() с | + | мыши (точнее говоря – щелчку первой кнопкой; будет ли первая кнопка левой кнопкой мыши, зависит от настроек). Константа |
− | + | '''BUTTON2_PRESSED''' указывает, что программа должна реагировать | |
− | + | ||
− | Константа BUTTON1_CLICKED соответствует щелчку левой кнопкой | + | |
− | мыши (точнее говоря – щелчку первой кнопкой; будет ли | + | |
− | + | ||
− | BUTTON2_PRESSED указывает, что программа должна реагировать | + | |
на нажатие пользователем второй (обычно – правой) кнопки мыши. | на нажатие пользователем второй (обычно – правой) кнопки мыши. | ||
− | Константа REPORT_MOUSE_POSITION указывает, что мы хотим | + | Константа '''REPORT_MOUSE_POSITION''' указывает, что мы хотим отслеживать движение указателя мыши, а константа '''ALL_MOUSE_EVENTS''' заставляет программу реагировать на все события мыши. Более полное описание констант событий вы найдете на ''man''-странице функции '''mousemask(3x)'''. В качестве результирующего значения функция |
− | + | '''mousemask()''' возвращает маску из выбранных нами событий, которые | |
− | заставляет программу реагировать на все события мыши. Более | + | фактически могут быть обработаны. Если функция возвращает 0, значит, работа с мышью в консоли не поддерживается. |
− | + | ||
− | + | Каждый раз, когда в системе происходит одно из «наблюдаемых» | |
− | mousemask() возвращает маску из выбранных нами событий, которые | + | |
− | фактически могут быть обработаны. Если функция возвращает 0, | + | |
− | + | ||
− | + | ||
событий мыши, в потоке ввода программы появляется специальный | событий мыши, в потоке ввода программы появляется специальный | ||
− | символ KEY_MOUSE. Точнее говоря, по умолчанию, в потоке ввода | + | символ '''KEY_MOUSE'''. Точнее говоря, по умолчанию, в потоке ввода |
− | программы Linux появляется Esc-последовательность, | + | программы Linux появляется Esc-последовательность, соответствующая этому символу, так что в программе ''cursedmouse'' мы тоже должны вызвать функцию '''keypad()''' с ненулевым вторым параметром. |
− | + | ||
− | вызвать функцию keypad() с ненулевым вторым параметром. | + | После того, как мы считали из потока ввода специальный символ |
− | + | '''KEY_MOUSE''', мы можем получить более подробную информацию о | |
− | KEY_MOUSE, мы можем получить более подробную информацию о | + | |
вызвавшем его событии мыши. Делается это с помощью функции | вызвавшем его событии мыши. Делается это с помощью функции | ||
− | getmouse(). Аргументом функции getmouse() должен быть указатель | + | '''getmouse()'''. Аргументом функции '''getmouse()''' должен быть указатель |
− | на структуру MEVENT. Определение структуры MEVENT выглядит | + | на структуру '''MEVENT'''. Определение структуры '''MEVENT''' выглядит следующим образом: |
− | + | ||
+ | <source lang=c> | ||
typedef struct { | typedef struct { | ||
short id; /* идентификатор для различения нескольких устройств */ | short id; /* идентификатор для различения нескольких устройств */ | ||
Строка 413: | Строка 394: | ||
mmask_t bstate; /* маска событий */ | mmask_t bstate; /* маска событий */ | ||
} MEVENT; | } MEVENT; | ||
− | + | </source> | |
− | + | ||
− | + | Координаты указателя возвращаются в формате строка ('''y'''), столбец ('''x'''). Поле '''bstate''' содержит один-единственный бит, соответствующий константе события. | |
− | + | ||
+ | В программе ''cursedmouse'' мы считываем поступающие во входной | ||
поток символы в цикле. Если во входном потоке появляется символ | поток символы в цикле. Если во входном потоке появляется символ | ||
− | KEY_MOUSE, мы, с помощью функции getmouse(), определяем | + | '''KEY_MOUSE''', мы, с помощью функции '''getmouse()''', определяем координаты указателя мыши в момент возникновения события и распечатываем их в левом верхнем углу экрана, а затем переводим курсор туда, |
− | + | ||
− | + | ||
куда указывала мышь в момент возникновения события. Появление в | куда указывала мышь в момент возникновения события. Появление в | ||
− | потоке ввода символа, отличного от KEY_MOUSE, приводит к | + | потоке ввода символа, отличного от '''KEY_MOUSE''', приводит к завершению программы. |
− | + | ||
− | + | Осталось обратить внимание читателя на обработку сигнала | |
− | SIGWINCH в программе cursedmouse. Изменение размеров экрана | + | '''SIGWINCH''' в программе ''cursedmouse''. Изменение размеров экрана |
при включенной поддержке мыши приведет к появлению в потоке | при включенной поддержке мыши приведет к появлению в потоке | ||
− | ввода символов Esc-последовательности специального символа | + | ввода символов Esc-последовательности специального символа '''KEY_RESIZE''' (это еще один способ предупредить программу о том, что |
− | + | размеры экрана изменились). В программе ''cursedmouse'' появление в | |
− | размеры экрана изменились). В программе cursedmouse появление в | + | потоке ввода каких-либо кодов, отличных от '''KEY_MOUSE''', приводит |
− | потоке ввода каких-либо кодов, отличных от KEY_MOUSE, приводит | + | к завершению программы. Для того чтобы избежать этого, в обработчике сигнала '''SIGWINCH''' мы опустошаем поток ввода с помощью |
− | к завершению программы. Для того чтобы избежать этого, в | + | функции '''flushinp()'''. Естественно, этот способ спасения программы |
− | + | ||
− | функции flushinp(). Естественно, этот способ спасения программы | + | |
от досрочного завершения годится далеко не всегда, ведь в момент | от досрочного завершения годится далеко не всегда, ведь в момент | ||
изменения размеров окна терминала поток ввода может содержать | изменения размеров окна терминала поток ввода может содержать | ||
− | важную информацию. Все это лишний раз демонстрирует, | + | важную информацию. Все это лишний раз демонстрирует, насколько нетривиальной является обработка изменения размеров экрана в |
− | + | программах ''ncurses''. | |
− | программах ncurses. | + | |
− | + | На этом я заканчиваю (честное слово!) серию статей, посвященную | |
Unix API. Я благодарю вас за внимание, проявленное к этой серии, и | Unix API. Я благодарю вас за внимание, проявленное к этой серии, и | ||
надеюсь, что с помощью моих статей вы получили некоторое общее | надеюсь, что с помощью моих статей вы получили некоторое общее | ||
представление о низкоуровневом программировании в Linux/Unix, а | представление о низкоуровневом программировании в Linux/Unix, а | ||
самое главное, смогли ответить на вопрос – нужно ли вам все это. '''LXF''' | самое главное, смогли ответить на вопрос – нужно ли вам все это. '''LXF''' |
Текущая версия на 15:17, 26 ноября 2008
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард 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!”); <source> Определенная нами функция '''get_password()''' принимает три параметра – идентификатор окна, в котором выполняется ввод, адрес буфера, в который записываются вводимые символы и число, указывающее длину буфера (вместе с завершающим нулем). Прототип функции '''strcmp()''', которую мы используем для сравнения переданной пользователем строки и пароля, находится в файле '''string.h'''. Рассмотрим теперь саму функцию '''get_password()''': <source lang=c> 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