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