Skip to content

Commit

Permalink
more refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
sezanzeb committed Oct 2, 2024
1 parent 57a7c07 commit e0a8127
Show file tree
Hide file tree
Showing 40 changed files with 756 additions and 539 deletions.
19 changes: 16 additions & 3 deletions bin/input-remapper-control
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ from typing import Union, Optional

from inputremapper.configs.global_config import GlobalConfig
from inputremapper.configs.migrations import Migrations
from inputremapper.injection.global_uinputs import GlobalUInputs, UInput, FrontendUInput
from inputremapper.logging.logger import logger


Expand Down Expand Up @@ -66,8 +67,13 @@ class Options:


class InputRemapperControl:
def __init__(self, global_config: GlobalConfig):
def __init__(
self,
global_config: GlobalConfig,
migrations: Migrations,
):
self.global_config = global_config
self.migrations = migrations

def run(self, cmd) -> None:
"""Run and log a command."""
Expand Down Expand Up @@ -149,7 +155,7 @@ class InputRemapperControl:
# config_dir is either the cli arg or the default path in home
config_dir = os.path.dirname(self.global_config.path)
self.daemon.set_config_dir(config_dir)
Migrations.migrate()
self.migrations.migrate()

def _stop(self, device: str) -> None:
group = self._require_group(device)
Expand Down Expand Up @@ -270,7 +276,14 @@ class InputRemapperControl:


def main(options: Options) -> None:
input_remapper_control = InputRemapperControl()
global_config = GlobalConfig()
global_uinputs = GlobalUInputs(FrontendUInput)
migrations = Migrations(global_uinputs)
input_remapper_control = InputRemapperControl(
global_config,
migrations,
)

if options.debug:
logger.update_verbosity(True)

Expand Down
9 changes: 6 additions & 3 deletions bin/input-remapper-gtk
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,17 @@ if __name__ == "__main__":
from inputremapper.gui.data_manager import DataManager
from inputremapper.gui.user_interface import UserInterface
from inputremapper.gui.controller import Controller
from inputremapper.injection.global_uinputs import GlobalUInputs
from inputremapper.injection.global_uinputs import GlobalUInputs, FrontendUInput
from inputremapper.groups import _Groups
from inputremapper.gui.reader_client import ReaderClient
from inputremapper.daemon import Daemon
from inputremapper.configs.global_config import GlobalConfig
from inputremapper.configs.migrations import Migrations

Migrations.migrate()
global_uinputs = GlobalUInputs(FrontendUInput)
migrations = Migrations(global_uinputs)

Migrations(global_uinputs).migrate()

message_broker = MessageBroker()

Expand All @@ -101,7 +104,7 @@ if __name__ == "__main__":
global_config,
reader_client,
daemon,
GlobalUInputs(),
global_uinputs,
system_mapping,
)
controller = Controller(message_broker, data_manager)
Expand Down
4 changes: 3 additions & 1 deletion bin/input-remapper-reader-service
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import sys
from argparse import ArgumentParser

from inputremapper.groups import _Groups
from inputremapper.injection.global_uinputs import GlobalUInputs, FrontendUInput
from inputremapper.logging.logger import logger

if __name__ == "__main__":
Expand Down Expand Up @@ -57,5 +58,6 @@ if __name__ == "__main__":

atexit.register(on_exit)
groups = _Groups()
reader_service = ReaderService(groups)
global_uinputs = GlobalUInputs(FrontendUInput)
reader_service = ReaderService(groups, global_uinputs)
asyncio.run(reader_service.run())
6 changes: 5 additions & 1 deletion bin/input-remapper-service
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import sys
from argparse import ArgumentParser

from inputremapper.configs.global_config import GlobalConfig
from inputremapper.injection.global_uinputs import GlobalUInputs, UInput
from inputremapper.injection.mapping_handlers.mapping_parser import MappingParser
from inputremapper.logging.logger import logger

if __name__ == "__main__":
Expand Down Expand Up @@ -57,7 +59,9 @@ if __name__ == "__main__":
logger.log_info("input-remapper-service")

global_config = GlobalConfig()
global_uinputs = GlobalUInputs(UInput)
mapping_parser = MappingParser(global_uinputs)

