День 15. Строчный редактор

День 15. Строчный редактор

Чтение кода клавиши. Простой драйвер клавиатуры.

Чтение кода клавиши

EMIT читает код символа, введенного с клавиатуры, а нам нужен код клавиши. Это для того чтобы различать функциональные и управляющие клаивши на клавиатуре.

Примеры нашел в файлах src/spf_con_io.f Файл документирован. Слова довольно просто читается, т.к. небольшие и неиспользуют "навороченные" приемы. Сделал простой пример для тестирования и просмотра кода нажатой клавиши.

 :NONAME
 BEGIN
 BEGIN
    EKEY?
 WHILE
       EKEY EKEY>SCAN . . CR
 REPEAT
 AGAIN
; EXECUTE

Программа ловит код нажатой клавиши и печатет его на консоли. Здесь используется прием с неименноваными словами – слово компилируется и сразу же исполняется. Для отладки это неплохо, но это засоряет словарь.

На самом деле EKEY? возвращает флаг – нажата ли клавиша в момент обращения к слову, причем состояние сбрасывает EKEY. EKEY возвращает код события на консоли – два числа, которые слово EKEY>SCAN преобразует к более читаемому виду ( флаг скан-код ) Если флаг 0, то клаиша была отпущена, если -1 то нажата в момент опроса.

Выход из программы традиционно, жестким способом, по Ctrl+Break.

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

 :NONAME
 BEGIN
 BEGIN
    EKEY?
 WHILE
       EKEY EKEY>SCAN IF . CR ELSE DROP THEN
 REPEAT
 AGAIN
; EXECUTE

Простой драйвер клавиатуры

Задача драйвера по скан-коду клавиши выдавать символ либо выполнять назначенную этой клавише команду. Будем рассматривать нажатие клавиши (имеено, как нажатие!, чтобы можно задейсвовать автоповтор) на клавиатуре как вызов заранее назначенной команды, которыя выполняет предписанное ей действие – либо выводит какой-то символ, в зависимости от внутренних установок системы, либо их меняет, либо выполняет другое действие.

Понятно, что здесь мы будем широко использовать векторные слова. Зваведем массив, в котором код нажатой клавиши будет играть роль индекса, а значение ячейки массива – адрес команды.

Работу драйвера можно организовать в модели последовательного процесса выполнения программы, по типу ACCEPT. Когда назначаем выход из программы по определенному условию, а можно использовать принци много поточности, и запустить драйвер клавиатуры в отдельном потоке, а с остальными потоками обменивать информацией через разделяемые глобальные переменные. Первый вариант несколько прост в реализации, зато второй – гибче.

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

Структуры данных драйвера Прежде всего это буфер в котором будет хранится вводимая строка. Он же будет отображаться на экране. Т.к. клавиатура одна, драйвер один на все потоки то и буфер – глобальная переменная.

VALUE БуферСтроки
   256 ALLOCATE DROP TO БуферСтроки

VALUE ОбработчикКлавиатуры 256 ALLOCATE DROP TO ОбработчикКлавиатуры

Используем здесь VALUE переменную потому как работаем с адресами, чтобы не использовать двойную конструкцию @ @ .

Под буфер выделяем 256 байт буфера. Все остальное определялось в процессе написания программы. Были введены переменные, отображающие состояние служебных клавиш, для ctrl, shift, alt дополнительно отслеживается, являются ли они в текущий момент нажатыми. Например, для реализации гарячих клавиш это обязательно понадобиться. Сейчас же это понадобилось для определения состояния регистра вводимого символа. Естественным образом был решен вопрос с переключением раскладки клавиатуры. Я ее повесил на Ctrl. Т.к. скан коды левого и правого одинаковы, то раскладку переключает любой из них. Введено понятие курсора.

Пока программа в режиме становления, оптимизации я ее не сильно описываю. Но баг один словил, о нем ниже.

\ Драйвер клавиатуры
 REQUIRE AT-XY ~day\common\console.f

\ ----------------------- Глобальные переменные --------------------------- 0 VALUE БуферСтроки 256 ALLOCATE DROP TO БуферСтроки 0 VALUE ДлинаСтроки 0 VALUE ПозицКурсора

0 VALUE ОбработчикКлавиатуры 256 ALLOCATE DROP TO ОбработчикКлавиатуры

