Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

159 lignes
5.7 KiB

#!/usr/bin/env python3
from collections import defaultdict
import os
import subprocess
import click
import yaml
class FynnWacom:
def __init__(self, config, verbose=False):
self.config = config
self.verbose = verbose
self.validate_config()
def validate_config(self):
try:
self.usbid = self.config['device']['usbid'].lower()
self.device_name = self.config['device']['name']
self.profile_file = os.path.expanduser(self.config['device']['profile_file'])
# check default profile
default_profile = self.config['device']['default_profile']
if default_profile not in self.config['mappings']:
raise click.ClickException("Profile {} missing from mappigns".format(default_profile))
# check mappings
for name, mapping in self.config['mappings'].items():
# does the baseprofile exist?
if 'base_profile' in mapping and mapping['base_profile'] not in self.config['mappings']:
raise click.ClickException("Base profile {} not found for profile {}"
"".format(mapping['base_profile'], name))
for key in mapping:
if key in ('base_profile',):
continue
pass
# check recursion
pass
except KeyError as e:
raise click.ClickException("Key missing in config: {}".format(e))
def get_current_profile(self):
try:
with open(self.profile_file) as pf:
return pf.read().strip()
except IOError:
return self.config['device']['default_profile']
@property
def wheel_state(self):
"""Read the wacom tablet wheel status from /sys, int in [0, 3]"""
basepath = '/sys/bus/hid/drivers/wacom/'
wacom_dirs = [d for d in os.listdir(basepath) if self.usbid in d.lower()]
if not wacom_dirs:
raise click.ClickException("Could not find usbid {} in {}".format(self.usbid, basepath))
ledpath = os.path.join(basepath, sorted(wacom_dirs)[0], 'wacom_led/status_led0_select')
with open(ledpath) as ledfile:
return int(ledfile.read())
@staticmethod
def _call(cmd):
# print(" >> Calling: ", " ".join(cmd))
return subprocess.check_output(cmd, encoding='utf-8')
def guess_devices(self, subname):
found = False
for device in self._call(["xinput", "list", "--name-only"]).split("\n"):
if not device.startswith(self.device_name):
continue
rest = device.replace(self.device_name, "")
if subname.lower() in rest.lower():
yield device
found = True
if not found:
raise click.ClickException("Subdevice {} for {} not found".format(subname, self.device_name))
def build_mapping(self, profile, wheel_state):
conf = self.config['mappings'][profile]
mapping = defaultdict(dict)
if 'base_profile' in conf:
mapping = self.build_mapping(conf['base_profile'], wheel_state)
# copy mappings into mapping dict
for key, keymap in conf.items():
if key in ('base_profile', 'wheel'):
continue
mapping[key].update(keymap)
# handle wheel, if present and in wheel_state range
if 'wheel' in conf and len(conf['wheel']) > wheel_state and conf['wheel'][wheel_state]:
for device, key_mapping in conf['wheel'][wheel_state].items():
mapping[device].update(key_mapping)
return mapping
def configure(self, profile):
if profile not in self.config['mappings']:
raise click.ClickException("Profile {} does not exist".format(profile))
mapping = self.build_mapping(profile, self.wheel_state)
if self.verbose:
print("Current mapping for profile {}: {}".format(profile, mapping))
for subdevice, keymap in mapping.items():
for button, keysym in keymap.items():
if len(keysym) == 1:
keysym = "key {}".format(keysym)
try:
btn = ["Button", str(int(button))]
except ValueError:
btn = [button]
for device in self.guess_devices(subdevice):
cmd = ["xsetwacom", "set", device] + btn + [keysym]
if self.verbose:
print(" >> xsetwacom set '{}' '{}' '{}'".format(device, btn, keysym))
self._call(cmd)
with open(self.profile_file, 'w') as f:
f.write(profile + "\n")
@click.command()
@click.option('-c', '--config', default='~/.config/fynncom/fynncom.yaml',
help='Path to config file')
@click.option('-p', '--profile', default=None,
help='Profile to switch to')
@click.option('-v', '--verbose', is_flag=True, default=False,
help='Be more verbose')
def cli(config, profile, verbose):
# check for xinput / xsetwacom
for cmd in ('xinput', 'xsetwacom'):
if subprocess.call(["which", cmd], stdout=subprocess.DEVNULL) != 0:
raise click.ClickException("Could not find `{}`, please install it".format(cmd))
# load config
try:
with open(os.path.expanduser(config)) as f:
config = yaml.safe_load(f)
except IOError as e:
raise click.ClickException("Could not load config: {}".format(e))
fynncom = FynnWacom(config, verbose)
profile = profile or fynncom.get_current_profile()
fynncom.configure(profile)
print("Switched to profile {} with weel-state {}".format(profile, fynncom.wheel_state))
if __name__ == '__main__':
cli()