Add keymapper functionality to change shift behavior and de-couple different keys for shift status, to support Programmer Dvorak and basically any other format

This commit is contained in:
Travis Burtrum 2014-12-22 01:35:31 -05:00
parent 61cd9779d5
commit 7828a7990e
3 changed files with 121 additions and 23 deletions

View File

@ -94,6 +94,9 @@ else:
if args.keymap:
from uinputmapper.keymapper import parse_keymap
runtime_keymaps, active_keymap_index, revert_keymap_index, active_keymap, revert_default_code, switch_layout_codes, switch_layout_mode, num_codes_to_index = parse_keymap(args, rev_event_keys, event_keys)
shift_down = -1
rshift = event_keys[1]['KEY_RIGHTSHIFT']
lshift = event_keys[1]['KEY_LEFTSHIFT']
if args.direct:
# setup a new input device
@ -136,13 +139,25 @@ while True:
ev = f.next_event()
pre_event = None
post_event = None
if args.keymap:
#if ev.type == 4: # MSC_SCAN, works for some keyboards (serio) but not others (usb), not needed if we grab the input
# ev.value = active_keymap[ev.value]
if ev.type == 1: # EV_KEY
ev.code = active_keymap[ev.code]
new_code, invert_shift = active_keymap[ev.code].get_code(ev.code, shift_down != -1)
# we want some events to happen when a key is first pressed or lifted
if ev.value == 1: # pressed
if ev.code == lshift or ev.code == rshift: # shift
shift_down = ev.code
if invert_shift: # swap shift behavior
pre_event = input_event(ev.time, 1, shift_down, 0)
if shift_down == -1:
pre_event.code = lshift
pre_event.value = 1
if ev.code == revert_default_code:
active_keymap = runtime_keymaps[revert_keymap_index]
elif ev.code in switch_layout_codes:
@ -157,11 +172,22 @@ while True:
# and continue so as to not send this key
continue
elif ev.value == 0: # lifted
if ev.code == lshift or ev.code == rshift: # shift
shift_down = -1
if invert_shift: # swap shift behavior
post_event = input_event(ev.time, 1, shift_down, 1)
if shift_down == -1:
post_event.code = lshift
post_event.value = 0
if ev.code == revert_default_code:
active_keymap = runtime_keymaps[active_keymap_index]
elif ev.code in switch_layout_codes:
switch_layout_codes[ev.code] = False
switch_layout_mode = False
# now actually map the key
ev.code = new_code
if args.direct:
# try to fire our own events?
@ -170,20 +196,31 @@ while True:
d = ofs[idx]
else:
d = ofs[i]
if pre_event is not None:
d.fire_event(pre_event)
d.fire_event(ev)
if post_event is not None:
d.fire_event(post_event)
elif args.dump:
for ev in [pre_event, ev, post_event]:
if ev is not None:
try:
#print 'ev.type:', ev.type
print i, ev.time.tv_sec, ev.time.tv_usec
s = '%s(%d) %s(%d) %d' % (rev_events[ev.type], ev.type,
rev_event_keys[ev.type][ev.code], ev.code, ev.value)
s = '%s(%d) %s(%d) %d' % (rev_events[ev.type], ev.type, rev_event_keys[ev.type][ev.code], ev.code, ev.value)
print 'Event type:', s
except KeyError:
pass
else:
if not args.compat:
if pre_event is not None:
p.dump((i, pre_event))
p.dump((i, ev))
if post_event is not None:
p.dump((i, post_event))
else:
p.dump((i, (ev.time.tv_sec, ev.time.tv_usec,
ev.type, ev.code, ev.value)))
if pre_event is not None:
p.dump((i, (pre_event.time.tv_sec, pre_event.time.tv_usec, pre_event.type, pre_event.code, pre_event.value)))
p.dump((i, (ev.time.tv_sec, ev.time.tv_usec, ev.type, ev.code, ev.value)))
if post_event is not None:
p.dump((i, (post_event.time.tv_sec, post_event.time.tv_usec, post_event.type, post_event.code, post_event.value)))
sys.stdout.flush()

View File

