mirror of
https://github.com/moparisthebest/uinput-mapper
synced 2024-12-12 08: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:
|
except ImportError:
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
import optparse
|
import argparse
|
||||||
|
|
||||||
_usage = 'input-read /dev/input/event<0> ... /dev/input/event<N>'
|
_usage = 'input-read /dev/input/event<0> ... /dev/input/event<N>'
|
||||||
parser = optparse.OptionParser(description='Read input devices.',
|
parser = argparse.ArgumentParser(description='Read input devices.',
|
||||||
usage = _usage,
|
usage = _usage)
|
||||||
version='0.01')
|
parser.add_argument('--version', action='version', version='0.01')
|
||||||
parser.add_option('-D', '--dump', action='store_false',
|
parser.add_argument('-D', '--dump', action='store_false',
|
||||||
default=True, help='Dump will marshall all the events to stdout')
|
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)')
|
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')
|
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.add_argument('-d', '--direct', action='store_true',
|
||||||
parser.print_help()
|
default=False, help='Will directly create input devices to send events to, instead of piping to input-create')
|
||||||
exit(0)
|
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
|
# Open input devices
|
||||||
fs = map(InputDevice, input_file)
|
fs = map(InputDevice, args.input_file)
|
||||||
|
|
||||||
# Create configuration
|
# Create configuration
|
||||||
config = {}
|
config = {}
|
||||||
@ -42,6 +56,13 @@ for idx, f in enumerate(fs):
|
|||||||
|
|
||||||
config.update(c)
|
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:
|
if args.verbose:
|
||||||
pretty_conf_print(config)
|
pretty_conf_print(config)
|
||||||
|
|
||||||
@ -62,17 +83,42 @@ if args.dump:
|
|||||||
d = f.get_exposed_events()
|
d = f.get_exposed_events()
|
||||||
for k, v in d.iteritems():
|
for k, v in d.iteritems():
|
||||||
print k + ':', ', '.join(v)
|
print k + ':', ', '.join(v)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Dump initial information over pickle to stdout
|
# Dump initial information over pickle to stdout
|
||||||
p = pickle.Pickler(sys.stdout)
|
p = pickle.Pickler(sys.stdout)
|
||||||
|
|
||||||
p.dump(len(fs))
|
p.dump(len(fs))
|
||||||
|
|
||||||
p.dump(config)
|
p.dump(config)
|
||||||
|
|
||||||
sys.stdout.flush()
|
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:
|
while True:
|
||||||
events = pp.poll()
|
events = pp.poll()
|
||||||
|
|
||||||
@ -90,20 +136,54 @@ while True:
|
|||||||
|
|
||||||
ev = f.next_event()
|
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:
|
try:
|
||||||
|
#print 'ev.type:', ev.type
|
||||||
print i, ev.time.tv_sec, ev.time.tv_usec
|
print i, ev.time.tv_sec, ev.time.tv_usec
|
||||||
s = '%s %s %d' % (rev_events[ev.type],
|
s = '%s(%d) %s(%d) %d' % (rev_events[ev.type], ev.type,
|
||||||
rev_event_keys[ev.type][ev.code], ev.value)
|
rev_event_keys[ev.type][ev.code], ev.code, ev.value)
|
||||||
print 'Event type:', s
|
print 'Event type:', s
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not args.compat:
|
if not args.compat:
|
||||||
p.dump((i, ev))
|
p.dump((i, ev))
|
||||||
else:
|
else:
|
||||||
p.dump((i, (ev.time.tv_sec, ev.time.tv_usec,
|
p.dump((i, (ev.time.tv_sec, ev.time.tv_usec,
|
||||||
ev.type, ev.code, ev.value)))
|
ev.type, ev.code, ev.value)))
|
||||||
|
|
||||||
sys.stdout.flush()
|
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