diff --git a/keymapper/injection/context.py b/keymapper/injection/context.py index 952c6894..b89b7d22 100644 --- a/keymapper/injection/context.py +++ b/keymapper/injection/context.py @@ -29,8 +29,7 @@ from keymapper.config import NONE, MOUSE, WHEEL class Context: - """ - Stores injection-process wide information. + """Stores injection-process wide information. In some ways this is a wrapper for the mapping that derives some information that is specifically important to the injection. diff --git a/keymapper/injection/injector.py b/keymapper/injection/injector.py index b14381f2..e844f011 100644 --- a/keymapper/injection/injector.py +++ b/keymapper/injection/injector.py @@ -22,10 +22,8 @@ """Keeps injecting keycodes in the background based on the mapping.""" -import re import asyncio import time -import subprocess import multiprocessing import evdev @@ -33,11 +31,13 @@ from evdev.ecodes import EV_KEY, EV_REL from keymapper.logger import logger from keymapper.getdevices import get_devices, is_gamepad -from keymapper.injection.keycode_mapper import KeycodeMapper from keymapper import utils -from keymapper.injection.event_producer import EventProducer from keymapper.mapping import DISABLE_CODE +from keymapper.injection.keycode_mapper import KeycodeMapper from keymapper.injection.context import Context +from keymapper.injection.event_producer import EventProducer +from keymapper.injection.numlock import set_numlock, is_numlock_on, \ + ensure_numlock DEV_NAME = 'key-mapper' @@ -57,62 +57,6 @@ STOPPED = 5 NO_GRAB = 6 -def is_numlock_on(): - """Get the current state of the numlock.""" - try: - xset_q = subprocess.check_output( - ['xset', 'q'], - stderr=subprocess.STDOUT - ).decode() - num_lock_status = re.search( - r'Num Lock:\s+(.+?)\s', - xset_q - ) - - if num_lock_status is not None: - return num_lock_status[1] == 'on' - - return False - except (FileNotFoundError, subprocess.CalledProcessError): - # tty - return None - - -def set_numlock(state): - """Set the numlock to a given state of True or False.""" - if state is None: - return - - value = { - True: 'on', - False: 'off' - }[state] - - try: - subprocess.check_output(['numlockx', value]) - except subprocess.CalledProcessError: - # might be in a tty - pass - except FileNotFoundError: - # doesn't seem to be installed everywhere - logger.debug('numlockx not found') - - -def ensure_numlock(func): - """Decorator to reset the numlock to its initial state afterwards.""" - def wrapped(*args, **kwargs): - # for some reason, grabbing a device can modify the num lock state. - # remember it and apply back later - numlock_before = is_numlock_on() - - result = func(*args, **kwargs) - - set_numlock(numlock_before) - - return result - return wrapped - - def is_in_capabilities(key, capabilities): """Are this key or one of its sub keys in the capabilities? diff --git a/keymapper/injection/keycode_mapper.py b/keymapper/injection/keycode_mapper.py index 58120b8a..c449d3bd 100644 --- a/keymapper/injection/keycode_mapper.py +++ b/keymapper/injection/keycode_mapper.py @@ -31,6 +31,7 @@ from keymapper.logger import logger from keymapper.mapping import DISABLE_CODE from keymapper import utils + # this state is shared by all KeycodeMappers of this process # maps mouse buttons to macro instances that have been executed. @@ -42,6 +43,7 @@ from keymapper import utils # Only sequentially. active_macros = {} + # mapping of future release event (type, code) to an Unreleased object, # All key-up events have a value of 0, so it is not added to # the tuple. This is needed in order to release the correct event diff --git a/keymapper/injection/numlock.py b/keymapper/injection/numlock.py new file mode 100644 index 00000000..54b4279b --- /dev/null +++ b/keymapper/injection/numlock.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# key-mapper - GUI for device specific keyboard mappings +# Copyright (C) 2021 sezanzeb +# +# This file is part of key-mapper. +# +# key-mapper 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. +# +# key-mapper 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 key-mapper. If not, see . + + +"""Functions to handle numlocks. + +For unknown reasons the numlock status can change when starting injections, +which is why these functions exist. +""" + + +import re +import subprocess + +from keymapper.logger import logger + + +def is_numlock_on(): + """Get the current state of the numlock.""" + try: + xset_q = subprocess.check_output( + ['xset', 'q'], + stderr=subprocess.STDOUT + ).decode() + num_lock_status = re.search( + r'Num Lock:\s+(.+?)\s', + xset_q + ) + + if num_lock_status is not None: + return num_lock_status[1] == 'on' + + return False + except (FileNotFoundError, subprocess.CalledProcessError): + # tty + return None + + +def set_numlock(state): + """Set the numlock to a given state of True or False.""" + if state is None: + return + + value = { + True: 'on', + False: 'off' + }[state] + + try: + subprocess.check_output(['numlockx', value]) + except subprocess.CalledProcessError: + # might be in a tty + pass + except FileNotFoundError: + # doesn't seem to be installed everywhere + logger.debug('numlockx not found') + + +def ensure_numlock(func): + """Decorator to reset the numlock to its initial state afterwards.""" + def wrapped(*args, **kwargs): + # for some reason, grabbing a device can modify the num lock state. + # remember it and apply back later + numlock_before = is_numlock_on() + + result = func(*args, **kwargs) + + set_numlock(numlock_before) + + return result + return wrapped diff --git a/keymapper/injection/readme.md b/keymapper/injection/readme.md index 5de7b226..92710a2c 100644 --- a/keymapper/injection/readme.md +++ b/keymapper/injection/readme.md @@ -1,6 +1,6 @@ # Injection This folder contains all classes that are only relevant for the injection -process. There is one process for each device that is being injected for, -and one context object per process that is being passed around for all +process. There is one process for each hardware device that is being injected +for, and one context object per process that is being passed around for all classes to use. diff --git a/readme/pylint.svg b/readme/pylint.svg index cc38f2d4..bde3a939 100644 --- a/readme/pylint.svg +++ b/readme/pylint.svg @@ -17,7 +17,7 @@ pylint - 9.75 - 9.75 + 9.76 + 9.76 \ No newline at end of file diff --git a/tests/testcases/test_injector.py b/tests/testcases/test_injector.py index a28adf1e..9802e84d 100644 --- a/tests/testcases/test_injector.py +++ b/tests/testcases/test_injector.py @@ -28,9 +28,10 @@ from evdev.ecodes import EV_REL, EV_KEY, EV_ABS, ABS_HAT0X, BTN_LEFT, KEY_A, \ REL_X, REL_Y, REL_WHEEL, REL_HWHEEL, BTN_A, ABS_X, ABS_Y, \ ABS_Z, ABS_RZ, ABS_VOLUME -from keymapper.injection.injector import is_numlock_on, set_numlock, \ - ensure_numlock, Injector, is_in_capabilities, \ +from keymapper.injection.injector import Injector, is_in_capabilities, \ STARTING, RUNNING, STOPPED, NO_GRAB, UNKNOWN +from keymapper.injection.numlock import is_numlock_on, set_numlock, \ + ensure_numlock from keymapper.state import custom_mapping, system_mapping from keymapper.mapping import Mapping, DISABLE_CODE, DISABLE_NAME from keymapper.config import config, NONE, MOUSE, WHEEL, BUTTONS