Refactored python code into library.

This commit is contained in:
Richard Neumann 2020-03-26 11:47:41 +01:00
parent aae65e3825
commit 309bafd5d1
7 changed files with 247 additions and 203 deletions

View file

@ -1,132 +1,7 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
"""Sets the system mode.""" """Sets the system mode."""
from argparse import ArgumentParser from tabletmode.cli import main
from subprocess import DEVNULL, CalledProcessError, check_call, run
from sys import stderr
DESCRIPTION = 'Sets or toggles the system mode.'
LAPTOP_MODE_SERVICE = 'laptop-mode.service'
TABLET_MODE_SERVICE = 'tablet-mode.service'
def get_arguments():
"""Returns the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-n', '--notify', action='store_true',
help='display an on-screen notification')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('toggle', help='toggles the system mode')
subparsers.add_parser('laptop', help='switch to laptop mode')
subparsers.add_parser('tablet', help='switch to tablet mode')
subparsers.add_parser('default', help='do not disable any input devices')
return parser.parse_args()
def systemctl(action, unit, *, root=False):
"""Runs systemctl."""
command = ['/usr/bin/sudo'] if root else []
command.append('systemctl')
command.append(action)
command.append(unit)
try:
check_call(command, stdout=DEVNULL) # Return 0 on success.
except CalledProcessError:
return False
return True
def notify_send(summary, body=None):
"""Sends the respective message."""
command = ['/usr/bin/notify-send', summary]
if body is not None:
command.append(body)
return run(command, stdout=DEVNULL)
def notify_laptop_mode():
"""Notifies about laptop mode."""
return notify_send('Laptop mode.', 'The system is now in laptop mode.')
def notify_tablet_mode():
"""Notifies about tablet mode."""
return notify_send('Tablet mode.', 'The system is now in tablet mode.')
def toggle_mode(notify):
"""Toggles between laptop and tablet mode."""
if systemctl('status', TABLET_MODE_SERVICE):
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
else:
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def default_mode(notify):
"""Restores all blocked input devices."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('stop', TABLET_MODE_SERVICE, root=True)
if notify:
notify_send('Default mode.', 'The system is now in default mode.')
def laptop_mode(notify):
"""Starts the laptop mode."""
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def tablet_mode(notify):
"""Starts the tablet mode."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
def main():
"""Runs the main program."""
arguments = get_arguments()
if arguments.mode == 'toggle':
toggle_mode(arguments.notify)
elif arguments.mode == 'default':
default_mode(arguments.notify)
elif arguments.mode == 'laptop':
laptop_mode(arguments.notify)
elif arguments.mode == 'tablet':
tablet_mode(arguments.notify)
else:
print('Must specify a mode.', file=stderr, flush=True)
if __name__ == '__main__': if __name__ == '__main__':

20
setup.py Executable file
View file

@ -0,0 +1,20 @@
#! /usr/bin/env python
from setuptools import setup
setup(
name='tabletmode',
version_format='{tag}',
setup_requires=['setuptools-git-version'],
author='Richard Neumann',
author_email='mail@richard-neumann.de',
python_requires='>=3.8',
packages=['tabletmode'],
scripts=['setsysmode', 'sysmoded'],
url='https://github.com/conqp/tablet-mode',
license='GPLv3',
description='Tablet mode switch for GNOME 3.',
long_description=open('README.md').read(),
long_description_content_type="text/markdown",
keywords='tablet mode tent convertible switch'
)

View file

@ -1,83 +1,7 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
"""System mode daemon.""" """System mode daemon."""
from argparse import ArgumentParser from tabletmode.daemon import main
from json import load
from logging import DEBUG, INFO, basicConfig, getLogger
from pathlib import Path
from subprocess import Popen
CONFIG_FILE = Path('/etc/tablet-mode.json')
DESCRIPTION = 'Setup system for laptop or tablet mode.'
EVTEST = '/usr/bin/evtest'
LOG_FORMAT = '[%(levelname)s] %(name)s: %(message)s'
LOGGER = getLogger(__file__)
def get_arguments():
"""Parses the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-v', '--verbose', action='store_true',
help='turn on verbose logging')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('laptop', help='enable laptop mode')
subparsers.add_parser('tablet', help='enable tablet mode')
return parser.parse_args()
def load_configuration():
"""Returns the configuration."""
try:
with CONFIG_FILE.open('r') as cfg:
return load(cfg)
except FileNotFoundError:
LOGGER.warning('Config file %s does not exist.', CONFIG_FILE)
return {}
def disable_device(device):
"""Disables the respective device via evtest."""
return Popen((EVTEST, '--grab', device))
def disable_devices(devices):
"""Disables the given devices."""
subprocesses = []
for device in devices:
subprocess = disable_device(device)
subprocesses.append(subprocess)
for subprocess in subprocesses:
subprocess.wait()
def get_devices(mode):
"""Reads the device from the config file."""
configuration = load_configuration()
devices = configuration.get(mode) or ()
if not devices:
LOGGER.info('No devices configured to disable.')
return devices
def main():
"""Runs the main program."""
arguments = get_arguments()
level = DEBUG if arguments.verbose else INFO
basicConfig(level=level, format=LOG_FORMAT)
devices = get_devices(arguments.mode)
disable_devices(devices)
if __name__ == '__main__': if __name__ == '__main__':

