Fix USB menu key mapping
[keycodemapdb.git] / tools / keymap-gen
1 #!/usr/bin/python
2 # -*- python -*-
3 #
4 # Keycode Map Generator
5 #
6 # Copyright (C) 2009-2017 Red Hat, Inc.
7 #
8 # This file is dual license under the terms of the GPLv2 or later
9 # and 3-clause BSD licenses.
10 #
11
12 # Requires >= 2.6
13 from __future__ import print_function
14
15 import csv
16 import argparse
17 import hashlib
18 import time
19 import sys
20
21 class Database:
22
23     # Linux: linux/input.h
24     MAP_LINUX = "linux"
25
26     # OS-X: Carbon/HIToolbox/Events.h
27     MAP_OSX = "osx"
28
29     # AT Set 1: linux/drivers/input/keyboard/atkbd.c
30     #           (atkbd_set2_keycode + atkbd_unxlate_table)
31     MAP_ATSET1 = "atset1"
32
33     # AT Set 2: linux/drivers/input/keyboard/atkbd.c
34     #           (atkbd_set2_keycode)
35     MAP_ATSET2 = "atset2"
36
37     # AT Set 3: linux/drivers/input/keyboard/atkbd.c
38     #           (atkbd_set3_keycode)
39     MAP_ATSET3 = "atset3"
40
41     # Linux RAW: linux/drivers/char/keyboard.c (x86_keycodes)
42     MAP_XTKBD = "xtkbd"
43
44     # USB HID: linux/drivers/hid/usbhid/usbkbd.c (usb_kbd_keycode)
45     MAP_USB = "usb"
46
47     # Win32: mingw32/winuser.h
48     MAP_WIN32 = "win32"
49
50     # XWin XT: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h}
51     #          (xt + manually transcribed)
52     MAP_XWINXT = "xwinxt"
53
54     # X11: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h
55     MAP_X11 = "x11"
56
57     # XKBD XT: xf86-input-keyboard/src/at_scancode.c
58     #          (xt + manually transcribed)
59     MAP_XKBDXT = "xkbdxt"
60
61     # Xorg with evdev: linux + an offset
62     MAP_XORGEVDEV = "xorgevdev"
63
64     # Xorg with kbd: xkbdxt + an offset
65     MAP_XORGKBD = "xorgkbd"
66
67     # Xorg with OS-X: osx + an offset
68     MAP_XORGXQUARTZ = "xorgxquartz"
69
70     # Xorg + Cygwin: xwinxt + an offset
71     MAP_XORGXWIN = "xorgxwin"
72
73     # XT over RFB: xtkbd + special re-encoding of high bit
74     MAP_RFB = "rfb"
75
76     # HTML codes
77     MAP_HTML = "html"
78
79     # XKB key names
80     MAP_XKB = "xkb"
81
82     # QEMU keycodes
83     MAP_QCODE = "qcode"
84
85     # Sun / Sparc  scan codes
86     # Reference: "SPARC International Keyboard Spec 1", page 7 "US scan set"
87     MAP_SUN = "sun"
88
89     # Apple Desktop Bus
90     # Reference: http://www.archive.org/stream/apple-guide-macintosh-family-hardware/Apple_Guide_to_the_Macintosh_Family_Hardware_2e#page/n345/mode/2up
91     MAP_ADB = "adb"
92
93     MAP_LIST = (
94         MAP_LINUX,
95         MAP_OSX,
96         MAP_ATSET1,
97         MAP_ATSET2,
98         MAP_ATSET3,
99         MAP_USB,
100         MAP_WIN32,
101         MAP_XWINXT,
102         MAP_XKBDXT,
103         MAP_X11,
104         MAP_HTML,
105         MAP_XKB,
106         MAP_QCODE,
107         MAP_SUN,
108         MAP_ADB,
109
110         # These are derived from maps above
111         MAP_XTKBD,
112         MAP_XORGEVDEV,
113         MAP_XORGKBD,
114         MAP_XORGXQUARTZ,
115         MAP_XORGXWIN,
116         MAP_RFB,
117     )
118
119     CODE_COLUMNS = {
120         MAP_LINUX: 1,
121         MAP_OSX: 3,
122         MAP_ATSET1: 4,
123         MAP_ATSET2: 5,
124         MAP_ATSET3: 6,
125         MAP_USB: 7,
126         MAP_WIN32: 9,
127         MAP_XWINXT: 10,
128         MAP_XKBDXT: 11,
129         MAP_X11: 13,
130         MAP_HTML: 14,
131         MAP_XKB: 15,
132         MAP_SUN: 17,
133         MAP_ADB: 18,
134     }
135
136     ENUM_COLUMNS = {
137         MAP_QCODE: 14,
138     }
139
140     NAME_COLUMNS = {
141         MAP_LINUX: 0,
142         MAP_OSX: 2,
143         MAP_WIN32: 8,
144         MAP_X11: 12,
145         MAP_HTML: 14,
146         MAP_XKB: 15,
147         MAP_QCODE: 16,
148     }
149
150     ENUM_BOUND = {
151         MAP_QCODE: "Q_KEY_CODE__MAX",
152     }
153
154     def __init__(self):
155
156         self.mapto = {}
157         self.mapfrom = {}
158         self.mapname = {}
159         self.mapchecksum = None
160
161         for name in self.MAP_LIST:
162             # Key is a MAP_LINUX, value is a MAP_XXX
163             self.mapto[name] = {}
164             # key is a MAP_XXX, value is a MAP_LINUX
165             self.mapfrom[name] = {}
166
167         for name in self.NAME_COLUMNS.keys():
168             # key is a MAP_LINUX, value is a string
169             self.mapname[name] = {}
170
171     def _generate_checksum(self, filename):
172         hash = hashlib.sha256()
173         with open(filename, "rb") as f:
174             for chunk in iter(lambda: f.read(4096), b""):
175                 hash.update(chunk)
176         self.mapchecksum = hash.hexdigest()
177
178     def load(self, filename):
179         self._generate_checksum(filename)
180
181         with open(filename, 'r') as f:
182             reader = csv.reader(f)
183
184             first = True
185
186             for row in reader:
187                 # Discard column headings
188                 if first:
189                     first = False
190                     continue
191
192                 # We special case MAP_LINUX since that is out
193                 # master via which all other mappings are done
194                 linux = self.load_linux(row)
195
196                 # Now load all the remaining master data values
197                 self.load_data(row, linux)
198
199                 # Then load all the keycode names
200                 self.load_names(row, linux)
201
202                 # Finally calculate derived key maps
203                 self.derive_data(row, linux)
204
205     def load_linux(self, row):
206         col = self.CODE_COLUMNS[self.MAP_LINUX]
207         linux = row[col]
208
209         if linux.startswith("0x"):
210             linux = int(linux, 16)
211         else:
212             linux = int(linux, 10)
213
214         self.mapto[self.MAP_LINUX][linux] = linux
215         self.mapfrom[self.MAP_LINUX][linux] = linux
216
217         return linux
218
219
220     def load_data(self, row, linux):
221         for mapname in self.CODE_COLUMNS:
222             if mapname == self.MAP_LINUX:
223                 continue
224
225             col = self.CODE_COLUMNS[mapname]
226             val = row[col]
227
228             if val == "":
229                 continue
230
231             if val.startswith("0x"):
232                 val = int(val, 16)
233             elif val.isdigit():
234                 val = int(val, 10)
235
236             self.mapto[mapname][linux] = val
237             self.mapfrom[mapname][val] = linux
238
239     def load_names(self, row, linux):
240         for mapname in self.NAME_COLUMNS:
241             col = self.NAME_COLUMNS[mapname]
242             val = row[col]
243
244             if val == "":
245                 continue
246
247             self.mapname[mapname][linux] = val
248
249
250     def derive_data(self, row, linux):
251         # Linux RAW is XT scan codes with special encoding of the
252         # 0xe0 scan codes
253         if linux in self.mapto[self.MAP_ATSET1]:
254             at1 = self.mapto[self.MAP_ATSET1][linux]
255             if at1 > 0x7f:
256                 assert((at1 & ~0x7f) == 0xe000)
257                 xtkbd = 0x100 | (at1 & 0x7f)
258             else:
259                 xtkbd = at1
260             self.mapto[self.MAP_XTKBD][linux] = xtkbd
261             self.mapfrom[self.MAP_XTKBD][xtkbd] = linux
262
263         # Xorg KBD is XKBD XT offset by 8
264         if linux in self.mapto[self.MAP_XKBDXT]:
265             xorgkbd = self.mapto[self.MAP_XKBDXT][linux] + 8
266             self.mapto[self.MAP_XORGKBD][linux] = xorgkbd
267             self.mapfrom[self.MAP_XORGKBD][xorgkbd] = linux
268
269         # Xorg evdev is Linux offset by 8
270         self.mapto[self.MAP_XORGEVDEV][linux] = linux + 8
271         self.mapfrom[self.MAP_XORGEVDEV][linux + 8] = linux
272
273         # Xorg XQuartx is OS-X offset by 8
274         if linux in self.mapto[self.MAP_OSX]:
275             xorgxquartz = self.mapto[self.MAP_OSX][linux] + 8
276             self.mapto[self.MAP_XORGXQUARTZ][linux] = xorgxquartz
277             self.mapfrom[self.MAP_XORGXQUARTZ][xorgxquartz] = linux
278
279         # Xorg Xwin (aka Cygwin) is XWin XT offset by 8
280         if linux in self.mapto[self.MAP_XWINXT]:
281             xorgxwin = self.mapto[self.MAP_XWINXT][linux] + 8
282             self.mapto[self.MAP_XORGXWIN][linux] = xorgxwin
283             self.mapfrom[self.MAP_XORGXWIN][xorgxwin] = linux
284
285         # RFB keycodes are XT scan codes with a slightly
286         # different encoding of 0xe0 scan codes
287         if linux in self.mapto[self.MAP_ATSET1]:
288             at1 = self.mapto[self.MAP_ATSET1][linux]
289             if at1 > 0x7f:
290                 assert((at1 & ~0x7f) == 0xe000)
291                 rfb = 0x80 | (at1 & 0x7f)
292             else:
293                 rfb = at1
294             self.mapto[self.MAP_RFB][linux] = rfb
295             self.mapfrom[self.MAP_RFB][rfb] = linux
296
297         if linux in self.mapname[self.MAP_QCODE]:
298             qcodeenum = self.mapname[self.MAP_QCODE][linux]
299             qcodeenum = "Q_KEY_CODE_" + qcodeenum.upper()
300             self.mapto[self.MAP_QCODE][linux] = qcodeenum
301             self.mapfrom[self.MAP_QCODE][qcodeenum] = linux
302
303 class LanguageGenerator(object):
304
305     def _boilerplate(self, lines):
306         raise NotImplementedError()
307
308     def generate_header(self, database, args):
309         today = time.strftime("%Y-%m-%d %H:%M")
310         self._boilerplate([
311             "This file is auto-generated from keymaps.csv on %s" % today,
312             "Database checksum sha256(%s)" % database.mapchecksum,
313             "To re-generate, run:",
314             "  %s" % args,
315         ])
316
317 class LanguageSrcGenerator(LanguageGenerator):
318
319     TYPE_INT = "integer"
320     TYPE_STRING = "string"
321     TYPE_ENUM = "enum"
322
323     def _array_start(self, varname, length, defvalue, fromtype, totype):
324         raise NotImplementedError()
325
326     def _array_end(self, fromtype, totype):
327         raise NotImplementedError()
328
329     def _array_entry(self, index, value, comment, fromtype, totype):
330         raise NotImplementedError()
331
332     def generate_code_map(self, varname, database, frommapname, tomapname):
333         if frommapname not in database.mapfrom:
334             raise Exception("Unknown map %s, expected one of %s" % (
335                             frommapname, ", ".join(database.mapfrom.keys())))
336         if tomapname not in database.mapto:
337             raise Exception("Unknown map %s, expected one of %s" % (
338                             tomapname, ", ".join(database.mapto.keys())))
339
340         tolinux = database.mapfrom[frommapname]
341         fromlinux = database.mapto[tomapname]
342
343         if varname is None:
344             varname = "code_map_%s_to_%s" % (frommapname, tomapname)
345
346         if frommapname in database.ENUM_COLUMNS:
347             fromtype = self.TYPE_ENUM
348         elif type(tolinux.keys()[0]) == str:
349             fromtype = self.TYPE_STRING
350         else:
351             fromtype = self.TYPE_INT
352
353         if tomapname in database.ENUM_COLUMNS:
354             totype = self.TYPE_ENUM
355         elif type(fromlinux.values()[0]) == str:
356             totype = self.TYPE_STRING
357         else:
358             totype = self.TYPE_INT
359
360         keys = tolinux.keys()
361         keys.sort()
362         if fromtype == self.TYPE_INT:
363             keys = range(keys[-1] + 1)
364
365         if fromtype == self.TYPE_ENUM:
366             keymax = database.ENUM_BOUND[frommapname]
367         else:
368             keymax = len(keys)
369
370         defvalue = fromlinux.get(0, None)
371         if fromtype == self.TYPE_ENUM:
372             self._array_start(varname, keymax, defvalue, fromtype, totype)
373         else:
374             self._array_start(varname, keymax, None, fromtype, totype)
375
376         for src in keys:
377             linux = tolinux.get(src, None)
378             if linux is None:
379                 dst = None
380             else:
381                 dst = fromlinux.get(linux, defvalue)
382
383             comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux),
384                                           self._label(database, Database.MAP_LINUX, linux, linux),
385                                           self._label(database, tomapname, dst, linux))
386             self._array_entry(src, dst, comment, fromtype, totype)
387         self._array_end(fromtype, totype)
388
389     def generate_code_table(self, varname, database, mapname):
390         if mapname not in database.mapto:
391             raise Exception("Unknown map %s, expected one of %s" % (
392                             mapname, ", ".join(database.mapto.keys())))
393
394         keys = database.mapto[Database.MAP_LINUX].keys()
395         keys.sort()
396         names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
397
398         if varname is None:
399             varname = "code_table_%s" % mapname
400
401         if mapname in database.ENUM_COLUMNS:
402             totype = self.TYPE_ENUM
403         elif type(database.mapto[mapname].values()[0]) == str:
404             totype = self.TYPE_STRING
405         else:
406             totype = self.TYPE_INT
407
408         self._array_start(varname, len(keys), None, self.TYPE_INT, totype)
409
410         defvalue = database.mapto[mapname].get(0, None)
411         for i in range(len(keys)):
412             key = keys[i]
413             dst = database.mapto[mapname].get(key, defvalue)
414             self._array_entry(i, dst, names[i], self.TYPE_INT, totype)
415
416         self._array_end(self.TYPE_INT, totype)
417
418     def generate_name_map(self, varname, database, frommapname, tomapname):
419         if frommapname not in database.mapfrom:
420             raise Exception("Unknown map %s, expected one of %s" % (
421                             frommapname, ", ".join(database.mapfrom.keys())))
422         if tomapname not in database.mapname:
423             raise Exception("Unknown map %s, expected one of %s" % (
424                             tomapname, ", ".join(database.mapname.keys())))
425
426         tolinux = database.mapfrom[frommapname]
427         fromlinux = database.mapname[tomapname]
428
429         if varname is None:
430             varname = "name_map_%s_to_%s" % (frommapname, tomapname)
431
432         keys = tolinux.keys()
433         keys.sort()
434         if type(keys[0]) == int:
435             keys = range(keys[-1] + 1)
436
437         if type(keys[0]) == int:
438             fromtype = self.TYPE_INT
439         else:
440             fromtype = self.TYPE_STRING
441
442         self._array_start(varname, len(keys), None, fromtype, self.TYPE_STRING)
443
444         for src in keys:
445             linux = tolinux.get(src, None)
446             if linux is None:
447                 dst = None
448             else:
449                 dst = fromlinux.get(linux, None)
450
451             comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux),
452                                           self._label(database, Database.MAP_LINUX, linux, linux),
453                                           self._label(database, tomapname, dst, linux))
454             self._array_entry(src, dst, comment, fromtype, self.TYPE_STRING)
455         self._array_end(fromtype, self.TYPE_STRING)
456
457     def generate_name_table(self, varname, database, mapname):
458         if mapname not in database.mapname:
459             raise Exception("Unknown map %s, expected one of %s" % (
460                             mapname, ", ".join(database.mapname.keys())))
461
462         keys = database.mapto[Database.MAP_LINUX].keys()
463         keys.sort()
464         names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
465
466         if varname is None:
467             varname = "name_table_%s" % mapname
468
469         self._array_start(varname, len(keys), None, self.TYPE_INT, self.TYPE_STRING)
470
471         for i in range(len(keys)):
472             key = keys[i]
473             dst = database.mapname[mapname].get(key, None)
474             self._array_entry(i, dst, names[i], self.TYPE_INT, self.TYPE_STRING)
475
476         self._array_end(self.TYPE_INT, self.TYPE_STRING)
477
478     def _label(self, database, mapname, val, linux):
479         if mapname in database.mapname:
480             return "%s:%s (%s)" % (mapname, val, database.mapname[mapname].get(linux, "unnamed"))
481         else:
482             return "%s:%s" % (mapname, val)
483
484 class LanguageDocGenerator(LanguageGenerator):
485
486     def _array_start_name_doc(self, varname, namemap):
487         raise NotImplementedError()
488
489     def _array_start_code_doc(self, varname, namemap, codemap):
490         raise NotImplementedError()
491
492     def _array_end(self):
493         raise NotImplementedError()
494
495     def _array_name_entry(self, value, name):
496         raise NotImplementedError()
497
498     def _array_code_entry(self, value, name):
499         raise NotImplementedError()
500
501     def generate_name_docs(self, varname, database, mapname):
502         if mapname not in database.mapname:
503             raise Exception("Unknown map %s, expected one of %s" % (
504                             mapname, ", ".join(database.mapname.keys())))
505
506         keys = database.mapto[Database.MAP_LINUX].keys()
507         keys.sort()
508         names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
509
510         if varname is None:
511             varname = mapname
512
513         self._array_start_name_doc(varname, mapname)
514
515         for i in range(len(keys)):
516             key = keys[i]
517             dst = database.mapname[mapname].get(key, None)
518             self._array_name_entry(key, dst)
519
520         self._array_end()
521
522
523     def generate_code_docs(self, varname, database, mapname):
524         if mapname not in database.mapfrom:
525             raise Exception("Unknown map %s, expected one of %s" % (
526                             mapname, ", ".join(database.mapfrom.keys())))
527
528         tolinux = database.mapfrom[mapname]
529         keys = tolinux.keys()
530         keys.sort()
531         if mapname in database.mapname:
532             names = database.mapname[mapname]
533             namemap = mapname
534         else:
535             names = database.mapname[Database.MAP_LINUX]
536             namemap = Database.MAP_LINUX
537
538         if varname is None:
539             varname = mapname
540
541         self._array_start_code_doc(varname, mapname, namemap)
542
543         for i in range(len(keys)):
544             key = keys[i]
545             self._array_code_entry(key, names.get(tolinux[key], "unnamed"))
546
547         self._array_end()
548
549 class CLanguageGenerator(LanguageSrcGenerator):
550
551     def __init__(self, inttypename, strtypename):
552         self.inttypename = inttypename
553         self.strtypename = strtypename
554
555     def _boilerplate(self, lines):
556         print("/*")
557         for line in lines:
558             print(" * %s" % line)
559         print("*/")
560
561     def _array_start(self, varname, length, defvalue, fromtype, totype):
562         self._varname = varname;
563         totypename = self.strtypename if totype == self.TYPE_STRING else self.inttypename
564         if fromtype in (self.TYPE_INT, self.TYPE_ENUM):
565             if type(length) == str:
566                 print("const %s %s[%s] = {" % (totypename, varname, length))
567             else:
568                 print("const %s %s[%d] = {" % (totypename, varname, length))
569         else:
570             print("const struct _%s {" % varname)
571             print("  const %s from;" % self.strtypename)
572             print("  const %s to;" % totypename)
573             print("} %s[] = {" % varname)
574
575         if defvalue != None:
576             if totype == self.TYPE_ENUM:
577                 if type(length) == str:
578                     print("  [0 ... %s-1] = %s," % (length, defvalue))
579                 else:
580                     print("  [0 ... 0x%x-1] = %s," % (length, defvalue))
581             else:
582                 if type(length) == str:
583                     print("  [0 ... %s-1] = 0x%x," % (length, defvalue))
584                 else:
585                     print("  [0 ... 0x%x-1] = 0x%x," % (length, defvalue))
586
587     def _array_end(self, fromtype, totype):
588         print("};")
589         print("const unsigned int %s_len = sizeof(%s)/sizeof(%s[0]);" %
590               (self._varname, self._varname, self._varname))
591
592     def _array_entry(self, index, value, comment, fromtype, totype):
593         if value is None:
594             return
595         if fromtype == self.TYPE_INT:
596             indexfmt = "0x%x"
597         elif fromtype == self.TYPE_ENUM:
598             indexfmt = "%s"
599         else:
600             indexfmt = "\"%s\""
601
602         if totype == self.TYPE_INT:
603             valuefmt = "0x%x"
604         elif totype == self.TYPE_ENUM:
605             valuefmt = "%s"
606         else:
607             valuefmt = "\"%s\""
608
609         if fromtype != self.TYPE_STRING:
610             print(("  [" + indexfmt + "] = " + valuefmt + ", /* %s */") % (index, value, comment))
611         else:
612             print(("  {" + indexfmt + ", " + valuefmt + "}, /* %s */") % (index, value, comment))
613
614 class CppLanguageGenerator(CLanguageGenerator):
615
616     def _array_start(self, varname, length, defvalue, fromtype, totype):
617         if fromtype == self.TYPE_ENUM:
618             raise NotImplementedError("Enums not supported as source in C++ generator")
619         totypename = "const " + self.strtypename if totype == self.TYPE_STRING else self.inttypename
620         if fromtype == self.TYPE_INT:
621             print("#include <vector>")
622             print("const std::vector<%s> %s = {" % (totypename, varname))
623         else:
624             print("#include <map>")
625             print("#include <string>")
626             print("const std::map<const std::string, %s> %s = {" % (totypename, varname))
627
628     def _array_end(self, fromtype, totype):
629         print("};")
630
631     # designated initializers not available in C++
632     def _array_entry(self, index, value, comment, fromtype, totype):
633         if fromtype == self.TYPE_STRING:
634             return super(CppLanguageGenerator, self)._array_entry(index, value, comment, fromtype, totype)
635
636         if value is None:
637             print("  0, /* %s */" % comment)
638         elif totype == self.TYPE_INT:
639             print("  0x%x, /* %s */" % (value, comment))
640         elif totype == self.TYPE_ENUM:
641             print("  %s, /* %s */" % (value, comment))
642         else:
643             print("  \"%s\", /* %s */" % (value, comment))
644
645 class StdCLanguageGenerator(CLanguageGenerator):
646
647     def __init__(self):
648         super(StdCLanguageGenerator, self).__init__("unsigned short", "char *")
649
650 class StdCppLanguageGenerator(CppLanguageGenerator):
651
652     def __init__(self):
653         super(StdCppLanguageGenerator, self).__init__("unsigned short", "char *")
654
655 class GLib2LanguageGenerator(CLanguageGenerator):
656
657     def __init__(self):
658         super(GLib2LanguageGenerator, self).__init__("guint16", "gchar *")
659
660 class PythonLanguageGenerator(LanguageSrcGenerator):
661
662     def _boilerplate(self, lines):
663         print("#")
664         for line in lines:
665             print("# %s" % line)
666         print("#")
667
668     def _array_start(self, varname, length, defvalue, fromtype, totype):
669         if fromtype == self.TYPE_ENUM:
670             raise NotImplementedError("Enums not supported as source in Python generator")
671
672         if fromtype != self.TYPE_STRING:
673             print("%s = [" % varname)
674         else:
675             print("%s = {" % varname)
676
677     def _array_end(self, fromtype, totype):
678         if fromtype != self.TYPE_STRING:
679             print("]")
680         else:
681             print("}")
682
683     def _array_entry(self, index, value, comment, fromtype, totype):
684         if fromtype == self.TYPE_INT:
685             if value is None:
686                 print("  None, # %s" % (comment))
687             elif totype == self.TYPE_INT:
688                 print("  0x%x, # %s" % (value, comment))
689             elif totype == self.TYPE_ENUM:
690                 print("  %s, # %s" % (value, comment))
691             else:
692                 print("  \"%s\", # %s" % (value, comment))
693         else:
694             if value is None:
695                 print("  \"%s\": None, # %s" % (index, comment))
696             elif totype == self.TYPE_INT:
697                 print("  \"%s\": 0x%x, # %s" % (index, value, comment))
698             elif totype == self.TYPE_ENUM:
699                 print("  \"%s\": %s, # %s" % (index, value, comment))
700             else:
701                 print("  \"%s\": \"%s\", # %s" % (index, value, comment))
702
703 class PerlLanguageGenerator(LanguageSrcGenerator):
704
705     def _boilerplate(self, lines):
706         print("#")
707         for line in lines:
708             print("# %s" % line)
709         print("#")
710
711     def _array_start(self, varname, length, defvalue, fromtype, totype):
712         if fromtype == self.TYPE_ENUN:
713             raise NotImplementedError("Enums not supported as source in Python generator")
714         if fromtype == self.TYPE_INT:
715             print("my @%s = (" % varname)
716         else:
717             print("my %%%s = (" % varname)
718
719     def _array_end(self, fromtype, totype):
720         print(");")
721
722     def _array_entry(self, index, value, comment, fromtype, totype):
723         if fromtype == self.TYPE_INT:
724             if value is None:
725                 print("  undef, # %s" % (comment))
726             elif totype == self.TYPE_INT:
727                 print("  0x%x, # %s" % (value, comment))
728             elif totype == self.TYPE_ENUM:
729                 print("  %s, # %s" % (value, comment))
730             else:
731                 print("  \"%s\", # %s" % (value, comment))
732         else:
733             if value is None:
734                 print("  \"%s\", undef, # %s" % (index, comment))
735             elif totype == self.TYPE_INT:
736                 print("  \"%s\", 0x%x, # %s" % (index, value, comment))
737             elif totype == self.TYPE_ENUM:
738                 print("  \"%s\", 0x%x, # %s" % (index, value, comment))
739             else:
740                 print("  \"%s\", \"%s\", # %s" % (index, value, comment))
741
742 class JavaScriptLanguageGenerator(LanguageSrcGenerator):
743
744     def _boilerplate(self, lines):
745         print("/*")
746         for line in lines:
747             print(" * %s" % line)
748         print("*/")
749
750     def _array_start(self, varname, length, defvalue, fromtype, totype):
751         print("export default {")
752
753     def _array_end(self, fromtype, totype):
754         print("};")
755
756     def _array_entry(self, index, value, comment, fromtype, totype):
757         if value is None:
758             return
759
760         if fromtype == self.TYPE_INT:
761             fromfmt = "0x%x"
762         elif fromtype == self.TYPE_ENUM:
763             fromfmt = "%s"
764         else:
765             fromfmt = "\"%s\""
766
767         if totype == self.TYPE_INT:
768             tofmt = "0x%x"
769         elif totype == self.TYPE_ENUM:
770             tofmt = "%s"
771         else:
772             tofmt = "\"%s\""
773
774         print(("  " + fromfmt + ": " + tofmt + ", /* %s */") % (index, value, comment))
775
776 class PodLanguageGenerator(LanguageDocGenerator):
777
778     def _boilerplate(self, lines):
779         print("#")
780         for line in lines:
781             print("# %s" % line)
782         print("#")
783
784     def _array_start_name_doc(self, varname, namemap):
785         print("=head1 %s" % varname)
786         print("")
787         print("List of %s key code names, with corresponding key code values" % namemap)
788         print("")
789         print("=over 4")
790         print("")
791
792     def _array_start_code_doc(self, varname, codemap, namemap):
793         print("=head1 %s" % varname)
794         print("")
795         print("List of %s key code values, with corresponding %s key code names" % (codemap, namemap))
796         print("")
797         print("=over 4")
798         print("")
799
800     def _array_end(self):
801         print("=back")
802         print("")
803
804     def _array_name_entry(self, value, name):
805         print("=item %s" % name)
806         print("")
807         print("Key value %d (0x%x)" % (value, value))
808         print("")
809
810     def _array_code_entry(self, value, name):
811         print("=item %d (0x%x)" % (value, value))
812         print("")
813         print("Key name %s" % name)
814         print("")
815
816 SRC_GENERATORS = {
817     "stdc": StdCLanguageGenerator(),
818     "stdc++": StdCppLanguageGenerator(),
819     "glib2": GLib2LanguageGenerator(),
820     "python2": PythonLanguageGenerator(),
821     "python3": PythonLanguageGenerator(),
822     "perl": PerlLanguageGenerator(),
823     "js": JavaScriptLanguageGenerator(),
824 }
825 DOC_GENERATORS = {
826     "pod": PodLanguageGenerator(),
827 }
828
829 def code_map(args):
830     database = Database()
831     database.load(args.keymaps)
832
833     cliargs = ["keymap-gen", "--lang=%s" % args.lang]
834     if args.varname is not None:
835         cliargs.append("--varname=%s" % args.varname)
836     cliargs.extend(["code-map", "keymaps.csv", args.frommapname, args.tomapname])
837     SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
838
839     SRC_GENERATORS[args.lang].generate_code_map(args.varname, database, args.frommapname, args.tomapname)
840
841 def code_table(args):
842     database = Database()
843     database.load(args.keymaps)
844
845     cliargs = ["keymap-gen", "--lang=%s" % args.lang]
846     if args.varname is not None:
847         cliargs.append("--varname=%s" % args.varname)
848     cliargs.extend(["code-table", "keymaps.csv", args.mapname])
849     SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
850
851     SRC_GENERATORS[args.lang].generate_code_table(args.varname, database, args.mapname)
852
853 def name_map(args):
854     database = Database()
855     database.load(args.keymaps)
856
857     cliargs = ["keymap-gen", "--lang=%s" % args.lang]
858     if args.varname is not None:
859         cliargs.append("--varname=%s" % args.varname)
860     cliargs.extend(["name-map", "keymaps.csv", args.frommapname, args.tomapname])
861     SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
862
863     SRC_GENERATORS[args.lang].generate_name_map(args.varname, database, args.frommapname, args.tomapname)
864
865 def name_table(args):
866     database = Database()
867     database.load(args.keymaps)
868
869
870     cliargs = ["keymap-gen", "--lang=%s" % args.lang]
871     if args.varname is not None:
872         cliargs.append("--varname=%s" % args.varname)
873     cliargs.extend(["name-table", "keymaps.csv", args.mapname])
874     SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
875
876     SRC_GENERATORS[args.lang].generate_name_table(args.varname, database, args.mapname)
877
878 def code_docs(args):
879     database = Database()
880     database.load(args.keymaps)
881
882
883     cliargs = ["keymap-gen", "--lang=%s" % args.lang]
884     if args.varname is not None:
885         cliargs.append("--varname=%s" % args.varname)
886     cliargs.extend(["code-docs", "keymaps.csv", args.mapname])
887     DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
888
889     DOC_GENERATORS[args.lang].generate_code_docs(args.varname, database, args.mapname)
890
891 def name_docs(args):
892     database = Database()
893     database.load(args.keymaps)
894
895
896     cliargs = ["keymap-gen", "--lang=%s" % args.lang]
897     if args.varname is not None:
898         cliargs.append("--varname=%s" % args.varname)
899     cliargs.extend(["name-docs", "keymaps.csv", args.mapname])
900     DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
901
902     DOC_GENERATORS[args.lang].generate_name_docs(args.varname, database, args.mapname)
903
904 def usage():
905     print ("Please select a command:")
906     print ("  'code-map', 'code-table', 'name-map', 'name-table', 'docs'")
907     sys.exit(1)
908
909 def main():
910     parser = argparse.ArgumentParser()
911
912     parser.add_argument("--lang", default="stdc",
913                         help="Output language, (src=%s, doc=%s)" % (
914                             ",".join(SRC_GENERATORS.keys()),
915                             ",".join(DOC_GENERATORS.keys())))
916     parser.add_argument("--varname", default=None,
917                         help="Data variable name")
918
919     subparsers = parser.add_subparsers(help="sub-command help")
920
921     codemapparser = subparsers.add_parser("code-map", help="Generate a mapping between code tables")
922     codemapparser.add_argument("keymaps", help="Path to keymap CSV data file")
923     codemapparser.add_argument("frommapname", help="Source code table name")
924     codemapparser.add_argument("tomapname", help="Target code table name")
925     codemapparser.set_defaults(func=code_map)
926
927     codetableparser = subparsers.add_parser("code-table", help="Generate a flat code table")
928     codetableparser.add_argument("keymaps", help="Path to keymap CSV data file")
929     codetableparser.add_argument("mapname", help="Code table name")
930     codetableparser.set_defaults(func=code_table)
931
932     namemapparser = subparsers.add_parser("name-map", help="Generate a mapping to names")
933     namemapparser.add_argument("keymaps", help="Path to keymap CSV data file")
934     namemapparser.add_argument("frommapname", help="Source code table name")
935     namemapparser.add_argument("tomapname", help="Target name table name")
936     namemapparser.set_defaults(func=name_map)
937
938     nametableparser = subparsers.add_parser("name-table", help="Generate a flat name table")
939     nametableparser.add_argument("keymaps", help="Path to keymap CSV data file")
940     nametableparser.add_argument("mapname", help="Name table name")
941     nametableparser.set_defaults(func=name_table)
942
943     codedocsparser = subparsers.add_parser("code-docs", help="Generate code documentation")
944     codedocsparser.add_argument("keymaps", help="Path to keymap CSV data file")
945     codedocsparser.add_argument("mapname", help="Code table name")
946     codedocsparser.set_defaults(func=code_docs)
947
948     namedocsparser = subparsers.add_parser("name-docs", help="Generate name documentation")
949     namedocsparser.add_argument("keymaps", help="Path to keymap CSV data file")
950     namedocsparser.add_argument("mapname", help="Name table name")
951     namedocsparser.set_defaults(func=name_docs)
952
953     args = parser.parse_args()
954     if hasattr(args, "func"):
955         args.func(args)
956     else:
957         usage()
958
959
960 main()