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