daemon = Daemon(global_config)
daemon = Daemon(global_config, global_uinputs, mapping_parser)
daemon.publish()
daemon.run()
98 changes: 45 additions & 53 deletions inputremapper/configs/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
from inputremapper.configs.paths import PathUtils
from inputremapper.configs.preset import Preset
from inputremapper.configs.system_mapping import system_mapping
from inputremapper.injection.global_uinputs import global_uinputs
from inputremapper.injection.global_uinputs import GlobalUInputs
from inputremapper.injection.macros.parse import is_this_a_macro
from inputremapper.logging.logger import logger, VERSION
from inputremapper.user import UserUtils
Expand All @@ -66,43 +66,46 @@ class Config(TypedDict):


class Migrations:
@staticmethod
def migrate():
def __init__(self, global_uinputs: GlobalUInputs):
self.global_uinputs = global_uinputs

def migrate(self):
"""Migrate config files to the current release."""

Migrations._rename_to_input_remapper()
self._rename_to_input_remapper()

Migrations._copy_to_v2()
self._copy_to_v2()

v = Migrations.config_version()
v = self.config_version()

if v < pkg_resources.parse_version("0.4.0"):
Migrations._config_suffix()
Migrations._preset_path()
self._config_suffix()
self._preset_path()

if v < pkg_resources.parse_version("1.2.2"):
Migrations._mapping_keys()
self._mapping_keys()

if v < pkg_resources.parse_version("1.4.0"):
global_uinputs.prepare_all()
Migrations._add_target()
self.global_uinputs.prepare_all()
self._add_target()

if v < pkg_resources.parse_version("1.4.1"):
Migrations._otherwise_to_else()
self._otherwise_to_else()

if v < pkg_resources.parse_version("1.5.0"):
Migrations._remove_logs()
self._remove_logs()

if v < pkg_resources.parse_version("1.6.0-beta"):
Migrations._convert_to_individual_mappings()
self._convert_to_individual_mappings()

# add new migrations here

if v < pkg_resources.parse_version(VERSION):
Migrations._update_version()
self._update_version()

@staticmethod
def all_presets() -> Iterator[Tuple[os.PathLike, Dict | List]]:
def all_presets(
self,
) -> Iterator[Tuple[os.PathLike, Dict | List]]:
"""Get all presets for all groups as list."""
if not os.path.exists(PathUtils.get_preset_path()):
return
Expand All @@ -124,8 +127,7 @@ def all_presets() -> Iterator[Tuple[os.PathLike, Dict | List]]:
logger.warning('Invalid json format in preset "%s"', preset)
continue

@staticmethod
def config_version():
def config_version(self):
"""Get the version string in config.json as packaging.Version object."""
config_path = os.path.join(PathUtils.config_path(), "config.json")

Expand All @@ -140,17 +142,15 @@ def config_version():

return pkg_resources.parse_version("0.0.0")

@staticmethod
def _config_suffix():
def _config_suffix(self):
"""Append the .json suffix to the config file."""
deprecated_path = os.path.join(PathUtils.config_path(), "config")
config_path = os.path.join(PathUtils.config_path(), "config.json")
if os.path.exists(deprecated_path) and not os.path.exists(config_path):
logger.info('Moving "%s" to "%s"', deprecated_path, config_path)
os.rename(deprecated_path, config_path)

