25
25
from .keycode_us_ref import Keycode
26
26
from .virtualkey_table_us import VIRTUAL_KEY_US
27
27
28
- """ debug level dev: only show dev prints """
28
+ """ debug level dev: only show dev prints (use for print debugging) """
29
29
DEBUG_DEV = - 1
30
30
""" debug level error: only show errors """
31
31
DEBUG_ERROR = 1
71
71
72
72
"""
73
73
74
+
74
75
def _echo (* text , nl = True , ** kwargs ):
75
- """ printout things using click with some defaults """
76
+ """printout things using click joining all varargs """
76
77
text = [
77
78
(item if isinstance (item , str ) else repr (item ))
78
79
for item in text
79
80
]
80
81
click .secho (" " .join (text ), nl = nl , ** kwargs )
81
82
83
+
82
84
def echo (* text , nl = True , ** kwargs ):
83
- """ print as info """
85
+ """print as info"""
84
86
if DEBUG_LEVEL >= DEBUG_INFO :
85
87
_echo (* text , nl = nl , ** kwargs )
86
88
89
+
87
90
def echoE (* text , nl = True , ** kwargs ):
88
- """ print as error, in red by default """
91
+ """print as error, in red by default"""
89
92
if "fg" not in kwargs :
90
93
kwargs ["fg" ] = "red"
91
94
if DEBUG_LEVEL >= DEBUG_ERROR :
92
95
_echo (* text , nl = nl , ** kwargs )
93
96
94
- def echoF (* text , nl = True , ** kwargs ):
95
- """ print only in dev mode """
97
+
98
+ def echoD (* text , nl = True , ** kwargs ):
99
+ """print only in dev mode, use for print debugging"""
96
100
if DEBUG_LEVEL == DEBUG_DEV :
97
101
_echo (* text , nl = nl , ** kwargs )
98
102
99
103
100
104
def jprint (data , nl = True , ** kwargs ):
105
+ """dump a structure as json"""
101
106
echo ("<<< " + str (len (data )) + " >>>" , nl , ** kwargs )
102
107
echo (json .dumps (data , indent = 2 ), nl , ** kwargs )
103
108
104
109
105
- def filter_codepoints (text ):
106
- return text .replace ("\r " , "\n " )
110
+ def get_v_to_k ():
111
+ """create the reverse virtualkey/keyname table"""
112
+ virtualkey_to_keyname = {}
113
+ for name , vkey in name_to_virtualkey .items ():
114
+ if vkey not in virtualkey_to_keyname :
115
+ virtualkey_to_keyname [vkey ] = []
116
+ virtualkey_to_keyname [vkey ].append (name )
117
+ return virtualkey_to_keyname
118
+
119
+
120
+ virtualkey_to_keyname = get_v_to_k ()
107
121
108
122
109
- virtualkey_to_keyname = {}
110
- for name , vkey in name_to_virtualkey .items ():
111
- if vkey not in virtualkey_to_keyname :
112
- virtualkey_to_keyname [vkey ] = []
113
- virtualkey_to_keyname [vkey ].append (name )
123
+ def filter_codepoints (text ):
124
+ """filter converted codepoints from XML"""
125
+ return text .replace ("\r " , "\n " )
114
126
115
127
116
128
def list_keycode_name (key , value ):
129
+ """list the keycode names associated with a virtual key name"""
117
130
output = []
118
131
if key in virtualkey_to_keyname :
119
132
for name in virtualkey_to_keyname [key ]:
120
- output .append ( (name , value ) )
133
+ output .append ((name , value ))
121
134
else :
122
135
output = [(key , value )]
123
136
return output
124
137
125
138
126
139
def get_name_to_keycode ():
140
+ """
141
+ create the table mapping virtual key names to keycodes
142
+ from the adafruit_hid Keycode file
143
+ """
127
144
name_to_kc = {}
128
145
kcnums = [
129
146
(name , getattr (Keycode , name ))
@@ -161,6 +178,16 @@ def modif(res):
161
178
162
179
163
180
def get_vk_to_sc (data ):
181
+ """
182
+ Analyse the XML file to make the table of all keys.
183
+ Each entry:
184
+ - is indexed by a virtual key name or made-up name
185
+ - is associated with a scancode
186
+ - has a letter associated with different modifiers (or lack thereof)
187
+ - dead keys and combined keys have additional information
188
+ - dead = True means it's the dead key (don't press it alone)
189
+ - firstkey/secondkey are the respective keys for dead key combinations
190
+ """
164
191
keything = xmltodict .parse (data )
165
192
physical_keys = keything ["KeyboardLayout" ]["PhysicalKeys" ]["PK" ]
166
193
# jprint(physical_keys)
@@ -210,10 +237,12 @@ def get_vk_to_sc(data):
210
237
firstkey = res ["DeadKeyTable" ]["@Accent" ]
211
238
# the name of the dead key for the Keycode table
212
239
if "@Name" in res ["DeadKeyTable" ]:
213
- deadname = res ["DeadKeyTable" ]["@Name" ].replace (" " ,"_" )
240
+ deadname = res ["DeadKeyTable" ]["@Name" ].replace (" " , "_" )
214
241
else :
215
242
# if none, generate one with "_" to exclude it from Keycode
216
- deadname = "_accent" + "" .join (["_" + str (ord (x )) for x in firstkey ])
243
+ deadname = "_accent" + "" .join (
244
+ ["_" + str (ord (x )) for x in firstkey ]
245
+ )
217
246
# dead key base: in keycode, not in layout
218
247
if deadname not in vk_to_sc :
219
248
vk_to_sc [deadname ] = {
@@ -242,6 +271,7 @@ def get_vk_to_sc(data):
242
271
243
272
244
273
def get_scancode_to_keycode ():
274
+ """create the table associating scancodes and keycodes from the US XML file"""
245
275
name_to_kc = get_name_to_keycode ()
246
276
name_to_kc_left = set (name_to_kc )
247
277
vk_to_sc = get_vk_to_sc (VIRTUAL_KEY_US )
@@ -257,42 +287,49 @@ def get_scancode_to_keycode():
257
287
return sc_to_kc
258
288
259
289
260
- # TODO: add non-US scancodes/keycodes in `sc_to_kc`
290
+ # TODO: are there missing non-US scancodes/keycodes in `sc_to_kc` ?
261
291
262
292
263
- """
264
- The actual conversion from scan codes to key codes
265
- Missing unidentified names
266
- NUMPAD is particularly missing (it's refed as arrows, page up, etc.)
267
- """
268
- # jprint(sc_to_kc)
269
- # print("name_to_kc_left", len(name_to_kc_left), sorted(name_to_kc_left))
270
- # print("vk_to_sc_left", len(vk_to_sc_left), sorted(vk_to_sc_left))
271
- # kc_to_sc = dict([(y,x) for (x,y) in sc_to_kc.items()])
272
-
273
293
########################################################################
274
294
275
295
276
296
class LayoutData :
297
+ """
298
+ Asimple struct class to carry around the layout information.
299
+ - asciis has the keycode information for each low ascii character (with shift bit)
300
+ - charas has said characters, for display in the comment string and testing existence
301
+ - atgr is the list of letters that need alt-gr pressed
302
+ - high is the list of high-ascii/unicode letters and their keycode
303
+ - keycodes is the table associating key names with keycodes (for the Keycode class)
304
+ - combined is the table of combined keys
305
+ A combined key has two bytes:
306
+ - the keycode to the first key, with the high bit as the shift bit
307
+ - the letter for the second key (assumed to be low ascii)
308
+ - the second key's high bit is the altgr bit for the first key
309
+ """
277
310
def __init__ (self , asciis , charas , altgr , high , keycodes , combined ):
278
311
self .asciis = asciis
279
312
self .charas = charas
280
313
self .altgr = altgr
281
314
self .high = high
282
315
self .keycodes = keycodes
283
316
self .combined = combined
317
+
284
318
def __repr__ (self ):
285
- return repr ({
286
- "asciis" : self .asciis ,
287
- "charas" : self .charas ,
288
- "altgr" : self .altgr ,
289
- "high" : self .high ,
290
- "keycodes" : self .keycodes ,
291
- "combined" : self .combined ,
292
- })
319
+ return repr (
320
+ {
321
+ "asciis" : self .asciis ,
322
+ "charas" : self .charas ,
323
+ "altgr" : self .altgr ,
324
+ "high" : self .high ,
325
+ "keycodes" : self .keycodes ,
326
+ "combined" : self .combined ,
327
+ }
328
+ )
293
329
294
330
295
331
def get_layout_data (virtual_key_defs_lang ):
332
+ """Create the layout data from a language file."""
296
333
asciis = [0 ] * 128
297
334
charas = ["" ] * 128
298
335
NEED_ALTGR = []
@@ -347,15 +384,15 @@ def get_layout_data(virtual_key_defs_lang):
347
384
asciis [pos ] = keycode
348
385
charas [pos ] = letter
349
386
else :
350
- echoF ("dead" , key_info )
387
+ echoD ("dead" , key_info )
351
388
KEYCODES .update (list_keycode_name (virtualkey , keycode ))
352
389
# KEYCODES[virtualkey] = keycode
353
390
else :
354
391
if letter not in HIGHER_ASCII :
355
392
if not dead :
356
393
HIGHER_ASCII [letter ] = keycode
357
394
else :
358
- echoF ("dead" , key_info )
395
+ echoD ("dead" , key_info )
359
396
KEYCODES .update (list_keycode_name (virtualkey , keycode ))
360
397
# KEYCODES[virtualkey] = keycode
361
398
else :
@@ -430,6 +467,7 @@ def add_alt_gr(letter):
430
467
431
468
432
469
def make_layout_file (layout_data ):
470
+ """make the layout file contents"""
433
471
output_file_data = (
434
472
COMMON_HEADER_COPYRIGHT
435
473
+ "from keyboard_layout import KeyboardLayoutBase\n "
@@ -461,40 +499,38 @@ def make_layout_file(layout_data):
461
499
)
462
500
for k , c in layout_data .high .items ():
463
501
output_file_data += f" { repr (k )} : 0x{ c :02x} ,\n "
464
- output_file_data += (
465
- " }\n "
466
- " COMBINED_KEYS = {\n "
467
- )
502
+ output_file_data += " }\n " " COMBINED_KEYS = {\n "
468
503
for k , c in layout_data .combined .items ():
469
504
first , second , altgr = c
470
505
second = ord (second ) | altgr
471
506
output_file_data += (
472
507
f" { repr (k )} : "
473
- f"b \ "\\ x{ first :02x} \\ x{ second :02x} \" ,"
508
+ f'b "\\ x{ first :02x} \\ x{ second :02x} ",'
474
509
"\n "
475
510
)
476
- output_file_data += (
477
- " }\n "
478
- )
511
+ output_file_data += " }\n "
479
512
return output_file_data
480
513
514
+
481
515
def output_layout_file (output_file , output_file_data ):
516
+ """write out the layout file"""
482
517
with open (output_file , "w" ) as fp :
483
518
fp .write (output_file_data )
484
519
485
520
486
521
def make_keycode_file (layout_data ):
487
- output_file_data = (
488
- COMMON_HEADER_COPYRIGHT + "class Keycode:\n "
489
- )
522
+ """make the keycode file contents"""
523
+ output_file_data = COMMON_HEADER_COPYRIGHT + "class Keycode:\n "
524
+
490
525
def ck (x ):
491
526
l = x [0 ]
492
527
if len (l ) == 2 :
493
528
l = l + " "
494
529
if len (l ) > 5 :
495
530
l = l .ljust (20 )
496
531
return (len (l ), l )
497
- for name ,code in natsort .natsorted (layout_data .keycodes .items (), key = ck ):
532
+
533
+ for name , code in natsort .natsorted (layout_data .keycodes .items (), key = ck ):
498
534
# code = layout_data.keycodes[name]
499
535
if name [0 ] != "_" :
500
536
output_file_data += f" { name } = 0x{ code :02x} \n "
@@ -509,21 +545,45 @@ def modifier_bit(cls, keycode):
509
545
"""
510
546
return output_file_data
511
547
548
+
512
549
def output_keycode_file (output_file , output_file_data ):
550
+ """write out the keycode file"""
513
551
with open (output_file , "w" ) as fp :
514
552
fp .write (output_file_data )
515
553
516
554
517
555
@click .group (invoke_without_command = True )
518
- @click .option ("--keyboard" , "-k" , required = True )
519
- @click .option ("--lang" , "-l" , default = "" )
520
- @click .option ("--platform" , "-p" , default = "win" )
521
- @click .option ("--output" , "-o" , is_flag = True )
522
- @click .option ("--output-layout" , default = "" )
523
- @click .option ("--output-keycode" , default = "" )
524
- @click .option ("--debug" , "-d" , type = click .INT , default = 1 )
556
+ @click .option (
557
+ "--keyboard" , "-k" , required = True ,
558
+ help = "The XML layout file, or URL to the layout on kbdlayout.info."
559
+ )
560
+ @click .option (
561
+ "--lang" , "-l" , default = "" ,
562
+ help = "The language string to use in the output file name, defaults to the last part of the file name or the language part of the URL."
563
+ )
564
+ @click .option (
565
+ "--platform" , "-p" , default = "win" ,
566
+ help = "The platform string to use in the output file name. Only windows currently."
567
+ )
568
+ @click .option (
569
+ "--output" , "-o" , is_flag = True ,
570
+ help = "Activate writing out the layout and keycode files."
571
+ )
572
+ @click .option (
573
+ "--output-layout" , default = "" ,
574
+ help = "Override the layout output file path and name."
575
+ )
576
+ @click .option (
577
+ "--output-keycode" , default = "" ,
578
+ help = "Override the keycode output file path and name."
579
+ )
580
+ @click .option (
581
+ "--debug" , "-d" , default = 1 ,
582
+ help = "Set the debug level, -1 (dev only), 0 (silent), 1 (errors), 2 (all), default is 1"
583
+ )
525
584
@click .option ("--show" , "-s" , default = "" )
526
585
def main (keyboard , lang , platform , output , output_layout , output_keycode , debug , show ):
586
+ """Make keyboard layout files from layout XML data."""
527
587
global DEBUG_LEVEL
528
588
DEBUG_LEVEL = debug
529
589
echo (">" , keyboard , fg = "green" )
@@ -584,5 +644,6 @@ def main(keyboard, lang, platform, output, output_layout, output_keycode, debug,
584
644
if show == "keycode" or show == "s" :
585
645
print (data_keycode )
586
646
647
+
587
648
if __name__ == "__main__" :
588
649
main ()
0 commit comments