@ -9,8 +9,8 @@ revert_default_key = 'LEFTCTRL'
revert_keymap_index = 0
# this is the default index to use when the program first starts
# in this case, 1 means Dvorak
default_keymap_index = 1
# in this case, 2 means modified Progammer Dvorak
default_keymap_index = 2
# these are the keymaps available, you can add as many as you want or re-order them, just be aware the mapping is
# always done from the first one to all subsequent ones, so you probably want to leave QWERTY or similar up top
@ -33,4 +33,24 @@ keymaps = [
LSFT,SCLN,Q, J, K, X, B, M, W, V, Z, RSFT, UP, P1, P2, P3,
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT
""",
# Programmer Dvorak http://www.kaufmann.no/roland/dvorak/ without numpad re-ordered
# (to be strict QUOT/SCLN are swapped from regular dvorak, though I can't understand why so I ignored it...)
"""
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
^4:^GRV, ^7:^5, LBRC:7, ^LBRC:5, ^RBRC:3, ^9:1, EQL:9, ^8:0, ^0:2, ^EQL:4, RBRC:6, ^1:8, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
TAB, QUOT, COMM, DOT, P, Y, F, G, C, R, L, SLSH, ^2:^6, BSLS, DEL, END, PGDN, P7:^A, P8:^B, P9:^C,
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
LSFT, SCLN, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P1:EQL, P2:X, P3:^SCLN,
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
""",
# Programmer Dvorak http://www.kaufmann.no/roland/dvorak/
# (to be strict QUOT/SCLN are swapped from regular dvorak, though I can't understand why so I ignored it...)
"""
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
^4:^GRV, ^7:^5, LBRC:7, ^LBRC:5, ^RBRC:3, ^9:1, EQL:9, ^8:0, ^0:2, ^EQL:4, RBRC:6, ^1:8, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
TAB, QUOT, COMM, DOT, P, Y, F, G, C, R, L, SLSH, ^2:^6, BSLS, DEL, END, PGDN, P1:^A, P2:^B, P3:^C,
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
LSFT, SCLN, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P7:EQL, P8:X, P9:^SCLN,
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
""",
]

View File

@ -3,6 +3,33 @@
Module to help with creating fast keycode mapping arrays
"""
class KeyCodeMapper(object):
def __init__(self, noshift_code, noshift_invert_shift, shift_code, shift_invert_shift):
self.noshift_code = noshift_code
self.noshift_invert_shift = noshift_invert_shift
self.shift_code = shift_code
self.shift_invert_shift = shift_invert_shift
def get_code(self, old_code, shift_down):
# returns new_code, invert_shift
if shift_down:
return self.shift_code, self.shift_invert_shift
else:
return self.noshift_code, self.noshift_invert_shift
def __repr__(self):
return "KCM{%3s (%5s) : %3s (%5s)}" % (self.noshift_code, self.noshift_invert_shift, self.shift_code, self.shift_invert_shift)
class NoopKCM(object):
def get_code(self, old_code, shift_down):
# returns new_code, invert_shift
return old_code, False
def __repr__(self):
return "KCM{NOOP}"
def parse_keymap(args, rev_event_keys, event_keys):
"""
Reads in a keymap configuration and returns structures to implement it
@ -67,31 +94,43 @@ def parse_keymap(args, rev_event_keys, event_keys):
'SLSH':'SLASH',
'SPC':'SPACE',
}
shift_key_list = []
keymap_list = []
for keymap in keymaps:
key_list = []
keymap_list.append(key_list)
for key in keymap.split(','):
key = key.strip()
key = key.strip() # remove whitespace
key_arr = key.split(':')
noshift_invert_shift = '^' == key_arr[0][0]
key = key_arr[0].strip('^')
new_key = 'KEY_'+short_to_long.get(key, key)
if args.dump and new_key not in event_keys[1]: # todo: probably should exit with some helpful error here?
#print 'Key', key, 'does not exist!'
#print "'", key, "':'',"
print "'%s':''," % key
key_list.append(new_key)
noshift_code = event_keys[1][new_key]
if len(key_arr) == 1:
shift_invert_shift = noshift_invert_shift
shift_code = noshift_code
else:
shift_invert_shift = '^' != key_arr[1][0]
key = key_arr[1].strip('^')
new_key = 'KEY_'+short_to_long.get(key, key)
if args.dump and new_key not in event_keys[1]: # todo: probably should exit with some helpful error here?
print "'%s':''," % key
shift_code = event_keys[1][new_key]
key_list.append(KeyCodeMapper(noshift_code, noshift_invert_shift, shift_code, shift_invert_shift))
#pprint.pprint(keymap_list)
#exit(0)
mykeymaps = []
default_keymap = keymap_list[0]
keymap_range = range(0, len(default_keymap))
from array import array
for keymap in keymap_list:
mykeymap = {}
mykeymaps.append(mykeymap)
for y in keymap_range:
if default_keymap[y] != keymap[y]:
mykeymap[default_keymap[y]] = keymap[y]
mykeymap[default_keymap[y].noshift_code] = keymap[y]
#pprint.pprint(mykeymaps)
#exit(0)
@ -103,17 +142,19 @@ def parse_keymap(args, rev_event_keys, event_keys):
#exit(0)
# convert mykeymap once, at startup, to a faster array, possibly using a little more memory
keycnt_range = range(0, event_keys[1]['KEY_CNT'])
noop = NoopKCM()
runtime_keymaps = []
for mykeymap in mykeymaps:
mycodemap = array('H')
mycodemap = []
runtime_keymaps.append(mycodemap)
for x in keycnt_range:
key = rev_event_keys[1].get(x, 'NO_KEY_EXISTS_FOR_THIS_INDEX')
value = event_keys[1].get(mykeymap.get(key, key), x)
mycodemap.append(value)
mycodemap.append(mykeymap.get(x, noop))
#print 'mycodemap: ', mycodemap
#pprint.pprint(runtime_keymaps)
#exit(0)
active_keymap_index = default_keymap_index
revert_default_code = event_keys[1]['KEY_'+revert_default_key]
active_keymap = runtime_keymaps[active_keymap_index]