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