середа, 11 серпня 2010 р.

Як я провів вихідні


В оригінальній програмі, яка переключає розкладки клавіатур, перша кнопка на новій клавіатурі вводиться зі старою розкладкою. Я чесно намагався звикнути до цього, але не зміг. І тоді вирішив зробити неможливе: виправити цей недолік. Проблема була в тому, що подія WM_INPUT відбувається вже після обробки події натискання на кнопку. Мені ж необхідно було вставити повідомлення про перемикання розкладки до повідомлення про натискання кнопки. Для того, щоб перехопити цю подію, у Вікнах існує зачіпка на WH_KEYBOARD_LL, яка викликається після обробки клавіши драйвером. Здавалося б, проблем нема, але... в цей момент неможливо визначити фізичну клавіатуру. Очевидно, добрі дрібном'які програмісти вирішили нас уберегти від зайвої інформації...

Я так розумію, правильний підхід — це скачати WDK, розібратися як працює клавіатурний фільтр і навчитися передавати з нього повідомлення про поточну клавіатуру, щоб програма встигала переключити розкладку... Якби я був справжнім програмістом, я б так і зробив: уявляю собі вже справжній інсталятор, який вимагає пароль адміністратора, щоб установити клавіатурний фільтр, а після установки перезавантажуватиме систему. Саме так все і працює під Вікнами в реальному житті!

Та я вирішив обійтися тим, що є. Отже, для кожної клавіатурної події спочатку виконується зачіпка на WH_KEYBOARD_LL, а потім обробник події WM_INPUT який знає, яку розкладку вибрати. Перше, що прийшло в голову, — не ґенерувати подію натискання кнопки, а натомість зберігати всі кнопки за допомогою зачіпки, щоб після перемикання розкладки, відправити їх по черзі за допомогою keybd_event. Весь прикол в тому, що якщо перервати генерування події натискання кнопки, вернувши зачіпкою -1, то і подія WM_INPUT не настає! Таким чином, узнати з якої клавіатури прийшов сигнал про натискання кнопки можна тільки після того, як цей сигнал було оброблено.

Символ з кодуванням від чужої клавіатури обробляється при натисканні на кнопку. А відпускання кнопки зазвичай нічого поганого не робить. Отож можна почекати коли кнопку відпущено, передати цей сигнал, визначити клавіатуру, перемкнути, якщо потрібно, розкладку, а потім ввести цей символ ще раз за допомогою keybd_event. Це призводить до наступного ефекту: якщо ми натискаємо, наприклад, SHIFT отримуємо таку послідовність подій — SHIFT відпущено, SHIFT натиснуто, SHIFT відпущено замість SHIFT натиснуто, а потім відпущено. Сама по собі ця послідовність не має фатальних наслідків. Але разом з відповідними неконтрольованими подіями для CTRL і ALT може переключати в якісь незвичайні режими, коли система думає, що ваш SHIFT натиснуто, у той час, коли ви його відпустили. Вирішення цієї проблеми полягає в тому, що при натисканні цих трьох кнопок, ми завжди створюємо подію.

Це працює. Але створює одну незручність: якщо відпустити, наприклад, SHIFT до того, як подію натискання на букву було оброблено (натиснути SHIFT, натиснути букву, відпустити SHIFT, відпустити букву), SHIFT не спрацьовує — і набирається маленька буква. Те саме стосується ALT і CTRL. Подію відпускання обов'язково необхідно передати, бо инакше WM_INPUT не відбудеться, а значить можна впустити ланцюг виконання буфера. Тому відправляється подія відпускання, а в буфер записується віртуальне натискання цієї ж кнопки спочатку і відпускання її ж укінці.

Та найцікавіше було попереду. При переході від одної клавіатури до иншої, якщо натиснути одночасно кілька кнопок, вони натискаються з CTRL. Як з'ясувалося, причина в тому, що після повідомлення про зміну розкладки, з клавіатури приходить подія віпускання CTRL. Прапорець виставлений, як подія на реальній клавіатурі, при тому що ніякого CTRL я не торкаюся! Тобто ця подія приходить саме від драйвера, і я навіть уявити боюся чому... Відтак я зрозумів, що моя клавіатурна новація не вносить особливого хаосу в уже цілком хаотичну поведінку системи. Тому після зміни розкладки, я просто виставляю прапорець не зберігати CTRL, ALT і SHIFT у буфер доки буфер не очиститься і позбавляюся цієї проблеми.

Втім, нехорошу поведінку виключити повністю я не зумів. Якщо, наприклад, натиснути SHIFT на одній клавіатурі, а букву на иншій, програма смішно намагається встигнути переключитися двічі, але їй це не вдається. В результаті вводиться маленька буква з розкладки клавіатури, на якій натиснуто SHIFT. Взагалі, якщо потренуватися, можна зробити там переповнення буфера, абощо — та в нормальному режимі працює досить пристойно як для збочення...

Ще один не дуже приємний ефект — символ вводиться після відпускання кнопки, а не після натискання. З цієї ж причини неадекватно обробляється затискання одної кнопки на деякий час. Символи з'являються з запізненням і перший буде зі старої клавіатури. Думаю, якщо ви звикли до сліпого набору, то, можливо, зачіпку краще відключити і просто пристосуватися до неправильного першого символу при переході з одної клавіатури на иншу.


Ну і про неосновне, але найбільш помітне... Я зробив вибір розкладки для кожної мови. Це перетворює програмку на досить таку універсальну штуку. Уявіть, що у вас п'ять клавіатур з різною кількістю кнопок. Це не прикол: у вас може бути ноутбук плюс USB-клавіатура вдома, на роботі, в друзів — всюди різні. І ви набираєте текст десятьма мовами. За допомогою цієї програми ви можете налаштувати для кожної пари клавіатура-мова окрему розкладку з автоматичним переключенням на неї при наборі. Для деяких клавіатур можна задати мови, для яких вони не працюють, — на них не можна буде переключитися за допомогою цієї клавіатури. Для цього при першому натисканні кнопки у таких мовах слід задати розкладку з иншої мови.

Найпростіша конфіґурація: дві мови, — наприклад: українська та англійська, — і дві клавіатури — по одній для кожної мови.
  • Спочатку вибираємо англійську мову і натискаємо на кнопку «української» клавіатури.
  • Ставимо їй розкладку української мови. Програма переключається на українську мову.
  • Тепер натискаємо кнопку «англійської» клавіатури і вибираємо їй англійську розкладку.
  • Відтепер кнопки на англійській клавіатурі автоматично вводитимуть англійські букви, а української — українські.
Програмку відключати не потрібно. Це ще одна особливість, яку я додав: програма бачить нові клавіатури і розкладки на льоту. Ну і, звичайно, пам'ятає всі клавіатури, які коли-небудь підключалися до вашого комп'ютера. Якщо ви видалите розкладку, яку використовує якась із клавіатур, програма не зможе на неї переключитися і можуть бути проблеми. Те саме стосується особливо розумних програм, на кшталт Белазару, які завантажують власні розкладки — у цьому разі вам доведеться налаштовувати реакцію програми на нові мови вводу. Додавати нові розкладки можна без обмежень, проте одна клавіатура може використовувати тільки одну розкладку для кожної мови.

Скачати: програму або код.

Якщо код вас не злякає, буду радий коментарям і пропозиціям.

Нема коментарів

Дописати коментар