File: global_config.py

package info (click to toggle)
input-remapper 2.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,876 kB
  • sloc: python: 27,262; sh: 191; xml: 33; makefile: 3
file content (142 lines) | stat: -rw-r--r-- 5,025 bytes parent folder | download
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 = {}