mirror of
https://github.com/moparisthebest/uinput-mapper
synced 2024-12-03 04:02:16 -05:00
Implement keymap support into input-read, along with directly writing to devices without piping to input-create
This commit is contained in:
parent
5b62ccd15a
commit
aced80c40d
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
**.pyc
|
||||
uinputmapper/uinput_gen.py
|
122
input-read
122
input-read
@ -12,28 +12,42 @@ try:
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
import optparse
|
||||
import argparse
|
||||
|
||||
_usage = 'input-read /dev/input/event<0> ... /dev/input/event<N>'
|
||||
parser = optparse.OptionParser(description='Read input devices.',
|
||||
usage = _usage,
|
||||
version='0.01')
|
||||
parser.add_option('-D', '--dump', action='store_false',
|
||||
parser = argparse.ArgumentParser(description='Read input devices.',
|
||||
usage = _usage)
|
||||
parser.add_argument('--version', action='version', version='0.01')
|
||||
parser.add_argument('-D', '--dump', action='store_false',
|
||||
default=True, help='Dump will marshall all the events to stdout')
|
||||
parser.add_option('-v', '--verbose', action='store_true',
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
default=False, help='Enable verbose mode (do not combine with -D)')
|
||||
|
||||
parser.add_option('-C', '--compat', action='store_true',
|
||||
parser.add_argument('-k', '--keymap', dest='keymap',
|
||||
help='Python keymap configuration to source')
|
||||
|
||||
parser.add_argument('-C', '--compat', action='store_true',
|
||||
help='Enable compatibility mode; for Python < 2.7')
|
||||
|
||||
args, input_file = parser.parse_args()
|
||||
parser.add_argument('-ng', '--no-grab', action='store_true',
|
||||
default=False, help='Will not grab input devices, so other programs like X might still read from them')
|
||||
|
||||
if len(input_file) == 0:
|
||||
parser.print_help()
|
||||
exit(0)
|
||||
parser.add_argument('-d', '--direct', action='store_true',
|
||||
default=False, help='Will directly create input devices to send events to, instead of piping to input-create')
|
||||
parser.add_argument('-c', '--config', metavar='config', nargs='*',
|
||||
help='Path to config files you would normally send into input-create, implies --direct')
|
||||
|
||||
parser.add_argument('input_file', metavar='input_file', nargs='+',
|
||||
help='/dev/input/event<0> ... /dev/input/event<N>')
|
||||
|
||||
args = parser.parse_args()
|
||||
args.has_config = args.config != None and len(args.config) > 0
|
||||
|
||||
#print 'args:',args
|
||||
#exit(0)
|
||||
|
||||
# Open input devices
|
||||
fs = map(InputDevice, input_file)
|
||||
fs = map(InputDevice, args.input_file)
|
||||
|
||||
# Create configuration
|
||||
config = {}
|
||||
@ -42,6 +56,13 @@ for idx, f in enumerate(fs):
|
||||
|
||||
config.update(c)
|
||||
|
||||
if args.has_config:
|
||||
# Allow configurations to change our current configuration
|
||||
args.direct = True
|
||||
for path in args.config:
|
||||
config_merge = imp.load_source('', path).config_merge
|
||||
config_merge(config)
|
||||
|
||||
if args.verbose:
|
||||
pretty_conf_print(config)
|
||||
|
||||
@ -62,17 +83,42 @@ if args.dump:
|
||||
d = f.get_exposed_events()
|
||||
for k, v in d.iteritems():
|
||||
print k + ':', ', '.join(v)
|
||||
|
||||
else:
|
||||
# Dump initial information over pickle to stdout
|
||||
p = pickle.Pickler(sys.stdout)
|
||||
|
||||
p.dump(len(fs))
|
||||
|
||||
p.dump(config)
|
||||
|
||||
sys.stdout.flush()
|
||||
|
||||
# parse keymap
|
||||
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)
|
||||
|
||||
if args.direct:
|
||||
# setup a new input device
|
||||
m = KeyMapper(config)
|
||||
|
||||
# Get number of output devices (determined from config)
|
||||
from uinputmapper.mapper import get_exported_device_count
|
||||
nofd = get_exported_device_count(config)
|
||||
# Create and expose uinput devices
|
||||
ofs = []
|
||||
for fd in xrange(nofd):
|
||||
d = UInputDevice()
|
||||
m.expose(d, fd)
|
||||
d.setup('keymapper input device'+str(fd))
|
||||
ofs.append(d)
|
||||
|
||||
# grab all inputs so nothing else reads them :)
|
||||
if not args.no_grab:
|
||||
for idx, f in enumerate(fs):
|
||||
f.grab()
|
||||
|
||||
# try to free as much memory as we can before entering loop
|
||||
from gc import collect as gc_collect
|
||||
gc_collect()
|
||||
|
||||
while True:
|
||||
events = pp.poll()
|
||||
|
||||
@ -90,20 +136,54 @@ while True:
|
||||
|
||||
ev = f.next_event()
|
||||
|
||||
if args.dump:
|
||||
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]
|
||||
# we want some events to happen when a key is first pressed or lifted
|
||||
if ev.value == 1: # pressed
|
||||
if ev.code == revert_default_code:
|
||||
active_keymap = runtime_keymaps[revert_keymap_index]
|
||||
elif ev.code in switch_layout_codes:
|
||||
switch_layout_codes[ev.code] = True
|
||||
# so loop through switch_layout_codes and see if all are True
|
||||
switch_layout_mode = True
|
||||
for x,y in switch_layout_codes.iteritems():
|
||||
switch_layout_mode &= y
|
||||
elif switch_layout_mode and ev.code in num_codes_to_index:
|
||||
active_keymap_index = num_codes_to_index[ev.code]
|
||||
active_keymap = runtime_keymaps[active_keymap_index]
|
||||
# and continue so as to not send this key
|
||||
continue
|
||||
elif ev.value == 0: # lifted
|
||||
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
|
||||
|
||||
if args.direct:
|
||||
# try to fire our own events?
|
||||
if args.has_config:
|
||||
idx, ev = m.map_event(ev, i)
|
||||
d = ofs[idx]
|
||||
else:
|
||||
d = ofs[i]
|
||||
d.fire_event(ev)
|
||||
elif args.dump:
|
||||
try:
|
||||
#print 'ev.type:', ev.type
|
||||
print i, ev.time.tv_sec, ev.time.tv_usec
|
||||
s = '%s %s %d' % (rev_events[ev.type],
|
||||
rev_event_keys[ev.type][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:
|
||||
p.dump((i, ev))
|
||||
else:
|
||||
p.dump((i, (ev.time.tv_sec, ev.time.tv_usec,
|
||||
ev.type, ev.code, ev.value)))
|
||||
|
||||
sys.stdout.flush()
|
||||
|
36
keymaps/dvorak.py
Normal file
36
keymaps/dvorak.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
# pressing all of these keys along with a number key representing the index of keymaps changes the layout
|
||||
# ie, in this case pressing both and 0 would go QWERTY, while both and 1 would go dvorak
|
||||
switch_layout_keys = ['LEFTSHIFT','RIGHTSHIFT']
|
||||
|
||||
# pressing QWERTY reverts to the index specified in revert_keymap_index for only the duration of the pressing
|
||||
# used so QWERTY shortcuts like Ctrl+C still work
|
||||
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
|
||||
|
||||
# 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
|
||||
keymaps = [
|
||||
# default key layout, QWERTY in this case
|
||||
"""
|
||||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
|
||||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS,
|
||||
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSLS, DEL, END, PGDN, P7, P8, P9,
|
||||
CAPS,A, S, D, F, G, H, J, K, L, SCLN,QUOT, ENT, P4, P5, P6, PPLS,
|
||||
LSFT,Z, X, C, V, B, N, M, COMM,DOT, SLSH, RSFT, UP, P1, P2, P3,
|
||||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT
|
||||
""",
|
||||
# Dvorak http://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard
|
||||
"""
|
||||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
|
||||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, LBRC,RBRC,BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS,
|
||||
TAB, QUOT,COMM,DOT, P, Y, F, G, C, R, L, SLSH,EQL, BSLS, DEL, END, PGDN, P7, P8, P9,
|
||||
CAPS,A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4, P5, P6, PPLS,
|
||||
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
|
||||
""",
|
||||
]
|
131
uinputmapper/keymapper.py
Normal file
131
uinputmapper/keymapper.py
Normal file
@ -0,0 +1,131 @@
|
||||
# encoding: utf-8
|
||||
"""
|
||||
Module to help with creating fast keycode mapping arrays
|
||||
"""
|
||||
|
||||
def parse_keymap(args, rev_event_keys, event_keys):
|
||||
"""
|
||||
Reads in a keymap configuration and returns structures to implement it
|
||||
"""
|
||||
keymap_config = {}
|
||||
execfile(args.keymap, keymap_config)
|
||||
switch_layout_keys = keymap_config['switch_layout_keys']
|
||||
revert_default_key = keymap_config['revert_default_key']
|
||||
revert_keymap_index = keymap_config['revert_keymap_index']
|
||||
default_keymap_index = keymap_config['default_keymap_index']
|
||||
keymaps = keymap_config['keymaps']
|
||||
#print keymap_config
|
||||
#exit(0)
|
||||
#import pprint
|
||||
short_to_long = {
|
||||
'APP':'COMPOSE',
|
||||
'BRK':'BREAK',
|
||||
'BSLS':'BACKSLASH',
|
||||
'BSPC':'BACKSPACE',
|
||||
'CAPS':'CAPSLOCK',
|
||||
'COMM':'COMMA',
|
||||
'DEL':'DELETE',
|
||||
'ENT':'ENTER',
|
||||
'EQL':'EQUAL',
|
||||
'GRV':'GRAVE',
|
||||
'INS':'INSERT',
|
||||
'LALT':'LEFTALT',
|
||||
'LBRC':'LEFTBRACE',
|
||||
'LCTL':'LEFTCTRL',
|
||||
'LGUI':'LEFTMETA',
|
||||
'LSFT':'LEFTSHIFT',
|
||||
'MINS':'MINUS',
|
||||
'NLCK':'NUMLOCK',
|
||||
'P0':'KP0',
|
||||
'P1':'KP1',
|
||||
'P2':'KP2',
|
||||
'P3':'KP3',
|
||||
'P4':'KP4',
|
||||
'P5':'KP5',
|
||||
'P6':'KP6',
|
||||
'P7':'KP7',
|
||||
'P8':'KP8',
|
||||
'P9':'KP9',
|
||||
'PAST':'KPASTERISK',
|
||||
'PDOT':'KPDOT',
|
||||
'PENT':'KPENTER',
|
||||
'PGDN':'PAGEDOWN',
|
||||
'PGUP':'PAGEUP',
|
||||
'PMNS':'KPMINUS',
|
||||
'PPLS':'KPPLUS',
|
||||
'PSCR':'SYSRQ',#'PRINT', # not sure?
|
||||
'PSLS':'KPSLASH',
|
||||
'QUOT':'APOSTROPHE',
|
||||
'RALT':'RIGHTALT',
|
||||
'RBRC':'RIGHTBRACE',
|
||||
'RCTL':'RIGHTCTRL',
|
||||
'RGHT':'RIGHT',
|
||||
'RGUI':'RIGHTMETA',
|
||||
'RSFT':'RIGHTSHIFT',
|
||||
'SCLN':'SEMICOLON',
|
||||
'SLCK':'SCROLLLOCK',
|
||||
'SLSH':'SLASH',
|
||||
'SPC':'SPACE',
|
||||
}
|
||||
keymap_list = []
|
||||
for keymap in keymaps:
|
||||
key_list = []
|
||||
keymap_list.append(key_list)
|
||||
for key in keymap.split(','):
|
||||
key = key.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)
|
||||
|
||||
#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]
|
||||
|
||||
#pprint.pprint(mykeymaps)
|
||||
#exit(0)
|
||||
#print 'mykeymap: ', mykeymap
|
||||
#print 'rev_event_keys[1]: ', rev_event_keys[1]
|
||||
#print 'event_keys[1]: ', event_keys[1]
|
||||
#print 'event_keys[1]: '
|
||||
#pprint.pprint(event_keys[1])
|
||||
#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'])
|
||||
runtime_keymaps = []
|
||||
for mykeymap in mykeymaps:
|
||||
mycodemap = array('H')
|
||||
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)
|
||||
#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]
|
||||
|
||||
switch_layout_codes = {}
|
||||
for key in switch_layout_keys:
|
||||
switch_layout_codes[event_keys[1]['KEY_'+key]] = False
|
||||
switch_layout_mode = False
|
||||
num_codes_to_index = {}
|
||||
for x in range(0, len(runtime_keymaps)):
|
||||
num_codes_to_index[event_keys[1].get('KEY_'+str(x))] = x
|
||||
#pprint.pprint(num_codes_to_index)
|
||||
#exit(0)
|
||||
|
||||
return runtime_keymaps, active_keymap_index, revert_keymap_index, active_keymap, revert_default_code, switch_layout_codes, switch_layout_mode, num_codes_to_index
|
Loading…
Reference in New Issue
Block a user