Ввод текста. Взаимодействие с клавиатурой
Программная обработка скан-кодов и кодов клавиш
Исходный код, отвечающий за обработку скан-кодов и кодов клавиш, входит в состав многих программ, распространяемых согласно свободной лицензии. К ним относятся kbd (мы будем рассматривать версию 1.15.3) и console-tools (версия 0.2.3, например). Архивы с исходными текстами и kbd и console-tools содержат файл showkey.c , в котором реализуется механизм вывода скан-кодов и кодов (keycodes) клавиш, генерируемых когда пользователь нажимает кнопки на клавиатуре.
И kbd и console-tools являются приложениями с текстовым интерфейсом. В среде GNU/Linux такие программы почти всегда включают в свой состав стандартную функцию getopt_long() , осуществляющую синтаксический анализ аргументов командной строки. Достаточно подробные пояснения по поводу использования этой функции даны в работе [МитчеллОулдемСамьюэл2003, С. 31-34].
Применяется ли getopt_long() в kbd и console-tools ? Да. Код в файле src/showkey.c (из набора программ kbd-1.15.3 ) служит наглядным примером того, как это делается.
Версию пакета kbd , используемую в вашей системе, можно узнать, например, выполнив команду showkey —version . В ответ будет выведено сообщение, похожее на «showkey from kbd 1.15» .
Исходя из того, что одним из основных недостатков современной литературы о программировании является почти полное отсутствие примеров кода, решающего реальные, а не учебные задачи, остановимся на src/showkey.c подробнее.
/*— Начало файла src/showkey.c с комментариями —*/
/* Директивы препроцессора, начинающиеся со слова #include (в переводе с английского — «включить в состав») подключают заголовочные файлы (header files). Препроцессор — это один из элементов компилятора языка Си, осуществляющий предварительную обработку кода программы (с его помощью можно включать в состав программы внешние файлы, осуществлять макроподстановки и условные включения). Подробные сведения о препроцессоре Си указаны в отличной книге [КерниганРитчи2006, С. 101-104]. */
/* В системах GNU/Linux заголовочные файлы обычно хранятся в каталоге /usr/include . Иными словами, если в командной строке ввести «less /usr/include/stdio.h» , можно будет увидеть содержание заголовочного файла, ответственного за реализацию ввода/вывода в приложении. В английском языке словам «ввод» и «вывод» соответствуют «input» и «output». Таким образом, stdio.h расшифровывается как «Standard Input/Output Header file». */
/* Имена заголовочных файлов, находящихся в каталоге /usr/include (или его аналоге), заключаются в угловые скобки. В свою очередь, двойные кавычки применяются для выделения имён заголовочных файлов, расположенных в одном каталоге с кодом программы, которую предстоит компилировать (переводить с языка высокого уровня на язык понятный компьютеру, то есть — в машинный код). */
/* Имена переменных tmp , fd и oldkbmode говорят сами за себя. Tmp — сокращение от слова temporary (временный); fd — сокращение от file descriptor (дескриптор файла, являющийся ссылкой на «объект» открытого файла; подробнее см. [Вахалия2003, С. 338-340]); oldkbmode (old keyboard mode) служит для хранения названия режима, в котором находится драйвер клавиатуры (об этих режимах мы ещё поговорим). Сокращение int указывает на тип переменной (см. [КерниганРитчи2006, С. 50-54]). */
/* Структура termios используется функциями управления терминалом в Unix-подобных системах. Подробнее см. [Рочкинд2005, С. 226-228]. Структура — это совокупность нескольких переменных, часто различных типов, сгруппированных под единым именем для удобства обращения. [КернигаРитчи2006, с. 139] */
/* Область действия функции get_mode() ограничивается данным файлом исходного кода: от точки объявления до конца (на это указывает слово static перед именем функции). Иными словами, к функции get_mode() нельзя обращаться за пределами файла, в котором она объявлена. */
/* В свою очередь, тип void (в переводе с английского — «пустой») обозначает пустое множество значений. Он используется для указания типа возвращаемого функцией значения в случае если функция ничего не возвращает. */
/* Предшествующая строка кода указывает, что функция get_mode() не только не возвращает, но и не получает никаких значений на вход. */
/* *m является указателем и представляет из себя группу ячеек памяти, которые могут содержать адрес переменной типа char . */
/* KDGKMODE (Get Keyboard Mode) объявляется в файле /usr/include/kd.h и хранит название текущего режима работы драйвера клавиатуры. Нам не удалось найти документации, где пояснялось бы как расшифровывается «KD» . Вероятнее всего, «KD» — это Keyboard Driver . */
/* Функция perror() входит в стандартную библиотеку языка Си и определяется в заголовочном файле stdio.h . Она выводит содержимое своего аргумента, а также сообщение об ошибке. */
/* ioctl — системный вызов общего назначения, служащий для управления символьными устройствами всех типов. Его синтаксис описывается следующим образом: ioctl(fd, cmd, arg) , где fd — дескриптор файла; cmd — целое число, указывающее вызываемую команду; arg — дополнительный аргумент команды (обычно — адрес блока параметров). */
/* KDGKBMODE — это имя, которое в результате макроподстановки, осуществляемой препроцессором (подробнее см. [КерниганРитчи2006, С. 102-103]), заменяется на число, указывающее на команду, результатом которой является получение информации о текущем режиме работы драйвера клавиатуры. Упомянутая макроподстановка происходит благодаря содержимому файла /usr/include/linux/kd.h . */
/* Аргумент &oldkbmode содержит адрес переменной oldkbmode . Именно по этому адресу будет записан результат выполнения вызовы ioctl . K_RAW , K_XLATE , K_MEDIUMRAW , K_UNICODE — это имена, используемые для макроподстановки в файле /usr/include/linux/kd.h . */
/* Таким образом, вместо KDGKBMODE , K_RAW , K_XLATE , K_MEDIUMRAW и K_UNICODE подставляются числа, указанные через макроопределение в файле /usr/include/linux/kd.h : 0x4B44, 0x00, 0x01, 0x02, 0x03 соответственно. Именно поэтому переменная oldkbmode имеет тип int . */
/* KDSKBMODE (Set Keyboard Mode) содержит число, указывающее на команду установки режима работы драйвера клавиатуры. */
/* Конструкция еxit(выражение) эквивалентна конструкции return . Обычно она возравщает 0 если всё идёт хорошо, а 1 — когда произошла ошибка. */
/* Системный вызов tcsetattr задаёт атрибуты терминала, предварительно сохранённые в структуре old (копия структуры termios ). */
/* Атрибут attr_noreturn указывает на то, что функция никогда не возвращает значений. */
/* Функция clean_up() восстанавливает режим работы драйвера клавиатуры, исходя из данных, полученных с помощью системного вызова ioctl . */
/* Атрибут attr_unused указывает на то, что аргумент функции никак не обрабатывается. */
/* Функция usage() , выводящая информацию об аргументах, которые принимает программа, не нуждается в особых пояснениях. */
/* *short_opts — это указатель. Значение записанное в адрес, на который ссылается этот указатель, впоследствии не меняется, так как в начале строки с его объявлением стоит модификатор const . Строка, хранимая по адресу *short_opts , содержит возможные короткие опции, каждая из которых представлена одной буквой. */
/* long_opts[] (в переводе — «длинные опции», то есть содержащие более одной буквы в своём имени) представляет из себя массив структур. Каждая структура массива имеет следующие элементы:
< "имя опции", наличие_аргумента, флаг, значение>. */
/* Крайняя структура в массиве должна содержать только нули. */
/* Элемент «наличие_аргумента» может содержать либо числа (0, 1 ,2), либо имена имена для макроподстановки, указанные в файле /usr/include/getopt.h и соответствующие этим числам ( no_argument соответствует нулю; required_argument соответствует единице; optional_argument соответствует двойке). */
/* Если элемент «значение» содержит короткую опцию из строки, на которую указывает *short_opts , то элемент «флаг» , будучи установленным в NULL , позволяет обеспечить соответствие между короткой и длинной опцией (например, как показано ниже, короткая опция -h соответствует длинной опции —help ). Если же заменить элемент «флаг» на указатель, то он будет указывать на переменную, имя которой находится в элементе «значение» . */
/* Следующие три строки кода отвечают за локализацию интерфейса программы средствами GNU gettext . */
/* Пятый арумент функции getopt_long может содержать указатель на структуру в массиве long_opts[] . Подробнее см. man getopt_long (данная страница справочного руководства, помимо всего прочего, содержит пример исходного кода, где пятый аргумент является указателем, а не нулём, как в рассматриваемом нами файле). */
/* Как уже отмечалось выше, структура new — это копия структуры termios . */
/* Две строки кода, представленные далее, иллюстрируют приёмы работы с элементами структуры (локальными флагами копии структуры termios ). Флаги ICANON и ISIG устанавливаются в нуль, ECHO и ECHOCTL — в единицу (подробнее см. [КерниганРитчи2006, с. 160]). */
/* При флаге ICANON равном нулю, вводимые символы не составляются в строки вплоть до чтения (выполнения системного вызова read ). ISIG , будучи обнулённым, блокирует обработку управляющих символов (терминал перестаёт реагировать на управляющие последовательности, вызывающие появление сигналов). К этим символам относятся: INTR (Ctrl+C), QUIT (Ctrl+\), SUSP (Ctrl+z) и DSUSP (Ctrl+y) . Подробнее см. [ТаненбаумВудхалл2006, С. 270-277].*/
/* Обратите внимание, что рассматриваемый блок кода выполняется только при условии если установлена в 1 опция -a (print_ascii). Согласно документации ( man 3 termios ), при ECHOCTL == 1 и ECHO == 1 вводимые пользователем управляющие символы ASCII (за исключением TAB, NL, START и STOP) отображаются в виде ^X , где вместо X размещается символ из таблицы ASCII с кодом на 0x40 больше, чем у управляющего символа. Например, в ответ на символ возврата каретки (клавиша с надписью Enter) появляется ^M (шестнадцатеричный код M в таблице ASCII равен 0x4D , a «Enter» соответствует коду 0xD ). Также следует отметить, что ECHOCTL требует, чтобы в коде программы было определено (через #define в файле /usr/include/features.h) имя _BSD_SOURCE или _SVID_SOURCE (подробнее об этих именах см. http://www.aquaphoenix.com/ref/gnu_c_library/libc_12.html ). */
/* Следующая строка устанавливает все флаги ввода (IGNBRK, BRKINT, IGNPAR, PARMRK, INPCK, ISTRIP, INLCR, IGNCR, ICRNL, IUCLC, IXON, IXANY, IXOFF, IMAXBEL, IUTF8) в нуль. Подробнее см. /usr/include/bits/termios.h , а также комментарии по флагам режимов . */
/* Когда число символов в очереди будет равно значению, записанному в new.c_cc[VMIN] , или когда пройдёт количество десятых долей секунды, указанное в new.c_cc[VTIME] (код рассматриваемого файла отключает таймер так как в new.c_cc[VTIME] заносится нуль) будет выполнен системный вызов read . Подробнее см. [Рочкинд2005, С. 233-236]. */
/* Изменения в структуре termios , хранимые в структуре new , применяются системным вызовом tcsetattr() . Аргумент TCSAFLUSH говорит о том, что помимо применения изменений должны быть очищены буферы ввода/вывода терминала (подробнее см. раздел 3.1 документа Serial Programming Guide for POSIX Operating Systems и его не самый качественный, но всё же перевод на русский язык . */
/* Функция getfd() определена в файле getfd.c . */
/* По умолчанию showkey прекращает работу при отсутствии активности пользователя (нажатых клавиш) в течение 10 секунд. */
/* Системный вызов signal устанавливает реакцию программы showkey на сигналы (запросы на прерывание, осуществляемые на уровне процессов), имена которых определены в файле /usr/include/bits/signum.h (см. /usr/include/signal.h ). По сути речь идёт о назначении подпрограмм обработки для каждого из сигналов. Разработчики showkey сделали всё достаточно прямолинейно, назначив функцию die() обрабатывать все возможные сигналы, кроме того, что генерируется по истечении времени, заданного в качестве аргумента функции alarm() (см. выше). */
/* Очень подробные и наглядные материалы о сигналах Unix представлены в книге [НеметСнайдерСибассХейн2003, С. 68-71]. */
/* Как видно, чтение скан-кодов осуществляется с помощью системного вызова read , считывающего в бесконечном цикле байты из файла, представленного дескриптором fd (не забывайте, что в GNU/Linux устройства также являются файлами). */
/* Как отмечалось ранее (при обсуждении вывода программы getkeycodes ), отпускание клавиши результируется в скан-коде, старший бит которого равен единице (напомним, что речь идёт об особенностях набора скан-кодов Set 1 ). Иными словами, к скан-коду нажатой клавиши прибавляется шестнадцатеричное число 80 , которое в программах на языке Си записывается как 0x80 . */
/*— Конец файла src/showkey.c с комментариями —*/
Компилирировать представленный выше исходный код нужно вместе с файлом src/getfd.c , содержащем определение функции getfd() .
Проверить работоспособность получившегося исполняемого файла ( a.out ) лучше всего вне X сессии. Например, в виртуальном терминале, доступном по нажатию ++ .
Разумеется, для удобства, было бы хорошо иметь возможность управлять длительностью временного промежутка, который программа showkey ожидает ввода пользователя перед тем, как завершить свою работу (в kbd-1.15.3 этот промежуток времени равен 10 секундам). В рамках проекта console-tools данное пожелание реализовано (см. файл kbdtools/showkey.c в архиве). Комментарии по поводу использования некоторых приложений, входящих в состав console-tools см. в статье «VGA console basics and Linux console-tools» .
Источник
Учим дополнительным клавишам
Читать об этом здесь, а это точная копия текста. Спасибо автору! Автор: Юрий «bober» Раззоренов
Содержание
www.xakep.ru
Определение скан-кода клавиш
Что бы ты там не нажимал на своей клавиатуре, X-серверу и ядру, в общем-то, все равно, что на ней написано или нарисовано. Их интересуют исключительно скан-код кнопки, причем сначала иксы считывают таблицу кодов клавиш ядра, а затем уже код клавиши привязывается к собственной таблице кодов. Если в Windows проблемы настройки мультимедийных клавиш в консоли как таковой не существует, то в Linux приходится отдельно настраивать реакцию на нажатие кнопок в консоли и в X-Window.
Чтобы узнать код клавиши, следует использовать утилиту xev, входящую в состав Х-сервера. После ее запуска появляется окно Event Tester, теперь последовательно нажимаем клавиши, запоминая выдаваемый код:
Вывод может быть огромен, так как отслеживается каждое движение мышки при проходе над окном Event Tester. Клавишу описывает блок KeyRelease, в частности, значение keycode как раз и является скан-кодом, который мы хотим узнать. В приведенном примере нажаты две клавиши. Клавише с кодом 236 соответствует код клавиши для X-сервера, указанный в keysym, а также действие XF86Mail, которое в KDE запускает используемый по умолчанию почтовый клиент. Для клавиши с номером 161 код и действие не определены.
Возможна ситуация, когда клавиша нажимается, но ее скан-код не выдается. Это означает, что ядро не может найти соответствующее значение. В выводе dmesg должна быть такая строка:
То есть тебе предлагают установить скан-код клавиши самостоятельно при помощи setkeycodes, при этом значение keycode выбрать очень просто. Переведи полученную цифру в десятичное число (большинство калькуляторов это умеют) и прибавь 128. В данном примере 0xec=236, то есть получаем скан-код 364. Если есть сомнения, список задействованных и незадействованных скан-кодов можно просмотреть, запустив в консоли утилиту getkeycodes или dumpkeys. Например, если вывод «getkeycodes | grep » ничего не дал, значит этот код можно смело использовать.
Помочь определить скан-код способна и утилита ХKeycaps (www.jwz.org/xkeycaps), которая является графическим фронт-эндом к Xmodmap.
В консоли программа xev, разумеется, не работает. Чтобы узнать скан-код, выдаваемый ядром, следует использовать утилиту showkey или getkeycodes:
клавиатура была в режиме UNICODE нажмите любую клавишу (программа завершится через 10 сек после последнего нажатия).
Первые две цифры соответствуют нажатой клавише, вторые – отсутствию нажатия.
Настройка привязки скан-кодов в X-Window
Итак, скан-коды теперь у нас есть, нужно указать Х-серверу, что он, собственно, должен делать при нажатии этой клавиши, то есть присвоить ей символьное имя. Список символьных имен приведен в файле заголовков XF86keysym.h. По умолчанию заголовочные файлы Х-сервера в современных дистрибутивах не устанавливаются. Чтобы увидеть его в Ubuntu, нужно установить пакет x11proto-core-dev, после чего этот файл будет лежать в каталоге /usr/include/X11. Как вариант — можно обратиться к CVS-серверу X.Org. Смотрим:
Если мы сравним последнюю строку с выводом xev, то увидим, что значения совпадают с клавишей с keycode 236 — keysym 0x1008ff19, XF86Mail (без суффикса XK_). Список всех доступных значений в том виде, в каком они должны использоваться, ты найдешь в /usr/share/X11/XKeysymDB.
Составить свой вариант раскладки можно двумя способами: создать описание своей клавиатуры или использовать Xmodmap. Последний способ самый простой, поэтому о нем и будем говорить далее. В домашнем каталоге пользователя создаем файл .Xmodmap, в который заносим желаемые значения:
И так далее, принцип, думаю, ясен. Причем код клавиш можно заносить как в десятичном, так и шестнадцатеричном виде. По моим наблюдениям, коды большинства клавиш стандартизированы. Поэтому, если ты один раз настроишь реакцию на нажатие клавиши и перенесешь файл на другой комп, есть вероятность, что на другой клаве реакция на нажатие также подписанной клавиши будет аналогичная. Пользователи рабочего стола Gnome с GDM могут прописать все эти строки в общесистемный файл /etc/X11/Xmodmap.
В других случаях нам еще нужно указать X-серверу, чтобы он использовал созданный файл. В разных дистрибутивах это реализовано по-разному, основная идея состоит в запуске команды /usr/bin/xmodmap $HOME/.Xmodmap при входе пользователя в систему или при старте Х. Тут уже каждый танцует, как хочет. На форумах предлагают использовать файл $HOME/.xsession (в некоторых дистрибутивах он может называться .Xsession), .xprofile или системный /etc/X11/Xsession. И боюсь, что это далеко не все возможные варианты. Давай посмотрим, как сделано в KUbuntu:
В результате находим прелюбопытнейший файл /etc/X11/Xsession.d/80ubuntu-xmodmap такого содержания:
То есть загружается содержимое файла ubuntu.xmodmap и пользовательский .Xmodmap, если он существует. Открыв в редакторе ubuntu.xmodmap, ты обнаружишь список keycode и сопоставленные символьные имена. Отсюда можно сделать вывод: если разработчик сообщает о том, что его дистрибутив поддерживает мультимедийные клавиатуры, то с большой долей вероятности можно найти подобный файл. В других дистрибутивах присутствует аналогичная система запуска пользовательских xmodmap-файлов.
Теперь, когда символьные имена клавишам присвоены, можно назначать им желаемые действия. Некоторые оконные среды вроде KDE умеют отрабатывать действия по символьным именам. Так, при нажатии кнопки с XF86AudioPlay (ВОПРОС) начинает воспроизведение плеер, используемый по умолчанию. Чтобы установить нужную комбинацию, достаточно зайти в «Центр управления KDE -> Региональные и специальные возможности -> Комбинации клавиш» (в KUbuntu ищи в «System Setting —> Keyboard & Mouse»). Аналогичный пункт меню есть и в Gnome (можно просто вызвать gnome-keyboard-bindings), и в XFce. Плюс некоторые программы вроде Amarok, Konqueror, MPD также умеют обрабатывать нажатия клавиш. В других средах, не имеющих графических средств настройки, скорее всего, потребуется ручное вмешательство в конфигурационные файлы. Например, чтобы в IceWM по нажатии клавиши с символьным именем XF86AudioPlay запускался проигрыватель XMMS, а при повторном нажатии он становился на паузу, в файл
/.icewm, появляющийся после первого запуска, следует добавить строку:
В Fluxbox строка для запуска проигрывателя будет выглядеть так:
В конфигах обычно есть примеры, поэтому с остальными оконными менеджерами, думаю, ты без труда разберешься сам.
Настройка реакции в консоли
В консоли порядок действий несколько иной. Как ты помнишь, вывод dmesg рекомендовал назначить клавишные коды с помощью команды setkeycodes. Но здесь есть отличия — клавишных команд в консоли не может быть больше 128, следует выбирать значения от 0 до 127:
Посмотреть свободные значения можно в файле текущей клавиатурной раскладки. В Ubuntu и всех дистрибутивах, базирующихся на Debian, это обычно /etc/console-setup/boottime.kmap.gz. Если после запуска проблем с клавишами нет, заносим эту строку в один из стартовых скриптов, например в /etc/init.d/rc.local.
Теперь осталось задать соответствие клавиши и выполняемого действия. Здесь простор для творчества даже больше, чем в иксах. В keymaps(5) процедура установки соответствия keycode выглядит следующим образом:
Но можно создавать и свои варианты, указывая команду в переменных:
Другими словами, по нажатии клавиши с кодом 120 будет выполнено действие, указанное в переменной F100; в нашем случае задано выключение компьютера. Вместо F100, естественно, можно использовать любое другое имя.
Теперь не менее важная часть — куда все это записывать. В документации и в многочисленных советах предлагается использовать текущий файл консольной раскладки (в моем случае — boottime.kmap.gz). Кстати, это единственный файл описания раскладок, доступный после установки KUbuntu; чтобы увидеть остальные варианты, следует установить пакет console-data. После этого в /usr/share/keymaps/i386/ можно обнаружить несколько подкаталогов с файлами внутри. Но если тебе понадобится перейти на другую раскладку (в Ubuntu и некоторых других дистрибутивах для этих целей используется файл /etc/default/console-setup или
/.console-setup), все настройки нужно будет перенести в другой файл, что несколько неудобно. Если ты все-таки решишься на этот шаг, используй имеющиеся записи как шаблон, ничего не записывая на первую позицию, а в конце не забудь оставить пустую строку.
Немного о ноутбуке
Пока мне не попадался ноутбук, скан-коды клавиш которого определить не удалось бы. Поэтому настройки здесь ничем не отличаются от описанных выше. Хотя есть один прием, о котором хотелось бы рассказать. Я считаю очень удобным в использовании режим гибернации, когда, включив компьютер, обнаруживаешь все на своих местах. Современные дистрибутивы, как правило, его поддерживают, хотя настройка, в общем-то, несложна — достаточно установить пакет hibernate и переопределить необходимые параметры в конфигурационном файле. Единственное, каждый раз для перехода в этот режим нужно запускать скрипт /usr/sbin/hibernate, что не всегда удобно. Хочется просто закрыть крышку ноутбука, а вновь включив питание, обнаружить все на своих местах.
Это очень просто сделать, используя демон acpid, который представляет собой нечто вроде пользовательского интерфейса, позволяющего управлять любыми событиями ACPI, доступными через /proc/acpi/event. При этом acpid читает набор конфигурационных файлов из каталога /etc/acpi/events/. Если пакет с демоном в дистрибутиве отсутствует, устанавливаем его из репозитария; последнюю версию можно взять с сайта phobos.fs.tum.de/acpi. После установки необходимо в каталоге /etc/acpi/events создать два файла: lid и power. Первый описывает реакцию на закрытие крышки, второй — на нажатие кнопки включения питания.
Это несколько упрощенные варианты, в KUbuntu ты найдешь более сложные скрипты. После этого требуется перезапуск демона acpid:
Теперь при закрытии крышки ноутбука система будет впадать в спячку с выключенным питанием, а при нажатии на кнопку питания — выключаться. Просто и удобно.
Источник