1
tabletmode/__init__.py Normal file
View file

@ -0,0 +1 @@
"""Tablet mode library."""

133
tabletmode/cli.py Normal file
View file

@ -0,0 +1,133 @@
"""Sets the system mode."""
from argparse import ArgumentParser
from subprocess import DEVNULL, CalledProcessError, check_call, run
from sys import stderr
from tabletmode.config import load_configuration
DESCRIPTION = 'Sets or toggles the system mode.'
LAPTOP_MODE_SERVICE = 'laptop-mode.service'
TABLET_MODE_SERVICE = 'tablet-mode.service'
def get_arguments():
"""Returns the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-n', '--notify', action='store_true',
help='display an on-screen notification')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('toggle', help='toggles the system mode')
subparsers.add_parser('laptop', help='switch to laptop mode')
subparsers.add_parser('tablet', help='switch to tablet mode')
subparsers.add_parser('default', help='do not disable any input devices')
return parser.parse_args()
def systemctl(action, unit, *, root=False):
"""Runs systemctl."""
command = ['/usr/bin/sudo'] if root else []
command.append('systemctl')
command.append(action)
command.append(unit)
try:
check_call(command, stdout=DEVNULL) # Return 0 on success.
except CalledProcessError:
return False
return True
def notify_send(summary, body=None):
"""Sends the respective message."""
command = ['/usr/bin/notify-send', summary]
if body is not None:
command.append(body)
return run(command, stdout=DEVNULL, check=False)
def notify_laptop_mode():
"""Notifies about laptop mode."""
return notify_send('Laptop mode.', 'The system is now in laptop mode.')
def notify_tablet_mode():
"""Notifies about tablet mode."""
return notify_send('Tablet mode.', 'The system is now in tablet mode.')
def toggle_mode(notify=False):
"""Toggles between laptop and tablet mode."""
if systemctl('status', TABLET_MODE_SERVICE):
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
else:
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def default_mode(notify=False):
"""Restores all blocked input devices."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('stop', TABLET_MODE_SERVICE, root=True)
if notify:
notify_send('Default mode.', 'The system is now in default mode.')
def laptop_mode(notify=False):
"""Starts the laptop mode."""
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def tablet_mode(notify=False):
"""Starts the tablet mode."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
def main():
"""Runs the main program."""
arguments = get_arguments()
configuration = load_configuration()
notify = configuration.get('notify', False) or arguments.notify
if arguments.mode == 'toggle':
toggle_mode(notify=notify)
elif arguments.mode == 'default':
default_mode(notify=notify)
elif arguments.mode == 'laptop':
laptop_mode(notify=notify)
elif arguments.mode == 'tablet':
tablet_mode(notify=notify)
else:
print('Must specify a mode.', file=stderr, flush=True)

23
tabletmode/config.py Normal file
View file

@ -0,0 +1,23 @@
"""Configuration file parsing."""
from json import load
from logging import getLogger
from pathlib import Path
__all__ = ['load_configuration']
CONFIG_FILE = Path('/etc/tablet-mode.json')
LOGGER = getLogger('tabletmode')
def load_configuration():
"""Returns the configuration."""
try:
with CONFIG_FILE.open('r') as cfg:
return load(cfg)
except FileNotFoundError:
LOGGER.warning('Config file %s does not exist.', CONFIG_FILE)
return {}

68
tabletmode/daemon.py Normal file
View file

@ -0,0 +1,68 @@
"""System mode daemon."""
from argparse import ArgumentParser
from logging import DEBUG, INFO, basicConfig, getLogger
from subprocess import Popen
from tabletmode.config import load_configuration
DESCRIPTION = 'Setup system for laptop or tablet mode.'
EVTEST = '/usr/bin/evtest'
LOG_FORMAT = '[%(levelname)s] %(name)s: %(message)s'
LOGGER = getLogger('sysmoded')
def get_arguments():
"""Parses the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-v', '--verbose', action='store_true',
help='turn on verbose logging')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('laptop', help='enable laptop mode')
subparsers.add_parser('tablet', help='enable tablet mode')
return parser.parse_args()
def disable_device(device):
"""Disables the respective device via evtest."""
return Popen((EVTEST, '--grab', device))
def disable_devices(devices):
"""Disables the given devices."""
subprocesses = []
for device in devices:
subprocess = disable_device(device)
subprocesses.append(subprocess)
for subprocess in subprocesses:
subprocess.wait()
def get_devices(mode):
"""Reads the device from the config file."""
configuration = load_configuration()
devices = configuration.get(mode) or ()
if not devices:
LOGGER.info('No devices configured to disable.')
return devices
def main():
"""Runs the main program."""
arguments = get_arguments()
level = DEBUG if arguments.verbose else INFO
basicConfig(level=level, format=LOG_FORMAT)
devices = get_devices(arguments.mode)
disable_devices(devices)