Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 790097a2d045744642ce01f2998a0391f5212a38 @grompe committed May 25, 2016
@@ -0,0 +1,3 @@
+trash/
+reg_layout.exe
+*.dll
@@ -0,0 +1,6 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer]
+"AlwaysUnloadDll"=dword:00000000
+
+; Restore default DLL unload behavior
@@ -0,0 +1,7 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer]
+"AlwaysUnloadDll"=dword:00000001
+
+; For testing the keyboard layout, you may want to force the
+; unused DLL to unload immediately, this REG file does just that.
@@ -0,0 +1,183 @@
+Ассемблер/дизассемблер клавиатурных раскладок Windows с помощью flat assembler
+==============================================================================
+
+Знакомый линуксоид упрекнул меня, мол, в винде ни переключения языка
+Caps Lock'ом нет, ни даже раскладку нельзя отредактировать. Посмотрел я,
+и правда, все раскладки содержатся в файлах C:\Windows\System32\kbd*.dll,
+и редактировать такое hex-редактором ну никак не назвать удобным.
+
+Как достичь удобства? Для переключения раскладок Caps Lock'ом можно
+использовать всякие навесные программы, тяжёлые вроде Punto Switcher, или
+простые вроде lswitch. Для редактирования раскладок есть MSKLC, но он
+малофункционален и неудобен, а аналоги вроде KbdEdit или KLM32 платные.
+
+И тогда я решил написать на flat assembler'е код, собирающий DLL раскладки.
+
+За основу была взята раскладка kbdusx.dll, в системе она называется
+"США Международная". Мне она понравилась тем, что в ней есть ряд комбинаций
+для дополнительных символов. Не нравилась она тем, что превращает клавиши
+<`~>, <6^>, <'"> в так называемые dead keys, «мёртвые клавиши». Их особенность —
+при нажатии ничего не печатается, но изменяется следующий набранный символ.
+Таким образом можно вводить латинские буквы с диакритикой, для которых нет
+отдельного сочетания клавиш. Но мне, как программисту, было очень неприятно
+«залипание» таких часто используемых клавиш, и изначально я просто обнулил поля
+в hex-редакторе, отвечающие за мёртвые клавиши.
+
+Пришла пора разобраться с форматом раз и навсегда. Поначалу дело шло неспешно,
+потом я нашёл kbd.h и несколько примеров раскладок из Windows Driver Kit.
+
+
+Внутри раскладки
+================
+
+Забавно, что Microsoft решили поместить раскладки в формат DLL, хотя за
+исключением корейской kbdkor.dll и японской kbdjpn.dll там кода нет совсем,
+если не считать экспортируемую функцию KbdLayerDescriptor, возвращающую
+указатель на главную таблицу с раскладкой.
+
+Таблица имеет такой вид:
+
+-----------------------------------------------------
+ 32-bit Windows 64-bit или WOW64
+Название Размер Смещение Размер Смещение
+-----------------------------------------------------
+modifiers 4 0x00 8 0x00
+vk2wchar 4 0x04 8 0x08
+deadkeys 4 0x08 8 0x10
+keynames 4 0x0C 8 0x18
+keynamesExt 4 0x10 8 0x20
+keynamesDead 4 0x14 8 0x28
+scancode2vk 4 0x18 8 0x30
+scancode2vk_size 1 0x1C 1 0x38
+e0scancode2vk 4 0x20 8 0x40
+e1scancode2vk 4 0x24 8 0x48
+locale_flags 2 0x28 2 0x50
+version 2 0x2A 2 0x52
+ligature_chars 1 0x2C 1 0x54
+ligature_size 1 0x2D 1 0x55
+ligatures 4 0x30 8 0x58
+type 4 0x34 4 0x60
+subtype 4 0x38 4 0x64
+-----------------------------------------------------
+
+Таким образом, файл раскладки содержит в себе таблицы названия клавиш,
+конвертации скан-кодов в виртуальные (0x1C → VK_RETURN), конвертации виртуальных
+кодов в символы, комбинации мёртвых клавиш, «лигатуры».
+
+Судя по всему, названия клавиш используются редко, мне не удалось на своём
+компьютере найти программы, которая бы выводила их список из раскладки.
+
+Изменяя таблицу скан-кодов, можно переназначить любую клавишу на другую. Если
+сразу захотелось переназначить кнопки питания (Power, Sleep, WakeUp), то это
+тоже можно, но это не отменит их оригинальной функции. Её можно отключить
+в настройках электропитания.
+
+В таблице символов самый сок. Она решает, к чему приведёт нажатие клавиш K,
+Shift+K, AltGr+K, Shift+AltGr+K, влияет ли на неё Caps Lock, использует ли он
+тот же ряд символов, что и Shift+K или отдельный, влияет ли на неё Kana; будет
+ли символ напечатан сразу, или занесётся в очередь мёртвых клавиш, или
+напечатается ряд символов из «лигатуры», или или не произойдёт ничего.
+
+Список мёртвых клавиш содержит пары символов, которые должны преобразовываться
+в третий символ. Этот символ может печататься сразу, или снова проходить по
+списку для дальнейшего преобразования. Если пара символов не найдена в списке,
+то она просто напечатается как есть. С помощью цепочек мёртвых клавиш можно
+сымитировать поведение Compose Key, но некоторые программы, например, Firefox,
+не распознают преобразования дальше первого.
+
+Список «лигатур», который по сути является набором макросов, может позволить
+набирать до четырёх символов WCHAR по нажатию клавиши. На самом деле, у меня
+в Windows 7 работает до 16 символов, но с крайне неприятным исключением: Firefox
+при натыкании на такую раскладку напрочь зависает, а если раскладка с длинными
+«лигатурами» системная, то и вовсе перестаёт запускаться.
+
+
+Создание раскладки
+==================
+
+Сперва я хотел сделать две удобных раскладки, русскую и английскую, подходящую
+как для написания статей, так и программ. Типографская раскладка Ильи Бирмана
+хороша, но могло быть ещё лучше, тем более если делать лично для себя.
+
+Потом я узнал про клавишу Kana. Kana — переключающаяся подобно Caps Lock'у
+кнопка на японской клавиатуре. И тут я решил объединить английскую и русскую
+раскладки в одну, и переключаться между ними клавишей Kana. Которую я
+переназначил на Caps Lock. Написал простую программу-индикатор для отображения
+состояния Kana лампочкой Caps Lock'а.
+
+У такой объединённой раскладки возник один приятный сюрприз: раскладка теперь
+одна на всю систему, и остаётся при переключении между программами, а также
+«шибко умные» программы больше не могут переключать раскладку по своему желанию
+при редактировании текста или перемещении курсора.
+
+И один неприятный сюрприз: Psi+ почему-то стал съедать первый символ, введённый
+после переключения раскладки нажатием Kana.
+
+Тем не менее, я оставил эту раскладку в архиве вместе с индикатором Kana.
+
+Потом я обнаружил, что Caps Lock'у можно назначить отдельный ряд символов
+и заменил Kana на Caps Lock. Программа-индикатор стала ненужной, неприятный
+сюрприз изчез. При переделке обнаружилось только одно ограничение: Caps Lock
+работает только с рядами K и Shift+K — на него нельзя повесить AltGr+K и т.п.
+
+В попытках уместить все мёртвые клавиши в два ряда (с Kana было четыре), я
+наткнулся на идею перевернуть их порядок: так, чтоб набиралась AltGr+буква,
+а потом модификатор. Это позволило мне назначить гораздо больше символов, чем
+раньше. Я назвал это “undead keys”, «восставшие из мёртвых клавиши».
+
+И вот идеальная раскладка была готова.
+
+
+Установка
+=========
+
+Я просто заменяю системный файл C:\Windows\System32\kbdru.dll на свою раскладку,
+ведь она во всех отношениях лучше. На Windows XP или 2000 в таком случае нужно
+не забыть удалить C:\Windows\System32\dllcache\kbdru.dll.
+
+Но если вам не по душе такой насильный апгрейд винды, то раскладку можно
+скопировать в папку C:\Windows\System32\ и зарегистрировать в системе:
+
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\07430419]
+"Layout Text"="US+RU"
+"Layout Display Name"="United States-International + Russian + Extra"
+"Layout File"="kbdusru_undead.dll"
+"Layout Id"="00d0"
+
+Первая часть кода, 0743 — произвольные цифры для уникальной идентификации
+раскладки, а 0419 — код языка «Русский», под которым должна появиться
+раскладка. От кода языка зависит поведение раскладки в неюникодных (ANSI)
+программах.
+
+
+Дальше — больше
+===============
+
+Программировать на flat assembler'е мне доставляет одно удовольствие: мощный
+язык макросов, красивый и послушный синтаксис, ненужность всяких линковщиков
+и каши параметров в командной строке.
+
+Поэтому я решил написать на нём программу, которая при ассемблировании читает
+DLL раскладки, а на выходе — исходный код. Странно, правда? Использовать
+ассемблер, как дизассемблер. Но мощь fasm'а это позволяет.
+
+
+В результате у меня получились раскладки, которые позволяют переключаться между
+языками Caps Lock'ом без внешних программ глобально для всей системы, а также
+набор инструментов, который позволяет нам удобно редактировать раскладки
+клавиатуры Windows.
+
+
+
+
+Что ещё можно сделать
+=====================
+
+- Написать поддержку формата kbd из Windows 9x, для ретроманьяков?
+
+- Можно написать поддержку форматов других редакторов раскладок, но так как
+большинство из них умеют создавать DLL и её можно дизассемблировать, в этом
+немного пользы.
+
@@ -0,0 +1,112 @@
+; Detect between 32 and 64 bits
+virtual at 0
+ xchg eax,eax
+ detected_32bit = $-2
+end virtual
+
+include "const.inc"
+
+match =1, MAKE_DLL
+{
+ include 'macro/export.inc'
+ include 'macro/resource.inc'
+ include "encoding/utf8.inc"
+}
+maybe_rva equ
+match =1, MAKE_DLL
+{
+ maybe_rva equ rva
+}
+macro zeroalign value
+{
+ rb (value-1)-(maybe_rva $+value-1) mod value
+}
+macro palign value
+{
+ if detected_32bit & ~ WOW64
+ zeroalign 4
+ else
+ zeroalign 8
+ end if
+}
+macro vkrow VirtualKey, Attributes, wch&
+{
+ db VirtualKey, Attributes
+ du wch
+}
+; The following macros are to prevent assembly with missing or extra columns
+macro vkrow1 v, a, c1*
+{ vkrow v, a, c1 }
+macro vkrow2 v, a, c1*, c2*
+{ vkrow v, a, c1, c2 }
+macro vkrow3 v, a, c1*, c2*, c3*
+{ vkrow v, a, c1, c2, c3 }
+macro vkrow4 v, a, c1*, c2*, c3*, c4*
+{ vkrow v, a, c1, c2, c3, c4 }
+macro vkrow5 v, a, c1*, c2*, c3*, c4*, c5*
+{ vkrow v, a, c1, c2, c3, c4, c5 }
+macro vkrow6 v, a, c1*, c2*, c3*, c4*, c5*, c6*
+{ vkrow v, a, c1, c2, c3, c4, c5, c6 }
+macro vkrow7 v, a, c1*, c2*, c3*, c4*, c5*, c6*, c7*
+{ vkrow v, a, c1, c2, c3, c4, c5, c6, c7 }
+macro vkrow8 v, a, c1*, c2*, c3*, c4*, c5*, c6*, c7*, c8*
+{ vkrow v, a, c1, c2, c3, c4, c5, c6, c7, c8 }
+macro vkrow9 v, a, c1*, c2*, c3*, c4*, c5*, c6*, c7*, c8*, c9*
+{ vkrow v, a, c1, c2, c3, c4, c5, c6, c7, c8, c9 }
+macro vkrow10 v, a, c1*, c2*, c3*, c4*, c5*, c6*, c7*, c8*, c9*, c10*
+{ vkrow v, a, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 }
+
+struc equ! expr
+{
+ rept 1 value:expr
+ \{
+ restore .
+ . equ value
+ \}
+}
+
+stored_strings equ ""
+stst_index equ 1
+
+macro dp [v]
+{
+ forward
+ stst_index equ! stst_index+1
+ if v eqtype ""
+ match A,stst_index
+ \{
+ if detected_32bit & ~ WOW64
+ dd stst_\#A
+ else
+ dq stst_\#A
+ end if
+ \}
+ stored_strings equ stored_strings,v
+ else
+ if detected_32bit & ~ WOW64
+ dd v
+ else
+ dq v
+ end if
+ end if
+}
+
+macro store_strings
+{
+ stst_index equ 0
+ match a,stored_strings
+ \{
+ irp v,a
+ \\{
+ stst_index equ! stst_index+1
+ match A,stst_index
+ \\\{
+ stst_\\\#A:
+ if v eqtype ""
+ du v, 0
+ end if
+ palign
+ \\\}
+ \\}
+ \}
+}
@@ -0,0 +1,55 @@
+@echo off
+setlocal enabledelayedexpansion
+if not "%cd%"\=="%~pd0" cd /d "%~pd0"
+set target=%1
+if not "%target%"=="" goto:skipdefault
+set target=kbdusru_undead.dll
+:skipdefault
+if not exist %target% goto:notexist
+set d=################################
+echo %d%%d%%d%%d%%d%%d%%d%%d%>__dummy__.txt
+for /f "tokens=3" %%i in ('fc /b __dummy__.txt %target% ^| find "003C:"') do set pe=%%i
+for /f "tokens=3" %%i in ('fc /b __dummy__.txt %target% ^| find "00%pe%:"') do set sig=%%i
+if not %sig%==50 goto:notvalid
+set /a h=0x%pe%+4
+call:tohex %h%
+for /f "tokens=3" %%i in ('fc /b __dummy__.txt %target% ^| find "00%hex%:"') do set arch=%%i
+if %processor_architecture%==x86 goto:x86
+if %processor_architecture%==AMD64 goto:amd64
+del __dummy__.txt
+exit /b 1
+:x86
+if not %arch%==4C goto:badarch_need_x86
+goto:cleanup
+:amd64
+if not %arch%==64 goto:badarch_need_amd64
+:cleanup
+del __dummy__.txt
+exit /b 0
+:tohex
+set lookup=0123456789ABCDEF
+set hex=
+set /a a=%1
+:tohex_loop
+set /a b=!a! %% 16
+set /a a=!a! / 16
+set hex=!lookup:~%b%,1!%hex%
+if %a% gtr 0 goto:tohex_loop
+goto:eof
+:badarch_need_x86
+echo %target% is 64-bit, but your Windows is 32-bit^^!
+echo Modify its source and recompile.
+del __dummy__.txt
+exit /b 1
+:badarch_need_amd64
+echo %target% is 32-bit, but your Windows is 64-bit^^!
+echo Modify its source and recompile.
+del __dummy__.txt
+exit /b 1
+:notvalid
+echo %target% is not a valid PE file^^!
+del __dummy__.txt
+exit /b 1
+:notexist
+echo There is no %target% here, compile it first^^!
+exit /b 1
Oops, something went wrong.

0 comments on commit 790097a

Please sign in to comment.