mirror of
https://github.com/moparisthebest/uinput-mapper
synced 2024-11-20 23:45:01 -05:00
238 lines
8.9 KiB
Python
Executable File
238 lines
8.9 KiB
Python
Executable File
#!/usr/bin/env python
|
|
import ctypes, fcntl, os, sys
|
|
import select
|
|
|
|
import uinputmapper
|
|
import uinputmapper.linux_uinput
|
|
from uinputmapper.cinput import *
|
|
from uinputmapper.mapper import KeyMapper, parse_conf, pretty_conf_print
|
|
|
|
try:
|
|
import cPickle as pickle
|
|
except ImportError:
|
|
import pickle
|
|
|
|
import argparse
|
|
|
|
_usage = 'input-read /dev/input/event<0> ... /dev/input/event<N>'
|
|
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_argument('-v', '--verbose', action='store_true',
|
|
default=False, help='Enable verbose mode (do not combine with -D)')
|
|
|
|
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')
|
|
|
|
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')
|
|
|
|
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, args.input_file)
|
|
|
|
# Create configuration
|
|
config = {}
|
|
for idx, f in enumerate(fs):
|
|
c = parse_conf(f, idx)
|
|
|
|
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)
|
|
|
|
poll_obj, poll_mask = (select.poll, select.POLLIN) if args.compat else \
|
|
(select.epoll, select.EPOLLIN)
|
|
|
|
# Add all devices to epoll
|
|
pp = poll_obj()
|
|
for f in fs:
|
|
pp.register(f.get_fd(), poll_mask)
|
|
|
|
# Human readable info
|
|
if args.dump:
|
|
for f in fs:
|
|
print 'Version:', f.get_version()
|
|
print f.get_name()
|
|
|
|
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, caps_lock_no_effect = parse_keymap(args, rev_event_keys, event_keys)
|
|
shift_down = -1
|
|
caps_locked = False # todo: get/set this a sane way?
|
|
rshift = event_keys[1]['KEY_RIGHTSHIFT']
|
|
lshift = event_keys[1]['KEY_LEFTSHIFT']
|
|
capslck = event_keys[1]['KEY_CAPSLOCK']
|
|
|
|
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')
|
|
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()
|
|
|
|
for e in events:
|
|
fd, ev_mask = e
|
|
|
|
if not ev_mask & poll_mask:
|
|
continue
|
|
|
|
# Lets undo that epoll speedup ;-) FIXME XXX
|
|
for idx, _ in enumerate(fs):
|
|
if _.get_fd() == fd:
|
|
f = _
|
|
i = idx
|
|
|
|
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
|
|
new_code, invert_shift = active_keymap[ev.code].get_code(ev.code, caps_locked != (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
|
|
elif ev.code == capslck: # caps locked
|
|
caps_locked = not caps_locked
|
|
|
|
|
|
if caps_locked and caps_lock_no_effect[new_code]:
|
|
invert_shift = not invert_shift
|
|
if invert_shift: # swap shift behavior
|
|
# if (invert_shift and not(caps_locked and caps_lock_no_effect[new_code])) or (not(invert_shift) and (caps_locked and caps_lock_no_effect[new_code])): # not as clean as above?
|
|
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:
|
|
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 == lshift or ev.code == rshift: # shift
|
|
shift_down = -1
|
|
|
|
if caps_locked and caps_lock_no_effect[new_code]:
|
|
invert_shift = not invert_shift
|
|
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?
|
|
if args.has_config:
|
|
idx, ev = m.map_event(ev, i)
|
|
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 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)
|
|
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:
|
|
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()
|