2525from .keycode_us_ref import Keycode
2626from .virtualkey_table_us import VIRTUAL_KEY_US
2727
28- """ debug level dev: only show dev prints """
28+ """ debug level dev: only show dev prints (use for print debugging) """
2929DEBUG_DEV = - 1
3030""" debug level error: only show errors """
3131DEBUG_ERROR = 1
7171
7272"""
7373
74+
7475def _echo (* text , nl = True , ** kwargs ):
75- """ printout things using click with some defaults """
76+ """printout things using click joining all varargs """
7677 text = [
7778 (item if isinstance (item , str ) else repr (item ))
7879 for item in text
7980 ]
8081 click .secho (" " .join (text ), nl = nl , ** kwargs )
8182
83+
8284def echo (* text , nl = True , ** kwargs ):
83- """ print as info """
85+ """print as info"""
8486 if DEBUG_LEVEL >= DEBUG_INFO :
8587 _echo (* text , nl = nl , ** kwargs )
8688
89+
8790def echoE (* text , nl = True , ** kwargs ):
88- """ print as error, in red by default """
91+ """print as error, in red by default"""
8992 if "fg" not in kwargs :
9093 kwargs ["fg" ] = "red"
9194 if DEBUG_LEVEL >= DEBUG_ERROR :
9295 _echo (* text , nl = nl , ** kwargs )
9396
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"""
96100 if DEBUG_LEVEL == DEBUG_DEV :
97101 _echo (* text , nl = nl , ** kwargs )
98102
99103
100104def jprint (data , nl = True , ** kwargs ):
105+ """dump a structure as json"""
101106 echo ("<<< " + str (len (data )) + " >>>" , nl , ** kwargs )
102107 echo (json .dumps (data , indent = 2 ), nl , ** kwargs )
103108
104109
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 ()
107121
108122
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 " )
114126
115127
116128def list_keycode_name (key , value ):
129+ """list the keycode names associated with a virtual key name"""
117130 output = []
118131 if key in virtualkey_to_keyname :
119132 for name in virtualkey_to_keyname [key ]:
120- output .append ( (name , value ) )
133+ output .append ((name , value ))
121134 else :
122135 output = [(key , value )]
123136 return output
124137
125138
126139def get_name_to_keycode ():
140+ """
141+ create the table mapping virtual key names to keycodes
142+ from the adafruit_hid Keycode file
143+ """
127144 name_to_kc = {}
128145 kcnums = [
129146 (name , getattr (Keycode , name ))
@@ -161,6 +178,16 @@ def modif(res):
161178
162179
163180def 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+ """
164191 keything = xmltodict .parse (data )
165192 physical_keys = keything ["KeyboardLayout" ]["PhysicalKeys" ]["PK" ]
166193 # jprint(physical_keys)
@@ -210,10 +237,12 @@ def get_vk_to_sc(data):
210237 firstkey = res ["DeadKeyTable" ]["@Accent" ]
211238 # the name of the dead key for the Keycode table
212239 if "@Name" in res ["DeadKeyTable" ]:
213- deadname = res ["DeadKeyTable" ]["@Name" ].replace (" " ,"_" )
240+ deadname = res ["DeadKeyTable" ]["@Name" ].replace (" " , "_" )
214241 else :
215242 # 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+ )
217246 # dead key base: in keycode, not in layout
218247 if deadname not in vk_to_sc :
219248 vk_to_sc [deadname ] = {
@@ -242,6 +271,7 @@ def get_vk_to_sc(data):
242271
243272
244273def get_scancode_to_keycode ():
274+ """create the table associating scancodes and keycodes from the US XML file"""
245275 name_to_kc = get_name_to_keycode ()
246276 name_to_kc_left = set (name_to_kc )
247277 vk_to_sc = get_vk_to_sc (VIRTUAL_KEY_US )
@@ -257,42 +287,49 @@ def get_scancode_to_keycode():
257287 return sc_to_kc
258288
259289
260- # TODO: add non-US scancodes/keycodes in `sc_to_kc`
290+ # TODO: are there missing non-US scancodes/keycodes in `sc_to_kc` ?
261291
262292
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-
273293########################################################################
274294
275295
276296class 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+ """
277310 def __init__ (self , asciis , charas , altgr , high , keycodes , combined ):
278311 self .asciis = asciis
279312 self .charas = charas
280313 self .altgr = altgr
281314 self .high = high
282315 self .keycodes = keycodes
283316 self .combined = combined
317+
284318 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+ )
293329
294330
295331def get_layout_data (virtual_key_defs_lang ):
332+ """Create the layout data from a language file."""
296333 asciis = [0 ] * 128
297334 charas = ["" ] * 128
298335 NEED_ALTGR = []
@@ -347,15 +384,15 @@ def get_layout_data(virtual_key_defs_lang):
347384 asciis [pos ] = keycode
348385 charas [pos ] = letter
349386 else :
350- echoF ("dead" , key_info )
387+ echoD ("dead" , key_info )
351388 KEYCODES .update (list_keycode_name (virtualkey , keycode ))
352389 # KEYCODES[virtualkey] = keycode
353390 else :
354391 if letter not in HIGHER_ASCII :
355392 if not dead :
356393 HIGHER_ASCII [letter ] = keycode
357394 else :
358- echoF ("dead" , key_info )
395+ echoD ("dead" , key_info )
359396 KEYCODES .update (list_keycode_name (virtualkey , keycode ))
360397 # KEYCODES[virtualkey] = keycode
361398 else :
@@ -430,6 +467,7 @@ def add_alt_gr(letter):
430467
431468
432469def make_layout_file (layout_data ):
470+ """make the layout file contents"""
433471 output_file_data = (
434472 COMMON_HEADER_COPYRIGHT
435473 + "from keyboard_layout import KeyboardLayoutBase\n "
@@ -461,40 +499,38 @@ def make_layout_file(layout_data):
461499 )
462500 for k , c in layout_data .high .items ():
463501 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 "
468503 for k , c in layout_data .combined .items ():
469504 first , second , altgr = c
470505 second = ord (second ) | altgr
471506 output_file_data += (
472507 f" { repr (k )} : "
473- f"b \ "\\ x{ first :02x} \\ x{ second :02x} \" ,"
508+ f'b "\\ x{ first :02x} \\ x{ second :02x} ",'
474509 "\n "
475510 )
476- output_file_data += (
477- " }\n "
478- )
511+ output_file_data += " }\n "
479512 return output_file_data
480513
514+
481515def output_layout_file (output_file , output_file_data ):
516+ """write out the layout file"""
482517 with open (output_file , "w" ) as fp :
483518 fp .write (output_file_data )
484519
485520
486521def 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+
490525 def ck (x ):
491526 l = x [0 ]
492527 if len (l ) == 2 :
493528 l = l + " "
494529 if len (l ) > 5 :
495530 l = l .ljust (20 )
496531 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 ):
498534 # code = layout_data.keycodes[name]
499535 if name [0 ] != "_" :
500536 output_file_data += f" { name } = 0x{ code :02x} \n "
@@ -509,21 +545,45 @@ def modifier_bit(cls, keycode):
509545 """
510546 return output_file_data
511547
548+
512549def output_keycode_file (output_file , output_file_data ):
550+ """write out the keycode file"""
513551 with open (output_file , "w" ) as fp :
514552 fp .write (output_file_data )
515553
516554
517555@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+ )
525584@click .option ("--show" , "-s" , default = "" )
526585def main (keyboard , lang , platform , output , output_layout , output_keycode , debug , show ):
586+ """Make keyboard layout files from layout XML data."""
527587 global DEBUG_LEVEL
528588 DEBUG_LEVEL = debug
529589 echo (">" , keyboard , fg = "green" )
@@ -584,5 +644,6 @@ def main(keyboard, lang, platform, output, output_layout, output_keycode, debug,
584644 if show == "keycode" or show == "s" :
585645 print (data_keycode )
586646
647+
587648if __name__ == "__main__" :
588649 main ()
0 commit comments