Размышлизмы о тяжком бытие программиста или поддержка клавы в винде

Вот реализовывал в движке под винду низкоуровневую обработку событий клавиатуры с помощью RawInput и все думал какой м***ак писал это все на уровне ядра операционки, а после этого еще один м... писал под это документацию.

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

Судя по тому что он идет следом за флагом со значением 4, то для него наверное планировалось значение 8. Хотя если заглянуть в хедер winuser.h где определен этот флаг мы увидим:

  1. /*
  2.  * Define the keyboard input data Flags.
  3.  */
  4. RI_KEY_MAKE             0
  5. RI_KEY_BREAK            1
  6. RI_KEY_E0               2
  7. RI_KEY_E1               4
  8. RI_KEY_TERMSRV_SET_LED  8
  9. RI_KEY_TERMSRV_SHADOW   0x10

С интересом видим какието два странных флага RI_KEY_TERMSRV_SET_LED и RI_KEY_TERMSRV_SHADOW, о которых если верить google в мсдн вообще нет ни слова.

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

if ( Flags == RI_KEY_MAKE ) { клавиша была нажата }

if ( Flags & RI_KEY_BREAK ) { клавиша была отпущена }

Дальше больше. Выясняется что второй случай нормально работает при авторипите (когда клавишу нажали и держим) - т.е. сообщение с этим флагам приходит один раз при самом отпускание, тогда как первый вариант валится при авторипите постоянно - долго ломаем голову как реагировать только на первое нажатие, заводим массив состояний клавы и сверяясь с ним выясняем была ли клавиша уже нажата. Костыль, но траблу решает.

Дальше флаг RI_KEY_E0 для левых клавиш устанавливается почти нормально (устанавливается для всех левых двойных клавиш кроме левой клавиши "SHIFT", зато у шифтов отличаются приходящие сканкоды... зато для всех остальных двойных клавиш сканкод приходит одинаковый...).

Более того если клавиша всетаки присылает RI_KEY_E0 то поскольку на нажатие клавиши в нутрях системы гдето прописано RI_KEY_MAKE | RI_KEY_E0, и поскольку RI_KEY_MAKE == 0, то отловить нажатие клавиши с помощью проверки Flags == RI_KEY_MAKE становится уже не возможно, и на флаг RI_KEY_MAKE приходится забивать вообще и как-то обходится без него.

Если говорить о флаге RI_KEY_E1, то о нем повествование будет коротким - для правых клавиш мне не разу словить его не удалось...

Далее доходим до клавиши Pause, лезем в документацию например сюда и читаем "Make only", т.е. у этой клавиши нет Break-сканкода, но винда упорно шлет RI_KEY_BREAK вместо RI_KEY_MAKE на нажатие этой клавиши. (после этого закрадывается мысля, что на флаг RI_KEY_MAKE забил не только я но и разработчики винды).

Затем выясняем что нампадовские клавиши и клавиши дополнительной клавиатуры (Insert, Delete, pageup и т.д.) работают в двух режимах

  1. Когда нам-лок включен, и те и другие присылают ожидаемые значения.
  2. Когда нам-лок выключен, и INSERT на дополнительной клавиатуре и "0" на нампадовской (другие клавиши по аналогии) шлют одни и те же значения, вот только перед и после нажатия на клавишу дополнительной клавиатуры приходит странное сообщения с кодом виртуальной клавиши 0xFF которой в списке виртуальных клавиш винды вообще не существует. (Кстати этот левый код присылается также и перед/после нажатия на клавиши PrintScreen и PAUSE - только уже без всякой зависимости от состояния numlock)

Помучившись и научившись отлавливать этот левый код для того чтобы отличать клавиши дополнительной и нампадовской клавиатуры при выключенном нам-локе, ожидаешь что нажатия на Enter можно будет обработать по той же схеме, но...

Выясняется что в списке виртуальных клавиш винды нет Enter-а которой на нампаде (хотя все остальне нампадовские клавиши там есть) а есть только просто VK_RETURN, который шлет как клавиша на основной клавиатуре так и на нампадовской, зато... для нампадовского Enter-а приходит флаг RI_KEY_E0, который не устанавливается ни для одной другой нампадовской клавиши. Хотя внешне на стандартных клавах нампадовский Enter находится правее основного, и логичнее было бы слать флаг RI_KEY_E1, но видать на этот флаг разработчики винды забили точно так же как и на RI_KEY_MAKE.

и т.д. и т.п.

Остается только догадываться по каким техническим заданиям, какими индусами, и с какого бодуна писалась реализация всей этой виндошной прослойки а потом и документации к ней...

Последняя правка: ср, 13/03/2013 - 04:00
Submitted by Victor on

Комментарии

Зато сколько удовлетворения тебе и другим (прошедшим этот квест) подарил какой-то безымянный индус... Smile

Не очень понятно, зачем было вообще RAW INPUT для клавиатуры юзать — обычные мессаги и GetAsyncKeyState замечательно работают же.

Фигня с клавиатурами в том, что они хардварно разные бывают. Даже в ДОСе напрямую через контроллер работало с нюансами.

