Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CombinationHandler releasing #578

Merged
merged 68 commits into from
Feb 19, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
3bfb0cd
forward -> forward_to
sezanzeb Dec 15, 2022
5bffec9
forward -> forward_to
sezanzeb Dec 15, 2022
2c6cba7
include uniputs, and sources in Context
jonasBoss Dec 15, 2022
4c1c865
Merge branch 'fix_release_combination_keys' of github.com:jonasBoss/k…
sezanzeb Dec 15, 2022
a5a5d46
remove forward_to
sezanzeb Dec 15, 2022
afff661
I guess it works maybe
sezanzeb Dec 15, 2022
f56c0c6
some mypy
sezanzeb Dec 15, 2022
53cbc9a
mypy maybe
sezanzeb Dec 15, 2022
230da2f
mypy
sezanzeb Dec 15, 2022
0e6213b
mypy
sezanzeb Dec 15, 2022
1dd1382
leftover forward_to
sezanzeb Dec 15, 2022
c9a455c
remove forward_to from tests
sezanzeb Dec 15, 2022
e9ada56
forwarding
sezanzeb Dec 26, 2022
2a92e7d
using repr, repr usages now use the <... at id> format, logging the u…
sezanzeb Dec 26, 2022
9016268
using hex(id(self)) because python uses that by default
sezanzeb Dec 26, 2022
5ab88b6
mypy
sezanzeb Dec 26, 2022
8443fc8
oops
sezanzeb Dec 26, 2022
8f44e20
a few fixes
sezanzeb Dec 26, 2022
62acb9e
black
sezanzeb Dec 26, 2022
330bf31
reviewdog
sezanzeb Dec 26, 2022
44288f9
fixed test_combination, added helpful logs if handlers not found
sezanzeb Dec 26, 2022
bfc6130
black
sezanzeb Dec 26, 2022
164acb1
fix test_ignore_disabled
sezanzeb Dec 28, 2022
4db45d4
typo
sezanzeb Dec 28, 2022
2feed73
imports, deprecated usages
sezanzeb Dec 28, 2022
a1efdbb
a few test fixes
sezanzeb Dec 28, 2022
ea56613
missing import
sezanzeb Dec 28, 2022
e999fd0
using the shorthands
sezanzeb Dec 28, 2022
3bd1a82
black
sezanzeb Dec 28, 2022
be9079d
uniform usage of InputCombination and other stuff
sezanzeb Dec 30, 2022
1a2f8c8
deprecated convert_to_internal_events
sezanzeb Dec 30, 2022
28dff76
Capitalized some logs, removed deprecated comments
sezanzeb Dec 30, 2022
03ff62e
added hash to group discovery logs, fixed TestInjector
sezanzeb Dec 31, 2022
72f42a0
Update inputremapper/configs/input_config.py
sezanzeb Jan 8, 2023
838176a
fix logger test
sezanzeb Jan 12, 2023
beb011a
failed event-handlings don't crash the event_reader completely anymore
sezanzeb Jan 12, 2023
d18d6cf
Merge branch 'fix-release_combination_keys-forwarding' of github.com:…
sezanzeb Jan 12, 2023
5a962b7
fixed more tests
sezanzeb Jan 12, 2023
63955c7
all unit tests passing
sezanzeb Jan 12, 2023
38b8aa6
using boolean variable instead of set in send_to_handlers
sezanzeb Jan 12, 2023
b6d787c
fixed send_to_handlers
sezanzeb Jan 12, 2023
a4d9919
fixed some integration tests
sezanzeb Jan 15, 2023
5652480
¯\_(ツ)_/¯
sezanzeb Jan 15, 2023
673a5a4
all tests passing
sezanzeb Jan 15, 2023
7cac266
renamed to get_notify_callbacks, stuff
sezanzeb Jan 15, 2023
cca54eb
added Mapping.from_combination
sezanzeb Jan 15, 2023
235c6fd
InputCombination.from_tuples
sezanzeb Jan 15, 2023
cda8f47
add reasoning comment to hardocded version string
sezanzeb Feb 11, 2023
39ef8cc
mention that conda can cause issues
sezanzeb Feb 11, 2023
63d4720
fix chunked string for translation
sezanzeb Feb 11, 2023
b8868fc
black
sezanzeb Feb 12, 2023
1854e72
Merge branch 'beta' of github.com:sezanzeb/key-mapper into fix-releas…
sezanzeb Feb 12, 2023
905cb0a
fixed get_event_reader in test_key_axis_combination_to_disable
sezanzeb Feb 12, 2023
347cffd
printing the black version before checking
sezanzeb Feb 12, 2023
9346aad
formatting with black 23.1.0
sezanzeb Feb 12, 2023
7f58574
test get_notify_callbacks and get_evdev_constant_name
sezanzeb Feb 16, 2023
01c89f6
tests for combinationhandler releasing
sezanzeb Feb 18, 2023
f26fcdb
asyncio reader tests
sezanzeb Feb 19, 2023
629e2dd
testing that the reader service forwards to the dummy
sezanzeb Feb 19, 2023
733db4a
fixed most tests
sezanzeb Feb 19, 2023
389e8fa
pain
sezanzeb Feb 19, 2023
41e8a39
pain
sezanzeb Feb 19, 2023
73f13c3
black
sezanzeb Feb 19, 2023
e05a556
done I guess
sezanzeb Feb 19, 2023
77e6224
removed inputconfigs shorthands
sezanzeb Feb 19, 2023
a9f018b
Update inputremapper/configs/input_config.py
sezanzeb Feb 19, 2023
9552ff6
review
sezanzeb Feb 19, 2023
9d89ca2
Merge branch 'fix-release_combination_keys-forwarding' of github.com:…
sezanzeb Feb 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix test_ignore_disabled
  • Loading branch information
