Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[input]Provider matching for input postproc calibration #5870

Merged
merged 4 commits into from Oct 5, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 60 additions & 3 deletions kivy/input/postproc/calibration.py
Expand Up @@ -26,22 +26,45 @@
Now, the touches from the left screen will be within 0-0.3333 range, and the
touches from the middle screen will be within 0.3333-0.6666 range.

You can also match calibration rules to devices based on their provider type.
This is useful when probesysfs is used to match devices. For example::

[input]
mtdev_%(name)s = probesysfs,provider=mtdev

Then to apply calibration to any mtdev device, you can assign rules to the
provider name enclosed by parentheses::

[postproc:calibration]
(mtdev) = xratio=0.3333,xoffset=0.3333

Calibrating devices like this means the device's path doesn't need to be
configured ahead of time. Note that with this method, all mtdev inputs will
have the same calibration applied to them. For this reason, matching by
provider will typically be useful when expecting only one input device.
'''

__all__ = ('InputPostprocCalibration', )

from kivy.config import Config
from kivy.logger import Logger
from kivy.input import providers
from kivy.input.factory import MotionEventFactory
from kivy.input.motionevent import MotionEvent


class InputPostprocCalibration(object):
'''Recalibrate the inputs.

The configuration must go within a section named `postproc:calibration`.
Within the section, you must have line like::
Within the section, you must have a line like::

devicename = param=value,param=value

If you wish to match by provider, you must have a line like::

(provider) = param=value,param=value

:Parameters:
`xratio`: float
Value to multiply X
Expand All @@ -58,6 +81,7 @@ def __init__(self):
super(InputPostprocCalibration, self).__init__()
self.devices = {}
self.frame = 0
self.provider_map = self._get_provider_map()
if not Config.has_section('postproc:calibration'):
return
default_params = {'xoffset': 0, 'yoffset': 0, 'xratio': 1, 'yratio': 1}
Expand All @@ -74,6 +98,33 @@ def __init__(self):
params[key] = float(value)
self.devices[device_key] = params

def _get_provider_map(self):
"""Iterates through all registered input provider names and finds the
respective MotionEvent subclass for each. Returns a dict of MotionEvent
subclasses mapped to their provider name.
"""
provider_map = {}
for input_provider in MotionEventFactory.list():
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called once in __init__, and I was unsure if MotionEventFactory.list() might ever change its output after this. Can anyone confirm?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Touch providers are supposed/assumed to be registered before all of this already, while it would be cool to be allow to enable/disable them at runtime, this is currently not really possible anyway, so no worries here.

if not hasattr(providers, input_provider):
continue

p = getattr(providers, input_provider)
for m in p.__all__:
event = getattr(p, m)
if issubclass(event, MotionEvent):
provider_map[event] = input_provider

return provider_map
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what provider_map looks like:

{
<class 'kivy.input.providers.tuio.Tuio2dCurMotionEvent'>: 'tuio',
<class 'kivy.input.providers.tuio.Tuio2dObjMotionEvent'>: 'tuio',
<class 'kivy.input.providers.mtdev.MTDMotionEvent'>: 'mtdev',
<class 'kivy.input.providers.hidinput.HIDMotionEvent'>: 'hidinput',
<class 'kivy.input.providers.linuxwacom.LinuxWacomMotionEvent'>: 'linuxwacom',
<class 'kivy.input.providers.leapfinger.LeapFingerEvent'>: 'leapfinger'
}


def _get_provider_key(self, event):
"""Returns the provider key for the event if the provider is configured
for calibration.
"""
input_type = self.provider_map.get(event.__class__)
key = '({})'.format(input_type)
if input_type and key in self.devices:
return key

def process(self, events):
# avoid doing any processing if there is no device to calibrate at all.
if not self.devices:
Expand All @@ -86,14 +137,20 @@ def process(self, events):
# end events having been already processed
if etype == 'end':
continue
if event.device not in self.devices:

if event.device in self.devices:
dev = event.device
else:
dev = self._get_provider_key(event)
if not dev:
continue

# some providers use the same event to update and end
if 'calibration:frame' not in event.ud:
event.ud['calibration:frame'] = frame
elif event.ud['calibration:frame'] == frame:
continue
params = self.devices[event.device]
params = self.devices[dev]
event.sx = event.sx * params['xratio'] + params['xoffset']
event.sy = event.sy * params['yratio'] + params['yoffset']
event.ud['calibration:frame'] = frame
Expand Down