summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars-Dominik Braun <lars@6xq.net>2020-01-19 10:11:37 +0100
committerLars-Dominik Braun <lars@6xq.net>2020-01-19 10:19:21 +0100
commitf15b59b9319d4e5a43a3e3515cb0f0449c7224c7 (patch)
tree8afadd7358b5e16965e7ab4b3957db702c17869b
parent810c8ff0bea17214b4e4c5ce802ad89b5ad6e2c9 (diff)
downloadlulua-f15b59b9319d4e5a43a3e3515cb0f0449c7224c7.tar.gz
lulua-f15b59b9319d4e5a43a3e3515cb0f0449c7224c7.tar.bz2
lulua-f15b59b9319d4e5a43a3e3515cb0f0449c7224c7.zip
Add Windows driver
Generate C header file based on layout description and create a source bundle that must be compiled on a Windows system and then moved back to the source tree. This sucks, but cross-compiling on Linux is a pain, since Windows’ development headers assume a case-insensitive filesystem. Also I’m using MSKLC because the latest driver development kit cannot compile these drivers correctly. Dear @microsoft, please fix your shit: https://github.com/microsoft/Windows-driver-samples/issues/433 A remaining concern right now is licensing. keyboard.{c,h,def,rc} have been copied from a project generated by MSKLC and are probably non-free, although pretty much identical files like https://github.com/microsoft/Windows-driver-samples/blob/master/input/layout/kbdus/kbdus.c are covered by MS-PL. Also binds backspace key to \b and adjusts xmodmap/svg rendering accordingly. See #7.
-rw-r--r--README.rst22
-rw-r--r--doc/index.html11
-rwxr-xr-xgen.sh25
-rw-r--r--lulua/data/layouts/ar-lulua.yaml11
-rw-r--r--lulua/data/winkbd/README.txt11
-rwxr-xr-xlulua/data/winkbd/install.bat5
-rw-r--r--lulua/data/winkbd/keyboard.c350
-rw-r--r--lulua/data/winkbd/keyboard.def4
-rw-r--r--lulua/data/winkbd/keyboard.h36
-rw-r--r--lulua/data/winkbd/keyboard.rc29
-rw-r--r--lulua/data/winkbd/lulua.regbin0 -> 916 bytes
-rwxr-xr-xlulua/data/winkbd/make.bat22
-rw-r--r--lulua/keyboard.py72
-rw-r--r--lulua/render.py257
-rw-r--r--lulua/util.py1
-rwxr-xr-xmakezip.sh7
16 files changed, 857 insertions, 6 deletions
diff --git a/README.rst b/README.rst
index 123246a..25937c2 100644
--- a/README.rst
+++ b/README.rst
@@ -51,6 +51,28 @@ to analyze them and create pretty pictures as well as statistics in ``doc/``.
.. _me: lars+lulua@6xq.net
+Building Windows drivers
+------------------------
+
+There is no easy way to build Windows keyboard drivers, but the following
+instructions have worked in the past:
+
+.. code:: bash
+
+ ninja doc/_temp/winkbd/customization.h
+
+Share the folder `doc/_temp/winkbd` with a Windows system, install `Microsoft
+Keyboard Layout Creator 1.4`_ (MSKLC; the Windows Driver Kit (WDK) would work
+too, but is much larger), adjust ``make.bat`` pointing to your MSKLC
+installation and run it. This should generate two directories, ``System32`` and
+``SysWOW64``, which must be copied back to `doc/_temp/winkbd`. Then run:
+
+.. code:: bash
+
+ ninja doc/_build/ar-lulua-w64.zip
+
+.. _Microsoft Keyboard Layout Creator 1.4: https://www.microsoft.com/en-us/download/details.aspx?id=22339
+
Acknowledgements
----------------
diff --git a/doc/index.html b/doc/index.html
index 22f72d7..7e70ce5 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -77,13 +77,15 @@
<div class="lbox">
<h2>Usage</h2>
<dl>
- <dt>Linux</dt>
- <dd>Run: <code>xmodmap <a href="ar-lulua.xmodmap">ar-lulua.xmodmap</a></code></dd>
+ <dt>Windows</dt>
+ <dd>Download <a href="ar-lulua-w64.zip">driver</a> and follow instructions in <kbd>INSTALL.txt</kbd></dd>
<dt>Android</dt>
<dd>Install
<a href="https://play.google.com/store/apps/details?id=com.menny.android.anysoftkeyboard">AnySoftKeyboard</a>
and
<a href="https://play.google.com/store/apps/details?id=com.anysoftkeyboard.languagepack.arabic">Arabic for AnySoftKeyboard</a>
+ <dt>Linux</dt>
+ <dd>Run: <code>xmodmap <a href="ar-lulua.xmodmap">ar-lulua.xmodmap</a></code></dd>
</dd>
</dl>
</div>
@@ -180,6 +182,11 @@
<div class="pure-u-1 pure-u-md-1-2" lang="en">
<div class="lbox">
<h2>Related work</h2>
+ <p>This non-comprehensive section… Unicode’s
+ <abbr title="Unicode Common Locale Data Repository">CLDR</abbr>
+ lists
+ <a href="https://www.unicode.org/cldr/charts/latest/keyboards/layouts/ar.html">various
+ Arabic keyboard layouts</a> too.</p>
</div>
</div>
</div>
diff --git a/gen.sh b/gen.sh
index 17d4214..3d83839 100755
--- a/gen.sh
+++ b/gen.sh
@@ -101,12 +101,21 @@ rule html
rule cp
command = cp \$in \$out
+rule cpR
+ command = cp -R \$in \$out
+
rule gz
command = gzip -c \$in > \$out
rule configure-make
command = cd \$in && autoreconf --install && ./configure && make
+rule zipR
+ command = ./makezip.sh \$in \$out
+
+rule render-winkbd
+ command = lulua-render -l ar-lulua winkbd \$out
+
### build targets ###
build \$docdir/_build: mkdir
build \$docdir/_build/fonts: mkdir
@@ -124,6 +133,22 @@ build \$docdir/_build/fonts/IBMPlexArabic-Thin.woff2: cp \$fontdir/IBMPlexArabic
# build osmconvert
build \$osmconvert: configure-make 3rdparty/osmctools
+
+# windows drivers
+build \$docdir/_temp/winkbd: cpR lulua/data/winkbd
+build \$docdir/_temp/winkbd/customization.h: render-winkbd || \$docdir/_temp/winkbd
+build \$docdir/_temp/ar-lulua-w64: mkdir
+EOF
+
+w64zipfile="System32/kbdarlulua.dll SysWOW64/kbdarlulua.dll README.txt lulua.reg install.bat"
+deps=""
+for f in $w64zipfile; do
+ echo "build \$docdir/_temp/ar-lulua-w64/$f: cp \$docdir/_temp/winkbd/$f || \$docdir/_temp/ar-lulua-w64"
+ deps+=" \$docdir/_temp/ar-lulua-w64/$f"
+done
+cat <<EOF
+build \$docdir/_build/ar-lulua-w64.zip: zipR \$docdir/_temp/ar-lulua-w64 | $deps
+
EOF
# targets for every layout
diff --git a/lulua/data/layouts/ar-lulua.yaml b/lulua/data/layouts/ar-lulua.yaml
index 6509cd0..419df9a 100644
--- a/lulua/data/layouts/ar-lulua.yaml
+++ b/lulua/data/layouts/ar-lulua.yaml
@@ -1,5 +1,16 @@
layout:
- layer:
+ Bl2: "١"
+ Bl3: "٢"
+ Bl4: "٣"
+ Bl5: "٤"
+ Bl6: "٥"
+ Bl7: "٦"
+ Br6: "٧"
+ Br5: "٨"
+ Br4: "٩"
+ Br_bs: "\b"
+
Cl_tab: "\t"
Cl1: "ث"
Cl2: "ط"
diff --git a/lulua/data/winkbd/README.txt b/lulua/data/winkbd/README.txt
new file mode 100644
index 0000000..fcd28f4
--- /dev/null
+++ b/lulua/data/winkbd/README.txt
@@ -0,0 +1,11 @@
+Installation
+============
+
+Run install.bat as administrator and reboot the computer. Then go to Windows’
+language settings, add Arabic to the preferred languages, select options and
+add lulua as a new keyboard. Afterwards pick the keyboard from the keyboard
+selector in the task bar.
+
+Visit https://6xq.net/%D9%84%D8%A4%D9%84%D8%A4%D8%A9/ for more information
+about this layout.
+
diff --git a/lulua/data/winkbd/install.bat b/lulua/data/winkbd/install.bat
new file mode 100755
index 0000000..a68540b
--- /dev/null
+++ b/lulua/data/winkbd/install.bat
@@ -0,0 +1,5 @@
+copy %~dp0\system32\kbdarlulua.dll /B c:\windows\system32\
+copy %~dp0\syswow64\kbdarlulua.dll /B c:\windows\syswow64\
+reg import %~dp0\lulua.reg
+pause
+
diff --git a/lulua/data/winkbd/keyboard.c b/lulua/data/winkbd/keyboard.c
new file mode 100644
index 0000000..dcc89c3
--- /dev/null
+++ b/lulua/data/winkbd/keyboard.c
@@ -0,0 +1,350 @@
+/***************************************************************************\
+* keyboard layout
+*
+* Copyright (c) 1985-2001, Microsoft Corporation
+*
+* History:
+* KBDTOOL v3.40 - Created Sat Jan 04 14:19:07 2020
+\***************************************************************************/
+
+#include <windows.h>
+#include "kbd.h"
+#include "keyboard.h"
+
+#if defined(_M_IA64)
+#pragma section(".data")
+#define ALLOC_SECTION_LDATA __declspec(allocate(".data"))
+#else
+#pragma data_seg(".data")
+#define ALLOC_SECTION_LDATA
+#endif
+
+/***************************************************************************\
+* ausVK[] - Virtual Scan Code to Virtual Key conversion table
+\***************************************************************************/
+
+static ALLOC_SECTION_LDATA USHORT ausVK[] = {
+ T00, T01, T02, T03, T04, T05, T06, T07,
+ T08, T09, T0A, T0B, T0C, T0D, T0E, T0F,
+ T10, T11, T12, T13, T14, T15, T16, T17,
+ T18, T19, T1A, T1B, T1C, T1D, T1E, T1F,
+ T20, T21, T22, T23, T24, T25, T26, T27,
+ T28, T29, T2A, T2B, T2C, T2D, T2E, T2F,
+ T30, T31, T32, T33, T34, T35,
+
+ /*
+ * Right-hand Shift key must have KBDEXT bit set.
+ */
+ T36 | KBDEXT,
+
+ T37 | KBDMULTIVK, // numpad_* + Shift/Alt -> SnapShot
+
+ T38, T39, T3A, T3B, T3C, T3D, T3E,
+ T3F, T40, T41, T42, T43, T44,
+
+ /*
+ * NumLock Key:
+ * KBDEXT - VK_NUMLOCK is an Extended key
+ * KBDMULTIVK - VK_NUMLOCK or VK_PAUSE (without or with CTRL)
+ */
+ T45 | KBDEXT | KBDMULTIVK,
+
+ T46 | KBDMULTIVK,
+
+ /*
+ * Number Pad keys:
+ * KBDNUMPAD - digits 0-9 and decimal point.
+ * KBDSPECIAL - require special processing by Windows
+ */
+ T47 | KBDNUMPAD | KBDSPECIAL, // Numpad 7 (Home)
+ T48 | KBDNUMPAD | KBDSPECIAL, // Numpad 8 (Up),
+ T49 | KBDNUMPAD | KBDSPECIAL, // Numpad 9 (PgUp),
+ T4A,
+ T4B | KBDNUMPAD | KBDSPECIAL, // Numpad 4 (Left),
+ T4C | KBDNUMPAD | KBDSPECIAL, // Numpad 5 (Clear),
+ T4D | KBDNUMPAD | KBDSPECIAL, // Numpad 6 (Right),
+ T4E,
+ T4F | KBDNUMPAD | KBDSPECIAL, // Numpad 1 (End),
+ T50 | KBDNUMPAD | KBDSPECIAL, // Numpad 2 (Down),
+ T51 | KBDNUMPAD | KBDSPECIAL, // Numpad 3 (PgDn),
+ T52 | KBDNUMPAD | KBDSPECIAL, // Numpad 0 (Ins),
+ T53 | KBDNUMPAD | KBDSPECIAL, // Numpad . (Del),
+
+ T54, T55, T56, T57, T58, T59, T5A, T5B,
+ T5C, T5D, T5E, T5F, T60, T61, T62, T63,
+ T64, T65, T66, T67, T68, T69, T6A, T6B,
+ T6C, T6D, T6E, T6F, T70, T71, T72, T73,
+ T74, T75, T76, T77, T78, T79, T7A, T7B,
+ T7C, T7D, T7E
+
+};
+
+static ALLOC_SECTION_LDATA VSC_VK aE0VscToVk[] = {
+ { 0x10, X10 | KBDEXT }, // Speedracer: Previous Track
+ { 0x19, X19 | KBDEXT }, // Speedracer: Next Track
+ { 0x1D, X1D | KBDEXT }, // RControl
+ { 0x20, X20 | KBDEXT }, // Speedracer: Volume Mute
+ { 0x21, X21 | KBDEXT }, // Speedracer: Launch App 2
+ { 0x22, X22 | KBDEXT }, // Speedracer: Media Play/Pause
+ { 0x24, X24 | KBDEXT }, // Speedracer: Media Stop
+ { 0x2E, X2E | KBDEXT }, // Speedracer: Volume Down
+ { 0x30, X30 | KBDEXT }, // Speedracer: Volume Up
+ { 0x32, X32 | KBDEXT }, // Speedracer: Browser Home
+ { 0x35, X35 | KBDEXT }, // Numpad Divide
+ { 0x37, X37 | KBDEXT }, // Snapshot
+ { 0x38, X38 | KBDEXT }, // RMenu
+ { 0x47, X47 | KBDEXT }, // Home
+ { 0x48, X48 | KBDEXT }, // Up
+ { 0x49, X49 | KBDEXT }, // Prior
+ { 0x4B, X4B | KBDEXT }, // Left
+ { 0x4D, X4D | KBDEXT }, // Right
+ { 0x4F, X4F | KBDEXT }, // End
+ { 0x50, X50 | KBDEXT }, // Down
+ { 0x51, X51 | KBDEXT }, // Next
+ { 0x52, X52 | KBDEXT }, // Insert
+ { 0x53, X53 | KBDEXT }, // Delete
+ { 0x5B, X5B | KBDEXT }, // Left Win
+ { 0x5C, X5C | KBDEXT }, // Right Win
+ { 0x5D, X5D | KBDEXT }, // Application
+ { 0x5F, X5F | KBDEXT }, // Speedracer: Sleep
+ { 0x65, X65 | KBDEXT }, // Speedracer: Browser Search
+ { 0x66, X66 | KBDEXT }, // Speedracer: Browser Favorites
+ { 0x67, X67 | KBDEXT }, // Speedracer: Browser Refresh
+ { 0x68, X68 | KBDEXT }, // Speedracer: Browser Stop
+ { 0x69, X69 | KBDEXT }, // Speedracer: Browser Forward
+ { 0x6A, X6A | KBDEXT }, // Speedracer: Browser Back
+ { 0x6B, X6B | KBDEXT }, // Speedracer: Launch App 1
+ { 0x6C, X6C | KBDEXT }, // Speedracer: Launch Mail
+ { 0x6D, X6D | KBDEXT }, // Speedracer: Launch Media Selector
+ { 0x1C, X1C | KBDEXT }, // Numpad Enter
+ { 0x46, X46 | KBDEXT }, // Break (Ctrl + Pause)
+ { 0, 0 }
+};
+
+static ALLOC_SECTION_LDATA VSC_VK aE1VscToVk[] = {
+ { 0x1D, Y1D }, // Pause
+ { 0 , 0 }
+};
+
+/***************************************************************************\
+* aVkToBits[] - map Virtual Keys to Modifier Bits
+*
+* See kbd.h for a full description.
+*
+* The keyboard has only three shifter keys:
+* SHIFT (L & R) affects alphabnumeric keys,
+* CTRL (L & R) is used to generate control characters
+* ALT (L & R) used for generating characters by number with numpad
+\***************************************************************************/
+static ALLOC_SECTION_LDATA VK_TO_BIT aVkToBits[] = {
+ MODIFIER_BITS
+ {0, 0}
+};
+
+/***************************************************************************\
+* aModification[] - map character modifier bits to modification number
+*
+* See kbd.h for a full description.
+*
+\***************************************************************************/
+
+static ALLOC_SECTION_LDATA MODIFIERS CharModifiers = {
+ &aVkToBits[0],
+ CHAR_MODIFIERS_MASK,
+ CHAR_MODIFIERS
+};
+
+/***************************************************************************\
+*
+* aVkToWch2[] - Virtual Key to WCHAR translation for 2 shift states
+* aVkToWch3[] - Virtual Key to WCHAR translation for 3 shift states
+* aVkToWch4[] - Virtual Key to WCHAR translation for 4 shift states
+* aVkToWch5[] - Virtual Key to WCHAR translation for 5 shift states
+* aVkToWch6[] - Virtual Key to WCHAR translation for 6 shift states
+*
+* Table attributes: Unordered Scan, null-terminated
+*
+* Search this table for an entry with a matching Virtual Key to find the
+* corresponding unshifted and shifted WCHAR characters.
+*
+* Special values for VirtualKey (column 1)
+* 0xff - dead chars for the previous entry
+* 0 - terminate the list
+*
+* Special values for Attributes (column 2)
+* CAPLOK bit - CAPS-LOCK affect this key like SHIFT
+*
+* Special values for wch[*] (column 3 & 4)
+* WCH_NONE - No character
+* WCH_DEAD - Dead Key (diaresis) or invalid (US keyboard has none)
+* WCH_LGTR - Ligature (generates multiple characters)
+*
+\***************************************************************************/
+
+static ALLOC_SECTION_LDATA VK_TO_WCHARS6 aVkToWch6[] = {
+ VK_TO_WCH6
+ {0,0,0,0,0,0,0,0}
+ };
+
+// Put this last so that VkKeyScan interprets number characters
+// as coming from the main section of the kbd (aVkToWch2 and
+// aVkToWch5) before considering the numpad (aVkToWch1).
+
+static ALLOC_SECTION_LDATA VK_TO_WCHARS1 aVkToWch1[] = {
+ { VK_NUMPAD0 , 0 , '0' },
+ { VK_NUMPAD1 , 0 , '1' },
+ { VK_NUMPAD2 , 0 , '2' },
+ { VK_NUMPAD3 , 0 , '3' },
+ { VK_NUMPAD4 , 0 , '4' },
+ { VK_NUMPAD5 , 0 , '5' },
+ { VK_NUMPAD6 , 0 , '6' },
+ { VK_NUMPAD7 , 0 , '7' },
+ { VK_NUMPAD8 , 0 , '8' },
+ { VK_NUMPAD9 , 0 , '9' },
+ { 0 , 0 , '\0' }
+};
+
+static ALLOC_SECTION_LDATA VK_TO_WCHAR_TABLE aVkToWcharTable[] = {
+ { (PVK_TO_WCHARS1)aVkToWch6, 6, sizeof(aVkToWch6[0]) },
+ { (PVK_TO_WCHARS1)aVkToWch1, 1, sizeof(aVkToWch1[0]) },
+ { NULL, 0, 0 },
+};
+
+/***************************************************************************\
+* aKeyNames[], aKeyNamesExt[] - Virtual Scancode to Key Name tables
+*
+* Table attributes: Ordered Scan (by scancode), null-terminated
+*
+* Only the names of Extended, NumPad, Dead and Non-Printable keys are here.
+* (Keys producing printable characters are named by that character)
+\***************************************************************************/
+
+static ALLOC_SECTION_LDATA VSC_LPWSTR aKeyNames[] = {
+ 0x01, L"Esc",
+ 0x0e, L"Backspace",
+ 0x0f, L"Tab",
+ 0x1c, L"Enter",
+ 0x1d, L"Ctrl",
+ 0x2a, L"Shift",
+ 0x36, L"Right Shift",
+ 0x37, L"Num *",
+ 0x38, L"Alt",
+ 0x39, L"Space",
+ 0x3a, L"Caps Lock",
+ 0x3b, L"F1",
+ 0x3c, L"F2",
+ 0x3d, L"F3",
+ 0x3e, L"F4",
+ 0x3f, L"F5",
+ 0x40, L"F6",
+ 0x41, L"F7",
+ 0x42, L"F8",
+ 0x43, L"F9",
+ 0x44, L"F10",
+ 0x45, L"Pause",
+ 0x46, L"Scroll Lock",
+ 0x47, L"Num 7",
+ 0x48, L"Num 8",
+ 0x49, L"Num 9",
+ 0x4a, L"Num -",
+ 0x4b, L"Num 4",
+ 0x4c, L"Num 5",
+ 0x4d, L"Num 6",
+ 0x4e, L"Num +",
+ 0x4f, L"Num 1",
+ 0x50, L"Num 2",
+ 0x51, L"Num 3",
+ 0x52, L"Num 0",
+ 0x53, L"Num Del",
+ 0x54, L"Sys Req",
+ 0x57, L"F11",
+ 0x58, L"F12",
+ 0x7c, L"F13",
+ 0x7d, L"F14",
+ 0x7e, L"F15",
+ 0x7f, L"F16",
+ 0x80, L"F17",
+ 0x81, L"F18",
+ 0x82, L"F19",
+ 0x83, L"F20",
+ 0x84, L"F21",
+ 0x85, L"F22",
+ 0x86, L"F23",
+ 0x87, L"F24",
+ 0 , NULL
+};
+
+static ALLOC_SECTION_LDATA VSC_LPWSTR aKeyNamesExt[] = {
+ 0x1c, L"Num Enter",
+ 0x1d, L"Right Ctrl",
+ 0x35, L"Num /",
+ 0x37, L"Prnt Scrn",
+ 0x38, L"Right Alt",
+ 0x45, L"Num Lock",
+ 0x46, L"Break",
+ 0x47, L"Home",
+ 0x48, L"Up",
+ 0x49, L"Page Up",
+ 0x4b, L"Left",
+ 0x4d, L"Right",
+ 0x4f, L"End",
+ 0x50, L"Down",
+ 0x51, L"Page Down",
+ 0x52, L"Insert",
+ 0x53, L"Delete",
+ 0x54, L"<00>",
+ 0x56, L"Help",
+ 0x5b, L"Left Windows",
+ 0x5c, L"Right Windows",
+ 0x5d, L"Application",
+ 0 , NULL
+};
+
+static ALLOC_SECTION_LDATA KBDTABLES KbdTables = {
+ /*
+ * Modifier keys
+ */
+ &CharModifiers,
+
+ /*
+ * Characters tables
+ */
+ aVkToWcharTable,
+
+ /*
+ * Diacritics
+ */
+ NULL,
+
+ /*
+ * Names of Keys
+ */
+ aKeyNames,
+ aKeyNamesExt,
+ NULL,
+
+ /*
+ * Scan codes to Virtual Keys
+ */
+ ausVK,
+ sizeof(ausVK) / sizeof(ausVK[0]),
+ aE0VscToVk,
+ aE1VscToVk,
+
+ /*
+ * Locale-specific special processing
+ */
+ MAKELONG(0, KBD_VERSION),
+
+ /*
+ * Ligatures
+ */
+ 0,
+ 0,
+ NULL
+};
+
+PKBDTABLES KbdLayerDescriptor(VOID)
+{
+ return &KbdTables;
+}
diff --git a/lulua/data/winkbd/keyboard.def b/lulua/data/winkbd/keyboard.def
new file mode 100644
index 0000000..11b56eb
--- /dev/null
+++ b/lulua/data/winkbd/keyboard.def
@@ -0,0 +1,4 @@
+LIBRARY Keyboard
+
+ EXPORTS
+ KbdLayerDescriptor @1
diff --git a/lulua/data/winkbd/keyboard.h b/lulua/data/winkbd/keyboard.h
new file mode 100644
index 0000000..0ef87d4
--- /dev/null
+++ b/lulua/data/winkbd/keyboard.h
@@ -0,0 +1,36 @@
+/****************************** Module Header ******************************\
+* keyboard layout header
+*
+* Copyright (c) 1985-2001, Microsoft Corporation
+*
+* Various defines for use by keyboard input code.
+*
+* History:
+*
+* created by KBDTOOL v3.40 Sat Jan 04 14:19:07 2020
+*
+\***************************************************************************/
+
+/*
+ * kbd type should be controlled by cl command-line argument
+ */
+#define KBD_TYPE 4
+
+/*
+* Include the basis of all keyboard table values
+*/
+#include "kbd.h"
+/***************************************************************************\
+* The table below defines the virtual keys for various keyboard types where
+* the keyboard differ from the US keyboard.
+*
+* _EQ() : all keyboard types have the same virtual key for this scancode
+* _NE() : different virtual keys for this scancode, depending on kbd type
+*
+* +------+ +----------+----------+----------+----------+----------+----------+
+* | Scan | | kbd | kbd | kbd | kbd | kbd | kbd |
+* | code | | type 1 | type 2 | type 3 | type 4 | type 5 | type 6 |
+\****+-------+_+----------+----------+----------+----------+----------+----------+*/
+
+#include "customization.h"
+
diff --git a/lulua/data/winkbd/keyboard.rc b/lulua/data/winkbd/keyboard.rc
new file mode 100644
index 0000000..c4576d7
--- /dev/null
+++ b/lulua/data/winkbd/keyboard.rc
@@ -0,0 +1,29 @@
+#include "winver.h"
+1 VERSIONINFO
+ FILEVERSION 1,0,0,02
+ PRODUCTVERSION 1,0,0,02
+ FILEFLAGSMASK 0x3fL
+ FILEFLAGS 0x0L
+FILEOS 0x40004L
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_DRV_KEYBOARD
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004B0"
+ BEGIN
+ VALUE "CompanyName", "6xq.net\0"
+ VALUE "FileDescription", "Ergonomic Arabic Keyboard Layout lulua\0"
+ VALUE "FileVersion", "0.2\0"
+ VALUE "InternalName", "kbdarlulua\0"
+ VALUE "ProductName","lulua\0"
+ VALUE "LegalCopyright", "see https://opensource.org/licenses/MIT\0"
+ VALUE "OriginalFilename","kbdarlulua\0"
+ VALUE "ProductVersion", "0.2\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0000, 0x04B0
+ END
+END
diff --git a/lulua/data/winkbd/lulua.reg b/lulua/data/winkbd/lulua.reg
new file mode 100644
index 0000000..326941c
--- /dev/null
+++ b/lulua/data/winkbd/lulua.reg
Binary files differ
diff --git a/lulua/data/winkbd/make.bat b/lulua/data/winkbd/make.bat
new file mode 100755
index 0000000..4c66cc0
--- /dev/null
+++ b/lulua/data/winkbd/make.bat
@@ -0,0 +1,22 @@
+REM taken from https://levicki.net/articles/2006/09/29/HOWTO_Build_keyboard_layouts_for_Windows_x64.php
+
+SET OUTNAME=kbdarlulua
+SET MSKLC="C:\Program Files (x86)\Microsoft Keyboard Layout Creator 1.4"
+
+mkdir System32
+
+%MSKLC%\bin\i386\rc.exe -r -i%MSKLC%\inc -DSTD_CALL -DCONDITION_HANDLING=1 -DNT_UP=1 -DNT_INST=0 -DWIN32=100 -D_NT1X_=100 -DWINNT=1 -D_WIN32_WINNT=0x0502 /DWINVER=0x0502 -D_WIN32_IE=0x0600 -DWIN32_LEAN_AND_MEAN=1 -DDEVL=1 -DFPO=1 -DNDEBUG -l 409 -Fokeyboard.res keyboard.rc || exit /b
+
+REM build 64 bit
+%MSKLC%\bin\i386\amd64\cl.exe -nologo -I%MSKLC%\inc -DNOGDICAPMASKS -DNOWINMESSAGES -DNOWINSTYLES -DNOSYSMETRICS -DNOMENUS -DNOICONS -DNOSYSCOMMANDS -DNORASTEROPS -DNOSHOWWINDOW -DOEMRESOURCE -DNOATOM -DNOCLIPBOARD -DNOCOLOR -DNOCTLMGR -DNODRAWTEXT -DNOGDI -DNOKERNEL -DNONLS -DNOMB -DNOMEMMGR -DNOMETAFILE -DNOMINMAX -DNOMSG -DNOOPENFILE -DNOSCROLL -DNOSERVICE -DNOSOUND -DNOTEXTMETRIC -DNOWINOFFSETS -DNOWH -DNOCOMM -DNOKANJI -DNOHELP -DNOPROFILER -DNODEFERWINDOWPOS -DNOMCX -DWIN32_LEAN_AND_MEAN -DRoster -DSTD_CALL -D_WIN32_WINNT=0x0502 /c /Zp8 /Gy /W3 /WX /Gz /Gm- /EHs-c- /GR- /GF -Z7 /Zl /Oxs -Fokeyboard64.obj keyboard.c || exit /b
+
+REM XXX: why use the 32 bit linker here? the one in amd64\ does not work
+%MSKLC%\bin\i386\link.exe -nologo -base:0x5FFE0000 -merge:.edata=.data -merge:.rdata=.data -merge:.text=.data -merge:.bss=.data -section:.data,re -MERGE:_PAGE=PAGE -MERGE:_TEXT=.text -MACHINE:AMD64 -SECTION:INIT,d -OPT:REF -OPT:ICF -IGNORE:4039,4078 -noentry -dll -subsystem:native,5.2 -merge:.rdata=.text -PDBPATH:NONE -STACK:0x40000,0x1000 /opt:nowin98 -debugtype:cv,fixup -debug -osversion:5.2 -version:5.2 /release -def:keyboard.def -out:system32\%OUTNAME%.dll keyboard.res keyboard64.obj || exit /b
+
+REM and now 32 bit
+mkdir SysWOW64
+
+%MSKLC%\bin\i386\cl.exe -nologo -I%MSKLC%\inc -DBUILD_WOW6432 -DNOGDICAPMASKS -DNOWINMESSAGES -DNOWINSTYLES -DNOSYSMETRICS -DNOMENUS -DNOICONS -DNOSYSCOMMANDS -DNORASTEROPS -DNOSHOWWINDOW -DOEMRESOURCE -DNOATOM -DNOCLIPBOARD -DNOCOLOR -DNOCTLMGR -DNODRAWTEXT -DNOGDI -DNOKERNEL -DNONLS -DNOMB -DNOMEMMGR -DNOMETAFILE -DNOMINMAX -DNOMSG -DNOOPENFILE -DNOSCROLL -DNOSERVICE -DNOSOUND -DNOTEXTMETRIC -DNOWINOFFSETS -DNOWH -DNOCOMM -DNOKANJI -DNOHELP -DNOPROFILER -DNODEFERWINDOWPOS -DNOMCX -DWIN32_LEAN_AND_MEAN -DRoster -DSTD_CALL -D_WIN32_WINNT=0x0502 /c /Zp8 /Gy /W3 /WX /Gz /Gm- /EHs-c- /GR- /GF -Z7 /Zl /Oxs -Fokeyboard32.obj keyboard.c || exit /b
+
+%MSKLC%\bin\i386\link.exe -nologo -base:0x5FFF0000 -merge:.edata=.data -merge:.rdata=.data -merge:.text=.data -merge:.bss=.data -section:.data,re -MERGE:_PAGE=PAGE -MERGE:_TEXT=.text -MACHINE:IX86 -SECTION:INIT,d -OPT:REF -OPT:ICF -IGNORE:4039,4078 -noentry -dll -subsystem:native,5.2 -merge:.rdata=.text -PDBPATH:NONE -STACK:0x40000,0x1000 /opt:nowin98 -debugtype:cv,fixup -debug -osversion:5.2 -version:5.2 /release -def:keyboard.def -out:SysWOW64\%OUTNAME%.dll keyboard.res keyboard32.obj || exit /b
+
diff --git a/lulua/keyboard.py b/lulua/keyboard.py
index bd1d449..0a7e2e6 100644
--- a/lulua/keyboard.py
+++ b/lulua/keyboard.py
@@ -157,6 +157,74 @@ _buttonToKeyman = {
'Fr_ctrl': 'RCTRL',
}
+# button to symbolic windows scancode usable in keyboard.c
+# see windows header kbd.h (#define TXX _EQ(YY))
+_buttonToWinScancode = {
+ 'Bl1': 'T29',
+ 'Bl2': 'T02',
+ 'Bl3': 'T03',
+ 'Bl4': 'T04',
+ 'Bl5': 'T05',
+ 'Bl6': 'T06',
+ 'Bl7': 'T07',
+ 'Br6': 'T08',
+ 'Br5': 'T09',
+ 'Br4': 'T0A',
+ 'Br3': 'T0B',
+ 'Br2': 'T0C',
+ 'Br1': 'T0D',
+ 'Br_bs': 'T0E',
+ 'Cl_tab': 'T0F',
+ 'Cl1': 'T10',
+ 'Cl2': 'T11',
+ 'Cl3': 'T12',
+ 'Cl4': 'T13',
+ 'Cl5': 'T14',
+ 'Cr7': 'T15',
+ 'Cr6': 'T16',
+ 'Cr5': 'T17',
+ 'Cr4': 'T18',
+ 'Cr3': 'T19',
+ 'Cr2': 'T1A',
+ 'Cr1': 'T1B',
+ 'CD_ret': 'T1C',
+ 'Dl_caps': 'T3A',
+ 'Dl1': 'T1E',
+ 'Dl2': 'T1F',
+ 'Dl3': 'T20',
+ 'Dl4': 'T21',
+ 'Dl5': 'T22',
+ 'Dr7': 'T23',
+ 'Dr6': 'T24',
+ 'Dr5': 'T25',
+ 'Dr4': 'T26',
+ 'Dr3': 'T27',
+ 'Dr2': 'T28',
+ 'Dr1': 'T2B',
+ 'El_shift': 'T2A',
+ 'El1': 'T56',
+ 'El2': 'T2C',
+ 'El3': 'T2D',
+ 'El4': 'T2E',
+ 'El5': 'T2F',
+ 'El6': 'T30',
+ 'Er5': 'T31',
+ 'Er4': 'T32',
+ 'Er3': 'T33',
+ 'Er2': 'T34',
+ 'Er1': 'T35',
+ 'Er_shift': 'T36',
+ 'Fl_ctrl': 'T1D',
+ 'Fl_win': 'X5B',
+ 'Fl_alt': 'T38',
+ 'Fl_space': 'T39',
+ 'Fr_space': 'T39',
+ 'Fr_altgr': 'X38',
+ 'Fr_win': 'X5C',
+ 'Fr_menu': 'X5D',
+ 'Fr_ctrl': 'X1D',
+ }
+
class Button:
__slots__ = ('width', 'isMarked', 'i')
_idToName : Dict[int, Text] = {}
@@ -199,6 +267,10 @@ class Button:
def keymanCode (self):
return _buttonToKeyman[self.name]
+ @property
+ def windowsScancode (self):
+ return _buttonToWinScancode[self.name]
+
@classmethod
def deserialize (self, data: Dict):
kindMap = {'standard': Button, 'letter': LetterButton, 'multi': MultiRowButton}
diff --git a/lulua/render.py b/lulua/render.py
index be3bf68..3bde0b7 100644
--- a/lulua/render.py
+++ b/lulua/render.py
@@ -290,10 +290,7 @@ def renderXmodmap (args):
if not layout.isModifier (frozenset ([btn])):
text = l.layout.get (btn)
if not text:
- if btn.name == 'Br_bs' and i == 0:
- text = 'BackSpace'
- else:
- text = 'NoSymbol'
+ text = 'NoSymbol'
else:
# some keys cannot be represented by unicode
# characters and must be mapped
@@ -301,6 +298,7 @@ def renderXmodmap (args):
'\t': 'Tab',
'\n': 'Return',
' ': 'space',
+ '\b': 'BackSpace',
}
text = specialMap.get (text, f'U{ord (text):04X}')
keycodeMap[btn].append (text)
@@ -426,6 +424,255 @@ def renderAsk (args):
tree = ET.ElementTree (kbdelem)
tree.write (args.output, encoding='utf-8', xml_declaration=True)
+def renderWinKbd (args):
+ keyboard = defaultKeyboards[args.keyboard]
+ layout = defaultLayouts[args.layout].specialize (keyboard)
+
+ if args.layout != 'ar-lulua':
+ logging.error (f'due to the delicate relationship between virtual keys and text output this command will probably not produce working files for your layout. Please have a look at renderWinKbd() in {__file__} and fix the code.')
+ return
+
+ resPath = pkg_resources.resource_filename (__package__, 'data/winkbd')
+
+ with open (args.output, 'w') as fd:
+ lines = []
+ lines.append (f'/* This header was auto-generated by {__package__}. It is included by {resPath}/keyboard.h. Do not modify. */')
+ lines.append ('')
+
+ # copied from kbdneo2.c as well
+ # XXX: modifier keys are fixed for now
+ # maps virtual keys (first value) to shift bitfield value (second value)
+ lines.append ('#define MODIFIER_BITS\\')
+ lines.append (""" { VK_SHIFT , KBDSHIFT }, \\
+ { VK_CONTROL , KBDCTRL }, \\
+ { VK_MENU , KBDALT }, \\
+ { VK_OEM_8 , KBDKANA }, \\
+ { VK_OEM_102 , 16 },""")
+
+ # copied from kbdneo2.c
+ # maps a shift bitfield value (array index) to a layer number in
+ # virtual key translation (VK_TO_WCHARS, array value)
+ lines.append ('#define CHAR_MODIFIERS_MASK 24')
+ lines.append ('#define CHAR_MODIFIERS\\')
+ lines.append ('\t{ 0, 1, 6, 7, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, 3, 8, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, 2, 4, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, SHFT_INVALID, 5, }')
+
+ # this is the standard layout from windows’ kbd.h (for KBD_TYPE == 4)
+ scancodeToVk = {
+ 'T01': 'ESCAPE',
+ 'T02': '1',
+ 'T03': '2',
+ 'T04': '3',
+ 'T05': '4',
+ 'T06': '5',
+ 'T07': '6',
+ 'T08': '7',
+ 'T09': '8',
+ 'T0A': '9',
+ 'T0B': '0',
+ 'T0C': 'OEM_MINUS',
+ 'T0D': 'OEM_PLUS',
+ 'T0E': 'BACK',
+ 'T0F': 'TAB',
+ 'T10': 'Q',
+ 'T11': 'W',
+ 'T12': 'E',
+ 'T13': 'R',
+ 'T14': 'T',
+ 'T15': 'Y',
+ 'T16': 'U',
+ 'T17': 'I',
+ 'T18': 'O',
+ 'T19': 'P',
+ 'T1A': 'OEM_4',
+ 'T1B': 'OEM_6',
+ 'T1C': 'RETURN',
+ 'T1D': 'LCONTROL',
+ 'T1E': 'A',
+ 'T1F': 'S',
+ 'T20': 'D',
+ 'T21': 'F',
+ 'T22': 'G',
+ 'T23': 'H',
+ 'T24': 'J',
+ 'T25': 'K',
+ 'T26': 'L',
+ 'T27': 'OEM_1',
+ 'T28': 'OEM_7',
+ 'T29': 'OEM_3',
+ 'T2A': 'LSHIFT',
+ 'T2B': 'OEM_5',
+ 'T2C': 'Z',
+ 'T2D': 'X',
+ 'T2E': 'C',
+ 'T2F': 'V',
+ 'T30': 'B',
+ 'T31': 'N',
+ 'T32': 'M',
+ 'T33': 'OEM_COMMA',
+ 'T34': 'OEM_PERIOD',
+ 'T35': 'OEM_2',
+ 'T36': 'RSHIFT',
+ 'T37': 'MULTIPLY',
+ 'T38': 'LMENU',
+ 'T39': 'SPACE',
+ 'T3A': 'CAPITAL',
+ 'T3B': 'F1',
+ 'T3C': 'F2',
+ 'T3D': 'F3',
+ 'T3E': 'F4',
+ 'T3F': 'F5',
+ 'T40': 'F6',
+ 'T41': 'F7',
+ 'T42': 'F8',
+ 'T43': 'F9',
+ 'T44': 'F10',
+ 'T45': 'NUMLOCK',
+ 'T46': 'SCROLL',
+ 'T47': 'HOME',
+ 'T48': 'UP',
+ 'T49': 'PRIOR',
+ 'T4A': 'SUBTRACT',
+ 'T4B': 'LEFT',
+ 'T4C': 'CLEAR',
+ 'T4D': 'RIGHT',
+ 'T4E': 'ADD',
+ 'T4F': 'END',
+ 'T50': 'DOWN',
+ 'T51': 'NEXT',
+ 'T52': 'INSERT',
+ 'T53': 'DELETE',
+ 'T54': 'SNAPSHOT',
+ 'T56': 'OEM_102',
+ 'T57': 'F11',
+ 'T58': 'F12',
+ 'T59': 'CLEAR',
+ 'T5A': 'OEM_WSCTRL',
+ 'T5B': 'OEM_FINISH',
+ 'T5C': 'OEM_JUMP',
+ 'T5D': 'EREOF',
+ 'T5E': 'OEM_BACKTAB',
+ 'T5F': 'OEM_AUTO',
+ 'T62': 'ZOOM',
+ 'T63': 'HELP',
+ 'T64': 'F13',
+ 'T65': 'F14',
+ 'T66': 'F15',
+ 'T67': 'F16',
+ 'T68': 'F17',
+ 'T69': 'F18',
+ 'T6A': 'F19',
+ 'T6B': 'F20',
+ 'T6C': 'F21',
+ 'T6D': 'F22',
+ 'T6E': 'F23',
+ 'T6F': 'OEM_PA3',
+ 'T71': 'OEM_RESET',
+ 'T73': 'ABNT_C1',
+ 'T76': 'F24',
+ 'T7B': 'OEM_PA1',
+ 'T7C': 'TAB',
+ 'T7E': 'ABNT_C2',
+ 'T7F': 'OEM_PA2',
+
+ 'X10': 'MEDIA_PREV_TRACK',
+ 'X19': 'MEDIA_NEXT_TRACK',
+ 'X1C': 'RETURN',
+ 'X1D': 'RCONTROL',
+ 'X20': 'VOLUME_MUTE',
+ 'X21': 'LAUNCH_APP2',
+ 'X22': 'MEDIA_PLAY_PAUSE',
+ 'X24': 'MEDIA_STOP',
+ 'X2E': 'VOLUME_DOWN',
+ 'X30': 'VOLUME_UP',
+ 'X32': 'BROWSER_HOME',
+ 'X35': 'DIVIDE',
+ 'X37': 'SNAPSHOT',
+ 'X38': 'RMENU',
+ 'X46': 'CANCEL',
+ 'X47': 'HOME',
+ 'X48': 'UP',
+ 'X49': 'PRIOR',
+ 'X4B': 'LEFT',
+ 'X4D': 'RIGHT',
+ 'X4F': 'END',
+ 'X50': 'DOWN',
+ 'X51': 'NEXT',
+ 'X52': 'INSERT',
+ 'X53': 'DELETE',
+ 'X5B': 'LWIN',
+ 'X5C': 'RWIN',
+ 'X5D': 'APPS',
+ 'X5E': 'POWER',
+ 'X5F': 'SLEEP',
+ 'X65': 'BROWSER_SEARCH',
+ 'X66': 'BROWSER_FAVORITES',
+ 'X67': 'BROWSER_REFRESH',
+ 'X68': 'BROWSER_STOP',
+ 'X69': 'BROWSER_FORWARD',
+ 'X6A': 'BROWSER_BACK',
+ 'X6B': 'LAUNCH_APP1',
+ 'X6C': 'LAUNCH_MAIL',
+ 'X6D': 'LAUNCH_MEDIA_SELECT',
+ }
+ # modifications copied from kbdneo2.c
+ # maps modifier keys to oem values used above.
+ scancodeToVk.update ({
+ # mod 3
+ 'T2B': 'OEM_102',
+ 'T3A': 'OEM_102',
+ # mod 4
+ 'X38': 'OEM_8',
+ 'T56': 'OEM_8',
+ })
+ for k, v in scancodeToVk.items ():
+ lines.append (f'#undef {k}')
+ if len (v) == 1:
+ # character value if not symbolic
+ v = f"'{v}'"
+ lines.append (f'#define {k} _EQ({v})')
+ lines.append ('')
+
+ lines.append ('#define VK_TO_WCH6 \\')
+ for btn in unique (keyboard.keys (), key=attrgetter ('windowsScancode')):
+ def toUnicode (s):
+ if s is None:
+ return 'WCH_NONE'
+ elif len (s) != 1:
+ logging.error (f'only single-character strings are supported, ignoring {s}')
+ return 'WCH_NONE'
+ elif s == '\n':
+ # convert to windows-convention
+ s = '\r'
+ return f'0x{ord (s):x} /*{repr (s)}*/'
+
+ text = list (layout.getButtonText (btn))
+ assert len (text) < 7, "supporting six layers only right now"
+
+ # skip unused keys
+ if sum (map (lambda x: 1 if x is not None else 0, text)) == 0:
+ continue
+
+ # fixed-length array, need padding
+ mappedText = [toUnicode (s) for s in text]
+ while len (mappedText) < 6:
+ mappedText.append ('WCH_NONE')
+
+ vk = scancodeToVk[btn.windowsScancode]
+ if len (vk) == 1:
+ # character value
+ vk = f"'{vk}'"
+ else:
+ # symbolic value
+ vk = f'VK_{vk}'
+ mappingStr = ', '.join (mappedText)
+ lines.append (f'\t{{ {vk}, 0, {mappingStr} }}, \\')
+ # NUL-termination entry is already present in keyboard.c
+ lines.append ('\n')
+
+ fd.write ('\n'.join (lines))
+ logging.info ('refer to README.rst on how to build a windows driver. '
+ f'Template files are located in {resPath}')
+
def yamlload (s):
try:
with open (s) as fd:
@@ -458,6 +705,8 @@ def render ():
sp.set_defaults (func=renderKeyman)
sp = subparsers.add_parser('ask')
sp.set_defaults (func=renderAsk)
+ sp = subparsers.add_parser('winkbd')
+ sp.set_defaults (func=renderWinKbd)
parser.add_argument('output', metavar='FILE', help='Output file')
logging.basicConfig (level=logging.INFO)
diff --git a/lulua/util.py b/lulua/util.py
index f4ad76c..ce6e887 100644
--- a/lulua/util.py
+++ b/lulua/util.py
@@ -80,6 +80,7 @@ def displayText (text):
'\t': '⭾',
'\n': '↳',
' ': '\u2423',
+ '\b': '⌦',
'\u200e': '[LRM]', # left to right mark
'\u061c': '[ALM]', # arabic letter mark
'\u202c': '[PDF]', # pop directional formatting
diff --git a/makezip.sh b/makezip.sh
new file mode 100755
index 0000000..e8b4f92
--- /dev/null
+++ b/makezip.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+tmpfile=$(mktemp -u)
+in=$1
+out=$2
+pushd $(dirname $in) && zip -r $tmpfile $(basename $in) && popd && cp $tmpfile $out && rm $tmpfile
+