sezanzeb committed Dec 28, 2022
commit 164acb1be5a57f7571f5d50cd4d490a184623ca1
24 changes: 23 additions & 1 deletion inputremapper/configs/input_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from typing import Tuple, Iterable, Union, List, Dict, Optional, Hashable, TypeAlias

from evdev import ecodes
from evdev._ecodes import EV_ABS, EV_KEY
from evdev._ecodes import EV_ABS, EV_KEY, EV_REL

from inputremapper.input_event import InputEvent
from pydantic import BaseModel, root_validator, validator
Expand Down Expand Up @@ -73,6 +73,8 @@ def __repr__(self):

@classmethod
def abs(cls, code, analog_threshold, origin_hash):
"""Create a new InputConfig object for an abs input like joysticks."""
# TODO analog_threshold default None and move to last param?
return InputConfig(
type=EV_ABS,
code=code,
Expand All @@ -82,12 +84,22 @@ def abs(cls, code, analog_threshold, origin_hash):

@classmethod
def key(cls, code, origin_hash):
"""Create a new InputConfig object for a key input."""
return InputConfig(
type=EV_KEY,
code=code,
origin_hash=origin_hash,
)

@classmethod
def rel(cls, code, origin_hash):
"""Create a new InputConfig object for a rel input like mouse movements."""
return InputConfig(
type=EV_REL,
code=code,
origin_hash=origin_hash,
)

@property
def input_match_hash(self) -> Hashable:
"""a Hashable object which is intended to match the InputConfig with a
Expand Down Expand Up @@ -340,6 +352,16 @@ def empty_combination(cls) -> InputCombination:
"""
return cls([{"type": 99, "code": 99, "analog_threshold": 99}])

@classmethod
def from_tuples(cls, *tuples):
"""Construct an InputCombination from (type, code, value) tuples."""
sezanzeb marked this conversation as resolved.
Show resolved Hide resolved
return cls(
[
{"type": tuple_[0], "code": tuple_[1], "analog_threshold": tuple_[2]}
for tuple_ in tuples
]
)

def is_problematic(self) -> bool:
"""Is this combination going to work properly on all systems?"""
if len(self) <= 1:
Expand Down
9 changes: 8 additions & 1 deletion inputremapper/injection/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@


"""Stores injection-process wide information."""

from __future__ import annotations

from collections import defaultdict
Expand Down Expand Up @@ -111,7 +112,13 @@ def get_entry_points(self, input_event: InputEvent) -> List[NotifyCallback]:
input_match_hash = input_event.input_match_hash
callbacks = self._notify_callbacks[input_match_hash]
if len(callbacks) == 0:
logger.warning("No NotifyCallbacks set for %s", input_match_hash)
logger.debug("No NotifyCallbacks set for %s", input_match_hash)

# TODO logs appearing when hitting forwarded keys
"""
22:51:45.774591 68627 service WARNING context.py:114: No NotifyCallbacks set for (1, 16, 'cba0d7f555a960fabbad51fdb8f28b70')
22:51:45.774713 68627 service ERROR event_reader.py:127: No NotifyCallbacks set
"""

return callbacks

Expand Down
9 changes: 4 additions & 5 deletions inputremapper/injection/event_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ async def read_loop(self) -> AsyncIterator[evdev.InputEvent]:
yield event

def send_to_handlers(self, event: InputEvent) -> bool:
"""Send the event to callback."""
"""Send the event to the NotifyCallbacks.

Return if anyone took care of the event.
"""
if event.type == evdev.ecodes.EV_MSC:
return False

Expand All @@ -122,10 +125,6 @@ def send_to_handlers(self, event: InputEvent) -> bool:
results = set()
notify_callbacks = self.context.get_entry_points(event)

if len(notify_callbacks) == 0:
# There definitely has to be a handler if the mapping is not empty
logger.error("No NotifyCallbacks set")

if notify_callbacks:
for notify_callback in notify_callbacks:
results.add(notify_callback(event, source=self._source))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def _handle_key_input(self, event: InputEvent):
if self._map_axis.type == evdev.ecodes.EV_ABS:
# send the last cached value so that the abs axis
# is at the correct position
logger.debug("starting axis for", self.mapping.input_combination)
logger.debug("starting axis for %s", self.mapping.input_combination)
event = InputEvent(
0,
0,
Expand Down
44 changes: 43 additions & 1 deletion inputremapper/input_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import enum
from dataclasses import dataclass
from typing import Tuple, Optional, Hashable
from typing import Tuple, Optional, Hashable, Literal

import evdev
from evdev import ecodes
Expand Down Expand Up @@ -97,10 +97,13 @@ def from_tuple(
cls, event_tuple: Tuple[int, int, int], origin_hash: Optional[str] = None
) -> InputEvent:
"""Create a InputEvent from a (type, code, value) tuple."""
# TODO use this as rarely as possible. Construct objects as early as possible
# instead.
if len(event_tuple) != 3:
raise TypeError(
f"failed to create InputEvent {event_tuple = } must have length 3"
)

return cls(
0,
0,
Expand All @@ -110,6 +113,45 @@ def from_tuple(
origin_hash=origin_hash,
)

@classmethod
def abs(cls, code: int, value: int, origin_hash: str):
"""Create an abs event, like joystick movements."""
return cls(
0,
0,
ecodes.EV_ABS,
code,
value,
origin_hash=origin_hash,
)

@classmethod
def rel(cls, code: int, value: int, origin_hash: str):
"""Create a rel event, like mouse movements."""
return cls(
0,
0,
ecodes.EV_REL,
code,
value,
origin_hash=origin_hash,
)

@classmethod
def key(cls, code: int, value: Literal[0, 1], origin_hash: str):
"""Create a key event, like keyboard keys or gamepad buttons.

A value of 1 means "press", a value of 0 means "release".
"""
return cls(
0,
0,
ecodes.EV_KEY,
code,
value,
origin_hash=origin_hash,
)

@property
def type_and_code(self) -> Tuple[int, int]:
"""Event type, code."""
Expand Down
16 changes: 8 additions & 8 deletions tests/integration/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,10 +916,10 @@ def test_hat_switch(self):
gtk_iteration()

# it should be possible to add all of them
ev_1 = (EV_ABS, evdev.ecodes.ABS_HAT0X, -1)
ev_2 = (EV_ABS, evdev.ecodes.ABS_HAT0X, 1)
ev_3 = (EV_ABS, evdev.ecodes.ABS_HAT0Y, -1)
ev_4 = (EV_ABS, evdev.ecodes.ABS_HAT0Y, 1)
ev_1 = InputEvent.from_tuple((EV_ABS, evdev.ecodes.ABS_HAT0X, -1))
ev_2 = InputEvent.from_tuple((EV_ABS, evdev.ecodes.ABS_HAT0X, 1))
ev_3 = InputEvent.from_tuple((EV_ABS, evdev.ecodes.ABS_HAT0Y, -1))
ev_4 = InputEvent.from_tuple((EV_ABS, evdev.ecodes.ABS_HAT0Y, 1))

def add_mapping(event_tuple, symbol) -> InputCombination:
"""adds mapping and returns the expected input combination"""
Expand Down Expand Up @@ -977,10 +977,10 @@ def test_combination(self):
gtk_iteration()

# it should be possible to write a combination
ev_1 = (EV_KEY, evdev.ecodes.KEY_A, 1)
ev_2 = (EV_ABS, evdev.ecodes.ABS_HAT0X, 1)
ev_3 = (EV_KEY, evdev.ecodes.KEY_C, 1)
ev_4 = (EV_ABS, evdev.ecodes.ABS_HAT0X, -1)
ev_1 = InputEvent.from_tuple((EV_KEY, evdev.ecodes.KEY_A, 1))
ev_2 = InputEvent.from_tuple((EV_ABS, evdev.ecodes.ABS_HAT0X, 1))
ev_3 = InputEvent.from_tuple((EV_KEY, evdev.ecodes.KEY_C, 1))
ev_4 = InputEvent.from_tuple((EV_ABS, evdev.ecodes.ABS_HAT0X, -1))
combination_1 = (ev_1, ev_2, ev_3)
combination_2 = (ev_2, ev_1, ev_3)

Expand Down
48 changes: 32 additions & 16 deletions tests/lib/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@ def get_combination_config(
*event_tuples: Tuple[int, int] | Tuple[int, int, int]
) -> Iterable[Dict[str, int]]:
"""Convenient function to get an iterable of dicts, InputEvent.event_tuples."""
# TODO replace usages of
# get_key_mapping(
# InputCombination(get_combination_config
# with
# get_key_mapping(
# input_combination=InputCombination.from_tuples(*combi_1),
# output_symbol="b",
# )
# ?
# TODO replace all tuples with proper objects
for event in event_tuples:
if len(event) == 3:
yield {"type": event[0], "code": event[1], "analog_threshold": event[2]}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[mypy] reported by reviewdog 🐶
error: Tuple index out of range [misc]

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lies

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, looks like mypy does not pick up on the type guard.
maybe you can do something like this:

config = {"type": event[0], "code": event[1]}
try:
    config["analog_threshold"] = event[3]
except IndexError:  # or whatever the error is called
    pass
yield config

Expand All @@ -321,37 +331,40 @@ def get_combination_config(
raise TypeError


def get_ui_mapping(combination=None, target_uinput="keyboard", output_symbol="a"):
def get_ui_mapping(input_combination=None, target_uinput="keyboard", output_symbol="a"):
"""Convenient function to get a valid mapping."""
from inputremapper.configs.mapping import UIMapping

if not combination:
combination = get_combination_config((99, 99))
if not input_combination:
input_combination = get_combination_config((99, 99))

return UIMapping(
input_combination=combination,
input_combination=input_combination,
target_uinput=target_uinput,
output_symbol=output_symbol,
)


def get_key_mapping(combination=None, target_uinput="keyboard", output_symbol="a"):
def get_key_mapping(
input_combination=None, target_uinput="keyboard", output_symbol="a"
):
"""Convenient function to get a valid mapping."""
from inputremapper.configs.mapping import Mapping

if not combination:
combination = [{"type": 99, "code": 99, "analog_threshold": 99}]
if not input_combination:
input_combination = [{"type": 99, "code": 99, "analog_threshold": 99}]

return Mapping(
input_combination=combination,
input_combination=input_combination,
target_uinput=target_uinput,
output_symbol=output_symbol,
)


def new_event(type, code, value, timestamp=None, offset=0):
"""Create a new input_event."""
from tests.lib.patches import InputEvent
"""Create a new InputEvent."""
# TODO probably deprecated with InputEvent.abs and InputEvent.key
from inputremapper.input_event import InputEvent

if timestamp is None:
timestamp = time.time() + offset
Expand All @@ -372,27 +385,30 @@ def prepare_presets():

preset1 = Preset(get_preset_path("Foo Device", "preset1"))
preset1.add(
get_key_mapping(combination=get_combination_config((1, 1)), output_symbol="b")
get_key_mapping(
input_combination=get_combination_config((1, 1)),
output_symbol="b",
)
)
preset1.add(get_key_mapping(combination=get_combination_config((1, 2))))
preset1.add(get_key_mapping(input_combination=get_combination_config((1, 2))))
preset1.save()

time.sleep(0.1)
preset2 = Preset(get_preset_path("Foo Device", "preset2"))
preset2.add(get_key_mapping(combination=get_combination_config((1, 3))))
preset2.add(get_key_mapping(combination=get_combination_config((1, 4))))
preset2.add(get_key_mapping(input_combination=get_combination_config((1, 3))))
preset2.add(get_key_mapping(input_combination=get_combination_config((1, 4))))
preset2.save()

# make sure the timestamp of preset 3 is the newest,
# so that it will be automatically loaded by the GUI
time.sleep(0.1)
preset3 = Preset(get_preset_path("Foo Device", "preset3"))
preset3.add(get_key_mapping(combination=get_combination_config((1, 5))))
preset3.add(get_key_mapping(input_combination=get_combination_config((1, 5))))
preset3.save()

with open(get_config_path("config.json"), "w") as file:
json.dump({"autoload": {"Foo Device 2": "preset2"}}, file, indent=4)

global_config.load_config()

return preset1, preset2, preset3
return preset1, preset2, preset3b
1 change: 1 addition & 0 deletions tests/lib/stuff.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

def convert_to_internal_events(events):
"""Convert an iterable of InputEvent to a list of inputremapper.InputEvent."""
# TODO deprecated
from inputremapper.input_event import InputEvent as InternalInputEvent

return [InternalInputEvent.from_event(event) for event in events]
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def test_daemon(self):
"""Injection 1"""

# should forward the event unchanged
push_events(fixtures.gamepad, [new_event(EV_KEY, BTN_B, 1)])
push_events(fixtures.gamepad, [InputEvent.key(BTN_B, 1)])

self.daemon = Daemon()

Expand Down
Loading