0 VALUE ВерхнийРегистр -1 VALUE АнглРаскладка 0 VALUE CtrlНажата 0 VALUE LShiftНажата 0 VALUE RShiftНажата 0 VALUE AltНажата

\ ----------------------- Вспомогательный слова -------------------------- : ВерхнийРегистр? ( – flag ) LShiftНажата RShiftНажата OR IF ВерхнийРегистр INVERT ELSE ВерхнийРегистр THEN ; : НазначитьКлавише ( xt scan-code – ) CELLS ОбработчикКлавиатуры + ! ;

: ВставитьСимвол ( – ) ПозицКурсора БуферСтроки + C! \ Режим замены ПозицКурсора 1+ DUP TO ПозицКурсора DUP ДлинаСтроки > IF TO ДлинаСтроки THEN ; : ОтобразитьБуфер БуферСтроки ДлинаСтроки ( ПозицКурсора ) TYPE CR ;

\ --------- Обработчики нажатие клавиш и их прописание в таблицу -------- :NONAME ( Клавиша CapsLock ) ВерхнийРегистр INVERT TO ВерхнийРегистр ; 58 НазначитьКлавише

:NONAME ( Клавиша LSHIFT Смена регистра ) TRUE TO LShiftНажата ; 42 НазначитьКлавише

:NONAME ( Клавиша RSHIFT Смена регистра ) TRUE TO RShiftНажата ; 54 НазначитьКлавише

:NONAME ( Клавиша Ctrl Переключение языка ) АнглРаскладка INVERT TO АнглРаскладка ; 29 НазначитьКлавише

:NONAME ( Клавиша Q ) АнглРаскладка IF ВерхнийРегистр? IF [CHAR] Q ВставитьСимвол ELSE [CHAR] q ВставитьСимвол THEN ELSE ВерхнийРегистр? IF [CHAR] Й ВставитьСимвол ELSE [CHAR] й ВставитьСимвол THEN THEN ; 16 НазначитьКлавише

\ -------------------- Сам драйвер --------------------- : ДрайверКлавиатуры BEGIN 0 3 AT-XY ОтобразитьБуфер

EKEY EKEY>SCAN ( scan-code flag ) IF \ клавиша нажата CELLS ОбработчикКлавиатуры + @ ( адр-слова ) ?DUP IF \ если адрес не нулеовй то выполняю его EXECUTE THEN ELSE \ клавиша отпущена DUP 29 = IF FALSE TO CtrlНажата THEN DUP 42 = IF FALSE TO LShiftНажата THEN DUP 54 = IF FALSE TO RShiftНажата THEN DUP 56 = IF FALSE TO AltНажата THEN THEN AGAIN ;

ДрайверКлавиатуры

Следующий пример демонстрирует баг в чистом виде

 REQUIRE AT-XY ~day\common\console.f

0 VALUE БуферСтроки 256 ALLOCATE DROP TO БуферСтроки 0 VALUE ДлинаСтроки 0 VALUE ПозицКурсора

: ВставитьСимвол ( код-симв – ) ПозицКурсора БуферСтроки + C! \ Режим замены \ 0 12 AT-XY ." ПозицКурсора " ПозицКурсора . ." БуферСтроки " БуферСтроки . \ <-- Раскоментировать?

ПозицКурсора 1+ TO ПозицКурсора \ <--- !!!! ПозицКурсора ДлинаСтроки > IF ПозицКурсора TO ДлинаСтроки THEN \ <--- !!! ; :NONAME 0 1 AT-XY ." ДлинаСтроки " ДлинаСтроки . ." ПозицКурсора " ПозицКурсора . 56 ВставитьСимвол 0 2 AT-XY ." ДлинаСтроки " ДлинаСтроки . ." ПозицКурсора " ПозицКурсора . ; EXECUTE

Суть примера в том, что если ПозицияСтроки больше ДлинаСтроки, то ДлинаСтроки становится равной ДлинаСтроки Этот момент обозначен на листинге. Так вот, это фрагмент срабатывает через раз!!! Или работает нормально если раскоментировать указанную строку. В чем проблема я так и не понял...

В основной программе я модифицировал этот фрагментв слове ВставитьСимвол активно используя стек (использование оператора DUP).

На сегодня все.

<<< Предыдущий Начало   Следующий >>>
Copyright © Alex Furashev 2004

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

Hosted by uCoz