Initial import of code

The data/keymaps.csv file is taken from the latest SPICE-GTK
repository. All contributions from this original file in both
SPICE-GTK and GTK-VNC repos were made under Red Hat copyright.
Red Hat grants permission to relicense to dual GPL2 or 3-clause
BSD.

The data/keymap-gen tool is a brand new file.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
diff --git a/LICENSE.BSD b/LICENSE.BSD
new file mode 100644
index 0000000..ec1a29d
--- /dev/null
+++ b/LICENSE.BSD
@@ -0,0 +1,27 @@
+Copyright (c) Individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+    3. Neither the name of PyCA Cryptography nor the names of its contributors
+       may be used to endorse or promote products derived from this software
+       without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSE.GPL2 b/LICENSE.GPL2
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/LICENSE.GPL2
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644
index 0000000..a3c3c9f
--- /dev/null
+++ b/README
@@ -0,0 +1,114 @@
+   Key code / scan code / key symbol mapping database
+   ==================================================
+
+This module provides a database that maps between different
+key code / scan code / key symbol sets:
+
+ - Linux evdev
+ - OS-X
+ - AT Set 1
+ - AT Set 2
+ - AT Set 3
+ - XT
+ - Linux XT KBD driver
+ - USB HID
+ - Win32
+ - XWin XT
+ - XKBD XT
+ - Xorg Evdev
+ - Xorg KBD
+ - Xorg OS-X
+ - XOrg Cygwin
+ - RFB
+
+Licensing
+---------
+
+The contents of this package are dual licensed under the terms of:
+
+ - GNU General Public License (version 2 or later)
+ - 3-clause BSD License
+
+The output files generated by keymap-gen may be distributed & used under
+the terms of either of the above licenses.
+
+Data formats
+------------
+
+The following output formats are possible
+
+ - Code map
+
+   An array mapping between key code sets values
+
+   Indexes in the array are values from the source code set.
+   Entries in the array are values from the target code set
+
+
+ - Code table
+
+   An array listing all values in a key code set
+
+   Indexes in the array are simply a numeric counter
+   Entries in the array are values from the key code set
+
+   The size of the array matches the total number of entries in
+   the keycode database.
+
+
+ - Name map
+
+   An array mapping between key code sets values and names
+
+   Indexes in the array are values from the source code set
+   Entries in the array are names from the target code set
+
+
+ - Name table
+
+   An array listing all names in a key code set
+
+   Indexes in the array are simply a numeric counter
+   Entries in the array are values from the key code set
+
+   The size of the array matches the total number of entries in
+   the keycode database.
+
+
+Output languages
+----------------
+
+The tool is capable of generating data tables for the following
+programming languages / environments
+
+ - Standard C
+ - GLib2 (standard C, but with GLib2 data types)
+ - Python
+ - Perl
+
+
+Usage
+-----
+
+Map values from AT Set 1 to USB HID, generating tables for the
+C programming language
+
+ $ keymap-gen --lang stdc code-map data/keymaps.csv atset1 usb
+
+Generate a tables of names for Linux key codes, OS-X key codes,
+in python - equivalent array indexes map between the two sets.
+A variable name override is used
+
+ $ keymap-gen --varname linux_keycodes --lang stdc \
+              code-table data/keymaps.csv linux
+ $ keymap-gen --varname osx_keycodes --lang stdc \
+              code-table data/keymaps.csv os-x
+
+Generate a mapping from XOrg XWin values to Win32 names
+
+ $ keymap-gen --lang perl name-map data/keymaps.csv xorgxwin win32
+
+Generate a table of names for Linux key codes in Perl
+
+ $ keymap-gen --lang perl name-table data/keymaps.csv linux
+
diff --git a/data/keymaps.csv b/data/keymaps.csv
new file mode 100644
index 0000000..ef5c33e
--- /dev/null
+++ b/data/keymaps.csv
@@ -0,0 +1,511 @@
+"Linux Name","Linux Keycode","OS-X Name","OS-X Keycode","AT set1 keycode","AT set2 keycode","AT set3 keycode",XT,"XT KBD","USB Keycodes","Win32 Name","Win32 Keycode","Xwin XT","Xfree86 KBD XT","X11 keysym","X11 keycode"
+KEY_RESERVED,0,,,,,,,,,,,,,,
+KEY_ESC,1,Escape,0x35,1,118,8,1,1,41,VK_ESCAPE,0x1b,1,1,XK_Escape,0xff1b
+KEY_1,2,ANSI_1,0x12,2,22,22,2,2,30,VK_1,0x31,2,2,XK_1,0x0031
+KEY_1,2,ANSI_1,0x12,2,22,22,2,2,30,VK_1,0x31,2,2,XK_exclam,0x0021
+KEY_2,3,ANSI_2,0x13,3,30,30,3,3,31,VK_2,0x32,3,3,XK_2,0x0032
+KEY_2,3,ANSI_2,0x13,3,30,30,3,3,31,VK_2,0x32,3,3,XK_at,0x0040
+KEY_3,4,ANSI_3,0x14,4,38,38,4,4,32,VK_3,0x33,4,4,XK_3,0x0033
+KEY_3,4,ANSI_3,0x14,4,38,38,4,4,32,VK_3,0x33,4,4,XK_numbersign,0x0023
+KEY_4,5,ANSI_4,0x15,5,37,37,5,5,33,VK_4,0x34,5,5,XK_4,0x0034
+KEY_4,5,ANSI_4,0x15,5,37,37,5,5,33,VK_4,0x34,5,5,XK_dollar,0x0024
+KEY_5,6,ANSI_5,0x17,6,46,46,6,6,34,VK_5,0x35,6,6,XK_5,0x0035
+KEY_5,6,ANSI_5,0x17,6,46,46,6,6,34,VK_5,0x35,6,6,XK_percent,0x0025
+KEY_6,7,ANSI_6,0x16,7,54,54,7,7,35,VK_6,0x36,7,7,XK_6,0x0036
+KEY_6,7,ANSI_6,0x16,7,54,54,7,7,35,VK_6,0x36,7,7,XK_asciicircum,0x005e
+KEY_7,8,ANSI_7,0x1a,8,61,61,8,8,36,VK_7,0x37,8,8,XK_7,0x0037
+KEY_7,8,ANSI_7,0x1a,8,61,61,8,8,36,VK_7,0x37,8,8,XK_ampersand,0x0026
+KEY_8,9,ANSI_8,0x1c,9,62,62,9,9,37,VK_8,0x38,9,9,XK_8,0x0038
+KEY_8,9,ANSI_8,0x1c,9,62,62,9,9,37,VK_8,0x38,9,9,XK_asterisk,0x002a
+KEY_9,10,ANSI_9,0x19,10,70,70,10,10,38,VK_9,0x39,10,10,XK_9,0x0039
+KEY_9,10,ANSI_9,0x19,10,70,70,10,10,38,VK_9,0x39,10,10,XK_parenleft,0x0028
+KEY_0,11,ANSI_0,0x1d,11,69,69,11,11,39,VK_0,0x30,11,11,XK_0,0x0030
+KEY_0,11,ANSI_0,0x1d,11,69,69,11,11,39,VK_0,0x30,11,11,XK_parenright,0x0029
+KEY_MINUS,12,ANSI_Minus,0x1b,12,78,78,12,12,45,VK_OEM_MINUS,0xbd,12,12,XK_minus,0x002d
+KEY_MINUS,12,ANSI_Minus,0x1b,12,78,78,12,12,45,VK_OEM_MINUS,0xbd,12,12,XK_underscore,0x005f
+KEY_EQUAL,13,ANSI_Equal,0x18,13,85,85,13,13,46,VK_OEM_PLUS,0xbb,13,13,XK_equal,0x003d
+KEY_EQUAL,13,ANSI_Equal,0x18,13,85,85,13,13,46,VK_OEM_PLUS,0xbb,13,13,XK_plus,0x002b
+KEY_BACKSPACE,14,Delete,0x33,14,102,102,14,14,42,VK_BACK,0x08,14,14,XK_BackSpace,0xff08
+KEY_TAB,15,Tab,0x30,15,13,13,15,15,43,VK_TAB,0x09,15,15,XK_Tab,0xff09
+KEY_Q,16,ANSI_Q,0xc,16,21,21,16,16,20,VK_Q,0x51,16,16,XK_Q,0x0051
+KEY_Q,16,ANSI_Q,0xc,16,21,21,16,16,20,VK_Q,0x51,16,16,XK_q,0x0071
+KEY_W,17,ANSI_W,0xd,17,29,29,17,17,26,VK_W,0x57,17,17,XK_W,0x0057
+KEY_W,17,ANSI_W,0xd,17,29,29,17,17,26,VK_W,0x57,17,17,XK_w,0x0077
+KEY_E,18,ANSI_E,0xe,18,36,36,18,18,8,VK_E,0x45,18,18,XK_E,0x0045
+KEY_E,18,ANSI_E,0xe,18,36,36,18,18,8,VK_E,0x45,18,18,XK_e,0x0065
+KEY_R,19,ANSI_R,0xf,19,45,45,19,19,21,VK_R,0x52,19,19,XK_R,0x0052
+KEY_R,19,ANSI_R,0xf,19,45,45,19,19,21,VK_R,0x52,19,19,XK_r,0x0072
+KEY_T,20,ANSI_T,0x11,20,44,44,20,20,23,VK_T,0x54,20,20,XK_T,0x0054
+KEY_T,20,ANSI_T,0x11,20,44,44,20,20,23,VK_T,0x54,20,20,XK_t,0x0074
+KEY_Y,21,ANSI_Y,0x10,21,53,53,21,21,28,VK_Y,0x59,21,21,XK_Y,0x0059
+KEY_Y,21,ANSI_Y,0x10,21,53,53,21,21,28,VK_Y,0x59,21,21,XK_y,0x0079
+KEY_U,22,ANSI_U,0x20,22,60,60,22,22,24,VK_U,0x55,22,22,XK_U,0x0055
+KEY_U,22,ANSI_U,0x20,22,60,60,22,22,24,VK_U,0x55,22,22,XK_u,0x0075
+KEY_I,23,ANSI_I,0x22,23,67,67,23,23,12,VK_I,0x49,23,23,XK_I,0x0049
+KEY_I,23,ANSI_I,0x22,23,67,67,23,23,12,VK_I,0x49,23,23,XK_i,0x0069
+KEY_O,24,ANSI_O,0x1f,24,68,68,24,24,18,VK_O,0x4f,24,24,XK_O,0x004f
+KEY_O,24,ANSI_O,0x1f,24,68,68,24,24,18,VK_O,0x4f,24,24,XK_o,0x006f
+KEY_P,25,ANSI_P,0x23,25,77,77,25,25,19,VK_P,0x50,25,25,XK_P,0x0050
+KEY_P,25,ANSI_P,0x23,25,77,77,25,25,19,VK_P,0x50,25,25,XK_p,0x0070
+KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,26,84,84,26,26,47,VK_OEM_4,0xdb,26,26,XK_bracketleft,0x005b
+KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,26,84,84,26,26,47,VK_OEM_4,0xdb,26,26,XK_braceleft,0x007b
+KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,27,91,91,27,27,48,VK_OEM_6,0xdd,27,27,XK_bracketright,0x005d
+KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,27,91,91,27,27,48,VK_OEM_6,0xdd,27,27,XK_braceright,0x007d
+KEY_ENTER,28,Return,0x24,28,90,90,28,28,40,VK_RETURN,0x0d,28,28,XK_Return,0xff0d
+KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_LCONTROL,0xa2,29,29,XK_Control_L,0xffe3
+KEY_LEFTCTRL,29,Control,0x3b,29,20,17,29,29,224,VK_CONTROL,0x11,29,29,XK_Control_L,0xffe3
+KEY_A,30,ANSI_A,0x0,30,28,28,30,30,4,VK_A,0x41,30,30,XK_A,0x0041
+KEY_A,30,ANSI_A,0x0,30,28,28,30,30,4,VK_A,0x41,30,30,XK_a,0x0061
+KEY_S,31,ANSI_S,0x1,31,27,27,31,31,22,VK_S,0x53,31,31,XK_S,0x0053
+KEY_S,31,ANSI_S,0x1,31,27,27,31,31,22,VK_S,0x53,31,31,XK_s,0x0073
+KEY_D,32,ANSI_D,0x2,32,35,35,32,32,7,VK_D,0x44,32,32,XK_D,0x0044
+KEY_D,32,ANSI_D,0x2,32,35,35,32,32,7,VK_D,0x44,32,32,XK_d,0x0064
+KEY_F,33,ANSI_F,0x3,33,43,43,33,33,9,VK_F,0x46,33,33,XK_F,0x0046
+KEY_F,33,ANSI_F,0x3,33,43,43,33,33,9,VK_F,0x46,33,33,XK_f,0x0066
+KEY_G,34,ANSI_G,0x5,34,52,52,34,34,10,VK_G,0x47,34,34,XK_G,0x0047
+KEY_G,34,ANSI_G,0x5,34,52,52,34,34,10,VK_G,0x47,34,34,XK_g,0x0067
+KEY_H,35,ANSI_H,0x4,35,51,51,35,35,11,VK_H,0x48,35,35,XK_H,0x0048
+KEY_H,35,ANSI_H,0x4,35,51,51,35,35,11,VK_H,0x48,35,35,XK_h,0x0068
+KEY_J,36,ANSI_J,0x26,36,59,59,36,36,13,VK_J,0x4a,36,36,XK_J,0x004a
+KEY_J,36,ANSI_J,0x26,36,59,59,36,36,13,VK_J,0x4a,36,36,XK_j,0x006a
+KEY_K,37,ANSI_K,0x28,37,66,66,37,37,14,VK_K,0x4b,37,37,XK_K,0x004b
+KEY_K,37,ANSI_K,0x28,37,66,66,37,37,14,VK_K,0x4b,37,37,XK_k,0x006b
+KEY_L,38,ANSI_L,0x25,38,75,75,38,38,15,VK_L,0x4c,38,38,XK_L,0x004c
+KEY_L,38,ANSI_L,0x25,38,75,75,38,38,15,VK_L,0x4c,38,38,XK_l,0x006c
+KEY_SEMICOLON,39,ANSI_Semicolon,0x29,39,76,76,39,39,51,VK_OEM_1,0xba,39,39,XK_semicolon,0x003b
+KEY_SEMICOLON,39,ANSI_Semicolon,0x29,39,76,76,39,39,51,VK_OEM_1,0xba,39,39,XK_colon,0x003a
+KEY_APOSTROPHE,40,ANSI_Quote,0x27,40,82,82,40,40,52,VK_OEM_7,0xde,40,40,XK_apostrophe,0x0027
+KEY_APOSTROPHE,40,ANSI_Quote,0x27,40,82,82,40,40,52,VK_OEM_7,0xde,40,40,XK_quotedbl,0x0022
+KEY_GRAVE,41,ANSI_Grave,0x32,41,14,14,41,41,53,VK_OEM_3,0xc0,41,41,XK_grave,0x0060
+KEY_GRAVE,41,ANSI_Grave,0x32,41,14,14,41,41,53,VK_OEM_3,0xc0,41,41,XK_asciitilde,0x007e
+KEY_SHIFT,42,Shift,0x38,42,18,18,42,42,225,VK_SHIFT,0x10,42,42,XK_Shift_L,0xffe1
+KEY_LEFTSHIFT,42,Shift,0x38,42,18,18,42,42,225,VK_LSHIFT,0xa0,42,42,XK_Shift_L,0xffe1
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,43,93,93,43,43,50,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,43,93,93,43,43,50,VK_OEM_5,0xdc,43,43,XK_bar,0x007c
+KEY_Z,44,ANSI_Z,0x6,44,26,26,44,44,29,VK_Z,0x5a,44,44,XK_Z,0x005a
+KEY_Z,44,ANSI_Z,0x6,44,26,26,44,44,29,VK_Z,0x5a,44,44,XK_z,0x007a
+KEY_X,45,ANSI_X,0x7,45,34,34,45,45,27,VK_X,0x58,45,45,XK_X,0x0058
+KEY_X,45,ANSI_X,0x7,45,34,34,45,45,27,VK_X,0x58,45,45,XK_x,0x0078
+KEY_C,46,ANSI_C,0x8,46,33,33,46,46,6,VK_C,0x43,46,46,XK_C,0x0043
+KEY_C,46,ANSI_C,0x8,46,33,33,46,46,6,VK_C,0x43,46,46,XK_c,0x0063
+KEY_V,47,ANSI_V,0x9,47,42,42,47,47,25,VK_V,0x56,47,47,XK_V,0x0056
+KEY_V,47,ANSI_V,0x9,47,42,42,47,47,25,VK_V,0x56,47,47,XK_v,0x0076
+KEY_B,48,ANSI_B,0xb,48,50,50,48,48,5,VK_B,0x42,48,48,XK_B,0x0042
+KEY_B,48,ANSI_B,0xb,48,50,50,48,48,5,VK_B,0x42,48,48,XK_b,0x0062
+KEY_N,49,ANSI_N,0x2d,49,49,49,49,49,17,VK_N,0x4e,49,49,XK_N,0x004e
+KEY_N,49,ANSI_N,0x2d,49,49,49,49,49,17,VK_N,0x4e,49,49,XK_n,0x006e
+KEY_M,50,ANSI_M,0x2e,50,58,58,50,50,16,VK_M,0x4d,50,50,XK_M,0x004d
+KEY_M,50,ANSI_M,0x2e,50,58,58,50,50,16,VK_M,0x4d,50,50,XK_m,0x006d
+KEY_COMMA,51,ANSI_Comma,0x2b,51,65,65,51,51,54,VK_OEM_COMMA,0xbc,51,51,XK_comma,0x002c
+KEY_COMMA,51,ANSI_Comma,0x2b,51,65,65,51,51,54,VK_OEM_COMMA,0xbc,51,51,XK_less,0x003c
+KEY_DOT,52,ANSI_Period,0x2f,52,73,73,52,52,55,VK_OEM_PERIOD,0xbe,52,52,XK_period,0x002e
+KEY_DOT,52,ANSI_Period,0x2f,52,73,73,52,52,55,VK_OEM_PERIOD,0xbe,52,52,XK_greater,0x003e
+KEY_SLASH,53,ANSI_Slash,0x2c,53,74,74,53,53,56,VK_OEM_2,0xbf,53,53,XK_slash,0x002f
+KEY_SLASH,53,ANSI_Slash,0x2c,53,74,74,53,53,56,VK_OEM_2,0xbf,53,53,XK_question,0x003f
+KEY_RIGHTSHIFT,54,RightShift,0x3c,54,89,89,54,54,229,VK_RSHIFT,0xa1,54,54,XK_Shift_R,0xffe2
+KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,55,124,126,55,55,85,VK_MULTIPLY,0x6a,55,55,XK_multiply,0x00d7
+KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_LMENU,0xa4,56,56,XK_Alt_L,0xffe9
+KEY_LEFTALT,56,Option,0x3a,56,17,25,56,56,226,VK_MENU,0x12,56,56,XK_Alt_L,0xffe9
+KEY_SPACE,57,Space,0x31,57,41,41,57,57,44,VK_SPACE,0x20,57,57,XK_space,0x0020
+KEY_CAPSLOCK,58,CapsLock,0x39,58,88,20,58,58,57,VK_CAPITAL,0x14,58,58,XK_Caps_Lock,0xffe5
+KEY_F1,59,F1,0x7a,59,5,7,59,59,58,VK_F1,0x70,59,59,XK_F1,0xffbe
+KEY_F2,60,F2,0x78,60,6,15,60,60,59,VK_F2,0x71,60,60,XK_F2,0xffbf
+KEY_F3,61,F3,0x63,61,4,23,61,61,60,VK_F3,0x72,61,61,XK_F3,0xffc0
+KEY_F4,62,F4,0x76,62,12,31,62,62,61,VK_F4,0x73,62,62,XK_F4,0xffc1
+KEY_F5,63,F5,0x60,63,3,39,63,63,62,VK_F5,0x74,63,63,XK_F5,0xffc2
+KEY_F6,64,F6,0x61,64,11,47,64,64,63,VK_F6,0x75,64,64,XK_F6,0xffc3
+KEY_F7,65,F7,0x62,65,259,55,65,65,64,VK_F7,0x76,65,65,XK_F7,0xffc4
+KEY_F8,66,F8,0x64,66,10,63,66,66,65,VK_F8,0x77,66,66,XK_F8,0xffc5
+KEY_F9,67,F9,0x65,67,1,71,67,67,66,VK_F9,0x78,67,67,XK_F9,0xffc6
+KEY_F10,68,F10,0x6d,68,9,79,68,68,67,VK_F10,0x79,68,68,XK_F10,0xffc7
+KEY_NUMLOCK,69,,,69,119,118,69,69,83,VK_NUMLOCK,0x90,69,69,XK_Num_Lock,0xff7f
+KEY_SCROLLLOCK,70,,,70,126,95,70,70,71,VK_SCROLL,0x91,70,70,XK_Scroll_Lock,0xff14
+KEY_KP7,71,ANSI_Keypad7,0x59,71,108,108,71,71,95,VK_NUMPAD7,0x67,71,71,XK_KP_7,0xffb7
+KEY_KP8,72,ANSI_Keypad8,0x5b,72,117,117,72,72,96,VK_NUMPAD8,0x68,72,72,XK_KP_8,0xffb8
+KEY_KP9,73,ANSI_Keypad9,0x5c,73,125,125,73,73,97,VK_NUMPAD9,0x69,73,73,XK_KP_9,0xffb9
+KEY_KPMINUS,74,ANSI_KeypadMinus,0x4e,74,123,132,74,74,86,VK_SUBTRACT,0x6d,74,74,XK_KP_Subtract,0xffad
+KEY_KP4,75,ANSI_Keypad4,0x56,75,107,107,75,75,92,VK_NUMPAD4,0x64,75,75,XK_KP_4,0xffb4
+KEY_KP5,76,ANSI_Keypad5,0x57,76,115,115,76,76,93,VK_NUMPAD5,0x65,76,76,XK_KP_5,0xffb5
+KEY_KP6,77,ANSI_Keypad6,0x58,77,116,116,77,77,94,VK_NUMPAD6,0x66,77,77,XK_KP_6,0xffb6
+KEY_KPPLUS,78,ANSI_KeypadPlus,0x45,78,121,124,78,78,87,VK_ADD,0x6b,78,78,XK_KP_Add,0xffab
+KEY_KP1,79,ANSI_Keypad1,0x53,79,105,105,79,79,89,VK_NUMPAD1,0x61,79,79,XK_KP_1,0xffb1
+KEY_KP2,80,ANSI_Keypad2,0x54,80,114,114,80,80,90,VK_NUMPAD2,0x62,80,80,XK_KP_2,0xffb2
+KEY_KP3,81,ANSI_Keypad3,0x55,81,122,122,81,81,91,VK_NUMPAD3,0x63,81,81,XK_KP_3,0xffb3
+KEY_KP0,82,ANSI_Keypad0,0x52,82,112,112,82,82,98,VK_NUMPAD0,0x60,82,82,XK_KP_0,0xffb0
+KEY_KPDOT,83,ANSI_KeypadDecimal,0x41,83,113,113,83,83,99,VK_DECIMAL,0x6e,83,83,XK_KP_Decimal,0xffae
+,84,,,,,,,84,,,,,,,
+KEY_ZENKAKUHANKAKU,85,,,118,95,,,118,148,,,,,,
+KEY_102ND,86,,,86,97,19,,86,100,VK_OEM_102,0xe1,86,86,,
+KEY_F11,87,F11,0x67,87,120,86,101,87,68,VK_F11,0x7a,87,87,XK_F11,0xffc8
+KEY_F12,88,F12,0x6f,88,7,94,102,88,69,VK_F12,0x7b,88,88,XK_F12,0xffc9
+KEY_RO,89,,,115,81,,,115,135,,,,,,
+KEY_KATAKANA,90,JIS_Kana????,0x68,120,99,,,120,146,VK_KANA,0x15,,,,
+KEY_HIRAGANA,91,,,119,98,,,119,147,,,,,,
+KEY_HENKAN,92,,,121,100,134,,121,138,,,,,,
+KEY_KATAKANAHIRAGANA,93,,,112,19,135,,112,136,,,0xc8,0xc8,,
+KEY_MUHENKAN,94,,,123,103,133,,123,139,,,,,,
+KEY_KPJPCOMMA,95,JIS_KeypadComma,0x5f,92,39,,,92,140,,,,,XK_KP_Separator,0xffac
+KEY_KPENTER,96,ANSI_KeypadEnter,0x4c,,158,121,,284,88,,,0x64,0x64,XK_KP_Enter,0xff8d
+KEY_RIGHTCTRL,97,RightControl,0x3e,,,88,,285,228,VK_RCONTROL,0xa3,0x65,0x65,XK_Control_R,0xffe4
+KEY_KPSLASH,98,ANSI_KeypadDivide,0x4b,,181,119,,309,84,VK_DIVIDE,0x6f,0x68,0x68,XK_KP_Divide,0xffaf
+KEY_SYSRQ,99,,,84,260,87,,84,70,"VK_SNAPSHOT ???",0x2c,0x67,0x67,XK_Sys_Req,0xff15
+KEY_RIGHTALT,100,RightOption,0x3d,,,57,,312,230,VK_RMENU,0xa5,0x69,0x69,XK_Alt_R,0xffea
+KEY_LINEFEED,101,,,,,,,91,,,,,,,
+KEY_HOME,102,Home,0x73,,224,110,,327,74,VK_HOME,0x24,0x59,0x59,XK_Home,0xff50
+KEY_UP,103,UpArrow,0x7e,,236,99,109,328,82,VK_UP,0x26,0x5a,0x5a,XK_Up,0xff52
+KEY_PAGEUP,104,PageUp,0x74,,201,111,,329,75,VK_PRIOR,0x21,0x5b,0x5b,XK_Page_Up,0xff55
+KEY_LEFT,105,LeftArrow,0x7b,,203,97,111,331,80,VK_LEFT,0x25,0x5c,0x5c,XK_Left,0xff51
+KEY_RIGHT,106,RightArrow,0x7c,,205,106,112,333,79,VK_RIGHT,0x27,0x5e,0x5e,XK_Right,0xff53
+KEY_END,107,End,0x77,,225,101,,335,77,VK_END,0x23,0x5f,0x5f,XK_End,0xff57
+KEY_DOWN,108,DownArrow,0x7d,,254,96,110,336,81,VK_DOWN,0x28,0x60,0x60,XK_Down,0xff54
+KEY_PAGEDOWN,109,PageDown,0x79,,243,109,,337,78,VK_NEXT,0x22,0x61,0x61,XK_Page_Down,0xff56
+KEY_INSERT,110,,,,210,103,107,338,73,VK_INSERT,0x2d,0x62,0x62,XK_Insert,0xff63
+KEY_DELETE,111,ForwardDelete,0x75,,244,100,108,339,76,VK_DELETE,0x2e,0x63,0x63,XK_Delete,0xffff
+KEY_MACRO,112,,,,239,142,,367,,,,,,,
+KEY_MUTE,113,Mute,0x4a,,251,156,,288,239,VK_VOLUME_MUTE,0xad,,,,
+KEY_VOLUMEDOWN,114,VolumeDown,0x49,,,157,,302,238,VK_VOLUME_DOWN,0xae,,,,
+KEY_VOLUMEUP,115,VolumeUp,0x48,,233,149,,304,237,VK_VOLUME_UP,0xaf,,,,
+KEY_POWER,116,,,,,,,350,102,,,,,,
+KEY_KPEQUAL,117,ANSI_KeypadEquals,0x51,89,15,,,89,103,,,0x76,0x76,XK_KP_Equal,0xffbd
+KEY_KPPLUSMINUS,118,,,,206,,,334,,,,,,,
+KEY_PAUSE,119,,,,198,98,,326,72,VK_PAUSE,0x013,0x66,0x66,XK_Pause,0xff13
+KEY_SCALE,120,,,,,,,267,,,,,,,
+KEY_KPCOMMA,121,ANSI_KeypadClear????,0x47,126,109,,,126,133,VK_SEPARATOR??,0x6c,,,,
+KEY_HANGEUL,122,,,,,,,,144,VK_HANGEUL,0x15,,,,
+KEY_HANJA,123,,,,,,,269,145,VK_HANJA,0x19,,,,
+KEY_YEN,124,JIS_Yen,0x5d,125,106,,,125,137,,,0x7d,0x7d,,
+KEY_LEFTMETA,125,Command,0x37,,,139,,347,227,VK_LWIN,0x5b,0x6b,0x6b,XK_Meta_L,0xffe7
+KEY_RIGHTMETA,126,,,,,140,,348,231,VK_RWIN,0x5c,0x6c,0x6c,XK_Meta_R,0xffe8
+KEY_COMPOSE,127,Function,0x3f,,,141,,349,101,VK_APPS,0x5d,0x6d,0x6d,,
+KEY_STOP,128,,,,,10,,360,243,VK_BROWSER_STOP,0xa9,,,,
+KEY_AGAIN,129,,,,,11,,261,121,,,,,,
+KEY_PROPS,130,,,,,12,,262,118,,,,,,
+KEY_UNDO,131,,,,,16,,263,122,,,,,,
+KEY_FRONT,132,,,,,,,268,119,,,,,,
+KEY_COPY,133,,,,,24,,376,124,,,,,,
+KEY_OPEN,134,,,,,32,,100,116,,,,,,
+KEY_PASTE,135,,,,,40,,101,125,,,,,,
+KEY_FIND,136,,,,,48,,321,244,,,,,,
+KEY_CUT,137,,,,,56,,316,123,,,,,,
+KEY_HELP,138,,,,,9,,373,117,VK_HELP,0x2f,,,XK_Help,0xff6a
+KEY_MENU,139,,,,,145,,286,,,,,,,
+KEY_CALC,140,,,,174,163,,289,251,,,,,,
+KEY_SETUP,141,,,,,,,102,,,,,,,
+KEY_SLEEP,142,,,,,,,351,248,VK_SLEEP,0x5f,,,,
+KEY_WAKEUP,143,,,,,,,355,,,,,,,
+KEY_FILE,144,,,,,,,103,,,,,,,
+KEY_SENDFILE,145,,,,,,,104,,,,,,,
+KEY_DELETEFILE,146,,,,,,,105,,,,,,,
+KEY_XFER,147,,,,,162,,275,,,,,,,
+KEY_PROG1,148,,,,,160,,287,,,,,,,
+KEY_PROG2,149,,,,,161,,279,,,,,,,
+KEY_WWW,150,,,,,,,258,240,,,,,,
+KEY_MSDOS,151,,,,,,,106,,,,,,,
+KEY_SCREENLOCK,152,,,,,150,,274,249,,,,,,
+KEY_DIRECTION,153,,,,,,,107,,,,,,,
+KEY_CYCLEWINDOWS,154,,,,,155,,294,,,,,,,
+KEY_MAIL,155,,,,,,,364,,,,,,,
+KEY_BOOKMARKS,156,,,,,,,358,,,,,,,
+KEY_COMPUTER,157,,,,,,,363,,,,,,,
+KEY_BACK,158,,,,,,,362,241,VK_BROWSER_BACK,0xa6,,,,
+KEY_FORWARD,159,,,,,,,361,242,VK_BROWSER_FORWARD,0xa7,,,,
+KEY_CLOSECD,160,,,,,154,,291,,,,,,,
+KEY_EJECTCD,161,,,,,,,108,236,,,,,,
+KEY_EJECTCLOSECD,162,,,,,,,381,,,,,,,
+KEY_NEXTSONG,163,,,,241,147,,281,235,VK_MEDIA_NEXT_TRACK,0xb0,,,,
+KEY_PLAYPAUSE,164,,,,173,,,290,232,VK_MEDIA_PLAY_PAUSE,0xb3,,,,
+KEY_PREVIOUSSONG,165,,,,250,148,,272,234,VK_MEDIA_PREV_TRACK,0xb1,,,,
+KEY_STOPCD,166,,,,164,152,,292,233,VK_MEDIA_STOP,0xb2,,,,
+KEY_RECORD,167,,,,,158,,305,,,,,,,
+KEY_REWIND,168,,,,,159,,280,,,,,,,
+KEY_PHONE,169,,,,,,,99,,,,,,,
+KEY_ISO,170,ISO_Section,0xa,,,,,112,,,,,,,
+KEY_CONFIG,171,,,,,,,257,,,,,,,
+KEY_HOMEPAGE,172,,,,178,151,,306,,VK_BROWSER_HOME,0xac,,,,
+KEY_REFRESH,173,,,,,,,359,250,VK_BROWSER_REFRESH,0xa8,,,,
+KEY_EXIT,174,,,,,,,113,,,,,,,
+KEY_MOVE,175,,,,,,,114,,,,,,,
+KEY_EDIT,176,,,,,,,264,247,,,,,,
+KEY_SCROLLUP,177,,,,,,,117,245,,,,,,
+KEY_SCROLLDOWN,178,,,,,,,271,246,,,,,,
+KEY_KPLEFTPAREN,179,,,,,,,374,182,,,,,,
+KEY_KPRIGHTPAREN,180,,,,,,,379,183,,,,,,
+KEY_NEW,181,,,,,,,265,,,,,,,
+KEY_REDO,182,,,,,,,266,,,,,,,
+KEY_F13,183,F13,0x69,93,47,127,,93,104,VK_F13,0x7c,0x6e,0x6e,,
+KEY_F14,184,F14,0x6b,94,55,128,,94,105,VK_F14,0x7d,0x6f,0x6f,,
+KEY_F15,185,F15,0x71,95,63,129,,95,106,VK_F15,0x7e,0x70,0x70,,
+KEY_F16,186,F16,0x6a,,,130,,85,107,VK_F16,0x7f,0x71,0x71,,
+KEY_F17,187,F17,0x40,,,131,,259,108,VK_F17,0x80,0x72,0x72,,
+KEY_F18,188,F18,0x4f,,,,,375,109,VK_F18,0x81,,,,
+KEY_F19,189,F19,0x50,,,,,260,110,VK_F19,0x82,,,,
+KEY_F20,190,F20,0x5a,,,,,90,111,VK_F20,0x83,,,,
+KEY_F21,191,,,,,,,116,112,VK_F21,0x84,,,,
+KEY_F22,192,,,,,,,377,113,VK_F22,0x85,,,,
+KEY_F23,193,,,,,,,109,114,VK_F23,0x86,,,,
+KEY_F24,194,,,,,,,111,115,VK_F24,0x87,,,,
+,195,,,,,,,277,,,,,,,
+,196,,,,,,,278,,,,,,,
+,197,,,,,,,282,,,,,,,
+,198,,,,,,,283,,,,,,,
+,199,,,,,,,295,,,,,,,
+KEY_PLAYCD,200,,,,,,,296,,,,,,,
+KEY_PAUSECD,201,,,,,,,297,,,,,,,
+KEY_PROG3,202,,,,,,,299,,,,,,,
+KEY_PROG4,203,,,,,,,300,,,,,,,
+KEY_DASHBOARD,204,,,,,,,301,,,,,,,
+KEY_SUSPEND,205,,,,,,,293,,,,,,,
+KEY_CLOSE,206,,,,,,,303,,,,,,,
+KEY_PLAY,207,,,,,,,307,,VK_PLAY,0xfa,,,,
+KEY_FASTFORWARD,208,,,,,,,308,,,,,,,
+KEY_BASSBOOST,209,,,,,,,310,,,,,,,
+KEY_PRINT,210,,,,,,,313,,VK_PRINT,0x2a,,,,
+KEY_HP,211,,,,,,,314,,,,,,,
+KEY_CAMERA,212,,,,,,,315,,,,,,,
+KEY_SOUND,213,,,,,,,317,,,,,,,
+KEY_QUESTION,214,,,,,,,318,,,,,,,
+KEY_EMAIL,215,,,,,,,319,,VK_LAUNCH_MAIL,0xb4,,,,
+KEY_CHAT,216,,,,,,,320,,,,,,,
+KEY_SEARCH,217,,,,,,,357,,VK_BROWSER_SEARCH,0xaa,,,,
+KEY_CONNECT,218,,,,,,,322,,,,,,,
+KEY_FINANCE,219,,,,,,,323,,,,,,,
+KEY_SPORT,220,,,,,,,324,,,,,,,
+KEY_SHOP,221,,,,,,,325,,,,,,,
+KEY_ALTERASE,222,,,,,,,276,,,,,,,
+KEY_CANCEL,223,,,,,,,330,,,,,,,
+KEY_BRIGHTNESSDOWN,224,,,,,,,332,,,,,,,
+KEY_BRIGHTNESSUP,225,,,,,,,340,,,,,,,
+KEY_MEDIA,226,,,,,,,365,,,,,,,
+KEY_SWITCHVIDEOMODE,227,,,,,,,342,,,,,,,
+KEY_KBDILLUMTOGGLE,228,,,,,,,343,,,,,,,
+KEY_KBDILLUMDOWN,229,,,,,,,344,,,,,,,
+KEY_KBDILLUMUP,230,,,,,,,345,,,,,,,
+KEY_SEND,231,,,,,,,346,,,,,,,
+KEY_REPLY,232,,,,,,,356,,,,,,,
+KEY_FORWARDMAIL,233,,,,,,,270,,,,,,,
+KEY_SAVE,234,,,,,,,341,,,,,,,
+KEY_DOCUMENTS,235,,,,,,,368,,,,,,,
+KEY_BATTERY,236,,,,,,,369,,,,,,,
+KEY_BLUETOOTH,237,,,,,,,370,,,,,,,
+KEY_WLAN,238,,,,,,,371,,,,,,,
+KEY_UWB,239,,,,,,,372,,,,,,,
+KEY_UNKNOWN,240,,,,,,,,,,,,,,
+KEY_VIDEO_NEXT,241,,,,,,,,,,,,,,
+KEY_VIDEO_PREV,242,,,,,,,,,,,,,,
+KEY_BRIGHTNESS_CYCLE,243,,,,,,,,,,,,,,
+KEY_BRIGHTNESS_ZERO,244,,,,,,,,,,,,,,
+KEY_DISPLAY_OFF,245,,,,,,,,,,,,,,
+KEY_WIMAX,246,,,,,,,,,,,,,,
+,247,,,,,,,,,,,,,,
+,248,,,,,,,,,,,,,,
+,249,,,,,,,,,,,,,,
+,250,,,,,,,,,,,,,,
+,251,,,,,,,,,,,,,,
+,252,,,,,,,,,,,,,,
+,253,,,,,,,,,,,,,,
+,254,,,,,,,,,,,,,,
+,255,,,,182,,,,,,,,,,
+BTN_MISC,0x100,,,,,,,,,,,,,,
+BTN_0,0x100,,,,,,,,,VK_LBUTTON,0x01,,,,
+BTN_1,0x101,,,,,,,,,VK_RBUTTON,0x02,,,,
+BTN_2,0x102,,,,,,,,,VK_MBUTTON,0x04,,,,
+BTN_3,0x103,,,,,,,,,VK_XBUTTON1,0x05,,,,
+BTN_4,0x104,,,,,,,,,VK_XBUTTON2,0x06,,,,
+BTN_5,0x105,,,,,,,,,,,,,,
+BTN_6,0x106,,,,,,,,,,,,,,
+BTN_7,0x107,,,,,,,,,,,,,,
+BTN_8,0x108,,,,,,,,,,,,,,
+BTN_9,0x109,,,,,,,,,,,,,,
+BTN_MOUSE,0x110,,,,,,,,,,,,,,
+BTN_LEFT,0x110,,,,,,,,,,,,,,
+BTN_RIGHT,0x111,,,,,,,,,,,,,,
+BTN_MIDDLE,0x112,,,,,,,,,,,,,,
+BTN_SIDE,0x113,,,,,,,,,,,,,,
+BTN_EXTRA,0x114,,,,,,,,,,,,,,
+BTN_FORWARD,0x115,,,,,,,,,,,,,,
+BTN_BACK,0x116,,,,,,,,,,,,,,
+BTN_TASK,0x117,,,,,,,,,,,,,,
+BTN_JOYSTICK,0x120,,,,,,,,,,,,,,
+BTN_TRIGGER,0x120,,,,,,,,,,,,,,
+BTN_THUMB,0x121,,,,,,,,,,,,,,
+BTN_THUMB2,0x122,,,,,,,,,,,,,,
+BTN_TOP,0x123,,,,,,,,,,,,,,
+BTN_TOP2,0x124,,,,,,,,,,,,,,
+BTN_PINKIE,0x125,,,,,,,,,,,,,,
+BTN_BASE,0x126,,,,,,,,,,,,,,
+BTN_BASE2,0x127,,,,,,,,,,,,,,
+BTN_BASE3,0x128,,,,,,,,,,,,,,
+BTN_BASE4,0x129,,,,,,,,,,,,,,
+BTN_BASE5,0x12a,,,,,,,,,,,,,,
+BTN_BASE6,0x12b,,,,,,,,,,,,,,
+BTN_DEAD,0x12f,,,,,,,,,,,,,,
+BTN_GAMEPAD,0x130,,,,,,,,,,,,,,
+BTN_A,0x130,,,,,,,,,,,,,,
+BTN_B,0x131,,,,,,,,,,,,,,
+BTN_C,0x132,,,,,,,,,,,,,,
+BTN_X,0x133,,,,,,,,,,,,,,
+BTN_Y,0x134,,,,,,,,,,,,,,
+BTN_Z,0x135,,,,,,,,,,,,,,
+BTN_TL,0x136,,,,,,,,,,,,,,
+BTN_TR,0x137,,,,,,,,,,,,,,
+BTN_TL2,0x138,,,,,,,,,,,,,,
+BTN_TR2,0x139,,,,,,,,,,,,,,
+BTN_SELECT,0x13a,,,,,,,,,,,,,,
+BTN_START,0x13b,,,,,,,,,,,,,,
+BTN_MODE,0x13c,,,,,,,,,,,,,,
+BTN_THUMBL,0x13d,,,,,,,,,,,,,,
+BTN_THUMBR,0x13e,,,,,,,,,,,,,,
+BTN_DIGI,0x140,,,,,,,,,,,,,,
+BTN_TOOL_PEN,0x140,,,,,,,,,,,,,,
+BTN_TOOL_RUBBER,0x141,,,,,,,,,,,,,,
+BTN_TOOL_BRUSH,0x142,,,,,,,,,,,,,,
+BTN_TOOL_PENCIL,0x143,,,,,,,,,,,,,,
+BTN_TOOL_AIRBRUSH,0x144,,,,,,,,,,,,,,
+BTN_TOOL_FINGER,0x145,,,,,,,,,,,,,,
+BTN_TOOL_MOUSE,0x146,,,,,,,,,,,,,,
+BTN_TOOL_LENS,0x147,,,,,,,,,,,,,,
+BTN_TOUCH,0x14a,,,,,,,,,,,,,,
+BTN_STYLUS,0x14b,,,,,,,,,,,,,,
+BTN_STYLUS2,0x14c,,,,,,,,,,,,,,
+BTN_TOOL_DOUBLETAP,0x14d,,,,,,,,,,,,,,
+BTN_TOOL_TRIPLETAP,0x14e,,,,,,,,,,,,,,
+BTN_TOOL_QUADTAP,0x14f,,,,,,,,,,,,,,
+BTN_WHEEL,0x150,,,,,,,,,,,,,,
+BTN_GEAR_DOWN,0x150,,,,,,,,,,,,,,
+BTN_GEAR_UP,0x151,,,,,,,,,,,,,,
+KEY_OK,0x160,,,,,,,,,,,,,,
+KEY_SELECT,0x161,,,,,,,,,VK_SELECT,0x29,,,XK_Select,0xff60
+KEY_GOTO,0x162,,,,,,,,,,,,,,
+KEY_CLEAR,0x163,,,,,,,,,,,,,,
+KEY_POWER2,0x164,,,,,,,,,,,,,,
+KEY_OPTION,0x165,,,,,,,,,,,,,,
+KEY_INFO,0x166,,,,,,,,,,,,,,
+KEY_TIME,0x167,,,,,,,,,,,,,,
+KEY_VENDOR,0x168,,,,,,,,,,,,,,
+KEY_ARCHIVE,0x169,,,,,,,,,,,,,,
+KEY_PROGRAM,0x16a,,,,,,,,,,,,,,
+KEY_CHANNEL,0x16b,,,,,,,,,,,,,,
+KEY_FAVORITES,0x16c,,,,,,,,,VK_BROWSER_FAVOURITES,0xab,,,,
+KEY_EPG,0x16d,,,,,,,,,,,,,,
+KEY_PVR,0x16e,,,,,,,,,,,,,,
+KEY_MHP,0x16f,,,,,,,,,,,,,,
+KEY_LANGUAGE,0x170,,,,,,,,,,,,,,
+KEY_TITLE,0x171,,,,,,,,,,,,,,
+KEY_SUBTITLE,0x172,,,,,,,,,,,,,,
+KEY_ANGLE,0x173,,,,,,,,,,,,,,
+KEY_ZOOM,0x174,,,,,,,,,VK_ZOOM,0xfb,,,,
+KEY_MODE,0x175,,,,,,,,,,,,,,
+KEY_KEYBOARD,0x176,,,,,,,,,,,,,,
+KEY_SCREEN,0x177,,,,,,,,,,,,,,
+KEY_PC,0x178,,,,,,,,,,,,,,
+KEY_TV,0x179,,,,,,,,,,,,,,
+KEY_TV2,0x17a,,,,,,,,,,,,,,
+KEY_VCR,0x17b,,,,,,,,,,,,,,
+KEY_VCR2,0x17c,,,,,,,,,,,,,,
+KEY_SAT,0x17d,,,,,,,,,,,,,,
+KEY_SAT2,0x17e,,,,,,,,,,,,,,
+KEY_CD,0x17f,,,,,,,,,,,,,,
+KEY_TAPE,0x180,,,,,,,,,,,,,,
+KEY_RADIO,0x181,,,,,,,,,,,,,,
+KEY_TUNER,0x182,,,,,,,,,,,,,,
+KEY_PLAYER,0x183,,,,,,,,,,,,,,
+KEY_TEXT,0x184,,,,,,,,,,,,,,
+KEY_DVD,0x185,,,,,,,,,,,,,,
+KEY_AUX,0x186,,,,,,,,,,,,,,
+KEY_MP3,0x187,,,,,,,,,,,,,,
+KEY_AUDIO,0x188,,,,,,,,,,,,,,
+KEY_VIDEO,0x189,,,,,,,,,,,,,,
+KEY_DIRECTORY,0x18a,,,,,,,,,,,,,,
+KEY_LIST,0x18b,,,,,,,,,,,,,,
+KEY_MEMO,0x18c,,,,,,,,,,,,,,
+KEY_CALENDAR,0x18d,,,,,,,,,,,,,,
+KEY_RED,0x18e,,,,,,,,,,,,,,
+KEY_GREEN,0x18f,,,,,,,,,,,,,,
+KEY_YELLOW,0x190,,,,,,,,,,,,,,
+KEY_BLUE,0x191,,,,,,,,,,,,,,
+KEY_CHANNELUP,0x192,,,,,,,,,,,,,,
+KEY_CHANNELDOWN,0x193,,,,,,,,,,,,,,
+KEY_FIRST,0x194,,,,,,,,,,,,,,
+KEY_LAST,0x195,,,,,,,,,,,,,,
+KEY_AB,0x196,,,,,,,,,,,,,,
+KEY_NEXT,0x197,,,,,,,,,,,,,,
+KEY_RESTART,0x198,,,,,,,,,,,,,,
+KEY_SLOW,0x199,,,,,,,,,,,,,,
+KEY_SHUFFLE,0x19a,,,,,,,,,,,,,,
+KEY_BREAK,0x19b,,,,,,,,,,,,,,
+KEY_PREVIOUS,0x19c,,,,,,,,,,,,,,
+KEY_DIGITS,0x19d,,,,,,,,,,,,,,
+KEY_TEEN,0x19e,,,,,,,,,,,,,,
+KEY_TWEN,0x19f,,,,,,,,,,,,,,
+KEY_VIDEOPHONE,0x1a0,,,,,,,,,,,,,,
+KEY_GAMES,0x1a1,,,,,,,,,,,,,,
+KEY_ZOOMIN,0x1a2,,,,,,,,,,,,,,
+KEY_ZOOMOUT,0x1a3,,,,,,,,,,,,,,
+KEY_ZOOMRESET,0x1a4,,,,,,,,,,,,,,
+KEY_WORDPROCESSOR,0x1a5,,,,,,,,,,,,,,
+KEY_EDITOR,0x1a6,,,,,,,,,,,,,,
+KEY_SPREADSHEET,0x1a7,,,,,,,,,,,,,,
+KEY_GRAPHICSEDITOR,0x1a8,,,,,,,,,,,,,,
+KEY_PRESENTATION,0x1a9,,,,,,,,,,,,,,
+KEY_DATABASE,0x1aa,,,,,,,,,,,,,,
+KEY_NEWS,0x1ab,,,,,,,,,,,,,,
+KEY_VOICEMAIL,0x1ac,,,,,,,,,,,,,,
+KEY_ADDRESSBOOK,0x1ad,,,,,,,,,,,,,,
+KEY_MESSENGER,0x1ae,,,,,,,,,,,,,,
+KEY_DISPLAYTOGGLE,0x1af,,,,,,,,,,,,,,
+KEY_SPELLCHECK,0x1b0,,,,,,,,,,,,,,
+KEY_LOGOFF,0x1b1,,,,,,,,,,,,,,
+KEY_DOLLAR,0x1b2,,,,,,,,,,,,,,
+KEY_EURO,0x1b3,,,,,,,,,,,,,,
+KEY_FRAMEBACK,0x1b4,,,,,,,,,,,,,,
+KEY_FRAMEFORWARD,0x1b5,,,,,,,,,,,,,,
+KEY_CONTEXT_MENU,0x1b6,,,,,,,,,,,,,,
+KEY_MEDIA_REPEAT,0x1b7,,,,,,,,,,,,,,
+KEY_DEL_EOL,0x1c0,,,,,,,,,,,,,,
+KEY_DEL_EOS,0x1c1,,,,,,,,,,,,,,
+KEY_INS_LINE,0x1c2,,,,,,,,,,,,,,
+KEY_DEL_LINE,0x1c3,,,,,,,,,,,,,,
+KEY_FN,0x1d0,,,,,,,,,,,,,,
+KEY_FN_ESC,0x1d1,,,,,,,,,,,,,,
+KEY_FN_F1,0x1d2,,,,,,,,,,,,,,
+KEY_FN_F2,0x1d3,,,,,,,,,,,,,,
+KEY_FN_F3,0x1d4,,,,,,,,,,,,,,
+KEY_FN_F4,0x1d5,,,,,,,,,,,,,,
+KEY_FN_F5,0x1d6,,,,,,,,,,,,,,
+KEY_FN_F6,0x1d7,,,,,,,,,,,,,,
+KEY_FN_F7,0x1d8,,,,,,,,,,,,,,
+KEY_FN_F8,0x1d9,,,,,,,,,,,,,,
+KEY_FN_F9,0x1da,,,,,,,,,,,,,,
+KEY_FN_F10,0x1db,,,,,,,,,,,,,,
+KEY_FN_F11,0x1dc,,,,,,,,,,,,,,
+KEY_FN_F12,0x1dd,,,,,,,,,,,,,,
+KEY_FN_1,0x1de,,,,,,,,,,,,,,
+KEY_FN_2,0x1df,,,,,,,,,,,,,,
+KEY_FN_D,0x1e0,,,,,,,,,,,,,,
+KEY_FN_E,0x1e1,,,,,,,,,,,,,,
+KEY_FN_F,0x1e2,,,,,,,,,,,,,,
+KEY_FN_S,0x1e3,,,,,,,,,,,,,,
+KEY_FN_B,0x1e4,,,,,,,,,,,,,,
+KEY_BRL_DOT1,0x1f1,,,,,,,,,,,,,,
+KEY_BRL_DOT2,0x1f2,,,,,,,,,,,,,,
+KEY_BRL_DOT3,0x1f3,,,,,,,,,,,,,,
+KEY_BRL_DOT4,0x1f4,,,,,,,,,,,,,,
+KEY_BRL_DOT5,0x1f5,,,,,,,,,,,,,,
+KEY_BRL_DOT6,0x1f6,,,,,,,,,,,,,,
+KEY_BRL_DOT7,0x1f7,,,,,,,,,,,,,,
+KEY_BRL_DOT8,0x1f8,,,,,,,,,,,,,,
+KEY_BRL_DOT9,0x1f9,,,,,,,,,,,,,,
+KEY_BRL_DOT10,0x1fa,,,,,,,,,,,,,,
+KEY_NUMERIC_0,0x200,,,,,,,,,,,,,,
+KEY_NUMERIC_1,0x201,,,,,,,,,,,,,,
+KEY_NUMERIC_2,0x202,,,,,,,,,,,,,,
+KEY_NUMERIC_3,0x203,,,,,,,,,,,,,,
+KEY_NUMERIC_4,0x204,,,,,,,,,,,,,,
+KEY_NUMERIC_5,0x205,,,,,,,,,,,,,,
+KEY_NUMERIC_6,0x206,,,,,,,,,,,,,,
+KEY_NUMERIC_7,0x207,,,,,,,,,,,,,,
+KEY_NUMERIC_8,0x208,,,,,,,,,,,,,,
+KEY_NUMERIC_9,0x209,,,,,,,,,,,,,,
+KEY_NUMERIC_STAR,0x20a,,,,,,,,,,,,,,
+KEY_NUMERIC_POUND,0x20b,,,,,,,,,,,,,,
+KEY_RFKILL,0x20c,,,,,,,,,,,,,,
diff --git a/tools/keymap-gen b/tools/keymap-gen
new file mode 100755
index 0000000..36e20a2
--- /dev/null
+++ b/tools/keymap-gen
@@ -0,0 +1,559 @@
+#!/usr/bin/python
+# -*- python -*-
+#
+# Keycode Map Generator
+#
+# Copyright (C) 2009-2017 Red Hat, Inc.
+#
+# This file is dual license under the terms of the GPLv2 or later
+# and 3-clause BSD licenses.
+#
+
+import csv
+import argparse
+import hashlib
+import time
+
+class Database:
+
+    # Linux: linux/input.h
+    MAP_LINUX = "linux"
+
+    # OS-X: Carbon/HIToolbox/Events.h
+    MAP_OSX = "osx"
+
+    # AT Set 1: linux/drivers/input/keyboard/atkbd.c
+    #           (atkbd_set2_keycode + atkbd_unxlate_table)
+    MAP_ATSET1 = "atset1"
+
+    # AT Set 2: linux/drivers/input/keyboard/atkbd.c
+    #           (atkbd_set2_keycode)
+    MAP_ATSET2 = "atset2"
+
+    # AT Set 3: linux/drivers/input/keyboard/atkbd.c
+    #           (atkbd_set3_keycode)
+    MAP_ATSET3 = "atset3"
+
+    # XT: linux/drivers/input/keyboard/xt.c (xtkbd_keycode)
+    MAP_XT = "xt"
+
+    # Linux RAW: linux/drivers/char/keyboard.c (x86_keycodes)
+    MAP_XTKBD = "xtkbd"
+
+    # USB HID: linux/drivers/hid/usbhid/usbkbd.c (usb_kbd_keycode)
+    MAP_USB = "usb"
+
+    # Win32: mingw32/winuser.h
+    MAP_WIN32 = "win32"
+
+    # XWin XT: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h}
+    #          (xt + manually transcribed)
+    MAP_XWINXT = "xwinxt"
+
+    # XKBD XT: xf86-input-keyboard/src/at_scancode.c
+    #          (xt + manually transcribed)
+    MAP_XKBDXT = "xkbdxt"
+
+    # Xorg with evdev: linux + an offset
+    MAP_XORGEVDEV = "xorgevdev"
+
+    # Xorg with kbd: xkbdxt + an offset
+    MAP_XORGKBD = "xorgkbd"
+
+    # Xorg with OS-X: osx + an offset
+    MAP_XORGXQUARTZ = "xorgxquartz"
+
+    # Xorg + Cygwin: xwinxt + an offset
+    MAP_XORGXWIN = "xorgxwin"
+
+    # XT over RFB: xtkbd + special re-encoding of high bit
+    MAP_RFB = "rfb"
+
+    MAP_LIST = (
+        MAP_LINUX,
+        MAP_OSX,
+        MAP_ATSET1,
+        MAP_ATSET2,
+        MAP_ATSET3,
+        MAP_XT,
+        MAP_XTKBD,
+        MAP_USB,
+        MAP_WIN32,
+        MAP_XWINXT,
+        MAP_XKBDXT,
+
+        # These are derived from maps above
+        MAP_XORGEVDEV,
+        MAP_XORGKBD,
+        MAP_XORGXQUARTZ,
+        MAP_XORGXWIN,
+        MAP_RFB,
+    )
+
+    CODE_COLUMNS = {
+        MAP_LINUX: 1,
+        MAP_OSX: 3,
+        MAP_ATSET1: 4,
+        MAP_ATSET2: 5,
+        MAP_ATSET3: 6,
+        MAP_XT: 7,
+        MAP_XTKBD: 8,
+        MAP_USB: 9,
+        MAP_WIN32: 11,
+        MAP_XWINXT: 12,
+        MAP_XKBDXT: 13,
+    }
+
+    NAME_COLUMNS = {
+        MAP_LINUX: 0,
+        MAP_OSX: 2,
+        MAP_WIN32: 10,
+    }
+
+    def __init__(self):
+
+        self.mapto = {}
+        self.mapfrom = {}
+        self.mapname = {}
+        self.mapchecksum = None
+
+        for name in self.MAP_LIST:
+            # Key is a MAP_LINUX, value is a MAP_XXX
+            self.mapto[name] = {}
+            # key is a MAP_XXX, value is a MAP_LINUX
+            self.mapfrom[name] = {}
+
+        for name in self.NAME_COLUMNS.keys():
+            # key is a MAP_LINUX, value is a string
+            self.mapname[name] = {}
+
+    def _generate_checksum(self, filename):
+        hash = hashlib.sha256()
+        with open(filename, "rb") as f:
+            for chunk in iter(lambda: f.read(4096), b""):
+                hash.update(chunk)
+        self.mapchecksum = hash.hexdigest()
+
+    def load(self, filename):
+        self._generate_checksum(filename)
+
+        with open(filename, 'rb') as f:
+            reader = csv.reader(f)
+
+            # Discard column headings
+            reader.next()
+
+            for row in reader:
+                # We special case MAP_LINUX since that is out
+                # master via which all other mappings are done
+                linux = self.load_linux(row)
+
+                # Now load all the remaining master data values
+                self.load_data(row, linux)
+
+                # Then load all the keycode names
+                self.load_names(row, linux)
+
+                # Finally calculate derived key maps
+                self.derive_data(row, linux)
+
+    def load_linux(self, row):
+        col = self.CODE_COLUMNS[self.MAP_LINUX]
+        linux = row[col]
+
+        if linux.startswith("0x"):
+            linux = int(linux, 16)
+        else:
+            linux = int(linux, 10)
+
+        self.mapto[self.MAP_LINUX][linux] = linux
+        self.mapfrom[self.MAP_LINUX][linux] = linux
+
+        return linux
+
+
+    def load_data(self, row, linux):
+        for mapname in self.CODE_COLUMNS:
+            if mapname == self.MAP_LINUX:
+                continue
+
+            col = self.CODE_COLUMNS[mapname]
+            val = row[col]
+
+            if val == "":
+                continue
+
+            if val.startswith("0x"):
+                val = int(val, 16)
+            else:
+                val = int(val, 10)
+
+            self.mapto[mapname][linux] = val
+            self.mapfrom[mapname][val] = linux
+
+    def load_names(self, row, linux):
+        for mapname in self.NAME_COLUMNS:
+            col = self.NAME_COLUMNS[mapname]
+            val = row[col]
+
+            if val == "":
+                continue
+
+            self.mapname[mapname][linux] = val
+
+
+    def derive_data(self, row, linux):
+        # Xorg KBD is XKBD XT offset by 8
+        if linux in self.mapto[self.MAP_XKBDXT]:
+            xorgkbd = self.mapto[self.MAP_XKBDXT][linux] + 8
+            self.mapto[self.MAP_XORGXQUARTZ][linux] = xorgkbd
+            self.mapfrom[self.MAP_XORGXQUARTZ][xorgkbd] = linux
+
+        # Xorg evdev is Linux offset by 8
+        self.mapto[self.MAP_XORGEVDEV][linux] = linux + 8
+        self.mapfrom[self.MAP_XORGEVDEV][linux + 8] = linux
+
+        # Xorg XQuartx is OS-X offset by 8
+        if linux in self.mapto[self.MAP_OSX]:
+            xorgxquartz = self.mapto[self.MAP_OSX][linux] + 8
+            self.mapto[self.MAP_XORGXQUARTZ][linux] = xorgxquartz
+            self.mapfrom[self.MAP_XORGXQUARTZ][xorgxquartz] = linux
+
+        # Xorg Xwin (aka Cygwin) is XWin XT offset by 8
+        if linux in self.mapto[self.MAP_XWINXT]:
+            xorgxwin = self.mapto[self.MAP_XWINXT][linux] + 8
+            self.mapto[self.MAP_XORGXWIN][linux] = xorgxwin
+            self.mapfrom[self.MAP_XORGXWIN][xorgxwin] = linux
+
+
+        # RFB keycodes are XT kbd keycodes with a slightly
+        # different encoding of 0xe0 scan codes. RFB uses
+        # the high bit of the first byte, instead of the low
+        # bit of the second byte
+        if linux in self.mapto[self.MAP_XTKBD]:
+            xtkbd = self.mapto[self.MAP_XTKBD][linux]
+            if xtkbd != 0:
+                rfb = ((xtkbd & 0x100) >> 1) | (xtkbd & 0x7f)
+            else:
+                rfb = 0
+            self.mapto[self.MAP_RFB][linux] = rfb
+            self.mapfrom[self.MAP_RFB][rfb] = linux
+
+class LanguageGenerator(object):
+
+    def generate_header(self, database, args):
+        today = time.strftime("%Y-%m-%d %H:%M")
+        self._boilerplate([
+            "This file is auto-generated from keymaps.csv on %s" % today,
+            "Database checksum sha256(%s)" % database.mapchecksum,
+            "To re-generate, run:",
+            "  %s" % args,
+        ])
+
+    def generate_code_map(self, varname, database, frommapname, tomapname):
+        if frommapname not in database.mapfrom:
+            raise Exception("Unknown map %s, expected %s",
+                            frommapname, ",".join(database.mapfrom.keys()))
+        if tomapname not in database.mapto:
+            raise Exception("Unknown map %s, expected %s",
+                            tomapname, ",".join(database.mapto.keys()))
+
+        tolinux = database.mapfrom[frommapname]
+        fromlinux = database.mapto[tomapname]
+
+        if varname is None:
+            varname = "code_map_%s_to_%s" % (frommapname, tomapname)
+
+        frommax = 0
+        for key in tolinux.keys():
+            if key > frommax:
+                frommax = key
+
+        self._array_start_code(varname, frommax + 1)
+        for src in range(frommax + 1):
+            linux = tolinux.get(src, None)
+            if linux is None:
+                dst = None
+            else:
+                dst = fromlinux.get(linux, None)
+
+            comment = "%s -> %s -> %s" % (self._label(database, frommapname, src),
+                                          self._label(database, Database.MAP_LINUX, linux),
+                                          self._label(database, tomapname, dst))
+            self._array_entry_code(src, dst, comment)
+        self._array_end()                                        
+
+    def generate_code_table(self, varname, database, mapname):
+        if mapname not in database.mapto:
+            raise Exception("Unknown map %s, expected %s",
+                            mapname, ",".join(database.mapto.keys()))
+
+        keys = database.mapto[Database.MAP_LINUX].keys()
+        keys.sort()
+        names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
+
+        if varname is None:
+            varname = "code_table_%s" % mapname
+
+        self._array_start_code(varname, len(keys))
+
+        for i in range(len(keys)):
+            key = keys[i]
+            dst = database.mapto[mapname].get(key, None)
+            self._array_entry_code(i, dst, names[i])
+
+        self._array_end()
+
+    def generate_name_map(self, varname, database, frommapname, tomapname):
+        if frommapname not in database.mapfrom:
+            raise Exception("Unknown map %s, expected %s",
+                            frommapname, ",".join(database.mapfrom.keys()))
+        if tomapname not in database.mapname:
+            raise Exception("Unknown map %s, expected %s",
+                            tomapname, ",".join(database.mapname.keys()))
+
+        tolinux = database.mapfrom[frommapname]
+        fromlinux = database.mapname[tomapname]
+
+        if varname is None:
+            varname = "name_map_%s_to_%s" % (frommapname, tomapname)
+
+        frommax = 0
+        for key in tolinux.keys():
+            if key > frommax:
+                frommax = key
+
+        self._array_start_name(varname, frommax + 1)
+
+        for src in range(frommax + 1):
+            linux = tolinux.get(src, None)
+            if linux is None:
+                dst = None
+            else:
+                dst = fromlinux.get(linux, None)
+
+            comment = "%s -> %s -> %s" % (self._label(database, frommapname, src),
+                                          self._label(database, Database.MAP_LINUX, linux),
+                                          self._label(database, tomapname, dst))
+            self._array_entry_name(src, dst, comment)
+        self._array_end()                                        
+
+    def generate_name_table(self, varname, database, mapname):
+        if mapname not in database.mapname:
+            raise Exception("Unknown map %s, expected %s",
+                            mapname, ",".join(database.mapname.keys()))
+
+        keys = database.mapto[Database.MAP_LINUX].keys()
+        keys.sort()
+        names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
+
+        if varname is None:
+            varname = "name_table_%s" % mapname
+
+        self._array_start_name(varname, len(keys))
+
+        for i in range(len(keys)):
+            key = keys[i]
+            dst = database.mapname[mapname].get(key, None)
+            self._array_entry_name(i, dst, names[i])
+
+        self._array_end()
+
+    def _label(self, database, mapname, val):
+        if mapname in database.mapname:
+            return "%s:%s (%s)" % (mapname, val, database.mapname[mapname].get(val, "unnamed"))
+        else:
+            return "%s:%s" % (mapname, val)
+
+
+class CLanguageGenerator(LanguageGenerator):
+
+    def __init__(self, inttypename, strtypename):
+        self.inttypename = inttypename
+        self.strtypename = strtypename
+
+    def _boilerplate(self, lines):
+        print "/*"
+        for line in lines:
+            print " * %s" % line
+        print "*/"
+
+    def _array_start_code(self, varname, length):
+        print "const %s %s[%d] = {" % (self.inttypename, varname, length)
+
+    def _array_start_name(self, varname, length):
+        print "const %s %s[%d] = {" % (self.strtypename, varname, length)
+
+    def _array_end(self):
+        print "};"
+
+    def _array_entry_code(self, index, value, comment):
+        if value is not None:
+            print "  [0x%x] = 0x%x, /* %s */" % (index, value, comment)
+
+    def _array_entry_name(self, index, value, comment):
+        if value is not None:
+            print "  [0x%x] = \"%s\", /* %s */" % (index, value, comment)
+
+class StdCLanguageGenerator(CLanguageGenerator):
+
+    def __init__(self):
+        super(StdCLanguageGenerator, self).__init__("int", "char *")
+
+class GLib2LanguageGenerator(CLanguageGenerator):
+
+    def __init__(self):
+        super(GLib2LanguageGenerator, self).__init__("gint32", "gchar *")
+
+class PythonLanguageGenerator(LanguageGenerator):
+
+    def _boilerplate(self, lines):
+        print "#"
+        for line in lines:
+            print "# %s" % line
+        print "#"
+
+    def _array_start_code(self, varname, length):
+        print "%s = [" % varname
+
+    def _array_start_name(self, varname, length):
+        print "%s = [" % varname
+
+    def _array_end(self):
+        print "]"
+
+    def _array_entry_code(self, index, value, comment):
+        if value is None:
+            print "  None, # %s" % (comment)
+        else:
+            print "  0x%x, # %s" % (value, comment)
+
+    def _array_entry_name(self, index, value, comment):
+        if value is None:
+            print "  None, # %s" % (comment)
+        else:
+            print "  \"%s\", # %s" % (value, comment)
+
+class PerlLanguageGenerator(LanguageGenerator):
+
+    def _boilerplate(self, lines):
+        print "#"
+        for line in lines:
+            print "# %s" % line
+        print "#"
+
+    def _array_start_code(self, varname, length):
+        print "my @%s = (" % varname
+
+    def _array_start_name(self, varname, length):
+        print "my @%s = (" % varname
+
+    def _array_end(self):
+        print ");"
+
+    def _array_entry_code(self, index, value, comment):
+        if value is None:
+            print "  undef, # %s" % (comment)
+        else:
+            print "  0x%x, # %s" % (value, comment)
+
+    def _array_entry_name(self, index, value, comment):
+        if value is None:
+            print "  undef, # %s" % (comment)
+        else:
+            print "  \"%s\", # %s" % (value, comment)
+
+GENERATORS = {
+    "stdc": StdCLanguageGenerator(),
+    "glib2": StdCLanguageGenerator(),
+    "python2": PythonLanguageGenerator(),
+    "python3": PythonLanguageGenerator(),
+    "perl": PerlLanguageGenerator(),
+}
+
+def code_map(args):
+    database = Database()
+    database.load(args.keymaps)
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["code-map", "keymaps.csv", args.frommapname, args.tomapname])
+    GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    GENERATORS[args.lang].generate_code_map(args.varname, database, args.frommapname, args.tomapname)
+
+def code_table(args):
+    database = Database()
+    database.load(args.keymaps)
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["code-table", "keymaps.csv", args.mapname])
+    GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    GENERATORS[args.lang].generate_code_table(args.varname, database, args.mapname)
+
+def name_map(args):
+    database = Database()
+    database.load(args.keymaps)
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["name-map", "keymaps.csv", args.frommapname, args.tomapname])
+    GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    GENERATORS[args.lang].generate_name_map(args.varname, database, args.frommapname, args.tomapname)
+
+def name_table(args):
+    database = Database()
+    database.load(args.keymaps)
+
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["name-table", "keymaps.csv", args.mapname])
+    GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    GENERATORS[args.lang].generate_name_table(args.varname, database, args.mapname)
+
+def main():
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument("--lang", default="stdc",
+                        help="Output language, %s" % ",".join(GENERATORS.keys()))
+    parser.add_argument("--varname", default=None,
+                        help="Data variable name")
+
+    subparsers = parser.add_subparsers(help="sub-command help")
+
+    codemapparser = subparsers.add_parser("code-map", help="Generate a mapping between code tables")
+    codemapparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    codemapparser.add_argument("frommapname", help="Source code table name")
+    codemapparser.add_argument("tomapname", help="Target code table name")
+    codemapparser.set_defaults(func=code_map)
+
+    codetableparser = subparsers.add_parser("code-table", help="Generate a flat code table")
+    codetableparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    codetableparser.add_argument("mapname", help="Code table name")
+    codetableparser.set_defaults(func=code_table)
+
+    namemapparser = subparsers.add_parser("name-map", help="Generate a mapping to names")
+    namemapparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    namemapparser.add_argument("frommapname", help="Source code table name")
+    namemapparser.add_argument("tomapname", help="Target name table name")
+    namemapparser.set_defaults(func=name_map)
+
+    nametableparser = subparsers.add_parser("name-table", help="Generate a flat name table")
+    nametableparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    nametableparser.add_argument("mapname", help="Name table name")
+    nametableparser.set_defaults(func=name_table)
+
+    args = parser.parse_args()
+    args.func(args)
+
+
+main()