diff options
| -rw-r--r-- | README.rst | 22 | ||||
| -rw-r--r-- | doc/index.html | 11 | ||||
| -rwxr-xr-x | gen.sh | 25 | ||||
| -rw-r--r-- | lulua/data/layouts/ar-lulua.yaml | 11 | ||||
| -rw-r--r-- | lulua/data/winkbd/README.txt | 11 | ||||
| -rwxr-xr-x | lulua/data/winkbd/install.bat | 5 | ||||
| -rw-r--r-- | lulua/data/winkbd/keyboard.c | 350 | ||||
| -rw-r--r-- | lulua/data/winkbd/keyboard.def | 4 | ||||
| -rw-r--r-- | lulua/data/winkbd/keyboard.h | 36 | ||||
| -rw-r--r-- | lulua/data/winkbd/keyboard.rc | 29 | ||||
| -rw-r--r-- | lulua/data/winkbd/lulua.reg | bin | 0 -> 916 bytes | |||
| -rwxr-xr-x | lulua/data/winkbd/make.bat | 22 | ||||
| -rw-r--r-- | lulua/keyboard.py | 72 | ||||
| -rw-r--r-- | lulua/render.py | 257 | ||||
| -rw-r--r-- | lulua/util.py | 1 | ||||
| -rwxr-xr-x | makezip.sh | 7 | 
16 files changed, 857 insertions, 6 deletions
| @@ -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> @@ -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.regBinary files differ new file mode 100644 index 0000000..326941c --- /dev/null +++ b/lulua/data/winkbd/lulua.reg 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 + | 
