Skip to content

Commit

Permalink
Making ReaderService terminate automatically after some time (sezanze…
Browse files Browse the repository at this point in the history
  • Loading branch information
sezanzeb authored Nov 11, 2022
1 parent 27e911b commit b5345ad
Show file tree
Hide file tree
Showing 41 changed files with 989 additions and 634 deletions.
16 changes: 9 additions & 7 deletions bin/input-remapper-control
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ HELLO = 'hello'

# internal stuff that the gui uses
START_DAEMON = 'start-daemon'
HELPER = 'helper'
START_READER_SERVICE = 'start-reader-service'


def run(cmd):
Expand All @@ -56,7 +56,7 @@ def run(cmd):

COMMANDS = [AUTOLOAD, START, STOP, HELLO, STOP_ALL]

INTERNALS = [START_DAEMON, HELPER]
INTERNALS = [START_DAEMON, START_READER_SERVICE]


def utils(options):
Expand Down Expand Up @@ -166,8 +166,8 @@ def internals(options):
"""
debug = ' -d' if options.debug else ''

if options.command == HELPER:
cmd = f'input-remapper-helper{debug}'
if options.command == START_READER_SERVICE:
cmd = f'input-remapper-reader-service{debug}'
elif options.command == START_DAEMON:
cmd = f'input-remapper-service --hide-info{debug}'
else:
Expand Down Expand Up @@ -202,14 +202,16 @@ def _systemd_finished():

def boot_finished():
"""Check if booting is completed."""
# Get as much information as needed to really safely determine if booting up is complete.
# Get as much information as needed to really safely determine if booting up is
# complete.
# - `who` returns an empty list on some system for security purposes
# - something might be broken and might make systemd_analyze fail:
# Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=0).
# Bootup is not yet finished
# (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=0).
# Please try again later.
# Hint: Use 'systemctl list-jobs' to see active jobs
if _systemd_finished():
logger.debug('Booting finished')
logger.debug('System is booted')
return True

if _num_logged_in_users() > 0:
Expand Down
38 changes: 15 additions & 23 deletions bin/input-remapper-gtk
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,35 @@
"""Starts the user interface."""
from __future__ import annotations

import os
import sys
import atexit
import logging
from argparse import ArgumentParser

from inputremapper.gui.gettext import _, LOCALE_DIR

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GLib', '2.0')
gi.require_version('GtkSource', '4')
from gi.repository import Gtk


# https://github.com/Nuitka/Nuitka/issues/607#issuecomment-650217096
Gtk.init()

from inputremapper.gui.gettext import _, LOCALE_DIR
from inputremapper.gui.reader_service import ReaderService
from inputremapper.daemon import DaemonProxy
from inputremapper.logger import logger, update_verbosity, log_info


def start_processes() -> DaemonProxy:
"""Start helper and daemon via pkexec to run in the background."""
"""Start reader-service and daemon via pkexec to run in the background."""
# this function is overwritten in tests
daemon = Daemon.connect()

debug = " -d" if logger.level <= logging.DEBUG else ""
cmd = f"pkexec input-remapper-control --command helper {debug}"

logger.debug("Running `%s`", cmd)
exit_code = os.system(cmd)

if exit_code != 0:
logger.error("Failed to pkexec the helper, code %d", exit_code)
try:
ReaderService.pkexec_reader_service()
except Exception as e:
logger.error(e)
sys.exit(11)

return daemon
return Daemon.connect()


if __name__ == '__main__':
Expand All @@ -81,22 +73,22 @@ if __name__ == '__main__':
from inputremapper.gui.controller import Controller
from inputremapper.injection.global_uinputs import GlobalUInputs
from inputremapper.groups import _Groups
from inputremapper.gui.reader import Reader
from inputremapper.daemon import Daemon, DaemonProxy
from inputremapper.gui.reader_client import ReaderClient
from inputremapper.daemon import Daemon
from inputremapper.configs.global_config import GlobalConfig
from inputremapper.configs.migrations import migrate

migrate()

message_broker = MessageBroker()

# create the reader before we start the helper (start_processes) otherwise it
# can come to race conditions with the creation of pipes
reader = Reader(message_broker, _Groups())
# create the reader before we start the reader-service (start_processes) otherwise
# it can come to race conditions with the creation of pipes
reader_client = ReaderClient(message_broker, _Groups())
daemon = start_processes()

data_manager = DataManager(
message_broker, GlobalConfig(), reader, daemon, GlobalUInputs(), system_mapping
message_broker, GlobalConfig(), reader_client, daemon, GlobalUInputs(), system_mapping
)
controller = Controller(message_broker, data_manager)
user_interface = UserInterface(message_broker, controller)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.


"""Starts the root helper."""
"""Starts the root reader-service."""


import os
Expand All @@ -44,7 +44,7 @@ if __name__ == '__main__':
update_verbosity(options.debug)

# import input-remapper stuff after setting the log verbosity
from inputremapper.gui.helper import RootHelper
from inputremapper.gui.reader_service import ReaderService

def on_exit():
"""Don't remain idle and alive when the GUI exits via ctrl+c."""
Expand All @@ -54,6 +54,7 @@ if __name__ == '__main__':
os.kill(os.getpid(), signal.SIGKILL)

atexit.register(on_exit)
# TODO import `groups` instead?
groups = _Groups()
helper = RootHelper(groups)
helper.run()
reader_service = ReaderService(groups)
reader_service.run()
4 changes: 2 additions & 2 deletions data/input-remapper.policy
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<action id="inputremapper">
<description>Run Input Remapper as root</description>
<message>Authentication is required to discover and read devices.</message>
<message xml:lang="sk">Vyžaduje sa prihlásenie na objavenie a prístup k zariadeniam.</message>
<message xml:lang="ru">Требуется аутентификация для обнаружения и чтения устройств.</message>
<message xml:lang="sk">Vyžaduje sa prihlásenie na objavenie a prístup k zariadeniam.</message>
<message xml:lang="ru">Требуется аутентификация для обнаружения и чтения устройств.</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
Expand Down
26 changes: 18 additions & 8 deletions inputremapper/configs/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.


"""Migration functions"""
"""Migration functions.
Only write changes to disk, if there actually are changes. Otherwise file-modification
dates are destroyed.
"""

import copy
import json
Expand Down Expand Up @@ -106,7 +110,7 @@ def _config_suffix():
def _preset_path():
"""Migrate the folder structure from < 0.4.0.
Move existing presets into the new subfolder "presets"
Move existing presets into the new subfolder 'presets'
"""
new_preset_folder = os.path.join(CONFIG_PATH, "presets")
if os.path.exists(get_preset_path()) or not os.path.exists(CONFIG_PATH):
Expand All @@ -128,18 +132,22 @@ def _preset_path():
def _mapping_keys():
"""Update all preset mappings.
Update all keys in preset to include value e.g.: "1,5"->"1,5,1"
Update all keys in preset to include value e.g.: '1,5'->'1,5,1'
"""
for preset, preset_dict in all_presets():
changes = 0
if "mapping" in preset_dict.keys():
mapping = copy.deepcopy(preset_dict["mapping"])
for key in mapping.keys():
if key.count(",") == 1:
preset_dict["mapping"][f"{key},1"] = preset_dict["mapping"].pop(key)
changes += 1

with open(preset, "w") as file:
json.dump(preset_dict, file, indent=4)
file.write("\n")
if changes:
with open(preset, "w") as file:
logger.info('Updating mapping keys of "%s"', preset)
json.dump(preset_dict, file, indent=4)
file.write("\n")


def _update_version():
Expand All @@ -148,12 +156,12 @@ def _update_version():
if not os.path.exists(config_file):
return

logger.info("Updating version in config to %s", VERSION)
with open(config_file, "r") as file:
config = json.load(file)

config["version"] = VERSION
with open(config_file, "w") as file:
logger.info('Updating version in config to "%s"', VERSION)
json.dump(config, file, indent=4)


Expand Down Expand Up @@ -183,7 +191,7 @@ def _find_target(symbol):
if capabilities[EV_KEY].issubset(uinput.capabilities()[EV_KEY]):
return name

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


Expand Down Expand Up @@ -217,6 +225,7 @@ def _add_target():
continue

with open(preset, "w") as file:
logger.info('Adding targets for "%s"', preset)
json.dump(preset_dict, file, indent=4)
file.write("\n")

Expand Down Expand Up @@ -253,6 +262,7 @@ def _otherwise_to_else():
continue

with open(preset, "w") as file:
logger.info('Changing otherwise to else for "%s"', preset)
json.dump(preset_dict, file, indent=4)
file.write("\n")

Expand Down
4 changes: 2 additions & 2 deletions inputremapper/configs/system_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ def _use_linux_evdev_symbols(self):
if name.startswith("KEY") or name.startswith("BTN"):
self._set(name, ecode)

self._set(DISABLE_NAME, DISABLE_CODE)

def populate(self):
"""Get a mapping of all available names to their keycodes."""
logger.debug("Gathering available keycodes")
Expand All @@ -136,6 +134,8 @@ def populate(self):

self._use_linux_evdev_symbols()

self._set(DISABLE_NAME, DISABLE_CODE)

def update(self, mapping: dict):
"""Update this with new keys.
Expand Down
10 changes: 5 additions & 5 deletions inputremapper/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@
from pathlib import PurePath
from typing import Protocol, Dict

import gi
from pydbus import SystemBus

import gi

gi.require_version("GLib", "2.0")
from gi.repository import GLib

Expand Down Expand Up @@ -214,14 +215,13 @@ def __init__(self):
macro_variables.start()

@classmethod
def connect(cls, fallback=True) -> DaemonProxy:
def connect(cls, fallback: bool = True) -> DaemonProxy:
"""Get an interface to start and stop injecting keystrokes.
Parameters
----------
fallback : bool
If true, returns an instance of the daemon instead if it cannot
connect
fallback
If true, starts the daemon via pkexec if it cannot connect.
"""
bus = SystemBus()
try:
Expand Down
6 changes: 6 additions & 0 deletions inputremapper/gui/autocompletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
from typing import Dict, Optional, List, Tuple

from evdev.ecodes import EV_KEY

import gi

gi.require_version("Gdk", "3.0")
gi.require_version("Gtk", "3.0")
gi.require_version("GLib", "2.0")
from gi.repository import Gdk, Gtk, GLib, GObject

from inputremapper.configs.mapping import MappingData
Expand Down
3 changes: 3 additions & 0 deletions inputremapper/gui/components/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@

from __future__ import annotations

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

from typing import Optional
Expand Down
8 changes: 7 additions & 1 deletion inputremapper/gui/components/device_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from __future__ import annotations
from typing import Optional

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

from inputremapper.gui.components.common import FlowBoxEntry, FlowBoxWrapper
Expand Down Expand Up @@ -65,7 +68,7 @@ def __init__(
def _on_gtk_toggle(self, *_, **__):
logger.debug('Selecting device "%s"', self.group_key)
self._controller.load_group(self.group_key)
self.message_broker.send(DoStackSwitch(Stack.presets_page))
self.message_broker.publish(DoStackSwitch(Stack.presets_page))


class DeviceGroupSelection(FlowBoxWrapper):
Expand Down Expand Up @@ -108,5 +111,8 @@ def _on_groups_changed(self, data: GroupsData):
)
self._gui.insert(device_group_entry, -1)

if self._controller.data_manager.active_group:
self.show_active_entry(self._controller.data_manager.active_group.key)

def _on_group_changed(self, data: GroupData):
self.show_active_entry(data.group_key)
6 changes: 6 additions & 0 deletions inputremapper/gui/components/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@

import cairo
from evdev.ecodes import EV_KEY, EV_ABS, EV_REL, bytype

import gi

gi.require_version("Gdk", "3.0")
gi.require_version("Gtk", "3.0")
gi.require_version("GtkSource", "4")
from gi.repository import Gtk, GtkSource, Gdk

from inputremapper.configs.mapping import MappingData
Expand Down
3 changes: 3 additions & 0 deletions inputremapper/gui/components/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@

from __future__ import annotations

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

from inputremapper.gui.controller import Controller
Expand Down
5 changes: 4 additions & 1 deletion inputremapper/gui/components/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@

from __future__ import annotations

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

from inputremapper.gui.components.common import FlowBoxEntry, FlowBoxWrapper
Expand Down Expand Up @@ -60,7 +63,7 @@ def __init__(
def _on_gtk_toggle(self, *_, **__):
logger.debug('Selecting preset "%s"', self.preset_name)
self._controller.load_preset(self.preset_name)
self.message_broker.send(DoStackSwitch(Stack.editor_page))
self.message_broker.publish(DoStackSwitch(Stack.editor_page))


class PresetSelection(FlowBoxWrapper):
Expand Down
Loading

0 comments on commit b5345ad

Please sign in to comment.