1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
# -*- coding: utf-8 -*-
# input-remapper - GUI for device specific keyboard mappings
# Copyright (C) 2025 sezanzeb <b8x45ygc9@mozmail.com>
#
# This file is part of input-remapper.
#
# input-remapper is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# input-remapper is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
"""Store which presets should be enabled for which device on login."""
from __future__ import annotations
import copy
import json
import os
from typing import Union, List, Optional, Callable, Any
from inputremapper.configs.paths import PathUtils
from inputremapper.logging.logger import logger, VERSION
from inputremapper.user import UserUtils
MOUSE = "mouse"
WHEEL = "wheel"
BUTTONS = "buttons"
NONE = "none"
INITIAL_CONFIG = {
"version": VERSION,
"autoload": {},
}
class GlobalConfig:
"""Configures stuff like autoloading in ~/.config/input-remapper-2/config.json."""
def __init__(self):
self.path = os.path.join(PathUtils.config_path(), "config.json")
self._config = copy.deepcopy(INITIAL_CONFIG)
def get_dir(self) -> str:
"""The folder containing this config."""
return os.path.split(self.path)[0]
def get_autoload_preset(self, group_key: str) -> Optional[str]:
# modifications are only allowed via the setter, because it needs to write
# the config file too. Therefore return a copy to prevent inconsistencies.
return copy.deepcopy(self._config["autoload"].get(group_key))
def set_autoload_preset(self, group_key: str, preset: Optional[str]):
"""Set a preset to be automatically applied on start.
Parameters
----------
group_key
the unique identifier of the group. This is used instead of the
name to enable autoloading two different presets when two similar
devices are connected.
preset
if None, don't autoload something for this device.
"""
if preset is not None:
self._config["autoload"][group_key] = preset
else:
logger.info('Not injecting for "%s" automatically anmore', group_key)
del self._config["autoload"][group_key]
self._save_config()
def iterate_autoload_presets(self):
"""Get tuples of (device, preset)."""
return self._config.get("autoload", {}).items()
def is_autoloaded(self, group_key: Optional[str], preset: Optional[str]):
"""Should this preset be loaded automatically?"""
if group_key is None or preset is None:
raise ValueError("Expected group_key and preset to not be None")
return self._config.get("autoload", {}).get(group_key) == preset
def load_config(self, path: Optional[str] = None):
"""Load the config from the file system.
Parameters
----------
path
If set, will change the path to load from and save to.
"""
if path is not None:
if not os.path.exists(path):
logger.error('Config at "%s" not found', path)
return
self.path = path
self._clear_config()
if not os.path.exists(self.path):
# treated like an empty config
logger.debug('Config "%s" doesn\'t exist yet', self.path)
self._clear_config()
self._config = copy.deepcopy(INITIAL_CONFIG)
self._save_config()
return
with open(self.path, "r") as file:
try:
self._config.update(json.load(file))
logger.info('Loaded config from "%s"', self.path)
except json.decoder.JSONDecodeError as error:
logger.error(
'Failed to parse config "%s": %s. Using defaults',
self.path,
str(error),
)
# uses the default configuration when the config object
# is empty automatically
def _save_config(self):
"""Save the config to the file system."""
if UserUtils.user == "root":
logger.debug("Skipping config file creation for the root user")
return
PathUtils.touch(self.path)
with open(self.path, "w") as file:
json.dump(self._config, file, indent=4)
logger.info("Saved config to %s", self.path)
file.write("\n")
def _clear_config(self):
"""Remove all configurations in memory."""
self._config = {}
|