mirror of
https://github.com/sezanzeb/input-remapper
synced 2024-11-20 03:25:43 +00:00
add event type to mapping key
This commit is contained in:
parent
5fe404dd2d
commit
edf71e82e0
@ -41,8 +41,13 @@ INITIAL_CONFIG = {
|
|||||||
'keystroke_sleep_ms': 10
|
'keystroke_sleep_ms': 10
|
||||||
},
|
},
|
||||||
'gamepad': {
|
'gamepad': {
|
||||||
'non_linearity': 4,
|
'joystick': {
|
||||||
'pointer_speed': 80
|
'non_linearity': 4,
|
||||||
|
'pointer_speed': 80,
|
||||||
|
},
|
||||||
|
'triggers': {
|
||||||
|
'button_threshold': 0.5
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,10 +57,15 @@ class _Config:
|
|||||||
self._config = {}
|
self._config = {}
|
||||||
self.load_config()
|
self.load_config()
|
||||||
|
|
||||||
def _resolve(self, path, func):
|
def _resolve(self, path, func, config=None):
|
||||||
"""Call func for the given config value."""
|
"""Call func for the given config value."""
|
||||||
chunks = path.split('.')
|
chunks = path.split('.')
|
||||||
child = self._config
|
|
||||||
|
if config is None:
|
||||||
|
child = self._config
|
||||||
|
else:
|
||||||
|
child = config
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
chunk = chunks.pop(0)
|
chunk = chunks.pop(0)
|
||||||
parent = child
|
parent = child
|
||||||
@ -97,19 +107,27 @@ class _Config:
|
|||||||
|
|
||||||
self._resolve(path, do)
|
self._resolve(path, do)
|
||||||
|
|
||||||
def get(self, path, default=None):
|
def get(self, path, log_unknown=True):
|
||||||
"""Get a config value.
|
"""Get a config value. If not set, return the default
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
path : string
|
path : string
|
||||||
For example 'macros.keystroke_sleep_ms'
|
For example 'macros.keystroke_sleep_ms'
|
||||||
default : any
|
log_unknown : bool
|
||||||
If the configured value is not available or None, return this
|
If True, write an error.
|
||||||
instead
|
|
||||||
"""
|
"""
|
||||||
resolved = self._resolve(path, lambda parent, child, chunk: child)
|
def do(parent, child, chunk):
|
||||||
return resolved if resolved is not None else default
|
return child
|
||||||
|
|
||||||
|
resolved = self._resolve(path, do)
|
||||||
|
if resolved is None:
|
||||||
|
resolved = self._resolve(path, do, INITIAL_CONFIG)
|
||||||
|
|
||||||
|
if resolved is None and log_unknown:
|
||||||
|
logger.error('Unknown config key "%s"', path)
|
||||||
|
|
||||||
|
return resolved
|
||||||
|
|
||||||
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.
|
||||||
@ -118,7 +136,7 @@ class _Config:
|
|||||||
----------
|
----------
|
||||||
device : string
|
device : string
|
||||||
preset : string or None
|
preset : string or None
|
||||||
if None, don't autoload something for this device
|
if None, don't autoload something for this device.
|
||||||
"""
|
"""
|
||||||
if preset is not None:
|
if preset is not None:
|
||||||
self.set(f'autoload.{device}', preset)
|
self.set(f'autoload.{device}', preset)
|
||||||
@ -131,7 +149,7 @@ class _Config:
|
|||||||
|
|
||||||
def is_autoloaded(self, device, preset):
|
def is_autoloaded(self, device, preset):
|
||||||
"""Should this preset be loaded automatically?"""
|
"""Should this preset be loaded automatically?"""
|
||||||
return self.get(f'autoload.{device}') == preset
|
return self.get(f'autoload.{device}', '') == preset
|
||||||
|
|
||||||
def load_config(self):
|
def load_config(self):
|
||||||
"""Load the config from the file system."""
|
"""Load the config from the file system."""
|
||||||
@ -139,7 +157,9 @@ class _Config:
|
|||||||
|
|
||||||
if not os.path.exists(CONFIG_PATH):
|
if not os.path.exists(CONFIG_PATH):
|
||||||
# treated like an empty config
|
# treated like an empty config
|
||||||
logger.debug('Config file "%s" doesn\'t exist', CONFIG_PATH)
|
logger.debug('Config "%s" doesn\'t exist yet', CONFIG_PATH)
|
||||||
|
self.clear_config()
|
||||||
|
self.save_config()
|
||||||
return
|
return
|
||||||
|
|
||||||
with open(CONFIG_PATH, 'r') as file:
|
with open(CONFIG_PATH, 'r') as file:
|
||||||
|
@ -57,8 +57,8 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device):
|
|||||||
max_value = input_device.absinfo(EV_ABS).max
|
max_value = input_device.absinfo(EV_ABS).max
|
||||||
max_speed = ((max_value ** 2) * 2) ** 0.5
|
max_speed = ((max_value ** 2) * 2) ** 0.5
|
||||||
|
|
||||||
pointer_speed = config.get('gamepad.pointer_speed', 80)
|
pointer_speed = config.get('gamepad.joystick.pointer_speed')
|
||||||
non_linearity = config.get('gamepad.non_linearity', 4)
|
non_linearity = config.get('gamepad.joystick.non_linearity')
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
@ -119,6 +119,13 @@ class KeycodeInjector:
|
|||||||
self._process = multiprocessing.Process(target=self._start_injecting)
|
self._process = multiprocessing.Process(target=self._start_injecting)
|
||||||
self._process.start()
|
self._process.start()
|
||||||
|
|
||||||
|
def map_ev_to_abs(self, capabilities):
|
||||||
|
"""Check if joystick movements can and should be mapped."""
|
||||||
|
# mapping buttons only works without ABS events in the capabilities,
|
||||||
|
# possibly due to some intentional constraints in the os. So always
|
||||||
|
# do this without the option to configure, if it is possible.
|
||||||
|
return evdev.ecodes.ABS_X in capabilities.get(EV_ABS, [])
|
||||||
|
|
||||||
def _prepare_device(self, path):
|
def _prepare_device(self, path):
|
||||||
"""Try to grab the device, return if not needed/possible.
|
"""Try to grab the device, return if not needed/possible.
|
||||||
|
|
||||||
@ -135,12 +142,13 @@ class KeycodeInjector:
|
|||||||
|
|
||||||
needed = False
|
needed = False
|
||||||
if capabilities.get(EV_KEY) is not None:
|
if capabilities.get(EV_KEY) is not None:
|
||||||
for keycode, _ in self.mapping:
|
for (ev_type, keycode), _ in self.mapping:
|
||||||
if keycode - KEYCODE_OFFSET in capabilities[EV_KEY]:
|
# TEST ev_type
|
||||||
|
if keycode - KEYCODE_OFFSET in capabilities.get(ev_type, []):
|
||||||
needed = True
|
needed = True
|
||||||
break
|
break
|
||||||
|
|
||||||
map_ev_abs = evdev.ecodes.ABS_X in capabilities.get(EV_ABS, [])
|
map_ev_abs = self.map_ev_to_abs(capabilities)
|
||||||
|
|
||||||
if map_ev_abs:
|
if map_ev_abs:
|
||||||
needed = True
|
needed = True
|
||||||
@ -194,10 +202,10 @@ class KeycodeInjector:
|
|||||||
if len(self.mapping) > 0 and capabilities.get(ecodes.EV_KEY) is None:
|
if len(self.mapping) > 0 and capabilities.get(ecodes.EV_KEY) is None:
|
||||||
capabilities[ecodes.EV_KEY] = []
|
capabilities[ecodes.EV_KEY] = []
|
||||||
|
|
||||||
for _, character in self.mapping:
|
for (ev_type, _), character in self.mapping:
|
||||||
keycode = system_mapping.get(character)
|
keycode = system_mapping.get(character)
|
||||||
if keycode is not None:
|
if keycode is not None:
|
||||||
capabilities[ecodes.EV_KEY].append(keycode - KEYCODE_OFFSET)
|
capabilities[ev_type].append(keycode - KEYCODE_OFFSET)
|
||||||
|
|
||||||
if map_ev_abs:
|
if map_ev_abs:
|
||||||
del capabilities[ecodes.EV_ABS]
|
del capabilities[ecodes.EV_ABS]
|
||||||
@ -320,7 +328,7 @@ class KeycodeInjector:
|
|||||||
# Parse all macros beforehand
|
# Parse all macros beforehand
|
||||||
logger.debug('Parsing macros')
|
logger.debug('Parsing macros')
|
||||||
macros = {}
|
macros = {}
|
||||||
for keycode, output in self.mapping:
|
for (ev_type, keycode), output in self.mapping:
|
||||||
keycode -= KEYCODE_OFFSET
|
keycode -= KEYCODE_OFFSET
|
||||||
|
|
||||||
if '(' in output and ')' in output and len(output) >= 4:
|
if '(' in output and ')' in output and len(output) >= 4:
|
||||||
|
@ -114,7 +114,7 @@ class _Macro:
|
|||||||
|
|
||||||
def add_keycode_pause(self):
|
def add_keycode_pause(self):
|
||||||
"""To add a pause between keystrokes."""
|
"""To add a pause between keystrokes."""
|
||||||
sleeptime = config.get('macros.keystroke_sleep_ms', 10) / 1000
|
sleeptime = config.get('macros.keystroke_sleep_ms') / 1000
|
||||||
|
|
||||||
async def sleep():
|
async def sleep():
|
||||||
await asyncio.sleep(sleeptime)
|
await asyncio.sleep(sleeptime)
|
||||||
|
@ -83,6 +83,7 @@ class _GetDevices(threading.Thread):
|
|||||||
# https://www.kernel.org/doc/html/latest/input/event-codes.html
|
# https://www.kernel.org/doc/html/latest/input/event-codes.html
|
||||||
capabilities = device.capabilities().keys()
|
capabilities = device.capabilities().keys()
|
||||||
if EV_KEY not in capabilities and EV_ABS not in capabilities:
|
if EV_KEY not in capabilities and EV_ABS not in capabilities:
|
||||||
|
# or gamepads, because they can be mapped like a keyboard
|
||||||
continue
|
continue
|
||||||
|
|
||||||
usb = device.phys.split('/')[0]
|
usb = device.phys.split('/')[0]
|
||||||
|
@ -27,6 +27,8 @@ gi.require_version('Gtk', '3.0')
|
|||||||
gi.require_version('GLib', '2.0')
|
gi.require_version('GLib', '2.0')
|
||||||
from gi.repository import Gtk, GLib
|
from gi.repository import Gtk, GLib
|
||||||
|
|
||||||
|
from evdev.ecodes import EV_KEY
|
||||||
|
|
||||||
from keymapper.state import custom_mapping
|
from keymapper.state import custom_mapping
|
||||||
from keymapper.logger import logger
|
from keymapper.logger import logger
|
||||||
|
|
||||||
@ -76,7 +78,7 @@ class Row(Gtk.ListBoxRow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# keycode is already set by some other row
|
# keycode is already set by some other row
|
||||||
if custom_mapping.get_character(new_keycode) is not None:
|
if custom_mapping.get_character(EV_KEY, new_keycode) is not None:
|
||||||
msg = f'Keycode {new_keycode} is already mapped'
|
msg = f'Keycode {new_keycode} is already mapped'
|
||||||
logger.info(msg)
|
logger.info(msg)
|
||||||
self.window.get('status_bar').push(CTX_KEYCODE, msg)
|
self.window.get('status_bar').push(CTX_KEYCODE, msg)
|
||||||
@ -98,7 +100,7 @@ class Row(Gtk.ListBoxRow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# else, the keycode has changed, the character is set, all good
|
# else, the keycode has changed, the character is set, all good
|
||||||
custom_mapping.change(new_keycode, character, previous_keycode)
|
custom_mapping.change(EV_KEY, new_keycode, character, previous_keycode)
|
||||||
|
|
||||||
def highlight(self):
|
def highlight(self):
|
||||||
"""Mark this row as changed."""
|
"""Mark this row as changed."""
|
||||||
@ -116,7 +118,7 @@ class Row(Gtk.ListBoxRow):
|
|||||||
self.highlight()
|
self.highlight()
|
||||||
|
|
||||||
if keycode is not None:
|
if keycode is not None:
|
||||||
custom_mapping.change(
|
custom_mapping.change(EV_KEY,
|
||||||
previous_keycode=None,
|
previous_keycode=None,
|
||||||
new_keycode=keycode,
|
new_keycode=keycode,
|
||||||
character=character
|
character=character
|
||||||
@ -178,7 +180,7 @@ class Row(Gtk.ListBoxRow):
|
|||||||
"""Destroy the row and remove it from the config."""
|
"""Destroy the row and remove it from the config."""
|
||||||
keycode = self.get_keycode()
|
keycode = self.get_keycode()
|
||||||
if keycode is not None:
|
if keycode is not None:
|
||||||
custom_mapping.clear(keycode)
|
custom_mapping.clear(EV_KEY, keycode)
|
||||||
self.character_input.set_text('')
|
self.character_input.set_text('')
|
||||||
self.keycode_input.set_label('')
|
self.keycode_input.set_label('')
|
||||||
self.delete_callback(self)
|
self.delete_callback(self)
|
||||||
|
@ -374,7 +374,7 @@ class Window:
|
|||||||
custom_mapping.load(self.selected_device, self.selected_preset)
|
custom_mapping.load(self.selected_device, self.selected_preset)
|
||||||
|
|
||||||
key_list = self.get('key_list')
|
key_list = self.get('key_list')
|
||||||
for keycode, output in custom_mapping:
|
for (_, keycode), output in custom_mapping:
|
||||||
single_key_mapping = Row(
|
single_key_mapping = Row(
|
||||||
window=self,
|
window=self,
|
||||||
delete_callback=self.on_row_removed,
|
delete_callback=self.on_row_removed,
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import copy
|
import copy
|
||||||
|
import evdev
|
||||||
|
|
||||||
from keymapper.logger import logger
|
from keymapper.logger import logger
|
||||||
from keymapper.paths import get_config_path, touch
|
from keymapper.paths import get_config_path, touch
|
||||||
@ -50,16 +51,18 @@ class Mapping:
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._mapping)
|
return len(self._mapping)
|
||||||
|
|
||||||
def change(self, new_keycode, character, previous_keycode=None):
|
def change(self, ev_type, new_keycode, character, previous_keycode=None):
|
||||||
"""Replace the mapping of a keycode with a different one.
|
"""Replace the mapping of a keycode with a different one.
|
||||||
|
|
||||||
Return True on success.
|
Return True on success.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
ev_type : int
|
||||||
|
one of evdev.events. The original event
|
||||||
new_keycode : int
|
new_keycode : int
|
||||||
The source keycode, what the mouse would report without any
|
The source keycode, what the mouse would report without any
|
||||||
modification.
|
modification. xkb keycode.
|
||||||
character : string or string[]
|
character : string or string[]
|
||||||
A single character known to xkb, Examples: KP_1, Shift_L, a, B.
|
A single character known to xkb, Examples: KP_1, Shift_L, a, B.
|
||||||
Can also be an array, which is used for reading the xkbmap output
|
Can also be an array, which is used for reading the xkbmap output
|
||||||
@ -67,7 +70,7 @@ class Mapping:
|
|||||||
previous_keycode : int or None
|
previous_keycode : int or None
|
||||||
If None, will not remove any previous mapping. If you recently
|
If None, will not remove any previous mapping. If you recently
|
||||||
used 10 for new_keycode and want to overwrite that with 11,
|
used 10 for new_keycode and want to overwrite that with 11,
|
||||||
provide 5 here.
|
provide 5 here. xkb keycode.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
new_keycode = int(new_keycode)
|
new_keycode = int(new_keycode)
|
||||||
@ -78,25 +81,28 @@ class Mapping:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if new_keycode and character:
|
if new_keycode and character:
|
||||||
self._mapping[new_keycode] = character
|
self._mapping[(ev_type, new_keycode)] = character
|
||||||
if new_keycode != previous_keycode:
|
if new_keycode != previous_keycode:
|
||||||
# clear previous mapping of that code, because the line
|
# clear previous mapping of that code, because the line
|
||||||
# representing that one will now represent a different one.
|
# representing that one will now represent a different one.
|
||||||
self.clear(previous_keycode)
|
self.clear(ev_type, previous_keycode)
|
||||||
self.changed = True
|
self.changed = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def clear(self, keycode):
|
def clear(self, ev_type, keycode):
|
||||||
"""Remove a keycode from the mapping.
|
"""Remove a keycode from the mapping.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
keycode : int
|
keycode : int
|
||||||
|
the xkb keycode
|
||||||
|
ev_type : int
|
||||||
|
one of evdev.events
|
||||||
"""
|
"""
|
||||||
if self._mapping.get(keycode) is not None:
|
if self._mapping.get((ev_type, keycode)) is not None:
|
||||||
del self._mapping[keycode]
|
del self._mapping[(ev_type, keycode)]
|
||||||
self.changed = True
|
self.changed = True
|
||||||
|
|
||||||
def empty(self):
|
def empty(self):
|
||||||
@ -119,13 +125,23 @@ class Mapping:
|
|||||||
logger.error('Invalid preset config at "%s"', path)
|
logger.error('Invalid preset config at "%s"', path)
|
||||||
return
|
return
|
||||||
|
|
||||||
for keycode, character in preset_dict['mapping'].items():
|
for key, character in preset_dict['mapping'].items():
|
||||||
|
if ',' not in key:
|
||||||
|
logger.error('Found invalid key: "%s"', key)
|
||||||
|
continue
|
||||||
|
|
||||||
|
ev_type, keycode = key.split(',')
|
||||||
try:
|
try:
|
||||||
keycode = int(keycode)
|
keycode = int(keycode)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.error('Found non-int keycode: %s', keycode)
|
logger.error('Found non-int keycode: "%s"', keycode)
|
||||||
continue
|
continue
|
||||||
self._mapping[keycode] = character
|
try:
|
||||||
|
ev_type = int(ev_type)
|
||||||
|
except ValueError:
|
||||||
|
logger.error('Found non-int ev_type: "%s"', ev_type)
|
||||||
|
continue
|
||||||
|
self._mapping[(ev_type, keycode)] = character
|
||||||
|
|
||||||
# add any metadata of the mapping
|
# add any metadata of the mapping
|
||||||
for key in preset_dict:
|
for key in preset_dict:
|
||||||
@ -152,18 +168,27 @@ class Mapping:
|
|||||||
with open(path, 'w') as file:
|
with open(path, 'w') as file:
|
||||||
# make sure to keep the option to add metadata if ever needed,
|
# make sure to keep the option to add metadata if ever needed,
|
||||||
# so put the mapping into a special key
|
# so put the mapping into a special key
|
||||||
preset_dict = {'mapping': self._mapping}
|
json_ready_mapping = {}
|
||||||
|
# tuple keys are not possible in json
|
||||||
|
for key, value in self._mapping.items():
|
||||||
|
new_key = f'{key[0]},{key[1]}'
|
||||||
|
json_ready_mapping[new_key] = value
|
||||||
|
|
||||||
|
preset_dict = {'mapping': json_ready_mapping}
|
||||||
preset_dict.update(self.config)
|
preset_dict.update(self.config)
|
||||||
json.dump(preset_dict, file, indent=4)
|
json.dump(preset_dict, file, indent=4)
|
||||||
file.write('\n')
|
file.write('\n')
|
||||||
|
|
||||||
self.changed = False
|
self.changed = False
|
||||||
|
|
||||||
def get_character(self, keycode):
|
def get_character(self, ev_type, keycode):
|
||||||
"""Read the character that is mapped to this keycode.
|
"""Read the character that is mapped to this keycode.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
keycode : int
|
keycode : int
|
||||||
|
ev_type : int
|
||||||
|
one of evdev.events. codes may be the same for various
|
||||||
|
event types.
|
||||||
"""
|
"""
|
||||||
return self._mapping.get(keycode)
|
return self._mapping.get((ev_type, keycode))
|
||||||
|
@ -30,9 +30,15 @@ class TestConfig(unittest.TestCase):
|
|||||||
self.assertEqual(len(config.iterate_autoload_presets()), 0)
|
self.assertEqual(len(config.iterate_autoload_presets()), 0)
|
||||||
config.save_config()
|
config.save_config()
|
||||||
|
|
||||||
|
def test_get_default(self):
|
||||||
|
config._config = {}
|
||||||
|
self.assertEqual(config.get('gamepad.joystick.non_linearity'), 4)
|
||||||
|
|
||||||
|
config.set('gamepad.joystick.non_linearity', 3)
|
||||||
|
self.assertEqual(config.get('gamepad.joystick.non_linearity'), 3)
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
self.assertEqual(config.get('a'), None)
|
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)
|
||||||
|
@ -25,6 +25,7 @@ import unittest
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import evdev
|
import evdev
|
||||||
|
from evdev.ecodes import EV_KEY
|
||||||
import gi
|
import gi
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
@ -84,8 +85,8 @@ class TestDaemon(unittest.TestCase):
|
|||||||
keycode_from_2 = 12
|
keycode_from_2 = 12
|
||||||
keycode_to_2 = 100
|
keycode_to_2 = 100
|
||||||
|
|
||||||
custom_mapping.change(keycode_from_1, 'a')
|
custom_mapping.change(EV_KEY, keycode_from_1, 'a')
|
||||||
custom_mapping.change(keycode_from_2, 'b')
|
custom_mapping.change(EV_KEY, keycode_from_2, 'b')
|
||||||
clear_system_mapping()
|
clear_system_mapping()
|
||||||
system_mapping['a'] = keycode_to_1
|
system_mapping['a'] = keycode_to_1
|
||||||
system_mapping['b'] = keycode_to_2
|
system_mapping['b'] = keycode_to_2
|
||||||
|
@ -76,7 +76,7 @@ class TestInjector(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
mapping = Mapping()
|
mapping = Mapping()
|
||||||
mapping.change(
|
mapping.change(EV_KEY,
|
||||||
new_keycode=80,
|
new_keycode=80,
|
||||||
character='a'
|
character='a'
|
||||||
)
|
)
|
||||||
@ -99,7 +99,7 @@ class TestInjector(unittest.TestCase):
|
|||||||
|
|
||||||
def test_grab(self):
|
def test_grab(self):
|
||||||
# path is from the fixtures
|
# path is from the fixtures
|
||||||
custom_mapping.change(10, 'a')
|
custom_mapping.change(EV_KEY, 10, 'a')
|
||||||
|
|
||||||
self.injector = KeycodeInjector('device 1', custom_mapping)
|
self.injector = KeycodeInjector('device 1', custom_mapping)
|
||||||
path = '/dev/input/event10'
|
path = '/dev/input/event10'
|
||||||
@ -124,7 +124,7 @@ class TestInjector(unittest.TestCase):
|
|||||||
|
|
||||||
def test_skip_unused_device(self):
|
def test_skip_unused_device(self):
|
||||||
# skips a device because its capabilities are not used in the mapping
|
# skips a device because its capabilities are not used in the mapping
|
||||||
custom_mapping.change(10, 'a')
|
custom_mapping.change(EV_KEY, 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_ev_abs = self.injector._prepare_device(path)
|
device, map_ev_abs = self.injector._prepare_device(path)
|
||||||
@ -166,11 +166,9 @@ 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_ev_abs returns true
|
config.set('gamepad.joystick.non_linearity', 1)
|
||||||
# in the .json file of the mapping.
|
|
||||||
config.set('gamepad.non_linearity', 1)
|
|
||||||
pointer_speed = 80
|
pointer_speed = 80
|
||||||
config.set('gamepad.pointer_speed', pointer_speed)
|
config.set('gamepad.joystick.pointer_speed', pointer_speed)
|
||||||
|
|
||||||
# same for ABS, 0 for x, 1 for y
|
# same for ABS, 0 for x, 1 for y
|
||||||
rel_x = evdev.ecodes.REL_X
|
rel_x = evdev.ecodes.REL_X
|
||||||
@ -286,11 +284,11 @@ class TestInjector(unittest.TestCase):
|
|||||||
self.assertIn(('b', 0), history)
|
self.assertIn(('b', 0), history)
|
||||||
|
|
||||||
def test_injector(self):
|
def test_injector(self):
|
||||||
custom_mapping.change(8, 'k(KEY_Q).k(w)')
|
custom_mapping.change(EV_KEY, 8, 'k(KEY_Q).k(w)')
|
||||||
custom_mapping.change(9, 'a')
|
custom_mapping.change(EV_KEY, 9, 'a')
|
||||||
# one mapping that is unknown in the system_mapping on purpose
|
# one mapping that is unknown in the system_mapping on purpose
|
||||||
input_b = 10
|
input_b = 10
|
||||||
custom_mapping.change(input_b, 'b')
|
custom_mapping.change(EV_KEY, input_b, 'b')
|
||||||
|
|
||||||
clear_system_mapping()
|
clear_system_mapping()
|
||||||
code_a = 100
|
code_a = 100
|
||||||
|
@ -24,6 +24,7 @@ import time
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
import evdev
|
import evdev
|
||||||
|
from evdev.events import EV_KEY
|
||||||
import json
|
import json
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from importlib.util import spec_from_loader, module_from_spec
|
from importlib.util import spec_from_loader, module_from_spec
|
||||||
@ -156,9 +157,9 @@ class TestIntegration(unittest.TestCase):
|
|||||||
def test_select_device(self):
|
def test_select_device(self):
|
||||||
# creates a new empty preset when no preset exists for the device
|
# creates a new empty preset when no preset exists for the device
|
||||||
self.window.on_select_device(FakeDropdown('device 1'))
|
self.window.on_select_device(FakeDropdown('device 1'))
|
||||||
custom_mapping.change(50, 'q')
|
custom_mapping.change(EV_KEY, 50, 'q')
|
||||||
custom_mapping.change(51, 'u')
|
custom_mapping.change(EV_KEY, 51, 'u')
|
||||||
custom_mapping.change(52, 'x')
|
custom_mapping.change(EV_KEY, 52, 'x')
|
||||||
self.assertEqual(len(custom_mapping), 3)
|
self.assertEqual(len(custom_mapping), 3)
|
||||||
self.window.on_select_device(FakeDropdown('device 2'))
|
self.window.on_select_device(FakeDropdown('device 2'))
|
||||||
self.assertEqual(len(custom_mapping), 0)
|
self.assertEqual(len(custom_mapping), 0)
|
||||||
@ -202,7 +203,7 @@ class TestIntegration(unittest.TestCase):
|
|||||||
gtk_iteration()
|
gtk_iteration()
|
||||||
self.assertEqual(len(self.window.get('key_list').get_children()), 2)
|
self.assertEqual(len(self.window.get('key_list').get_children()), 2)
|
||||||
|
|
||||||
self.assertEqual(custom_mapping.get_character(30), 'Shift_L')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 30), 'Shift_L')
|
||||||
self.assertEqual(row.get_character(), 'Shift_L')
|
self.assertEqual(row.get_character(), 'Shift_L')
|
||||||
self.assertEqual(row.get_keycode(), 30)
|
self.assertEqual(row.get_keycode(), 30)
|
||||||
|
|
||||||
@ -270,8 +271,8 @@ class TestIntegration(unittest.TestCase):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
self.assertEqual(len(self.get_rows()), num_rows_target)
|
self.assertEqual(len(self.get_rows()), num_rows_target)
|
||||||
|
|
||||||
self.assertEqual(custom_mapping.get_character(10), 'a')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 10), 'a')
|
||||||
self.assertEqual(custom_mapping.get_character(11), 'k(b).k(c)')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'k(b).k(c)')
|
||||||
self.assertTrue(custom_mapping.changed)
|
self.assertTrue(custom_mapping.changed)
|
||||||
|
|
||||||
self.window.on_save_preset_clicked(None)
|
self.window.on_save_preset_clicked(None)
|
||||||
@ -293,13 +294,13 @@ class TestIntegration(unittest.TestCase):
|
|||||||
row.get_style_context().list_classes()
|
row.get_style_context().list_classes()
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(custom_mapping.get_character(10), 'c')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 10), 'c')
|
||||||
self.assertEqual(custom_mapping.get_character(11), 'k(b).k(c)')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'k(b).k(c)')
|
||||||
self.assertTrue(custom_mapping.changed)
|
self.assertTrue(custom_mapping.changed)
|
||||||
|
|
||||||
# try to add a duplicate keycode, it should be ignored
|
# try to add a duplicate keycode, it should be ignored
|
||||||
self.change_empty_row(11, 'd', success=False)
|
self.change_empty_row(11, 'd', success=False)
|
||||||
self.assertEqual(custom_mapping.get_character(11), 'k(b).k(c)')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'k(b).k(c)')
|
||||||
# and the number of rows shouldn't change
|
# and the number of rows shouldn't change
|
||||||
self.assertEqual(len(self.get_rows()), num_rows_target)
|
self.assertEqual(len(self.get_rows()), num_rows_target)
|
||||||
|
|
||||||
@ -316,11 +317,11 @@ class TestIntegration(unittest.TestCase):
|
|||||||
gtk_iteration()
|
gtk_iteration()
|
||||||
self.assertEqual(len(self.get_rows()), 3)
|
self.assertEqual(len(self.get_rows()), 3)
|
||||||
|
|
||||||
self.assertEqual(custom_mapping.get_character(11), 'b')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'b')
|
||||||
|
|
||||||
def remove(row, code, char, num_rows_after):
|
def remove(row, code, char, num_rows_after):
|
||||||
if code is not None and char is not None:
|
if code is not None and char is not None:
|
||||||
self.assertEqual(custom_mapping.get_character(code), char)
|
self.assertEqual(custom_mapping.get_character(EV_KEY, code), char)
|
||||||
self.assertEqual(row.get_character(), char)
|
self.assertEqual(row.get_character(), char)
|
||||||
self.assertEqual(row.get_keycode(), code)
|
self.assertEqual(row.get_keycode(), code)
|
||||||
row.on_delete_button_clicked()
|
row.on_delete_button_clicked()
|
||||||
@ -328,7 +329,7 @@ class TestIntegration(unittest.TestCase):
|
|||||||
gtk_iteration()
|
gtk_iteration()
|
||||||
self.assertIsNone(row.get_keycode())
|
self.assertIsNone(row.get_keycode())
|
||||||
self.assertIsNone(row.get_character())
|
self.assertIsNone(row.get_character())
|
||||||
self.assertIsNone(custom_mapping.get_character(code))
|
self.assertIsNone(custom_mapping.get_character(EV_KEY, code))
|
||||||
self.assertEqual(len(self.get_rows()), num_rows_after)
|
self.assertEqual(len(self.get_rows()), num_rows_after)
|
||||||
|
|
||||||
remove(row_1, 10, 'a', 2)
|
remove(row_1, 10, 'a', 2)
|
||||||
@ -339,17 +340,17 @@ class TestIntegration(unittest.TestCase):
|
|||||||
remove(row_3, None, 'c', 1)
|
remove(row_3, None, 'c', 1)
|
||||||
|
|
||||||
def test_rename_and_save(self):
|
def test_rename_and_save(self):
|
||||||
custom_mapping.change(14, 'a', None)
|
custom_mapping.change(EV_KEY, 14, 'a', None)
|
||||||
self.assertEqual(self.window.selected_preset, 'new preset')
|
self.assertEqual(self.window.selected_preset, 'new preset')
|
||||||
self.window.on_save_preset_clicked(None)
|
self.window.on_save_preset_clicked(None)
|
||||||
self.assertEqual(custom_mapping.get_character(14), 'a')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 14), 'a')
|
||||||
|
|
||||||
custom_mapping.change(14, 'b', None)
|
custom_mapping.change(EV_KEY, 14, 'b', None)
|
||||||
self.window.get('preset_name_input').set_text('asdf')
|
self.window.get('preset_name_input').set_text('asdf')
|
||||||
self.window.on_save_preset_clicked(None)
|
self.window.on_save_preset_clicked(None)
|
||||||
self.assertEqual(self.window.selected_preset, 'asdf')
|
self.assertEqual(self.window.selected_preset, 'asdf')
|
||||||
self.assertTrue(os.path.exists(f'{CONFIG}/device 1/asdf.json'))
|
self.assertTrue(os.path.exists(f'{CONFIG}/device 1/asdf.json'))
|
||||||
self.assertEqual(custom_mapping.get_character(14), 'b')
|
self.assertEqual(custom_mapping.get_character(EV_KEY, 14), 'b')
|
||||||
|
|
||||||
def test_select_device_and_preset(self):
|
def test_select_device_and_preset(self):
|
||||||
# created on start because the first device is selected and some empty
|
# created on start because the first device is selected and some empty
|
||||||
@ -379,7 +380,7 @@ class TestIntegration(unittest.TestCase):
|
|||||||
gtk_iteration()
|
gtk_iteration()
|
||||||
self.assertEqual(self.window.selected_preset, 'new preset')
|
self.assertEqual(self.window.selected_preset, 'new preset')
|
||||||
self.assertFalse(os.path.exists(f'{CONFIG}/device 1/abc 123.json'))
|
self.assertFalse(os.path.exists(f'{CONFIG}/device 1/abc 123.json'))
|
||||||
custom_mapping.change(10, '1', None)
|
custom_mapping.change(EV_KEY, 10, '1', None)
|
||||||
self.window.on_save_preset_clicked(None)
|
self.window.on_save_preset_clicked(None)
|
||||||
gtk_iteration()
|
gtk_iteration()
|
||||||
self.assertEqual(self.window.selected_preset, 'abc 123')
|
self.assertEqual(self.window.selected_preset, 'abc 123')
|
||||||
|
@ -57,7 +57,7 @@ class TestMacros(unittest.TestCase):
|
|||||||
repeats = 20
|
repeats = 20
|
||||||
macro = f'r({repeats}, k(k))'
|
macro = f'r({repeats}, k(k))'
|
||||||
self.loop.run_until_complete(parse(macro, self.handler).run())
|
self.loop.run_until_complete(parse(macro, self.handler).run())
|
||||||
keystroke_sleep = config.get('macros.keystroke_sleep_ms', 10)
|
keystroke_sleep = config.get('macros.keystroke_sleep_ms')
|
||||||
sleep_time = 2 * repeats * keystroke_sleep / 1000
|
sleep_time = 2 * repeats * keystroke_sleep / 1000
|
||||||
self.assertGreater(time.time() - start, sleep_time * 0.9)
|
self.assertGreater(time.time() - start, sleep_time * 0.9)
|
||||||
self.assertLess(time.time() - start, sleep_time * 1.1)
|
self.assertLess(time.time() - start, sleep_time * 1.1)
|
||||||
@ -68,7 +68,7 @@ class TestMacros(unittest.TestCase):
|
|||||||
macro = 'r(3, k(m).w(100))'
|
macro = 'r(3, k(m).w(100))'
|
||||||
self.loop.run_until_complete(parse(macro, self.handler).run())
|
self.loop.run_until_complete(parse(macro, self.handler).run())
|
||||||
|
|
||||||
keystroke_time = 6 * config.get('macros.keystroke_sleep_ms', 10)
|
keystroke_time = 6 * config.get('macros.keystroke_sleep_ms')
|
||||||
total_time = keystroke_time + 300
|
total_time = keystroke_time + 300
|
||||||
total_time /= 1000
|
total_time /= 1000
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ class TestMacros(unittest.TestCase):
|
|||||||
self.loop.run_until_complete(parse(macro, self.handler).run())
|
self.loop.run_until_complete(parse(macro, self.handler).run())
|
||||||
|
|
||||||
num_pauses = 8 + 6 + 4
|
num_pauses = 8 + 6 + 4
|
||||||
keystroke_time = num_pauses * config.get('macros.keystroke_sleep_ms', 10)
|
keystroke_time = num_pauses * config.get('macros.keystroke_sleep_ms')
|
||||||
wait_time = 220
|
wait_time = 220
|
||||||
total_time = (keystroke_time + wait_time) / 1000
|
total_time = (keystroke_time + wait_time) / 1000
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
from evdev.events import EV_KEY
|
||||||
|
|
||||||
from keymapper.mapping import Mapping
|
from keymapper.mapping import Mapping
|
||||||
from keymapper.state import populate_system_mapping
|
from keymapper.state import populate_system_mapping
|
||||||
@ -31,6 +32,7 @@ class TestMapping(unittest.TestCase):
|
|||||||
self.assertFalse(self.mapping.changed)
|
self.assertFalse(self.mapping.changed)
|
||||||
|
|
||||||
def test_populate_system_mapping(self):
|
def test_populate_system_mapping(self):
|
||||||
|
# not actually a mapping object, just a dict
|
||||||
mapping = populate_system_mapping()
|
mapping = populate_system_mapping()
|
||||||
self.assertGreater(len(mapping), 100)
|
self.assertGreater(len(mapping), 100)
|
||||||
# xkb keycode 10 is typically mapped to '1'
|
# xkb keycode 10 is typically mapped to '1'
|
||||||
@ -41,20 +43,20 @@ class TestMapping(unittest.TestCase):
|
|||||||
|
|
||||||
def test_clone(self):
|
def test_clone(self):
|
||||||
mapping1 = Mapping()
|
mapping1 = Mapping()
|
||||||
mapping1.change(1, 'a')
|
mapping1.change(EV_KEY, 1, 'a')
|
||||||
mapping2 = mapping1.clone()
|
mapping2 = mapping1.clone()
|
||||||
mapping1.change(2, 'b')
|
mapping1.change(EV_KEY, 2, 'b')
|
||||||
|
|
||||||
self.assertEqual(mapping1.get_character(1), 'a')
|
self.assertEqual(mapping1.get_character(EV_KEY, 1), 'a')
|
||||||
self.assertEqual(mapping1.get_character(2), 'b')
|
self.assertEqual(mapping1.get_character(EV_KEY, 2), 'b')
|
||||||
|
|
||||||
self.assertEqual(mapping2.get_character(1), 'a')
|
self.assertEqual(mapping2.get_character(EV_KEY, 1), 'a')
|
||||||
self.assertIsNone(mapping2.get_character(2))
|
self.assertIsNone(mapping2.get_character(EV_KEY, 2))
|
||||||
|
|
||||||
def test_save_load(self):
|
def test_save_load(self):
|
||||||
self.mapping.change(10, '1')
|
self.mapping.change(EV_KEY, 10, '1')
|
||||||
self.mapping.change(11, '2')
|
self.mapping.change(EV_KEY, 11, '2')
|
||||||
self.mapping.change(12, '3')
|
self.mapping.change(EV_KEY, 12, '3')
|
||||||
self.mapping.config['foo'] = 'bar'
|
self.mapping.config['foo'] = 'bar'
|
||||||
self.mapping.save('device 1', 'test')
|
self.mapping.save('device 1', 'test')
|
||||||
|
|
||||||
@ -63,77 +65,77 @@ class TestMapping(unittest.TestCase):
|
|||||||
loaded.load('device 1', 'test')
|
loaded.load('device 1', 'test')
|
||||||
|
|
||||||
self.assertEqual(len(loaded), 3)
|
self.assertEqual(len(loaded), 3)
|
||||||
self.assertEqual(loaded.get_character(10), '1')
|
self.assertEqual(loaded.get_character(EV_KEY, 10), '1')
|
||||||
self.assertEqual(loaded.get_character(11), '2')
|
self.assertEqual(loaded.get_character(EV_KEY, 11), '2')
|
||||||
self.assertEqual(loaded.get_character(12), '3')
|
self.assertEqual(loaded.get_character(EV_KEY, 12), '3')
|
||||||
self.assertEqual(loaded.config['foo'], 'bar')
|
self.assertEqual(loaded.config['foo'], 'bar')
|
||||||
|
|
||||||
def test_change(self):
|
def test_change(self):
|
||||||
# 1 is not assigned yet, ignore it
|
# 1 is not assigned yet, ignore it
|
||||||
self.mapping.change(2, 'a', 1)
|
self.mapping.change(EV_KEY, 2, 'a', 1)
|
||||||
self.assertTrue(self.mapping.changed)
|
self.assertTrue(self.mapping.changed)
|
||||||
self.assertIsNone(self.mapping.get_character(1))
|
self.assertIsNone(self.mapping.get_character(EV_KEY, 1))
|
||||||
self.assertEqual(self.mapping.get_character(2), 'a')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 2), 'a')
|
||||||
self.assertEqual(len(self.mapping), 1)
|
self.assertEqual(len(self.mapping), 1)
|
||||||
|
|
||||||
# change 2 to 3 and change a to b
|
# change 2 to 3 and change a to b
|
||||||
self.mapping.change(3, 'b', 2)
|
self.mapping.change(EV_KEY, 3, 'b', 2)
|
||||||
self.assertIsNone(self.mapping.get_character(2))
|
self.assertIsNone(self.mapping.get_character(EV_KEY, 2))
|
||||||
self.assertEqual(self.mapping.get_character(3), 'b')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 3), 'b')
|
||||||
self.assertEqual(len(self.mapping), 1)
|
self.assertEqual(len(self.mapping), 1)
|
||||||
|
|
||||||
# add 4
|
# add 4
|
||||||
self.mapping.change(4, 'c', None)
|
self.mapping.change(EV_KEY, 4, 'c', None)
|
||||||
self.assertEqual(self.mapping.get_character(3), 'b')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 3), 'b')
|
||||||
self.assertEqual(self.mapping.get_character(4), 'c')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'c')
|
||||||
self.assertEqual(len(self.mapping), 2)
|
self.assertEqual(len(self.mapping), 2)
|
||||||
|
|
||||||
# change the mapping of 4 to d
|
# change the mapping of 4 to d
|
||||||
self.mapping.change(4, 'd', None)
|
self.mapping.change(EV_KEY, 4, 'd', None)
|
||||||
self.assertEqual(self.mapping.get_character(4), 'd')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'd')
|
||||||
self.assertEqual(len(self.mapping), 2)
|
self.assertEqual(len(self.mapping), 2)
|
||||||
|
|
||||||
# this also works in the same way
|
# this also works in the same way
|
||||||
self.mapping.change(4, 'e', 4)
|
self.mapping.change(EV_KEY, 4, 'e', 4)
|
||||||
self.assertEqual(self.mapping.get_character(4), 'e')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'e')
|
||||||
self.assertEqual(len(self.mapping), 2)
|
self.assertEqual(len(self.mapping), 2)
|
||||||
|
|
||||||
# and this
|
# and this
|
||||||
self.mapping.change('4', 'f', '4')
|
self.mapping.change(EV_KEY, '4', 'f', '4')
|
||||||
self.assertEqual(self.mapping.get_character(4), 'f')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'f')
|
||||||
self.assertEqual(len(self.mapping), 2)
|
self.assertEqual(len(self.mapping), 2)
|
||||||
|
|
||||||
# non-int keycodes are ignored
|
# non-int keycodes are ignored
|
||||||
self.mapping.change('b', 'c', 'a')
|
self.mapping.change(EV_KEY, 'b', 'c', 'a')
|
||||||
self.assertEqual(len(self.mapping), 2)
|
self.assertEqual(len(self.mapping), 2)
|
||||||
|
|
||||||
def test_clear(self):
|
def test_clear(self):
|
||||||
# does nothing
|
# does nothing
|
||||||
self.mapping.clear(40)
|
self.mapping.clear(EV_KEY, 40)
|
||||||
self.assertFalse(self.mapping.changed)
|
self.assertFalse(self.mapping.changed)
|
||||||
self.assertEqual(len(self.mapping), 0)
|
self.assertEqual(len(self.mapping), 0)
|
||||||
|
|
||||||
self.mapping._mapping[40] = 'b'
|
self.mapping._mapping[(EV_KEY, 40)] = 'b'
|
||||||
self.assertEqual(len(self.mapping), 1)
|
self.assertEqual(len(self.mapping), 1)
|
||||||
self.mapping.clear(40)
|
self.mapping.clear(EV_KEY, 40)
|
||||||
self.assertEqual(len(self.mapping), 0)
|
self.assertEqual(len(self.mapping), 0)
|
||||||
self.assertTrue(self.mapping.changed)
|
self.assertTrue(self.mapping.changed)
|
||||||
|
|
||||||
self.mapping.change(10, 'KP_1', None)
|
self.mapping.change(EV_KEY, 10, 'KP_1', None)
|
||||||
self.assertTrue(self.mapping.changed)
|
self.assertTrue(self.mapping.changed)
|
||||||
self.mapping.change(20, 'KP_2', None)
|
self.mapping.change(EV_KEY, 20, 'KP_2', None)
|
||||||
self.mapping.change(30, 'KP_3', None)
|
self.mapping.change(EV_KEY, 30, 'KP_3', None)
|
||||||
self.assertEqual(len(self.mapping), 3)
|
self.assertEqual(len(self.mapping), 3)
|
||||||
self.mapping.clear(20)
|
self.mapping.clear(EV_KEY, 20)
|
||||||
self.assertEqual(len(self.mapping), 2)
|
self.assertEqual(len(self.mapping), 2)
|
||||||
self.assertEqual(self.mapping.get_character(10), 'KP_1')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 10), 'KP_1')
|
||||||
self.assertIsNone(self.mapping.get_character(20))
|
self.assertIsNone(self.mapping.get_character(EV_KEY, 20))
|
||||||
self.assertEqual(self.mapping.get_character(30), 'KP_3')
|
self.assertEqual(self.mapping.get_character(EV_KEY, 30), 'KP_3')
|
||||||
|
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
self.mapping.change(10, '1')
|
self.mapping.change(EV_KEY, 10, '1')
|
||||||
self.mapping.change(11, '2')
|
self.mapping.change(EV_KEY, 11, '2')
|
||||||
self.mapping.change(12, '3')
|
self.mapping.change(EV_KEY, 12, '3')
|
||||||
self.assertEqual(len(self.mapping), 3)
|
self.assertEqual(len(self.mapping), 3)
|
||||||
self.mapping.empty()
|
self.mapping.empty()
|
||||||
self.assertEqual(len(self.mapping), 0)
|
self.assertEqual(len(self.mapping), 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user