#!/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' 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') 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()