pull/14/head
sezanzeb 4 years ago
parent 17445558fb
commit 3d2095f988

@ -63,11 +63,11 @@ class _Config:
if len(chunks) == 0: if len(chunks) == 0:
# child is the value _resolve is looking for # child is the value _resolve is looking for
return func(parent, child, chunk) return func(parent, child, chunk)
else:
# child is another object # child is another object
if child is None: if child is None:
parent[chunk] = {} parent[chunk] = {}
child = parent[chunk] child = parent[chunk]
def remove(self, path): def remove(self, path):
"""Remove a config key. """Remove a config key.
@ -104,8 +104,12 @@ class _Config:
---------- ----------
path : string path : string
For example 'macros.keystroke_sleep_ms' For example 'macros.keystroke_sleep_ms'
default : any
If the configured value is not available or None, return this
instead
""" """
return self._resolve(path, lambda parent, child, chunk: child) resolved = self._resolve(path, lambda parent, child, chunk: child)
return resolved if resolved is not None else default
def set_autoload_preset(self, device, preset): def set_autoload_preset(self, device, preset):
"""Set a preset to be automatically applied on start. """Set a preset to be automatically applied on start.

@ -22,6 +22,7 @@
"""Get stuff from /usr/share/key-mapper, depending on the prefix.""" """Get stuff from /usr/share/key-mapper, depending on the prefix."""
import sys
import os import os
import site import site
import pkg_resources import pkg_resources
@ -39,7 +40,7 @@ def get_data_path(filename=''):
data_path = '/usr/share/key-mapper' data_path = '/usr/share/key-mapper'
if not os.path.exists(data_path): if not os.path.exists(data_path):
logger.error('key-mapper data was not properly installed') logger.error('key-mapper data was not properly installed')
raise SystemExit(1) sys.exit(1)
return os.path.join('/usr/share/key-mapper', filename) return os.path.join('/usr/share/key-mapper', filename)
# depending on where this file is installed to, make sure to use the proper # depending on where this file is installed to, make sure to use the proper

