255 lines
8.9 KiB
Python
255 lines
8.9 KiB
Python
'''
|
|
Auto Create Input Provider Config Entry for Available MT Hardware (linux only).
|
|
===============================================================================
|
|
|
|
Thanks to Marc Tardif for the probing code, taken from scan-for-mt-device.
|
|
|
|
The device discovery is done by this provider. However, the reading of
|
|
input can be performed by other providers like: hidinput, mtdev and
|
|
linuxwacom. mtdev is used prior to other providers. For more
|
|
information about mtdev, check :py:class:`~kivy.input.providers.mtdev`.
|
|
|
|
Here is an example of auto creation::
|
|
|
|
[input]
|
|
# using mtdev
|
|
device_%(name)s = probesysfs,provider=mtdev
|
|
# using hidinput
|
|
device_%(name)s = probesysfs,provider=hidinput
|
|
# using mtdev with a match on name
|
|
device_%(name)s = probesysfs,provider=mtdev,match=acer
|
|
|
|
# using hidinput with custom parameters to hidinput (all on one line)
|
|
%(name)s = probesysfs,
|
|
provider=hidinput,param=min_pressure=1,param=max_pressure=99
|
|
|
|
# you can also match your wacom touchscreen
|
|
touch = probesysfs,match=E3 Finger,provider=linuxwacom,
|
|
select_all=1,param=mode=touch
|
|
# and your wacom pen
|
|
pen = probesysfs,match=E3 Pen,provider=linuxwacom,
|
|
select_all=1,param=mode=pen
|
|
|
|
By default, ProbeSysfs module will enumerate hardware from the /sys/class/input
|
|
device, and configure hardware with ABS_MT_POSITION_X capability. But for
|
|
example, the wacom screen doesn't support this capability. You can prevent this
|
|
behavior by putting select_all=1 in your config line. Add use_mouse=1 to also
|
|
include touchscreen hardware that offers core pointer functionality.
|
|
'''
|
|
|
|
__all__ = ('ProbeSysfsHardwareProbe', )
|
|
|
|
import os
|
|
from os.path import sep
|
|
|
|
if 'KIVY_DOC' in os.environ:
|
|
|
|
ProbeSysfsHardwareProbe = None
|
|
|
|
else:
|
|
import ctypes
|
|
from re import match, IGNORECASE
|
|
from glob import glob
|
|
from subprocess import Popen, PIPE
|
|
from kivy.logger import Logger
|
|
from kivy.input.provider import MotionEventProvider
|
|
from kivy.input.providers.mouse import MouseMotionEventProvider
|
|
from kivy.input.factory import MotionEventFactory
|
|
from kivy.config import _is_rpi
|
|
|
|
EventLoop = None
|
|
|
|
# See linux/input.h
|
|
ABS_MT_POSITION_X = 0x35
|
|
|
|
_cache_input = None
|
|
_cache_xinput = None
|
|
|
|
class Input(object):
|
|
|
|
def __init__(self, path):
|
|
query_xinput()
|
|
self.path = path
|
|
|
|
@property
|
|
def device(self):
|
|
base = os.path.basename(self.path)
|
|
return os.path.join("/dev", "input", base)
|
|
|
|
@property
|
|
def name(self):
|
|
path = os.path.join(self.path, "device", "name")
|
|
return read_line(path)
|
|
|
|
def get_capabilities(self):
|
|
path = os.path.join(self.path, "device", "capabilities", "abs")
|
|
line = "0"
|
|
try:
|
|
line = read_line(path)
|
|
except (IOError, OSError):
|
|
return []
|
|
|
|
capabilities = []
|
|
long_bit = ctypes.sizeof(ctypes.c_long) * 8
|
|
for i, word in enumerate(line.split(" ")):
|
|
word = int(word, 16)
|
|
subcapabilities = [bool(word & 1 << i)
|
|
for i in range(long_bit)]
|
|
capabilities[:0] = subcapabilities
|
|
|
|
return capabilities
|
|
|
|
def has_capability(self, capability):
|
|
capabilities = self.get_capabilities()
|
|
return len(capabilities) > capability and capabilities[capability]
|
|
|
|
@property
|
|
def is_mouse(self):
|
|
return self.device in _cache_xinput
|
|
|
|
def getout(*args):
|
|
try:
|
|
return Popen(args, stdout=PIPE).communicate()[0]
|
|
except OSError:
|
|
return ''
|
|
|
|
def query_xinput():
|
|
global _cache_xinput
|
|
if _cache_xinput is None:
|
|
_cache_xinput = []
|
|
devids = getout('xinput', '--list', '--id-only')
|
|
for did in devids.splitlines():
|
|
devprops = getout('xinput', '--list-props', did)
|
|
evpath = None
|
|
for prop in devprops.splitlines():
|
|
prop = prop.strip()
|
|
if (prop.startswith(b'Device Enabled') and
|
|
prop.endswith(b'0')):
|
|
evpath = None
|
|
break
|
|
if prop.startswith(b'Device Node'):
|
|
try:
|
|
evpath = prop.split('"')[1]
|
|
except Exception:
|
|
evpath = None
|
|
if evpath:
|
|
_cache_xinput.append(evpath)
|
|
|
|
def get_inputs(path):
|
|
global _cache_input
|
|
if _cache_input is None:
|
|
event_glob = os.path.join(path, "event*")
|
|
_cache_input = [Input(x) for x in glob(event_glob)]
|
|
return _cache_input
|
|
|
|
def read_line(path):
|
|
f = open(path)
|
|
try:
|
|
return f.readline().strip()
|
|
finally:
|
|
f.close()
|
|
|
|
class ProbeSysfsHardwareProbe(MotionEventProvider):
|
|
|
|
def __new__(self, device, args):
|
|
# hack to not return an instance of this provider.
|
|
# :)
|
|
instance = super(ProbeSysfsHardwareProbe, self).__new__(self)
|
|
instance.__init__(device, args)
|
|
|
|
def __init__(self, device, args):
|
|
super(ProbeSysfsHardwareProbe, self).__init__(device, args)
|
|
self.provider = 'mtdev'
|
|
self.match = None
|
|
self.input_path = '/sys/class/input'
|
|
self.select_all = True if _is_rpi else False
|
|
self.use_mouse = False
|
|
self.use_regex = False
|
|
self.args = []
|
|
|
|
args = args.split(',')
|
|
for arg in args:
|
|
if arg == '':
|
|
continue
|
|
arg = arg.split('=', 1)
|
|
# ensure it's a key = value
|
|
if len(arg) != 2:
|
|
Logger.error('ProbeSysfs: invalid parameters %s, not'
|
|
' key=value format' % arg)
|
|
continue
|
|
|
|
key, value = arg
|
|
if key == 'match':
|
|
self.match = value
|
|
elif key == 'provider':
|
|
self.provider = value
|
|
elif key == 'use_regex':
|
|
self.use_regex = bool(int(value))
|
|
elif key == 'select_all':
|
|
self.select_all = bool(int(value))
|
|
elif key == 'use_mouse':
|
|
self.use_mouse = bool(int(value))
|
|
elif key == 'param':
|
|
self.args.append(value)
|
|
else:
|
|
Logger.error('ProbeSysfs: unknown %s option' % key)
|
|
continue
|
|
|
|
self.probe()
|
|
|
|
def should_use_mouse(self):
|
|
return (self.use_mouse or
|
|
not any(p for p in EventLoop.input_providers
|
|
if isinstance(p, MouseMotionEventProvider)))
|
|
|
|
def probe(self):
|
|
global EventLoop
|
|
from kivy.base import EventLoop
|
|
|
|
inputs = get_inputs(self.input_path)
|
|
Logger.debug('ProbeSysfs: using probesysfs!')
|
|
|
|
use_mouse = self.should_use_mouse()
|
|
|
|
if not self.select_all:
|
|
inputs = [x for x in inputs if
|
|
x.has_capability(ABS_MT_POSITION_X) and
|
|
(use_mouse or not x.is_mouse)]
|
|
for device in inputs:
|
|
Logger.debug('ProbeSysfs: found device: %s at %s' % (
|
|
device.name, device.device))
|
|
|
|
# must ignore ?
|
|
if self.match:
|
|
if self.use_regex:
|
|
if not match(self.match, device.name, IGNORECASE):
|
|
Logger.debug('ProbeSysfs: device not match the'
|
|
' rule in config, ignoring.')
|
|
continue
|
|
else:
|
|
if self.match not in device.name:
|
|
continue
|
|
|
|
Logger.info('ProbeSysfs: device match: %s' % device.device)
|
|
|
|
d = device.device
|
|
devicename = self.device % dict(name=d.split(sep)[-1])
|
|
|
|
provider = MotionEventFactory.get(self.provider)
|
|
if provider is None:
|
|
Logger.info('ProbeSysfs: Unable to find provider %s' %
|
|
self.provider)
|
|
Logger.info('ProbeSysfs: fallback on hidinput')
|
|
provider = MotionEventFactory.get('hidinput')
|
|
if provider is None:
|
|
Logger.critical('ProbeSysfs: no input provider found'
|
|
' to handle this device !')
|
|
continue
|
|
|
|
instance = provider(devicename, '%s,%s' % (
|
|
device.device, ','.join(self.args)))
|
|
if instance:
|
|
EventLoop.add_input_provider(instance)
|
|
|
|
MotionEventFactory.register('probesysfs', ProbeSysfsHardwareProbe)
|