Low-level с кнопочками, из опыта, нормально работает только на консолях — с фиксированным железом и отсутствием мистических слоёв дриверов/ОС которые епонятно как работают.

Submitted by BLK Dragon on
BLK Dragon wrote:

Не очень понятно, зачем было вообще RAW INPUT для клавиатуры юзать — обычные мессаги и GetAsyncKeyState замечательно работают же...

Нужна была возможность лочить клаву и перехватывать нажатия всех клавиш. Сначало хотел директ-импут заюзать, но оказалось что он уже legacy в win8. Пришлось писать на "обычных месагах" но там всплыли все это непонятное поведение - спустился на уровень ниже, думал станет легче, дык нет (думаю сама винда тоже юзает RAW INPUT).

По поводу GetAsyncKeyState - проверил, приходит таже самая хрень с непонятками левых и правых клавиш, и нампадовой клавиатуры. Возможно это тоже надстройка над RAW INPUT.

Submitted by Victor on
Victor wrote:
BLK Dragon wrote:

Не очень понятно, зачем было вообще RAW INPUT для клавиатуры юзать — обычные мессаги и GetAsyncKeyState замечательно работают же...

Нужна была возможность лочить клаву и перехватывать нажатия всех клавиш. Сначало хотел директ-импут заюзать, но оказалось что он уже legacy в win8. Пришлось писать на "обычных месагах" но там всплыли все это непонятное поведение - спустился на уровень ниже, думал станет легче, дык нет (думаю сама винда тоже юзает RAW INPUT).

По поводу GetAsyncKeyState - проверил, приходит таже самая хрень с непонятками левых и правых клавиш, и нампадовой клавиатуры. Возможно это тоже надстройка над RAW INPUT.

Насколько помню описало дривер-модели -- всё насдстройка над raw-input. Другой вопрос что с этим raw-input зиллион нюансов.

У меня вот есть поддержка нескольких мышей через raw-input -- оно как-то не всегда и не везде работает так просто как хотелось бы.

 

Submitted by BLK Dragon on

Виктор, я вас прекрасно понимаю, скажу больше, а если нужно поддержать 2 одновременно клавиатуры подключеные к компьютеру (сразу скажу данные будут дублироваться). В общем посмотрите как это сделано у нас для управления роботом из консольного окна (https://code.google.com/p/roboticsby/source/browse/trunk/src/tigger_joystick_keyboard_module_test.cpp). Основные моменты, рекомендации:

1 поместите все в отдельный поток.

2 связь с другими потоками можно сделать через посредника в виде массива глобальных состояний нажато/не нажато.

3 всегда делайте проверку на наличие ошибки при работе с WinAPI функциями.

Надеюсь наш код вам поможет принять верные для вашего проекта решения.

Submitted by Necro on
Necro wrote:

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

Спасибо за код, но у нас немного другая была специфика - надо было без консоли и отдельного потока.

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

Submitted by Victor on

Отдельный поток для инпута вообще странная затея.

Т.е. выгоды сомнительны, проблемы очевидны — если в код приложения шлются event'ы типа key-up/key-down (а они типично таки шлются), придётся тут же синхронизацию вставлять (во избежание странных глюков); ну или буферизовать всё состояние инпута и позже обрабатывать — код усложняется без каких то вразумительных причин.

Лет 10 назад на PowerTV я как раз боролся с инпутом который в отдельном потоке был (OS так устроен) — тот ещё цирк.

Submitted by BLK Dragon on

Эта утилита тестовая она поддерживает и джойстик кстати тоже. Она формирует с помощью клавиатуры управляющий пакет и посылает его на COMM порт. Пока она поддерживает только одного робота. Планируем поддержки одновременно большего количества роботов. Через анализ доступных мак адресов сетевых беспроводных адаптеров роботов.

Поддержка множества клавиатур (USB) нужна для того, чтобы с одного компьютера можно было управлять многими роботами.

Сейчас кстати у меня дипломник будет делать работу по соединению с роботом через Wi-Fi интерфейс это будет прорыв для нашей Республики Беларусь в контексте беспроводных интерфейсов и возможности управления роботами.

Submitted by Necro on
Necro wrote:

Поддержка множества клавиатур (USB) нужна для того, чтобы с одного компьютера можно было управлять многими роботами.

Многими это сколько? Любопытства ради - сколько в среднем клавиш задействовано для управления одним средним роботом?

Necro wrote:

...соединению с роботом через Wi-Fi интерфейс это будет прорыв для нашей Республики Беларусь в контексте беспроводных интерфейсов и возможности управления роботами.

Это так сложно или мы на столько отстали?

Последняя правка: ср, 03/04/2013 - 15:52
Submitted by Victor on

Там в утилите написано, мы управляем скоростью и движением траков. 4+4+9 клавиш итого.

Не сильно отстали и действительно сложно ведь у нас такт системы управления 100 ms и для нас любая задержка/потеря пакета - критичны. Т. е. тут надо гарантированая передача команд роботу/с него.

Submitted by Necro on

GameDev.by