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