]>
Commit | Line | Data |
---|---|---|
6838f038 WB |
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 | today = time.strftime("%Y-%m-%d %H:%M") | |
321 | self._boilerplate([ | |
322 | "This file is auto-generated from keymaps.csv on %s" % today, | |
323 | "Database checksum sha256(%s)" % database.mapchecksum, | |
324 | "To re-generate, run:", | |
325 | " %s" % args, | |
326 | ]) | |
327 | ||
328 | class LanguageSrcGenerator(LanguageGenerator): | |
329 | ||
330 | TYPE_INT = "integer" | |
331 | TYPE_STRING = "string" | |
332 | TYPE_ENUM = "enum" | |
333 | ||
334 | def _array_start(self, varname, length, defvalue, fromtype, totype): | |
335 | raise NotImplementedError() | |
336 | ||
337 | def _array_end(self, fromtype, totype): | |
338 | raise NotImplementedError() | |
339 | ||
340 | def _array_entry(self, index, value, comment, fromtype, totype): | |
341 | raise NotImplementedError() | |
342 | ||
343 | def generate_code_map(self, varname, database, frommapname, tomapname): | |
344 | if frommapname not in database.mapfrom: | |
345 | raise Exception("Unknown map %s, expected one of %s" % ( | |
346 | frommapname, ", ".join(database.mapfrom.keys()))) | |
347 | if tomapname not in database.mapto: | |
348 | raise Exception("Unknown map %s, expected one of %s" % ( | |
349 | tomapname, ", ".join(database.mapto.keys()))) | |
350 | ||
351 | tolinux = database.mapfrom[frommapname] | |
352 | fromlinux = database.mapto[tomapname] | |
353 | ||
354 | if varname is None: | |
355 | varname = "code_map_%s_to_%s" % (frommapname, tomapname) | |
356 | ||
357 | if frommapname in database.ENUM_COLUMNS: | |
358 | fromtype = self.TYPE_ENUM | |
53e83913 | 359 | elif type(list(tolinux.keys())[0]) == str: |
6838f038 WB |
360 | fromtype = self.TYPE_STRING |
361 | else: | |
362 | fromtype = self.TYPE_INT | |
363 | ||
364 | if tomapname in database.ENUM_COLUMNS: | |
365 | totype = self.TYPE_ENUM | |
53e83913 | 366 | elif type(list(fromlinux.values())[0]) == str: |
6838f038 WB |
367 | totype = self.TYPE_STRING |
368 | else: | |
369 | totype = self.TYPE_INT | |
370 | ||
53e83913 | 371 | keys = list(tolinux.keys()) |
6838f038 WB |
372 | keys.sort() |
373 | if fromtype == self.TYPE_INT: | |
374 | keys = range(keys[-1] + 1) | |
375 | ||
376 | if fromtype == self.TYPE_ENUM: | |
377 | keymax = database.ENUM_BOUND[frommapname] | |
378 | else: | |
379 | keymax = len(keys) | |
380 | ||
381 | defvalue = fromlinux.get(0, None) | |
382 | if fromtype == self.TYPE_ENUM: | |
383 | self._array_start(varname, keymax, defvalue, fromtype, totype) | |
384 | else: | |
385 | self._array_start(varname, keymax, None, fromtype, totype) | |
386 | ||
387 | for src in keys: | |
388 | linux = tolinux.get(src, None) | |
389 | if linux is None: | |
390 | dst = None | |
391 | else: | |
392 | dst = fromlinux.get(linux, defvalue) | |
393 | ||
394 | comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux), | |
395 | self._label(database, Database.MAP_LINUX, linux, linux), | |
396 | self._label(database, tomapname, dst, linux)) | |
397 | self._array_entry(src, dst, comment, fromtype, totype) | |
398 | self._array_end(fromtype, totype) | |
399 | ||
400 | def generate_code_table(self, varname, database, mapname): | |
401 | if mapname not in database.mapto: | |
402 | raise Exception("Unknown map %s, expected one of %s" % ( | |
403 | mapname, ", ".join(database.mapto.keys()))) | |
404 | ||
53e83913 | 405 | keys = list(database.mapto[Database.MAP_LINUX].keys()) |
6838f038 WB |
406 | keys.sort() |
407 | names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] | |
408 | ||
409 | if varname is None: | |
410 | varname = "code_table_%s" % mapname | |
411 | ||
412 | if mapname in database.ENUM_COLUMNS: | |
413 | totype = self.TYPE_ENUM | |
53e83913 | 414 | elif type(list(database.mapto[mapname].values())[0]) == str: |
6838f038 WB |
415 | totype = self.TYPE_STRING |
416 | else: | |
417 | totype = self.TYPE_INT | |
418 | ||
419 | self._array_start(varname, len(keys), None, self.TYPE_INT, totype) | |
420 | ||
421 | defvalue = database.mapto[mapname].get(0, None) | |
422 | for i in range(len(keys)): | |
423 | key = keys[i] | |
424 | dst = database.mapto[mapname].get(key, defvalue) | |
425 | self._array_entry(i, dst, names[i], self.TYPE_INT, totype) | |
426 | ||
427 | self._array_end(self.TYPE_INT, totype) | |
428 | ||
429 | def generate_name_map(self, varname, database, frommapname, tomapname): | |
430 | if frommapname not in database.mapfrom: | |
431 | raise Exception("Unknown map %s, expected one of %s" % ( | |
432 | frommapname, ", ".join(database.mapfrom.keys()))) | |
433 | if tomapname not in database.mapname: | |
434 | raise Exception("Unknown map %s, expected one of %s" % ( | |
435 | tomapname, ", ".join(database.mapname.keys()))) | |
436 | ||
437 | tolinux = database.mapfrom[frommapname] | |
438 | fromlinux = database.mapname[tomapname] | |
439 | ||
440 | if varname is None: | |
441 | varname = "name_map_%s_to_%s" % (frommapname, tomapname) | |
442 | ||
53e83913 | 443 | keys = list(tolinux.keys()) |
6838f038 WB |
444 | keys.sort() |
445 | if type(keys[0]) == int: | |
446 | keys = range(keys[-1] + 1) | |
447 | ||
448 | if type(keys[0]) == int: | |
449 | fromtype = self.TYPE_INT | |
450 | else: | |
451 | fromtype = self.TYPE_STRING | |
452 | ||
453 | self._array_start(varname, len(keys), None, fromtype, self.TYPE_STRING) | |
454 | ||
455 | for src in keys: | |
456 | linux = tolinux.get(src, None) | |
457 | if linux is None: | |
458 | dst = None | |
459 | else: | |
460 | dst = fromlinux.get(linux, None) | |
461 | ||
462 | comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux), | |
463 | self._label(database, Database.MAP_LINUX, linux, linux), | |
464 | self._label(database, tomapname, dst, linux)) | |
465 | self._array_entry(src, dst, comment, fromtype, self.TYPE_STRING) | |
466 | self._array_end(fromtype, self.TYPE_STRING) | |
467 | ||
468 | def generate_name_table(self, varname, database, mapname): | |
469 | if mapname not in database.mapname: | |
470 | raise Exception("Unknown map %s, expected one of %s" % ( | |
471 | mapname, ", ".join(database.mapname.keys()))) | |
472 | ||
53e83913 | 473 | keys = list(database.mapto[Database.MAP_LINUX].keys()) |
6838f038 WB |
474 | keys.sort() |
475 | names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] | |
476 | ||
477 | if varname is None: | |
478 | varname = "name_table_%s" % mapname | |
479 | ||
480 | self._array_start(varname, len(keys), None, self.TYPE_INT, self.TYPE_STRING) | |
481 | ||
482 | for i in range(len(keys)): | |
483 | key = keys[i] | |
484 | dst = database.mapname[mapname].get(key, None) | |
485 | self._array_entry(i, dst, names[i], self.TYPE_INT, self.TYPE_STRING) | |
486 | ||
487 | self._array_end(self.TYPE_INT, self.TYPE_STRING) | |
488 | ||
489 | def _label(self, database, mapname, val, linux): | |
490 | if mapname in database.mapname: | |
491 | return "%s:%s (%s)" % (mapname, val, database.mapname[mapname].get(linux, "unnamed")) | |
492 | else: | |
493 | return "%s:%s" % (mapname, val) | |
494 | ||
495 | class LanguageDocGenerator(LanguageGenerator): | |
496 | ||
497 | def _array_start_name_doc(self, varname, namemap): | |
498 | raise NotImplementedError() | |
499 | ||
500 | def _array_start_code_doc(self, varname, namemap, codemap): | |
501 | raise NotImplementedError() | |
502 | ||
503 | def _array_end(self): | |
504 | raise NotImplementedError() | |
505 | ||
506 | def _array_name_entry(self, value, name): | |
507 | raise NotImplementedError() | |
508 | ||
509 | def _array_code_entry(self, value, name): | |
510 | raise NotImplementedError() | |
511 | ||
512 | def generate_name_docs(self, varname, database, mapname): | |
513 | if mapname not in database.mapname: | |
514 | raise Exception("Unknown map %s, expected one of %s" % ( | |
515 | mapname, ", ".join(database.mapname.keys()))) | |
516 | ||
53e83913 | 517 | keys = list(database.mapto[Database.MAP_LINUX].keys()) |
6838f038 WB |
518 | keys.sort() |
519 | names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] | |
520 | ||
521 | if varname is None: | |
522 | varname = mapname | |
523 | ||
524 | self._array_start_name_doc(varname, mapname) | |
525 | ||
526 | for i in range(len(keys)): | |
527 | key = keys[i] | |
528 | dst = database.mapname[mapname].get(key, None) | |
529 | self._array_name_entry(key, dst) | |
530 | ||
531 | self._array_end() | |
532 | ||
533 | ||
534 | def generate_code_docs(self, varname, database, mapname): | |
535 | if mapname not in database.mapfrom: | |
536 | raise Exception("Unknown map %s, expected one of %s" % ( | |
537 | mapname, ", ".join(database.mapfrom.keys()))) | |
538 | ||
539 | tolinux = database.mapfrom[mapname] | |
53e83913 | 540 | keys = list(tolinux.keys()) |
6838f038 WB |
541 | keys.sort() |
542 | if mapname in database.mapname: | |
543 | names = database.mapname[mapname] | |
544 | namemap = mapname | |
545 | else: | |
546 | names = database.mapname[Database.MAP_LINUX] | |
547 | namemap = Database.MAP_LINUX | |
548 | ||
549 | if varname is None: | |
550 | varname = mapname | |
551 | ||
552 | self._array_start_code_doc(varname, mapname, namemap) | |
553 | ||
554 | for i in range(len(keys)): | |
555 | key = keys[i] | |
556 | self._array_code_entry(key, names.get(tolinux[key], "unnamed")) | |
557 | ||
558 | self._array_end() | |
559 | ||
560 | class CLanguageGenerator(LanguageSrcGenerator): | |
561 | ||
562 | def __init__(self, inttypename, strtypename, lentypename): | |
563 | self.inttypename = inttypename | |
564 | self.strtypename = strtypename | |
565 | self.lentypename = lentypename | |
566 | ||
567 | def _boilerplate(self, lines): | |
568 | print("/*") | |
569 | for line in lines: | |
570 | print(" * %s" % line) | |
571 | print("*/") | |
572 | ||
573 | def _array_start(self, varname, length, defvalue, fromtype, totype): | |
574 | self._varname = varname; | |
575 | totypename = self.strtypename if totype == self.TYPE_STRING else self.inttypename | |
576 | if fromtype in (self.TYPE_INT, self.TYPE_ENUM): | |
577 | if type(length) == str: | |
578 | print("const %s %s[%s] = {" % (totypename, varname, length)) | |
579 | else: | |
580 | print("const %s %s[%d] = {" % (totypename, varname, length)) | |
581 | else: | |
582 | print("const struct _%s {" % varname) | |
583 | print(" const %s from;" % self.strtypename) | |
584 | print(" const %s to;" % totypename) | |
585 | print("} %s[] = {" % varname) | |
586 | ||
587 | if defvalue != None: | |
588 | if totype == self.TYPE_ENUM: | |
589 | if type(length) == str: | |
590 | print(" [0 ... %s-1] = %s," % (length, defvalue)) | |
591 | else: | |
592 | print(" [0 ... 0x%x-1] = %s," % (length, defvalue)) | |
593 | else: | |
594 | if type(length) == str: | |
595 | print(" [0 ... %s-1] = 0x%x," % (length, defvalue)) | |
596 | else: | |
597 | print(" [0 ... 0x%x-1] = 0x%x," % (length, defvalue)) | |
598 | ||
599 | def _array_end(self, fromtype, totype): | |
600 | print("};") | |
601 | print("const %s %s_len = sizeof(%s)/sizeof(%s[0]);" % | |
602 | (self.lentypename, self._varname, self._varname, self._varname)) | |
603 | ||
604 | def _array_entry(self, index, value, comment, fromtype, totype): | |
605 | if value is None: | |
606 | return | |
607 | if fromtype == self.TYPE_INT: | |
608 | indexfmt = "0x%x" | |
609 | elif fromtype == self.TYPE_ENUM: | |
610 | indexfmt = "%s" | |
611 | else: | |
612 | indexfmt = "\"%s\"" | |
613 | ||
614 | if totype == self.TYPE_INT: | |
615 | valuefmt = "0x%x" | |
616 | elif totype == self.TYPE_ENUM: | |
617 | valuefmt = "%s" | |
618 | else: | |
619 | valuefmt = "\"%s\"" | |
620 | ||
621 | if fromtype != self.TYPE_STRING: | |
622 | print((" [" + indexfmt + "] = " + valuefmt + ", /* %s */") % (index, value, comment)) | |
623 | else: | |
624 | print((" {" + indexfmt + ", " + valuefmt + "}, /* %s */") % (index, value, comment)) | |
625 | ||
626 | class CppLanguageGenerator(CLanguageGenerator): | |
627 | ||
628 | def _array_start(self, varname, length, defvalue, fromtype, totype): | |
629 | if fromtype == self.TYPE_ENUM: | |
630 | raise NotImplementedError("Enums not supported as source in C++ generator") | |
631 | totypename = "const " + self.strtypename if totype == self.TYPE_STRING else self.inttypename | |
632 | if fromtype == self.TYPE_INT: | |
633 | print("#include <vector>") | |
634 | print("const std::vector<%s> %s = {" % (totypename, varname)) | |
635 | else: | |
636 | print("#include <map>") | |
637 | print("#include <string>") | |
638 | print("const std::map<const std::string, %s> %s = {" % (totypename, varname)) | |
639 | ||
640 | def _array_end(self, fromtype, totype): | |
641 | print("};") | |
642 | ||
643 | # designated initializers not available in C++ | |
644 | def _array_entry(self, index, value, comment, fromtype, totype): | |
645 | if fromtype == self.TYPE_STRING: | |
646 | return super(CppLanguageGenerator, self)._array_entry(index, value, comment, fromtype, totype) | |
647 | ||
648 | if value is None: | |
649 | print(" 0, /* %s */" % comment) | |
650 | elif totype == self.TYPE_INT: | |
651 | print(" 0x%x, /* %s */" % (value, comment)) | |
652 | elif totype == self.TYPE_ENUM: | |
653 | print(" %s, /* %s */" % (value, comment)) | |
654 | else: | |
655 | print(" \"%s\", /* %s */" % (value, comment)) | |
656 | ||
657 | class StdCLanguageGenerator(CLanguageGenerator): | |
658 | ||
659 | def __init__(self): | |
660 | super(StdCLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") | |
661 | ||
662 | class StdCppLanguageGenerator(CppLanguageGenerator): | |
663 | ||
664 | def __init__(self): | |
665 | super(StdCppLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") | |
666 | ||
667 | class GLib2LanguageGenerator(CLanguageGenerator): | |
668 | ||
669 | def __init__(self): | |
670 | super(GLib2LanguageGenerator, self).__init__("guint16", "gchar *", "guint") | |
671 | ||
672 | class PythonLanguageGenerator(LanguageSrcGenerator): | |
673 | ||
674 | def _boilerplate(self, lines): | |
675 | print("#") | |
676 | for line in lines: | |
677 | print("# %s" % line) | |
678 | print("#") | |
679 | ||
680 | def _array_start(self, varname, length, defvalue, fromtype, totype): | |
681 | if fromtype == self.TYPE_ENUM: | |
682 | raise NotImplementedError("Enums not supported as source in Python generator") | |
683 | ||
684 | if fromtype != self.TYPE_STRING: | |
685 | print("%s = [" % varname) | |
686 | else: | |
687 | print("%s = {" % varname) | |
688 | ||
689 | def _array_end(self, fromtype, totype): | |
690 | if fromtype != self.TYPE_STRING: | |
691 | print("]") | |
692 | else: | |
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(" None, # %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\": None, # %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\": %s, # %s" % (index, value, comment)) | |
712 | else: | |
713 | print(" \"%s\": \"%s\", # %s" % (index, value, comment)) | |
714 | ||
715 | class PerlLanguageGenerator(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, defvalue, fromtype, totype): | |
724 | if fromtype == self.TYPE_ENUN: | |
725 | raise NotImplementedError("Enums not supported as source in Python generator") | |
726 | if fromtype == self.TYPE_INT: | |
727 | print("my @%s = (" % varname) | |
728 | else: | |
729 | print("my %%%s = (" % varname) | |
730 | ||
731 | def _array_end(self, fromtype, totype): | |
732 | print(");") | |
733 | ||
734 | def _array_entry(self, index, value, comment, fromtype, totype): | |
735 | if fromtype == self.TYPE_INT: | |
736 | if value is None: | |
737 | print(" undef, # %s" % (comment)) | |
738 | elif totype == self.TYPE_INT: | |
739 | print(" 0x%x, # %s" % (value, comment)) | |
740 | elif totype == self.TYPE_ENUM: | |
741 | print(" %s, # %s" % (value, comment)) | |
742 | else: | |
743 | print(" \"%s\", # %s" % (value, comment)) | |
744 | else: | |
745 | if value is None: | |
746 | print(" \"%s\", undef, # %s" % (index, comment)) | |
747 | elif totype == self.TYPE_INT: | |
748 | print(" \"%s\", 0x%x, # %s" % (index, value, comment)) | |
749 | elif totype == self.TYPE_ENUM: | |
750 | print(" \"%s\", 0x%x, # %s" % (index, value, comment)) | |
751 | else: | |
752 | print(" \"%s\", \"%s\", # %s" % (index, value, comment)) | |
753 | ||
754 | class JavaScriptLanguageGenerator(LanguageSrcGenerator): | |
755 | ||
756 | def _boilerplate(self, lines): | |
757 | print("/*") | |
758 | for line in lines: | |
759 | print(" * %s" % line) | |
760 | print("*/") | |
761 | ||
762 | def _array_start(self, varname, length, defvalue, fromtype, totype): | |
763 | print("export default {") | |
764 | ||
765 | def _array_end(self, fromtype, totype): | |
766 | print("};") | |
767 | ||
768 | def _array_entry(self, index, value, comment, fromtype, totype): | |
769 | if value is None: | |
770 | return | |
771 | ||
772 | if fromtype == self.TYPE_INT: | |
773 | fromfmt = "0x%x" | |
774 | elif fromtype == self.TYPE_ENUM: | |
775 | fromfmt = "%s" | |
776 | else: | |
777 | fromfmt = "\"%s\"" | |
778 | ||
779 | if totype == self.TYPE_INT: | |
780 | tofmt = "0x%x" | |
781 | elif totype == self.TYPE_ENUM: | |
782 | tofmt = "%s" | |
783 | else: | |
784 | tofmt = "\"%s\"" | |
785 | ||
786 | print((" " + fromfmt + ": " + tofmt + ", /* %s */") % (index, value, comment)) | |
787 | ||
788 | class PodLanguageGenerator(LanguageDocGenerator): | |
789 | ||
790 | def _boilerplate(self, lines): | |
791 | print("#") | |
792 | for line in lines: | |
793 | print("# %s" % line) | |
794 | print("#") | |
795 | ||
796 | def _array_start_name_doc(self, varname, namemap): | |
797 | print("=head1 %s" % varname) | |
798 | print("") | |
799 | print("List of %s key code names, with corresponding key code values" % namemap) | |
800 | print("") | |
801 | print("=over 4") | |
802 | print("") | |
803 | ||
804 | def _array_start_code_doc(self, varname, codemap, namemap): | |
805 | print("=head1 %s" % varname) | |
806 | print("") | |
807 | print("List of %s key code values, with corresponding %s key code names" % (codemap, namemap)) | |
808 | print("") | |
809 | print("=over 4") | |
810 | print("") | |
811 | ||
812 | def _array_end(self): | |
813 | print("=back") | |
814 | print("") | |
815 | ||
816 | def _array_name_entry(self, value, name): | |
817 | print("=item %s" % name) | |
818 | print("") | |
819 | print("Key value %d (0x%x)" % (value, value)) | |
820 | print("") | |
821 | ||
822 | def _array_code_entry(self, value, name): | |
823 | print("=item %d (0x%x)" % (value, value)) | |
824 | print("") | |
825 | print("Key name %s" % name) | |
826 | print("") | |
827 | ||
828 | SRC_GENERATORS = { | |
829 | "stdc": StdCLanguageGenerator(), | |
830 | "stdc++": StdCppLanguageGenerator(), | |
831 | "glib2": GLib2LanguageGenerator(), | |
832 | "python2": PythonLanguageGenerator(), | |
833 | "python3": PythonLanguageGenerator(), | |
834 | "perl": PerlLanguageGenerator(), | |
835 | "js": JavaScriptLanguageGenerator(), | |
836 | } | |
837 | DOC_GENERATORS = { | |
838 | "pod": PodLanguageGenerator(), | |
839 | } | |
840 | ||
841 | def code_map(args): | |
842 | database = Database() | |
843 | database.load(args.keymaps) | |
844 | ||
845 | cliargs = ["keymap-gen", "--lang=%s" % args.lang] | |
846 | if args.varname is not None: | |
847 | cliargs.append("--varname=%s" % args.varname) | |
848 | cliargs.extend(["code-map", "keymaps.csv", args.frommapname, args.tomapname]) | |
849 | SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) | |
850 | ||
851 | SRC_GENERATORS[args.lang].generate_code_map(args.varname, database, args.frommapname, args.tomapname) | |
852 | ||
853 | def code_table(args): | |
854 | database = Database() | |
855 | database.load(args.keymaps) | |
856 | ||
857 | cliargs = ["keymap-gen", "--lang=%s" % args.lang] | |
858 | if args.varname is not None: | |
859 | cliargs.append("--varname=%s" % args.varname) | |
860 | cliargs.extend(["code-table", "keymaps.csv", args.mapname]) | |
861 | SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) | |
862 | ||
863 | SRC_GENERATORS[args.lang].generate_code_table(args.varname, database, args.mapname) | |
864 | ||
865 | def name_map(args): | |
866 | database = Database() | |
867 | database.load(args.keymaps) | |
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-map", "keymaps.csv", args.frommapname, args.tomapname]) | |
873 | SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) | |
874 | ||
875 | SRC_GENERATORS[args.lang].generate_name_map(args.varname, database, args.frommapname, args.tomapname) | |
876 | ||
877 | def name_table(args): | |
878 | database = Database() | |
879 | database.load(args.keymaps) | |
880 | ||
881 | ||
882 | cliargs = ["keymap-gen", "--lang=%s" % args.lang] | |
883 | if args.varname is not None: | |
884 | cliargs.append("--varname=%s" % args.varname) | |
885 | cliargs.extend(["name-table", "keymaps.csv", args.mapname]) | |
886 | SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) | |
887 | ||
888 | SRC_GENERATORS[args.lang].generate_name_table(args.varname, database, args.mapname) | |
889 | ||
890 | def code_docs(args): | |
891 | database = Database() | |
892 | database.load(args.keymaps) | |
893 | ||
894 | ||
895 | cliargs = ["keymap-gen", "--lang=%s" % args.lang] | |
896 | if args.varname is not None: | |
897 | cliargs.append("--varname=%s" % args.varname) | |
898 | cliargs.extend(["code-docs", "keymaps.csv", args.mapname]) | |
899 | DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) | |
900 | ||
901 | DOC_GENERATORS[args.lang].generate_code_docs(args.varname, database, args.mapname) | |
902 | ||
903 | def name_docs(args): | |
904 | database = Database() | |
905 | database.load(args.keymaps) | |
906 | ||
907 | ||
908 | cliargs = ["keymap-gen", "--lang=%s" % args.lang] | |
909 | if args.varname is not None: | |
910 | cliargs.append("--varname=%s" % args.varname) | |
911 | cliargs.extend(["name-docs", "keymaps.csv", args.mapname]) | |
912 | DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) | |
913 | ||
914 | DOC_GENERATORS[args.lang].generate_name_docs(args.varname, database, args.mapname) | |
915 | ||
916 | def usage(): | |
917 | print ("Please select a command:") | |
918 | print (" 'code-map', 'code-table', 'name-map', 'name-table', 'docs'") | |
919 | sys.exit(1) | |
920 | ||
921 | def main(): | |
922 | parser = argparse.ArgumentParser() | |
923 | ||
924 | parser.add_argument("--lang", default="stdc", | |
925 | help="Output language, (src=%s, doc=%s)" % ( | |
926 | ",".join(SRC_GENERATORS.keys()), | |
927 | ",".join(DOC_GENERATORS.keys()))) | |
928 | parser.add_argument("--varname", default=None, | |
929 | help="Data variable name") | |
930 | ||
931 | subparsers = parser.add_subparsers(help="sub-command help") | |
932 | ||
933 | codemapparser = subparsers.add_parser("code-map", help="Generate a mapping between code tables") | |
934 | codemapparser.add_argument("keymaps", help="Path to keymap CSV data file") | |
935 | codemapparser.add_argument("frommapname", help="Source code table name") | |
936 | codemapparser.add_argument("tomapname", help="Target code table name") | |
937 | codemapparser.set_defaults(func=code_map) | |
938 | ||
939 | codetableparser = subparsers.add_parser("code-table", help="Generate a flat code table") | |
940 | codetableparser.add_argument("keymaps", help="Path to keymap CSV data file") | |
941 | codetableparser.add_argument("mapname", help="Code table name") | |
942 | codetableparser.set_defaults(func=code_table) | |
943 | ||
944 | namemapparser = subparsers.add_parser("name-map", help="Generate a mapping to names") | |
945 | namemapparser.add_argument("keymaps", help="Path to keymap CSV data file") | |
946 | namemapparser.add_argument("frommapname", help="Source code table name") | |
947 | namemapparser.add_argument("tomapname", help="Target name table name") | |
948 | namemapparser.set_defaults(func=name_map) | |
949 | ||
950 | nametableparser = subparsers.add_parser("name-table", help="Generate a flat name table") | |
951 | nametableparser.add_argument("keymaps", help="Path to keymap CSV data file") | |
952 | nametableparser.add_argument("mapname", help="Name table name") | |
953 | nametableparser.set_defaults(func=name_table) | |
954 | ||
955 | codedocsparser = subparsers.add_parser("code-docs", help="Generate code documentation") | |
956 | codedocsparser.add_argument("keymaps", help="Path to keymap CSV data file") | |
957 | codedocsparser.add_argument("mapname", help="Code table name") | |
958 | codedocsparser.set_defaults(func=code_docs) | |
959 | ||
960 | namedocsparser = subparsers.add_parser("name-docs", help="Generate name documentation") | |
961 | namedocsparser.add_argument("keymaps", help="Path to keymap CSV data file") | |
962 | namedocsparser.add_argument("mapname", help="Name table name") | |
963 | namedocsparser.set_defaults(func=name_docs) | |
964 | ||
965 | args = parser.parse_args() | |
966 | if hasattr(args, "func"): | |
967 | args.func(args) | |
968 | else: | |
969 | usage() | |
970 | ||
971 | ||
972 | main() |