@staticmethod
def _preset_path():
def _preset_path(self):
"""Migrate the folder structure from < 0.4.0.
Move existing presets into the new subfolder 'presets'
Expand All @@ -173,13 +173,12 @@ def _preset_path():

logger.info("done")

@staticmethod
def _mapping_keys():
def _mapping_keys(self):
"""Update all preset mappings.
Update all keys in preset to include value e.g.: '1,5'->'1,5,1'
"""
for preset, preset_structure in Migrations.all_presets():
for preset, preset_structure in self.all_presets():
if isinstance(preset_structure, list):
continue # the preset must be at least 1.6-beta version

Expand All @@ -199,8 +198,7 @@ def _mapping_keys():
json.dump(preset_structure, file, indent=4)
file.write("\n")

@staticmethod
def _update_version():
def _update_version(self):
"""Write the current version to the config file."""
config_file = os.path.join(PathUtils.config_path(), "config.json")
if not os.path.exists(config_file):
Expand All @@ -214,8 +212,7 @@ def _update_version():
logger.info('Updating version in config to "%s"', VERSION)
json.dump(config, file, indent=4)

@staticmethod
def _rename_to_input_remapper():
def _rename_to_input_remapper(self):
"""Rename .config/key-mapper to .config/input-remapper."""
old_config_path = os.path.join(UserUtils.home, ".config/key-mapper")
if not os.path.exists(PathUtils.config_path()) and os.path.exists(
Expand All @@ -224,8 +221,7 @@ def _rename_to_input_remapper():
logger.info("Moving %s to %s", old_config_path, PathUtils.config_path())
shutil.move(old_config_path, PathUtils.config_path())

@staticmethod
def _find_target(symbol):
def _find_target(self, symbol):
"""Try to find a uinput with the required capabilities for the symbol."""
capabilities = {EV_KEY: set(), EV_REL: set()}

Expand All @@ -239,17 +235,16 @@ def _find_target(symbol):
if len(capabilities[EV_REL]) > 0:
return "mouse"

for name, uinput in global_uinputs.devices.items():
for name, uinput in self.global_uinputs.devices.items():
if capabilities[EV_KEY].issubset(uinput.capabilities()[EV_KEY]):
return name

logger.info('could not find a suitable target UInput for "%s"', symbol)
return None

@staticmethod
def _add_target():
def _add_target(self):
"""Add the target field to each preset mapping."""
for preset, preset_structure in Migrations.all_presets():
for preset, preset_structure in self.all_presets():
if isinstance(preset_structure, list):
continue

Expand All @@ -261,7 +256,7 @@ def _add_target():
if isinstance(symbol, list):
continue

target = Migrations._find_target(symbol)
target = self._find_target(symbol)
if target is None:
target = "keyboard"
symbol = (
Expand All @@ -288,10 +283,9 @@ def _add_target():
json.dump(preset_structure, file, indent=4)
file.write("\n")

@staticmethod
def _otherwise_to_else():
def _otherwise_to_else(self):
"""Conditional macros should use an "else" parameter instead of "otherwise"."""
for preset, preset_structure in Migrations.all_presets():
for preset, preset_structure in self.all_presets():
if isinstance(preset_structure, list):
continue

Expand Down Expand Up @@ -328,8 +322,9 @@ def _otherwise_to_else():
json.dump(preset_structure, file, indent=4)
file.write("\n")

@staticmethod
def _input_combination_from_string(combination_string: str) -> InputCombination:
def _input_combination_from_string(
self, combination_string: str
) -> InputCombination:
configs = []
for event_str in combination_string.split("+"):
type_, code, analog_threshold = event_str.split(",")
Expand All @@ -343,14 +338,15 @@ def _input_combination_from_string(combination_string: str) -> InputCombination:

return InputCombination(configs)

@staticmethod
def _convert_to_individual_mappings() -> None:
def _convert_to_individual_mappings(
self,
) -> None:
"""Convert preset.json
from {key: [symbol, target]}
to [{input_combination: ..., output_symbol: symbol, ...}]
"""

for old_preset_path, old_preset in Migrations.all_presets():
for old_preset_path, old_preset in self.all_presets():
if isinstance(old_preset, list):
continue

Expand All @@ -363,9 +359,7 @@ def _convert_to_individual_mappings() -> None:
symbol_target,
)
try:
combination = Migrations._input_combination_from_string(
combination
)
combination = self._input_combination_from_string(combination)
except ValueError:
logger.error(
"unable to migrate mapping with invalid combination %s",
Expand Down Expand Up @@ -482,8 +476,7 @@ def _convert_to_individual_mappings() -> None:

migrated_preset.save()

@staticmethod
def _copy_to_v2():
def _copy_to_v2(self):
"""Move the beta config to the v2 path, or copy the v1 config to the v2 path."""
# TODO test
if os.path.exists(PathUtils.config_path()):
Expand All @@ -495,7 +488,7 @@ def _copy_to_v2():
old_path = os.path.join(UserUtils.home, ".config/input-remapper")
if os.path.exists(os.path.join(old_path, "config.json")):
# no beta path, only old presets exist. COPY to v2 path, which will then be
# migrated by the various migrations.
# migrated by the various self.
logger.debug("copying all from %s to %s", old_path, PathUtils.config_path())
shutil.copytree(old_path, PathUtils.config_path())
return
Expand All @@ -511,8 +504,7 @@ def _copy_to_v2():
logger.debug("moving %s to %s", beta_path, PathUtils.config_path())
shutil.move(beta_path, PathUtils.config_path())

@staticmethod
def _remove_logs():
def _remove_logs(self):
"""We will try to rely on journalctl for this in the future."""
try:
PathUtils.remove(f"{UserUtils.home}/.log/input-remapper")
Expand Down
Loading

0 comments on commit e0a8127

Please sign in to comment.