219 lines
7.1 KiB
Python
219 lines
7.1 KiB
Python
################################################################################
|
|
# #
|
|
# This file is part of the Buildbotics firmware. #
|
|
# #
|
|
# Copyright (c) 2015 - 2018, Buildbotics LLC #
|
|
# All rights reserved. #
|
|
# #
|
|
# This file ("the software") is free software: you can redistribute it #
|
|
# and/or modify it under the terms of the GNU General Public License, #
|
|
# version 2 as published by the Free Software Foundation. You should #
|
|
# have received a copy of the GNU General Public License, version 2 #
|
|
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
|
|
# #
|
|
# The software 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 #
|
|
# Lesser General Public License for more details. #
|
|
# #
|
|
# You should have received a copy of the GNU Lesser General Public #
|
|
# License along with the software. If not, see #
|
|
# <http://www.gnu.org/licenses/>. #
|
|
# #
|
|
# For information regarding this software email: #
|
|
# "Joseph Coffland" <joseph@buildbotics.com> #
|
|
# #
|
|
################################################################################
|
|
|
|
# The inevent Python module was adapted from pi3d.event from the pi3d
|
|
# project.
|
|
#
|
|
# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC.
|
|
# Copyright (c) 2015, Tim Skillman.
|
|
# Copyright (c) 2015, Paddy Gaunt.
|
|
# Copyright (c) 2015, Tom Ritchford.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person
|
|
# obtaining a copy of this software and associated documentation files
|
|
# (the "Software"), to deal in the Software without restriction,
|
|
# including without limitation the rights to use, copy, modify, merge,
|
|
# publish, distribute, sublicense, and/or sell copies of the Software,
|
|
# and to permit persons to whom the Software is furnished to do so,
|
|
# subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
|
|
import fcntl
|
|
import os
|
|
import select
|
|
import logging
|
|
|
|
from inevent.Constants import *
|
|
from inevent import ioctl
|
|
from inevent.AbsAxisScaling import AbsAxisScaling
|
|
from inevent import Event
|
|
from inevent.EventState import EventState
|
|
|
|
|
|
log = logging.getLogger('inevent')
|
|
|
|
EVIOCGRAB = ioctl._IOW(ord('E'), 0x90, "i") # Grab/Release device
|
|
|
|
|
|
|
|
class EventStream(object):
|
|
"""
|
|
encapsulates the event* file handling
|
|
|
|
Each device is represented by a file in /dev/input called eventN, where N is
|
|
a small number. (Actually, a keybaord can be represented by two such files.)
|
|
Instances of this class open one of these files and provide means to read
|
|
events from them.
|
|
|
|
Class methods also exist to read from multiple files simultaneously, and
|
|
also to grab and ungrab all instances of a given type.
|
|
"""
|
|
axisX = 0
|
|
axisY = 1
|
|
axisZ = 2
|
|
axisRX = 3
|
|
axisRY = 4
|
|
axisRZ = 5
|
|
axisHat0X = 6
|
|
axisHat0Y = 7
|
|
axisHat1X = 8
|
|
axisHat1Y = 9
|
|
axisHat2X = 10
|
|
axisHat2Y = 11
|
|
axisHat3X = 12
|
|
axisHat3Y = 13
|
|
axisThrottle = 14
|
|
axisRudder = 15
|
|
axisWheel = 16
|
|
axisGas = 17
|
|
axisBrake = 18
|
|
axisPressure = 19
|
|
axisDistance = 20
|
|
axisTiltX = 21
|
|
axisTiltY = 22
|
|
axisToolWidth = 23
|
|
numAxes = 24
|
|
|
|
axisToEvent = [
|
|
ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ, ABS_HAT0X, ABS_HAT0Y,
|
|
ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y, ABS_HAT3X, ABS_HAT3Y,
|
|
ABS_THROTTLE, ABS_RUDDER, ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_PRESSURE,
|
|
ABS_DISTANCE, ABS_TILT_X, ABS_TILT_Y, ABS_TOOL_WIDTH]
|
|
|
|
|
|
def __init__(self, devIndex, devType, devName):
|
|
"""
|
|
Opens the given /dev/input/event file and grabs it.
|
|
|
|
Also adds it to a class-global list of all existing streams.
|
|
"""
|
|
self.devIndex = devIndex
|
|
self.devType = devType
|
|
self.devName = devName
|
|
self.filename = "/dev/input/event" + str(devIndex)
|
|
self.filehandle = os.open(self.filename, os.O_RDWR)
|
|
self.state = EventState()
|
|
self.grab(True)
|
|
self.absInfo = [None] * ABS_MAX
|
|
|
|
if devType == "js":
|
|
for axis in range(ABS_MAX):
|
|
self.absInfo[axis] = AbsAxisScaling(self, axis)
|
|
|
|
|
|
def scale(self, axis, value):
|
|
"""
|
|
Scale the given value according to the given axis.
|
|
|
|
acquire_abs_info must have been previously called to acquire the data to
|
|
do the scaling.
|
|
"""
|
|
assert axis < ABS_MAX, "Axis number out of range"
|
|
|
|
if self.absInfo[axis]: return self.absInfo[axis].scale(value)
|
|
else: return value
|
|
|
|
|
|
def grab(self, grab = True):
|
|
"""
|
|
Grab (or release) exclusive access to all devices of the given type.
|
|
|
|
The devices are grabbed if grab is True and released if grab is False.
|
|
|
|
All devices are grabbed to begin with. We might want to ungrab the
|
|
keyboard for example to use it for text entry. While not grabbed, all
|
|
key-down and key-hold events are filtered out.
|
|
"""
|
|
fcntl.ioctl(self.filehandle, EVIOCGRAB, 1 if grab else 0)
|
|
self.grabbed = grab
|
|
|
|
|
|
def __iter__(self):
|
|
"""s
|
|
Required to make this class an iterator
|
|
"""
|
|
return self
|
|
|
|
|
|
def next(self): return self.__next__()
|
|
|
|
|
|
def __next__(self):
|
|
"""
|
|
Returns the next waiting event.
|
|
|
|
If no event is waiting, returns None.
|
|
"""
|
|
ready = select.select([self.filehandle], [], [], 0)[0]
|
|
if ready: return self.read()
|
|
|
|
|
|
def read(self):
|
|
"""
|
|
Read and return the next waiting event.
|
|
"""
|
|
try:
|
|
s = os.read(self.filehandle, Event.size)
|
|
if s:
|
|
event = Event.Event(self)
|
|
event.decode(s)
|
|
return event
|
|
|
|
except Exception as e:
|
|
log.info('Reading event: %s' % e)
|
|
|
|
|
|
def __enter__(self): return self
|
|
|
|
|
|
def release(self):
|
|
"Ungrabs the file and closes it."
|
|
|
|
try:
|
|
self.grab(False)
|
|
os.close(self.filehandle)
|
|
|
|
except:
|
|
pass
|
|
|
|
|
|
def __exit__(self, type, value, traceback):
|
|
"Ungrabs the file and closes it."
|
|
|
|
self.release()
|