@ -110,6 +110,12 @@ class KeycodeInjector:
self._process = None self._process = None
self._msg_pipe = multiprocessing.Pipe() self._msg_pipe = multiprocessing.Pipe()
# some EV_ABS mapping stuff
self.abs_x = 0
self.abs_y = 0
self.pending_x_rel = 0
self.pending_y_rel = 0
def start_injecting(self): def start_injecting(self):
"""Start injecting keycodes.""" """Start injecting keycodes."""
self._process = multiprocessing.Process(target=self._start_injecting) self._process = multiprocessing.Process(target=self._start_injecting)
@ -136,8 +142,8 @@ class KeycodeInjector:
needed = True needed = True
break break
map_ABS = self.map_ABS(device) map_ev_abs = self.map_ev_abs(device)
if map_ABS: if map_ev_abs:
needed = True needed = True
if not needed: if not needed:
@ -168,20 +174,20 @@ class KeycodeInjector:
time.sleep(0.15) time.sleep(0.15)
return device, map_ABS return device, map_ev_abs
def map_ABS(self, device): def map_ev_abs(self, device):
# TODO offer configuration via the UI if a gamepad is elected # TODO offer configuration via the UI if a gamepad is elected
capabilities = device.capabilities(absinfo=False) capabilities = device.capabilities(absinfo=False)
return evdev.ecodes.ABS_X in capabilities.get(EV_ABS, []) return evdev.ecodes.ABS_X in capabilities.get(EV_ABS, [])
def _modify_capabilities(self, input_device, map_ABS): def _modify_capabilities(self, input_device, map_ev_abs):
"""Adds all keycode into a copy of a devices capabilities. """Adds all keycode into a copy of a devices capabilities.
Prameters Prameters
--------- ---------
input_device : evdev.InputDevice input_device : evdev.InputDevice
map_ABS : bool map_ev_abs : bool
if ABS capabilities should be removed in favor of REL if ABS capabilities should be removed in favor of REL
""" """
ecodes = evdev.ecodes ecodes = evdev.ecodes
@ -199,7 +205,7 @@ class KeycodeInjector:
if keycode is not None: if keycode is not None:
capabilities[ecodes.EV_KEY].append(keycode - KEYCODE_OFFSET) capabilities[ecodes.EV_KEY].append(keycode - KEYCODE_OFFSET)
if map_ABS: if map_ev_abs:
del capabilities[ecodes.EV_ABS] del capabilities[ecodes.EV_ABS]
capabilities[ecodes.EV_REL] = [ capabilities[ecodes.EV_REL] = [
evdev.ecodes.REL_X, evdev.ecodes.REL_X,
@ -246,7 +252,7 @@ class KeycodeInjector:
# Watch over each one of the potentially multiple devices per hardware # Watch over each one of the potentially multiple devices per hardware
for path in paths: for path in paths:
input_device, map_ABS = self._prepare_device(path) input_device, map_ev_abs = self._prepare_device(path)
if input_device is None: if input_device is None:
continue continue
@ -256,17 +262,17 @@ class KeycodeInjector:
uinput = evdev.UInput( uinput = evdev.UInput(
name=f'{DEV_NAME} {self.device}', name=f'{DEV_NAME} {self.device}',
phys=DEV_NAME, phys=DEV_NAME,
events=self._modify_capabilities(input_device, map_ABS) events=self._modify_capabilities(input_device, map_ev_abs)
) )
# TODO separate file # TODO separate file
# keycode injection # keycode injection
coroutine = self._keycode_loop(input_device, uinput, map_ABS) coroutine = self._keycode_loop(input_device, uinput, map_ev_abs)
coroutines.append(coroutine) coroutines.append(coroutine)
# TODO separate file # TODO separate file
# mouse movement injection # mouse movement injection
if map_ABS: if map_ev_abs:
self.abs_x = 0 self.abs_x = 0
self.abs_y = 0 self.abs_y = 0
# events only take ints, so a movement of 0.3 needs to add # events only take ints, so a movement of 0.3 needs to add
@ -291,9 +297,9 @@ class KeycodeInjector:
if len(coroutines) > 0: if len(coroutines) > 0:
logger.debug('asyncio coroutines ended') logger.debug('asyncio coroutines ended')
def _write(self, device, type, keycode, value): def _write(self, device, ev_type, keycode, value):
"""Actually inject.""" """Actually inject."""
device.write(type, keycode, value) device.write(ev_type, keycode, value)
device.syn() device.syn()
def _macro_write(self, character, value, keymapper_device): def _macro_write(self, character, value, keymapper_device):
@ -360,7 +366,7 @@ class KeycodeInjector:
rel_x rel_x
) )
async def _keycode_loop(self, device, keymapper_device, map_ABS): async def _keycode_loop(self, device, keymapper_device, map_ev_abs):
"""Inject keycodes for one of the virtual devices. """Inject keycodes for one of the virtual devices.
Parameters Parameters
@ -369,8 +375,8 @@ class KeycodeInjector:
where to read keycodes from where to read keycodes from
keymapper_device : evdev.UInput keymapper_device : evdev.UInput
where to write keycodes to where to write keycodes to
map_ABS : bool map_ev_abs : bool
the value of map_ABS() for the original device the value of map_ev_abs() for the original device
""" """
# Parse all macros beforehand # Parse all macros beforehand
logger.debug('Parsing macros') logger.debug('Parsing macros')
@ -389,7 +395,7 @@ class KeycodeInjector:
) )
async for event in device.async_read_loop(): async for event in device.async_read_loop():
if map_ABS and event.type == EV_ABS: if map_ev_abs and event.type == EV_ABS:
if event.code not in [evdev.ecodes.ABS_X, evdev.ecodes.ABS_Y]: if event.code not in [evdev.ecodes.ABS_X, evdev.ecodes.ABS_Y]:
continue continue
if event.code == evdev.ecodes.ABS_X: if event.code == evdev.ecodes.ABS_X:

@ -189,7 +189,7 @@ def _count_brackets(macro):
if brackets != 0: if brackets != 0:
raise Exception(f'There are {brackets} closing brackets missing') raise Exception(f'There are {brackets} closing brackets missing')
return brackets, position return position
def _parse_recurse(macro, handler, macro_instance=None, depth=0): def _parse_recurse(macro, handler, macro_instance=None, depth=0):
@ -239,7 +239,7 @@ def _parse_recurse(macro, handler, macro_instance=None, depth=0):
raise Exception(f'Unknown function {call}') raise Exception(f'Unknown function {call}')
# get all the stuff inbetween # get all the stuff inbetween
brackets, position = _count_brackets(macro) position = _count_brackets(macro)
inner = macro[2:position - 1] inner = macro[2:position - 1]

@ -22,7 +22,6 @@
"""To check if access to devices in /dev is possible.""" """To check if access to devices in /dev is possible."""
import os
import grp import grp
import getpass import getpass

@ -22,10 +22,12 @@
"""Keeps reading keycodes in the background for the UI to use.""" """Keeps reading keycodes in the background for the UI to use."""
import evdev import sys
import select import select
import multiprocessing import multiprocessing
import evdev
from keymapper.logger import logger from keymapper.logger import logger
from keymapper.getdevices import get_devices, refresh_devices from keymapper.getdevices import get_devices, refresh_devices
from keymapper.state import KEYCODE_OFFSET from keymapper.state import KEYCODE_OFFSET
@ -104,7 +106,7 @@ class _KeycodeReader:
# value: 1 for down, 0 for up, 2 for hold. # value: 1 for down, 0 for up, 2 for hold.
if self._pipe[1].closed: if self._pipe[1].closed:
logger.debug('Pipe closed, reader stops.') logger.debug('Pipe closed, reader stops.')
exit(0) sys.exit(0)
if event.type == evdev.ecodes.EV_KEY and event.value == 1: if event.type == evdev.ecodes.EV_KEY and event.value == 1:
logger.spam( logger.spam(

@ -28,15 +28,14 @@ import shutil
from keymapper.logger import logger from keymapper.logger import logger
# try to find the user who called sudo # try to find the user who called sudo
try: try:
USER = os.getlogin() USER = os.getlogin()
except OSError: except OSError:
# failed in some ubuntu installations # failed in some ubuntu installations
USER = os.environ['USER'] USER = os.environ['USER']
if USER == 'root': if USER == 'root':
USER = os.envron.get('SUDO_USER', USER) USER = os.envron.get('SUDO_USER', USER)
CONFIG = os.path.join('/home', USER, '.config/key-mapper') CONFIG = os.path.join('/home', USER, '.config/key-mapper')

@ -9,13 +9,13 @@
</mask> </mask>
<g mask="url(#a)"> <g mask="url(#a)">
<path fill="#555" d="M0 0h63v20H0z"/> <path fill="#555" d="M0 0h63v20H0z"/>
<path fill="#97CA00" d="M63 0h36v20H63z"/> <path fill="#a4a61d" d="M63 0h36v20H63z"/>
<path fill="url(#b)" d="M0 0h99v20H0z"/> <path fill="url(#b)" d="M0 0h99v20H0z"/>
</g> </g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
<text x="31.5" y="14">coverage</text> <text x="31.5" y="14">coverage</text>
<text x="80" y="15" fill="#010101" fill-opacity=".3">90%</text> <text x="80" y="15" fill="#010101" fill-opacity=".3">76%</text>
<text x="80" y="14">90%</text> <text x="80" y="14">76%</text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 904 B

After

Width:  |  Height:  |  Size: 904 B

@ -17,7 +17,7 @@
<text x="22.0" y="14">pylint</text> <text x="22.0" y="14">pylint</text>
</g> </g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.83</text> <text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.71</text>
<text x="62.0" y="14">9.83</text> <text x="62.0" y="14">9.71</text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -2,7 +2,7 @@
coverage_badge() { coverage_badge() {
# https://github.com/dbrgn/coverage-badge # https://github.com/dbrgn/coverage-badge
coverage run --branch --source=/usr/lib/python3.8/site-packages/keymapper tests/test.py coverage run tests/test.py
python3 -m coverage_badge > readme/coverage.svg python3 -m coverage_badge > readme/coverage.svg
coverage combine coverage combine
coverage report -m coverage report -m

@ -31,6 +31,9 @@ class TestConfig(unittest.TestCase):
config.save_config() config.save_config()
def test_basic(self): def test_basic(self):
self.assertEqual(config.get('a'), None)
self.assertEqual(config.get('a', 'foo'), 'foo')
config.set('a', 1) config.set('a', 1)
self.assertEqual(config.get('a'), 1) self.assertEqual(config.get('a'), 1)

@ -84,7 +84,7 @@ class TestInjector(unittest.TestCase):
fake_device = FakeDevice() fake_device = FakeDevice()
capabilities = self.injector._modify_capabilities( capabilities = self.injector._modify_capabilities(
fake_device, fake_device,
map_ABS=False map_ev_abs=False
) )
self.assertIn(EV_KEY, capabilities) self.assertIn(EV_KEY, capabilities)
@ -102,8 +102,8 @@ class TestInjector(unittest.TestCase):
path = '/dev/input/event10' path = '/dev/input/event10'
# this test needs to pass around all other constraints of # this test needs to pass around all other constraints of
# _prepare_device # _prepare_device
device, map_ABS = self.injector._prepare_device(path) device, map_ev_abs = self.injector._prepare_device(path)
self.assertFalse(map_ABS) self.assertFalse(map_ev_abs)
self.assertEqual(self.failed, 2) self.assertEqual(self.failed, 2)
# success on the third try # success on the third try
device.name = fixtures[path]['name'] device.name = fixtures[path]['name']
@ -112,10 +112,10 @@ class TestInjector(unittest.TestCase):
self.injector = KeycodeInjector('gamepad', custom_mapping) self.injector = KeycodeInjector('gamepad', custom_mapping)
path = '/dev/input/event30' path = '/dev/input/event30'
device, map_ABS = self.injector._prepare_device(path) device, map_ev_abs = self.injector._prepare_device(path)
self.assertTrue(map_ABS) self.assertTrue(map_ev_abs)
capabilities = self.injector._modify_capabilities(device, map_ABS) capabilities = self.injector._modify_capabilities(device, map_ev_abs)
self.assertNotIn(evdev.ecodes.EV_ABS, capabilities) self.assertNotIn(evdev.ecodes.EV_ABS, capabilities)
self.assertIn(evdev.ecodes.EV_REL, capabilities) self.assertIn(evdev.ecodes.EV_REL, capabilities)
@ -124,8 +124,8 @@ class TestInjector(unittest.TestCase):
custom_mapping.change(10, 'a') custom_mapping.change(10, 'a')
self.injector = KeycodeInjector('device 1', custom_mapping) self.injector = KeycodeInjector('device 1', custom_mapping)
path = '/dev/input/event11' path = '/dev/input/event11'
device, map_ABS = self.injector._prepare_device(path) device, map_ev_abs = self.injector._prepare_device(path)
self.assertFalse(map_ABS) self.assertFalse(map_ev_abs)
self.assertEqual(self.failed, 0) self.assertEqual(self.failed, 0)
self.assertIsNone(device) self.assertIsNone(device)
@ -163,7 +163,7 @@ class TestInjector(unittest.TestCase):
def test_abs_to_rel(self): def test_abs_to_rel(self):
# maps gamepad joystick events to mouse events # maps gamepad joystick events to mouse events
# TODO enable this somewhere so that map_ABS returns true # TODO enable this somewhere so that map_ev_abs returns true
# in the .json file of the mapping. # in the .json file of the mapping.
config.set('gamepad.non_linearity', 1) config.set('gamepad.non_linearity', 1)
pointer_speed = 80 pointer_speed = 80

Loading…
Cancel
Save