diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 3d185e2..be2d47e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -9,7 +9,7 @@ RUN apt update \ && apt install -y \ build-essential git wget binfmt-support qemu gcc-9 \ parted gcc-avr avr-libc avrdude python3 python3-pip python3-tornado \ - curl unzip python3-setuptools gcc-arm-linux-gnueabihf bc vim sudo \ + curl unzip python3-setuptools gcc-arm-linux-gnueabihf bc vim locate sudo \ && update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9 \ && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ && apt install -y nodejs diff --git a/package-lock.json b/package-lock.json index a4779f0..e757821 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bbctrl", - "version": "1.0.10b10", + "version": "1.0.10b11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bbctrl", - "version": "1.0.10b10", + "version": "1.0.10b11", "hasInstallScript": true, "license": "GPL-3.0+", "dependencies": { diff --git a/package.json b/package.json index edc4d21..f433a40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bbctrl", - "version": "1.0.10b10", + "version": "1.0.10b11", "homepage": "https://onefinitycnc.com/", "repository": "https://github.com/OneFinityCNC/onefinity", "license": "GPL-3.0+", @@ -16,4 +16,4 @@ "lodash.merge": "4.6.2", "pug-cli": "^1.0.0-alpha6" } -} +} \ No newline at end of file diff --git a/setup.py b/setup.py index 7b3b375..5005f9b 100755 --- a/setup.py +++ b/setup.py @@ -18,7 +18,6 @@ setup( package_dir={'': 'src/py'}, packages=[ 'bbctrl', - 'inevent', 'camotics', 'iw_parse' ], diff --git a/src/py/bbctrl/Jog.py b/src/py/bbctrl/Jog.py index 39c1537..28847dc 100644 --- a/src/py/bbctrl/Jog.py +++ b/src/py/bbctrl/Jog.py @@ -1,87 +1,492 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ +from bbctrl.Ctrl import Ctrl +from bbctrl.Log import Logger +from evdev.ecodes import EV_ABS, EV_KEY +import errno +import evdev +import functools +import hashlib +import json +import os +import pyudev +import re +import traceback +import typing -import inevent -from inevent.Constants import * +userGamepadConfigs = {} + +gamepadConfigs = { + "default": { + "sign-x": 1, + "sign-y": -1, + "sign-z": -1, + "deadband": 0.15 + }, + "5C936FF2": { + "description": "Logitech 710, X mode", + "EV_KEY:308": "speed-4", + "EV_KEY:305": "speed-3", + "EV_KEY:304": "speed-2", + "EV_KEY:307": "speed-1", + "EV_ABS:0": "axis-x", + "EV_ABS:16": "axis-x", + "EV_ABS:1": "axis-y", + "EV_ABS:17": "axis-y", + "EV_ABS:4": "axis-z", + "EV_KEY:310": "lock-y", + "EV_KEY:311": "lock-x", + }, + "10E159EC": { + "description": "Logitech 710, D mode", + "EV_KEY:307": "speed-4", + "EV_KEY:306": "speed-3", + "EV_KEY:305": "speed-2", + "EV_KEY:304": "speed-1", + "EV_ABS:0": "axis-x", + "EV_ABS:16": "axis-x", + "EV_ABS:1": "axis-y", + "EV_ABS:17": "axis-y", + "EV_ABS:5": "axis-z", + "EV_KEY:308": "lock-y", + "EV_KEY:309": "lock-x", + }, + "5443E73C": { + "description": "EasySMX ESM-9013, top lights mode", + "EV_KEY:308": "speed-4", + "EV_KEY:305": "speed-3", + "EV_KEY:304": "speed-2", + "EV_KEY:307": "speed-1", + "EV_ABS:0": "axis-x", + "EV_ABS:16": "axis-x", + "EV_ABS:1": "axis-y", + "EV_ABS:17": "axis-y", + "EV_ABS:4": "axis-z", + "EV_KEY:310": "lock-y", + "EV_KEY:311": "lock-x", + }, + "951CA031": { + "description": "EasySMX ESM-9013, left lights mode", + "EV_KEY:304": "speed-4", + "EV_KEY:305": "speed-3", + "EV_KEY:306": "speed-2", + "EV_KEY:307": "speed-1", + "EV_ABS:0": "axis-x", + "EV_ABS:16": "axis-x", + "EV_ABS:1": "axis-y", + "EV_ABS:17": "axis-y", + "EV_ABS:5": "axis-z", + "EV_KEY:308": "lock-y", + "EV_KEY:309": "lock-x", + }, + "0BF75ED2": { + "description": "EasySMX ESM-9013, bottom lights mode", + "EV_KEY:308": "speed-4", + "EV_KEY:305": "speed-3", + "EV_KEY:304": "speed-2", + "EV_KEY:307": "speed-1", + "EV_ABS:0": "axis-x", + "EV_ABS:16": "axis-x", + "EV_ABS:1": "axis-y", + "EV_ABS:17": "axis-y", + "EV_ABS:5": "axis-z", + "EV_KEY:310": "lock-y", + "EV_KEY:311": "lock-x", + }, + "B3907139": { + "description": "Sony Playstation 4 Dual-Shock Controller", + "EV_KEY:307": "speed-4", + "EV_KEY:306": "speed-3", + "EV_KEY:305": "speed-2", + "EV_KEY:304": "speed-1", + "EV_ABS:0": "axis-x", + "EV_ABS:16": "axis-x", + "EV_ABS:1": "axis-y", + "EV_ABS:17": "axis-y", + "EV_ABS:5": "axis-z", + "EV_KEY:308": "lock-y", + "EV_KEY:309": "lock-x", + } +} + +udevPropertiesToLog = ["ID_MODEL", "ID_SERIAL", "ID_SERIAL_SHORT", "ID_VENDOR"] + +udevPropertiesToIgnore = [ + ".INPUT_CLASS", "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "ID_BUS", + "ID_FOR_SEAT", "ID_INPUT", "ID_INPUT_JOYSTICK", "ID_MODEL_ID", + "ID_MODEL_ENC", "ID_PATH", "ID_PATH_TAG", "ID_REVISION", "ID_TYPE", + "ID_USB_DRIVER", "ID_USB_INTERFACES", "ID_USB_INTERFACE_NUM", + "ID_VENDOR_ENC", "ID_VENDOR_ID", "LIBINPUT_DEVICE_GROUP", "MAJOR", "MINOR", + "SEQNUM", "SUBSYSTEM", "TAGS", "USEC_INITIALIZED" +] -# Listen for input events -class Jog(inevent.JogHandler): - def __init__(self, ctrl): - self.ctrl = ctrl - self.log = ctrl.log.get('Jog') +def safe_int(s, base=10, val=None): + try: + return int(s, base) + except ValueError: + return val - config = { - "Logitech Logitech RumblePad 2 USB": { - "deadband": 0.15, - "axes": [ABS_X, ABS_Y, ABS_RZ, ABS_Z], - "dir": [1, -1, -1, 1], - "arrows": [ABS_HAT0X, ABS_HAT0Y], - "speed": [0x120, 0x121, 0x122, 0x123], - "lock": [0x124, 0x125], - }, - "default": { - "deadband": 0.15, - "axes": [ABS_X, ABS_Y, ABS_RY, ABS_RX], - "dir": [1, -1, -1, 1], - "arrows": [ABS_HAT0X, ABS_HAT0Y], - "speed": [0x133, 0x130, 0x131, 0x134], - "lock": [0x136, 0x137], +def get_udev_prop(device: pyudev.Device, propertyName: str): + try: + return device.properties[propertyName] + except: + return None + + +def to_sorted_json(value): + return json.dumps(value, sort_keys=True) + + +AbsMinMax = typing.NamedTuple('AbsMinMax', [('min', float), ('max', float)]) + + +def processCapabilities(capabilities): + result = {} + + for (type, details) in capabilities.items(): + if type == EV_KEY: + result[type] = details + + if type == EV_ABS: + result[type] = { + code: AbsMinMax(float(info.min), float(info.max)) + for (code, info) in details } + + return result + + +class Gamepad(object): + _logRecord = set() + + def __init__(self, log: Logger, _evdev: evdev.InputDevice, + _udev: pyudev.Device): + self._log = log + self._evdev = _evdev + self._udev = _udev + + self._capabilities = processCapabilities(_evdev.capabilities()) + + self._details = { + "evdev": { + "name": _evdev.name, + "vendor": _evdev.info.vendor, + "product": _evdev.info.product, + "version": _evdev.info.version, + "capabilities": self._capabilities, + }, + "udev": + {key: get_udev_prop(_udev, key) + for key in udevPropertiesToLog} } - super().__init__(config) + json = to_sorted_json(self._details) + self.hash = hashlib.sha256(json.encode()).hexdigest()[-8:].upper() - self.v = [0.0] * 4 - self.lastV = self.v - self.callback() + self.config = { + **gamepadConfigs.get("default"), + **userGamepadConfigs.get("default", {}), + **gamepadConfigs.get(self.hash, {}), + **userGamepadConfigs.get(self.hash, {}) + } - self.processor = inevent.InEvent(ctrl.ioloop, self, types = ['js']) + def read(self): + return self._evdev.read() + + @property + def fd(self): + return self._evdev.fd + + @property + def devicePath(self): + return self._evdev.path + + def scaleAndClampValue(self, event: evdev.InputEvent): + if event.type != EV_ABS: + return event.value + + info = self._capabilities[EV_ABS].get(event.code) # type: AbsMinMax + if not info: + return 0 + + # Clamp the value to the device's min/max range + value = float(max(info.min, min(info.max, event.value))) + + # Remap the value from the device range to -1..1 + value = ((value - info.min) / (info.max - info.min)) * 2.0 - 1.0 + + sign = -1 if value < 0 else 1 + value = abs(value) + + deadband = self.config.get("deadband", 0.15) + if value < deadband: + return 0 + + # Remap the value to use the full range, with the "deadband" range removed + # e.g. if value == deadband, the new value will be zero + delta = value - deadband + range = 1 - deadband + return (delta * sign) / range + + def log(self, msg): + self._log.info("{}: {}".format(self.hash, msg)) + + def logOnce(self, msg): + if self.config.get("debug") or msg not in self._logRecord: + self._logRecord.add(msg) + self.log(msg) + + def logDebug(self, msg): + if self.config.get("debug"): + self.log(msg) + + def __str__(self) -> str: + return to_sorted_json({ + "devicePath": self.devicePath, + "bustype": self._evdev.info.bustype, + "details": self._details, + "hash": self.hash + }) - def callback(self): - if self.v != self.lastV: - self.lastV = self.v +class Command(object): + + def __init__(self, id: str, value: int, gamepad: Gamepad): + self.id = id + self.value = value + self.gamepad = gamepad + + def __str__(self): + return "Command(id='{}', value={}, gamepad='{}')".format( + self.id, self.value, self.gamepad.hash) + + +class Jog(object): + gamepads = {} # type: dict[typing.Union[int, str], Gamepad] + lock = {"x": False, "y": False} + axes = {"x": 0, "y": 0, "z": 0} + speed = 3 # a resonable default speed, not too fast, not too slow + changed = False + + def __init__(self, ctrl: Ctrl): + self.ctrl = ctrl + self.ioloop = ctrl.ioloop + self.log = ctrl.log.get('Jog') + + self._loadUserGamepadConfigs() + self._startMonitoring() + self._discoverGamepads() + self._updateJogging() + + def _loadUserGamepadConfigs(self): + path = self.ctrl.get_path('gamepads.json') + if os.path.exists(path): + with open(path, 'r') as f: + global userGamepadConfigs + userGamepadConfigs = json.load(f) + + def _startMonitoring(self): + self.udev_context = pyudev.Context() + self.monitor = pyudev.Monitor.from_netlink(self.udev_context) + self.monitor.filter_by(subsystem='input') + self.ctrl.ioloop.add_handler(self.monitor, self._udevHandler, + self.ctrl.ioloop.READ) + self.monitor.start() + + def _udevHandler(self, fd, events): + for udev in iter(functools.partial(self.monitor.poll, 0), None): + isEventDevice = re.search(r"/event\d+$", udev.device_node or "") + if not isEventDevice: + continue + + inputJoystick = safe_int(udev.properties["ID_INPUT_JOYSTICK"]) + + if inputJoystick != 1: + self.log.info("Ignoring non-gamepad device: {}".format( + to_sorted_json( + {key: udev.properties[key] + for key in udev.properties}))) + continue + + uniqueProperties = set(udev.properties).difference( + udevPropertiesToIgnore).difference(udevPropertiesToLog) + if len(uniqueProperties) > 0: + self.log.info("Unique properties: {}".format( + to_sorted_json({ + key: udev.properties[key] + for key in uniqueProperties + }))) + + if udev.action == 'add': + self._listen(udev.device_node) + elif udev.action == 'remove': + self._forget(udev.device_node) + + def _discoverGamepads(self): + with open("/proc/bus/input/devices", "r") as file: + for line in file: + # Matches lines from '/proc/bus/input/devices' that look like: + # H: Handlers=js1 event0 + if not re.match(r"H:\s*Handlers\s*=.*\bjs\d+\b", line): + continue + + match = re.search(r"\bevent\d+\b", line) + if not match: + continue + + self._listen("/dev/input/{}".format(match.group())) + + def _listen(self, devicePath: str): + gamepad = Gamepad( + self.log, evdev.InputDevice(devicePath), + pyudev.Devices.from_device_file(self.udev_context, devicePath)) + + self.log.info("Found gamepad: {}".format(str(gamepad))) + + self.gamepads[gamepad.fd] = self.gamepads[devicePath] = gamepad + + self.ioloop.add_handler(gamepad.fd, self._gamepadHandler, + self.ioloop.READ) + + def _forget(self, devicePath: str): + gamepad = self.gamepads.get(devicePath) + if not gamepad: + return + + self.log.info("Device removed: {}, {}".format(gamepad.hash, + devicePath)) + + self.ioloop.remove_handler(gamepad.fd) + del self.gamepads[gamepad.devicePath] + del self.gamepads[gamepad.fd] + + def _gamepadHandler(self, fd, events): + gamepad = self.gamepads.get(fd) + if not gamepad: + self.log.info("_gamepad_handler: Unknown gamepad? {}".format(fd)) + return + + try: + for event in gamepad.read(): + command = self._getCommandFromEvent(gamepad, event) + if command: + self._processCommand(command) + except BlockingIOError: + pass + except OSError as error: + if error.errno == errno.ENODEV: + self._forget(gamepad.devicePath) + else: + gamepad.log(traceback.format_exc()) + except Exception as error: + gamepad.log(traceback.format_exc()) + + def _getCommandFromEvent(self, gamepad: Gamepad, + event: evdev.InputEvent) -> Command: + if event.type not in [EV_ABS, EV_KEY]: + return + + eventSignature = "{}:{}".format(evdev.ecodes.EV[event.type], + event.code) + commandId = gamepad.config.get(eventSignature) + + if not commandId: + gamepad.logOnce("Unmapped event: {}:{}".format( + gamepad.hash, eventSignature)) + return + + gamepad.logDebug("Got event: {}".format(str(event))) + + return Command(commandId, gamepad.scaleAndClampValue(event), gamepad) + + def _processCommand(self, command: Command): + processor = self.commandProcessors.get(command.id) + if not processor: + command.gamepad.log("Unrecognized command: {}".format(command.id)) + return + + command.gamepad.logDebug("Processing command: {}".format(str(command))) + + processor(self, command) + + def _processSpeedCommand(self, command: Command): + match = re.match(r"^speed-(\d)$", command.id) + speed = int(match.group(1)) if match else 0 + if speed not in [1, 2, 3, 4]: + command.gamepad.log("Unrecognized speed command: {}".format( + str(command))) + + self.changed = self.changed or self.speed != speed + self.speed = speed + + def _processAxisCommand(self, command: Command): + match = re.match(r"^axis-(.)$", command.id) + axis = match.group(1) if match else "" + if axis not in ["x", "y", "z"]: + command.gamepad.log("Unrecognized axis command: {}".format( + str(command))) + + sign = command.gamepad.config.get("sign-{}".format(axis), 1) + oldValue = self.axes[axis] + locked = self.lock.get(axis, False) + + self.axes[axis] = 0 if locked else command.value * sign + self.changed = self.changed or oldValue != self.axes[axis] + + command.gamepad.logDebug("_processAxisCommand: {}".format( + json.dumps({ + "command.value": command.value, + "axis": axis, + "oldValue": oldValue, + "value": self.axes[axis], + "sign": sign, + "locked": locked, + "changed": self.changed + }))) + + def _processLockCommand(self, command: Command): + match = re.match(r"^lock-(.)$", command.id) + axis = match.group(1) if match else "" + if axis not in ["x", "y"]: + command.gamepad.log("Unrecognized lock command: {}".format( + str(command))) + + self.lock[axis] = bool(command.value) + + def _processDisabled(self, command: Command): + pass + + def _updateJogging(self): + try: + if not self.changed: + return + + self.changed = False + + if self.speed == 1: scale = 1.0 / 128.0 + if self.speed == 2: scale = 1.0 / 32.0 + if self.speed == 3: scale = 1.0 / 4.0 + if self.speed == 4: scale = 1.0 + + axes = {axis: value * scale for (axis, value) in self.axes.items()} try: - axes = {} - for i in range(len(self.v)): axes["xyzabc"[i]] = self.v[i] self.ctrl.mach.jog(axes) + except: + self.log.info(traceback.format_exc()) + finally: + # We only update 4 times a second, to keep from overwhelming the system + # EV_ABS events can happen hundreds of times a second. + self.ctrl.ioloop.call_later(0.25, self._updateJogging) - except Exception as e: - self.log.warning('Jog: %s', e) - - self.ctrl.ioloop.call_later(0.25, self.callback) - - - def changed(self): - scale = 1.0 - if self.speed == 1: scale = 1.0 / 128.0 - if self.speed == 2: scale = 1.0 / 32.0 - if self.speed == 3: scale = 1.0 / 4.0 - - self.v = [x * scale for x in self.axes] + commandProcessors = { + "speed-1": _processSpeedCommand, + "speed-2": _processSpeedCommand, + "speed-3": _processSpeedCommand, + "speed-4": _processSpeedCommand, + "axis-x": _processAxisCommand, + "axis-y": _processAxisCommand, + "axis-z": _processAxisCommand, + "lock-x": _processLockCommand, + "lock-y": _processLockCommand, + "disabled": _processDisabled + } # type: dict[str, typing.Callable[[Command], None]] diff --git a/src/py/bbctrl/Log.py b/src/py/bbctrl/Log.py index 87f694f..d333d37 100644 --- a/src/py/bbctrl/Log.py +++ b/src/py/bbctrl/Log.py @@ -128,7 +128,7 @@ class Log(object): def remove_listener(self, listener): self.listeners.remove(listener) - def get(self, name, level = None): + def get(self, name, level = None) -> Logger: if not name in self.loggers: self.loggers[name] = Logger(self, name, self.level) return self.loggers[name] diff --git a/src/py/inevent/AbsAxisScaling.py b/src/py/inevent/AbsAxisScaling.py deleted file mode 100644 index 4b00721..0000000 --- a/src/py/inevent/AbsAxisScaling.py +++ /dev/null @@ -1,106 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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 array -import fcntl -import struct -from inevent import ioctl - - -def EVIOCGABS(axis): - return ioctl._IOR(ord('E'), 0x40 + axis, "ffffff") # get abs value/limits - - - -class AbsAxisScaling(object): - """ - Fetches and implements the EV_ABS axis scaling. - - The constructor fetches the scaling values from the given stream for the - given axis using an ioctl. - - There is a scale method, which scales a given value to the range -1..+1. - """ - - def __init__(self, stream, axis): - """ - Fetch the scale values for this stream and fill in the instance - variables accordingly. - """ - s = array.array("i", [1, 2, 3, 4, 5, 6]) - try: - fcntl.ioctl(stream.filehandle, EVIOCGABS(axis), s) - - except IOError: - self.value = self.minimum = self.maximum = self.fuzz = self.flat = \ - self.resolution = 1 - - else: - self.value, self.minimum, self.maximum, self.fuzz, self.flat, \ - self.resolution = struct.unpack("iiiiii", s) - - - def __str__(self): - return "Value {0} Min {1}, Max {2}, Fuzz {3}, Flat {4}, Res {5}".format( - self.value, self.minimum, self.maximum, self.fuzz, self.flat, - self.resolution) - - - def scale(self, value): - """ - scales the given value into the range -1..+1 - """ - return (float(value) - float(self.minimum)) / \ - float(self.maximum - self.minimum) * 2.0 - 1.0 diff --git a/src/py/inevent/Constants.py b/src/py/inevent/Constants.py deleted file mode 100644 index 0f3a2ed..0000000 --- a/src/py/inevent/Constants.py +++ /dev/null @@ -1,124 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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. - -EV_SYN = 0x00 -EV_KEY = 0x01 -EV_REL = 0x02 -EV_ABS = 0x03 -EV_MSC = 0x04 -EV_SW = 0x05 -EV_LED = 0x11 -EV_SND = 0x12 -EV_REP = 0x14 -EV_FF = 0x15 -EV_PWR = 0x16 -EV_FF_STATUS = 0x17 - -ev_type_name = {} -ev_type_name[EV_SYN] = "SYN" -ev_type_name[EV_KEY] = "KEY" -ev_type_name[EV_REL] = "REL" -ev_type_name[EV_ABS] = "ABS" -ev_type_name[EV_MSC] = "MSC" -ev_type_name[EV_SW] = "SW" -ev_type_name[EV_LED] = "LED" -ev_type_name[EV_SND] = "SND" -ev_type_name[EV_REP] = "REP" -ev_type_name[EV_FF] = "FF" -ev_type_name[EV_PWR] = "PWR" -ev_type_name[EV_FF_STATUS] = "FF_STATUS" - -SYN_REPORT = 0 -SYN_CONFIG = 1 - -REL_X = 0x00 -REL_Y = 0x01 -REL_Z = 0x02 -REL_RX = 0x03 -REL_RY = 0x04 -REL_RZ = 0x05 -REL_HWHEEL = 0x06 -REL_DIAL = 0x07 -REL_WHEEL = 0x08 -REL_MISC = 0x09 -REL_MAX = 0x0f - -ABS_X = 0x00 -ABS_Y = 0x01 -ABS_Z = 0x02 -ABS_RX = 0x03 -ABS_RY = 0x04 -ABS_RZ = 0x05 -ABS_THROTTLE = 0x06 -ABS_RUDDER = 0x07 -ABS_WHEEL = 0x08 -ABS_GAS = 0x09 -ABS_BRAKE = 0x0a -ABS_HAT0X = 0x10 -ABS_HAT0Y = 0x11 -ABS_HAT1X = 0x12 -ABS_HAT1Y = 0x13 -ABS_HAT2X = 0x14 -ABS_HAT2Y = 0x15 -ABS_HAT3X = 0x16 -ABS_HAT3Y = 0x17 -ABS_PRESSURE = 0x18 -ABS_DISTANCE = 0x19 -ABS_TILT_X = 0x1a -ABS_TILT_Y = 0x1b -ABS_TOOL_WIDTH = 0x1c -ABS_VOLUME = 0x20 -ABS_MISC = 0x28 -ABS_MAX = 0x3f diff --git a/src/py/inevent/Event.py b/src/py/inevent/Event.py deleted file mode 100644 index bf8f2e1..0000000 --- a/src/py/inevent/Event.py +++ /dev/null @@ -1,142 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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 struct - -from inevent.Constants import * - - -_format = 'llHHi' -size = struct.calcsize(_format) - - -class Event(object): - """ - A single event from the linux input event system. - - Events are tuples: (Time, Type, Code, Value) - In addition we remember the stream it came from. - - Externally, only the unhandled event handler gets passed the whole event, - but the SYN handler gets the code and value. (Also the keyboard handler, but - those are renamed to key and value.) - - This class is responsible for converting the Linux input event structure into - one of these objects and back again. - """ - def __init__(self, stream, time = None, type = None, code = None, - value = None): - """ - Create a new event. - - Generally all but the stream parameter are left out; we will want to - populate the object from a Linux input event using decode. - """ - self.stream = stream - self.time = time - self.type = type - self.code = code - self.value = value - - - def get_type_name(self): - if self.type not in ev_type_name: return '0x%x' % self.type - return ev_type_name[self.type] - - - def get_source(self): - return "%s[%d]" % (self.stream.devType, self.stream.devIndex) - - - def __str__(self): - """ - Uses the stream to give the device type and whether it is currently grabbed. - """ - grabbed = "grabbed" if self.stream.grabbed else "ungrabbed" - - return "Event %s %s @%f: %s 0x%x=0x%x" % ( - self.get_source(), grabbed, self.time, self.get_type_name(), self.code, - self.value) - - - def __repr__(self): - return "Event(%s, %f, 0x%x, 0x%x, 0x%x)" % ( - repr(self.stream), self.time, self.type, self.code, self.value) - - - def encode(self): - """ - Encode this event into a Linux input event structure. - - The output is packed into a string. It is unlikely that this function - will be required, but it might as well be here. - """ - tsec = int(self.time) - tfrac = int((self.time - tsec) * 1000000) - - return struct.pack(_format, tsec, tfrac, self.type, self.code, self.value) - - - def decode(self, s): - """ - Decode a Linux input event into the fields of this object. - - Arguments: - *s* - A binary structure packed into a string. - """ - tsec, tfrac, self.type, self.code, self.value = struct.unpack(_format, s) - - self.time = tsec + tfrac / 1000000.0 diff --git a/src/py/inevent/EventHandler.py b/src/py/inevent/EventHandler.py deleted file mode 100644 index ccc9d7c..0000000 --- a/src/py/inevent/EventHandler.py +++ /dev/null @@ -1,146 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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. - -from inevent.Constants import * -from inevent.EventStream import EventStream - - -class EventHandler(object): - """ - A class to handle events. - - Four types of events are handled: REL (mouse movement), KEY (keybaord keys and - other device buttons), ABS (joysticks and gamepad analogue sticks) and SYN - (delimits simultaneous events such as mouse movements) - """ - def __init__(self): - self.buttons = dict() - - - def event(self, event, handler, name): - """ - Handles the given event. - - If the event is passed to a handler or otherwise handled then returns None, - else returns the event. All handlers are optional. - - All key events are handled by putting them in the self.buttons dict, and - optionally by calling the supplied handler. - - REL X, Y and wheel V and H events are all accumulated internally and - also optionally passed to the supplied handler. All these events are - handled. - - ABS X, Y, Z, RX, RY, RZ, Hat0X, Hat0Y are all accumulated internally and - also optionally passed to the supplied handler. Other ABS events are not - handled. - - All SYN events are passed to the supplied handler. - - There are several ABS events that we do not handle. In particular: - THROTTLE, RUDDER, WHEEL, GAS, BRAKE, HAT1, HAT2, HAT3, PRESSURE, - DISTANCE, TILT, TOOL_WIDTH. Implementing these is left as an exercise - for the interested reader. - - Likewise, since one handler is handling all events for all devices, we - may get the situation where two devices return the same button. The only - way to handle that would seem to be to have a key dict for every device, - which seems needlessly profligate for a situation that may never arise. - """ - - state = event.stream.state - - if event.type == EV_KEY: self.buttons[event.code] = event.value - elif event.type == EV_REL: state.rel[event.code] += event.value - elif event.type == EV_ABS: - state.abs[event.code] = event.stream.scale(event.code, event.value) - - if handler: handler.event(event, state, name) - - - def key_state(self, code): - """ - Returns the last event value for the given key code. - - Key names can be converted to key codes using codeOf[str]. - If the key is pressed the returned value will be 1 (pressed) or 2 (held). - If the key is not pressed, the returned value will be 0. - """ - return self.buttons.get(code, 0) - - - def clear_key(self, code): - """ - Clears the event value for the given key code. - - Key names can be converted to key codes using codeOf[str]. - This emulates a key-up but does not generate any events. - """ - self.buttons[code] = 0 - - - def get_keys(self): - """ - Returns the first of whichever keys have been pressed. - - Key names can be converted to key codes using codeOf[str]. - This emulates a key-up but does not generate any events. - """ - k_list = [] - - for k in self.buttons: - if self.buttons[k] != 0: k_list.append(k) - - return k_list diff --git a/src/py/inevent/EventState.py b/src/py/inevent/EventState.py deleted file mode 100644 index d1443bf..0000000 --- a/src/py/inevent/EventState.py +++ /dev/null @@ -1,145 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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. - -from inevent.Constants import * - - -class EventState: - def __init__(self): - self.abs = [0.0] * ABS_MAX - self.rel = [0.0] * REL_MAX - - - def __str__(self): - return ("({:6.3f}, {:6.3f}, {:6.3f}) ".format(*self.get_joystick3d()) + - "({:6.3f}, {:6.3f}, {:6.3f}) ".format(*self.get_joystickR3d()) + - "({:2.0f}, {:2.0f}) ".format(*self.get_hat()) + - "({:0.2f}, {:0.2f}) ".format(*self.get_mouse()) + - "({:0.2f}, {:0.2f})".format(*self.get_wheel())) - - - def get_joystick(self): - """ - Returns the x,y coordinates for a joystick or left gamepad analogue stick. - - The values are returned as a tuple. All values are -1.0 to +1.0 with - 0.0 being centred. - """ - return self.abs[ABS_X], self.abs[ABS_Y] - - - def get_joystick3d(self): - """ - Returns the x,y,z coordinates for a joystick or left gamepad analogue stick - - The values are returned as a tuple. All values are -1.0 to +1.0 with - 0.0 being centred. - """ - return self.abs[ABS_X], self.abs[ABS_Y], self.abs[ABS_Z] - - - def get_joystickR(self): - """ - Returns the x,y coordinates for a right gamepad analogue stick. - - The values are returned as a tuple. For some odd reason, the gamepad - returns values in the Z axes of both joysticks, with y being the first. - - All values are -1.0 to +1.0 with 0.0 being centred. - """ - return self.abs[ABS_RZ], self.abs[ABS_Z] - - - def get_joystickR3d(self): - """ - Returns the x,y,z coordinates for a 2nd joystick control - - The values are returned as a tuple. All values are -1.0 to +1.0 with - 0.0 being centred. - """ - return self.abs[ABS_RX], self.abs[ABS_RY], self.abs[ABS_RZ] - - - def get_hat(self): - """ - Returns the x,y coordinates for a joystick hat or gamepad direction pad - - The values are returned as a tuple. All values are -1.0 to +1.0 with - 0.0 being centred. - """ - return self.abs[ABS_HAT0X], self.abs[ABS_HAT0Y] - - - def get_mouse(self): - return self.rel[REL_X], self.rel[REL_Y] - - - def get_wheel(self): - return self.rel[REL_WHEEL], self.rel[REL_HWHEEL] - - - def get_mouse_movement(self): - """ - Returns the accumulated REL (mouse or other relative device) movements - since the last call. - - The returned value is a tuple: (X, Y, WHEEL, H-WHEEL) - """ - ret = self.get_mouse() + self.get_wheel() - - self.rel[REL_X] = self.rel[REL_Y] = 0 - self.rel[REL_WHEEL] = self.rel[REL_HWHEEL] = 0 - - return ret diff --git a/src/py/inevent/EventStream.py b/src/py/inevent/EventStream.py deleted file mode 100644 index aac648d..0000000 --- a/src/py/inevent/EventStream.py +++ /dev/null @@ -1,218 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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() diff --git a/src/py/inevent/FindDevices.py b/src/py/inevent/FindDevices.py deleted file mode 100644 index 2f3970a..0000000 --- a/src/py/inevent/FindDevices.py +++ /dev/null @@ -1,245 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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 re -import logging -from inevent.Constants import * - -log = logging.getLogger('inevent') - - -def test_bit(nlst, b): - index = b / 32 - bit = b % 32 - return index < len(nlst) and nlst[index] & (1 << bit) - - -def EvToStr(events): - s = [] - - if test_bit(events, EV_SYN): s.append("EV_SYN") - if test_bit(events, EV_KEY): s.append("EV_KEY") - if test_bit(events, EV_REL): s.append("EV_REL") - if test_bit(events, EV_ABS): s.append("EV_ABS") - if test_bit(events, EV_MSC): s.append("EV_MSC") - if test_bit(events, EV_LED): s.append("EV_LED") - if test_bit(events, EV_SND): s.append("EV_SND") - if test_bit(events, EV_REP): s.append("EV_REP") - if test_bit(events, EV_FF): s.append("EV_FF" ) - if test_bit(events, EV_PWR): s.append("EV_PWR") - if test_bit(events, EV_FF_STATUS): s.append("EV_FF_STATUS") - - return s - - -class DeviceCapabilities(object): - def __init__(self, firstLine, filehandle): - self.EV_SYNevents = [] - self.EV_KEYevents = [] - self.EV_RELevents = [] - self.EV_ABSevents = [] - self.EV_MSCevents = [] - self.EV_LEDevents = [] - self.EV_SNDevents = [] - self.EV_REPevents = [] - self.EV_FFevents = [] - self.EV_PWRevents = [] - self.EV_FF_STATUSevents = [] - self.eventTypes = [] - - match = re.search(".*Bus=([0-9A-Fa-f]+).*Vendor=([0-9A-Fa-f]+).*" - "Product=([0-9A-Fa-f]+).*Version=([0-9A-Fa-f]+).*", - firstLine) - - if not match: - log.warning("Do not understand device ID: %s", firstLine) - self.bus = 0 - self.vendor = 0 - self.product = 0 - self.version = 0 - - else: - self.bus = int(match.group(1), base = 16) - self.vendor = int(match.group(2), base = 16) - self.product = int(match.group(3), base = 16) - self.version = int(match.group(4), base = 16) - - for line in filehandle: - if not line.strip(): break - - if line[0] == "N": - match = re.search('Name="([^"]+)"', line) - if match: self.name = match.group(1) - else: self.name = "UNKNOWN" - - elif line[0] == "P": - match = re.search('Phys=(.+)', line) - if match: self.phys = match.group(1) - else: self.phys = "UNKNOWN" - - elif line[0] == "S": - match = re.search('Sysfs=(.+)', line) - if match: self.sysfs = match.group(1) - else: self.sysfs = "UNKNOWN" - - elif line[0] == "U": - match = re.search('Uniq=(.*)', line) - if match: self.uniq = match.group(1) - else: self.uniq = "UNKNOWN" - - elif line[0] == "H": - match = re.search('Handlers=(.+)', line) - if match: self.handlers = match.group(1).split() - else: self.handlers = [] - - elif line[:5] == "B: EV": - eventsNums = [int(x, base = 16) for x in line[6:].split()] - eventsNums.reverse() - self.eventTypes = eventsNums - - elif line[:6] == "B: KEY": - eventsNums = [int(x, base = 16) for x in line[7:].split()] - eventsNums.reverse() - self.EV_KEYevents = eventsNums - - elif line[:6] == "B: ABS": - eventsNums = [int(x, base = 16) for x in line[7:].split()] - eventsNums.reverse() - self.EV_ABSevents = eventsNums - - elif line[:6] == "B: MSC": - eventsNums = [int(x, base = 16) for x in line[7:].split()] - eventsNums.reverse() - self.EV_MSCevents = eventsNums - - elif line[:6] == "B: REL": - eventsNums = [int(x, base = 16) for x in line[7:].split()] - eventsNums.reverse() - self.EV_RELevents = eventsNums - - elif line[:6] == "B: LED": - eventsNums = [int(x, base = 16) for x in line[7:].split()] - eventsNums.reverse() - self.EV_LEDevents = eventsNums - - for handler in self.handlers: - if handler[:5] == "event": self.eventIndex = int(handler[5:]) - - self.isMouse = False - self.isKeyboard = False - self.isJoystick = False - - - def doesProduce(self, eventType, eventCode): - return test_bit(self.eventTypes, eventType) and ( - (eventType == EV_SYN and test_bit(self.EV_SYNevents, eventCode)) or - (eventType == EV_KEY and test_bit(self.EV_KEYevents, eventCode)) or - (eventType == EV_REL and test_bit(self.EV_RELevents, eventCode)) or - (eventType == EV_ABS and test_bit(self.EV_ABSevents, eventCode)) or - (eventType == EV_MSC and test_bit(self.EV_MSCevents, eventCode)) or - (eventType == EV_LED and test_bit(self.EV_LEDevents, eventCode)) or - (eventType == EV_SND and test_bit(self.EV_SNDevents, eventCode)) or - (eventType == EV_REP and test_bit(self.EV_REPevents, eventCode)) or - (eventType == EV_FF and test_bit(self.EV_FFevents, eventCode)) or - (eventType == EV_PWR and test_bit(self.EV_PWRevents, eventCode)) or - (eventType == EV_FF_STATUS and - test_bit(self.EV_FF_STATUSevents, eventCode))) - - - def __str__(self): - return ( - ("%s\n" - "Bus: %s Vendor: %s Product: %s Version: %s\n" - "Phys: %s\n" - "Sysfs: %s\n" - "Uniq: %s\n" - "Handlers: %s Event Index: %s\n" - "Keyboard: %s Mouse: %s Joystick: %s\n" - "Events: %s") % ( - self.name, self.bus, self.vendor, self.product, self.version, self.phys, - self.sysfs, self.uniq, self.handlers, self.eventIndex, self.isKeyboard, - self.isMouse, self.isJoystick, EvToStr(self.eventTypes))) - - -deviceCapabilities = [] - - -def get_devices(filename = "/proc/bus/input/devices"): - global deviceCapabilities - - with open("/proc/bus/input/devices", "r") as filehandle: - for line in filehandle: - if line[0] == "I": - deviceCapabilities.append(DeviceCapabilities(line, filehandle)) - - return deviceCapabilities - - -def print_devices(): - devs = get_devices() - - for dev in devs: - print(str(dev)) - print(" ABS: {}" - .format([x for x in range(64) if test_bit(dev.EV_ABSevents, x)])) - print(" REL: {}" - .format([x for x in range(64) if test_bit(dev.EV_RELevents, x)])) - print(" MSC: {}" - .format([x for x in range(64) if test_bit(dev.EV_MSCevents, x)])) - print(" KEY: {}" - .format([x for x in range(512) if test_bit(dev.EV_KEYevents, x)])) - print(" LED: {}" - .format([x for x in range(64) if test_bit(dev.EV_LEDevents, x)])) - print() diff --git a/src/py/inevent/InEvent.py b/src/py/inevent/InEvent.py deleted file mode 100644 index a85ea44..0000000 --- a/src/py/inevent/InEvent.py +++ /dev/null @@ -1,288 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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 pyudev -import re -import select -import errno -import functools -import logging - -from inevent.EventHandler import EventHandler -from inevent import Keys -from inevent.Constants import * -from inevent.EventStream import EventStream - - -log = logging.getLogger('inevent') - -_KEYS = (k for k in vars(Keys) if not k.startswith('_')) -KEY_CODE = dict((k, getattr(Keys, k)) for k in _KEYS) -CODE_KEY = {} -for v in KEY_CODE: CODE_KEY[KEY_CODE[v]] = v - - -def key_to_code(key): - return KEY_CODE.get(str(key), -1) \ - if isinstance(key, str) else key - - -def code_to_key(code): return CODE_KEY.get(code, '') - - -class InEvent(object): - """Encapsulates the entire InEvent subsystem. - - This is generally all you need to import. - - On instantiation, we open all devices that are keyboards, mice or joysticks. - That means we might have two of one sort of another, and that might be a - problem, but it would be rather rare. - - There are several ABS (joystick, touch) events that we do not handle, - specifically THROTTLE, RUDDER, WHEEL, GAS, BRAKE, HAT1, HAT2, HAT3, PRESSURE, - DISTANCE, TILT, TOOL_WIDTH. Implementing these is left as an exercise - for the interested reader. Similarly, we make no attempt to handle - multi-touch. - - Handlers can be supplied, in which case they are called for each event, but - it isn't necessary; API exists for all the events. - - The handler signature is: - - def handler_func(event, state) - - where: - event: - An Event object describing the event. - - state: - An EventState object describing the current state. - - Use key_to_code() to convert from the name of a key to its code, - and code_to_key() to convert a code to a name. - - The keys are listed in inevent.Constants.py or /usr/include/linux/input.h - Note that the key names refer to a US keyboard. - """ - def __init__(self, ioloop, cb, types = 'kbd mouse js'.split()): - self.ioloop = ioloop - self.cb = cb - self.streams = [] - self.handler = EventHandler() - self.types = types - - self.udevCtx = pyudev.Context() - self.udevMon = pyudev.Monitor.from_netlink(self.udevCtx) - self.udevMon.filter_by(subsystem = 'input') - - devs = list(self.find_devices(types)) - for index, type, name in devs: - self.add_stream(index, type, name) - - self.udevMon.start() - ioloop.add_handler(self.udevMon.fileno(), self.udev_handler, ioloop.READ) - - - def get_dev(self, index): - return pyudev.Device.from_name(self.udevCtx, 'input', 'event%s' % index) - - - def get_dev_name(self, index): - try: - dev = self.get_dev(index) - return dev.parent.attributes.asstring('name').decode('utf-8') - except: pass - - - def find_devices(self, types): - """Finds the event indices of all devices of the specified types. - - A type is a string on the handlers line of /proc/bus/input/devices. - Keyboards use "kbd", mice use "mouse" and joysticks (and gamepads) use "js". - - Returns a list of integer indexes N, where /dev/input/eventN is the event - stream for each device. - - If butNot is given it holds a list of tuples which the returned values - should not match. - - All devices of each type are returned; if you have two mice, they will both - be used. - """ - with open("/proc/bus/input/devices", "r") as filehandle: - for line in filehandle: - if line[0] == "H": - for type in types: - if type in line: - match = re.search("event([0-9]+)", line) - index = match and match.group(1) - if index: - yield int(index), type, self.get_dev_name(index) - break - - - def process_udev_event(self): - action, device = self.udevMon.receive_device() - if device is None: return - - match = re.search(r"/dev/input/event([0-9]+)", str(device.device_node)) - devIndex = match and match.group(1) - if not devIndex: return - devIndex = int(devIndex) - - if action == 'add': - for index, devType, devName in self.find_devices(self.types): - if index == devIndex: - self.add_stream(devIndex, devType, devName) - break - - if action == 'remove': self.remove_stream(devIndex) - - - def stream_handler(self, fd, events): - for stream in self.streams: - if stream.filehandle == fd: - while True: - event = stream.next() - if event: self.handler.event(event, self.cb, stream.devName) - else: break - - - def udev_handler(self, fd, events): - self.process_udev_event() - - - def add_stream(self, devIndex, devType, devName): - try: - stream = EventStream(devIndex, devType, devName) - self.streams.append(stream) - - self.ioloop.add_handler(stream.filehandle, self.stream_handler, - self.ioloop.READ) - - log.info('Added %s[%d] %s', devType, devIndex, devName) - - except OSError as e: - log.warning('Failed to add %s[%d]: %s', devType, devIndex, e) - - - def remove_stream(self, devIndex): - for stream in self.streams: - if stream.devIndex == devIndex: - self.streams.remove(stream) - self.ioloop.remove_handler(stream.filehandle) - stream.release() - self.cb.clear() - - log.info('Removed %s[%d]', stream.devType, devIndex) - - - def key_state(self, key): - """ - Returns the state of the given key. - - The returned value will be 0 for key-up, or 1 for key-down. This method - returns a key-held(2) as 1 to aid in using the returned value as a - movement distance. - - This function accepts either the key code or the string name of the key. - It would be more efficient to look-up and store the code of - the key with KEY_CODE[], rather than using the string every time. (Which - involves a dict look-up keyed with a string for every key_state call, every - time around the loop.) - - Gamepad keys are: - Select = BTN_BASE3, Start = BTN_BASE4 - L1 = BTN_TOP R1 = BTN_BASE - L2 = BTN_PINKIE R2 = BTN_BASE2 - - The action buttons are: - BTN_THUMB - BTN_TRIGGER - BTN_TOP - BTN_THUMB2 - - Analogue Left Button = BTN_BASE5 - Analogue Right Button = BTN_BASE6 - - Some of those may clash with extended mouse buttons, so if you are using - both at once, you'll see some overlap. - - The direction pad is hat0 (see get_hat) - """ - return self.handler.key_state(key_to_code(key)) - - - def clear_key(self, key): - """ - Clears the state of the given key. - - Emulates a key-up, but does not call any handlers. - """ - return self.handler.clear_key(key_to_code(key)) - - - def get_keys(self): - return [code_to_key(k) for k in self.handler.get_keys()] - - - def release(self): - """ - Ungrabs all streams and closes all files. - - Only do this when you're finished with this object. You can't use it again. - """ - for s in self.streams: s.release() diff --git a/src/py/inevent/JogHandler.py b/src/py/inevent/JogHandler.py deleted file mode 100644 index 2deac48..0000000 --- a/src/py/inevent/JogHandler.py +++ /dev/null @@ -1,115 +0,0 @@ -import logging - -from inevent.Constants import * - - -log = logging.getLogger('inevent') -log.setLevel(logging.INFO) - - -def axes_to_string(axes): - s = '' - for axis in axes: - if s: s += ', ' - else: s = '(' - s += '{:6.3f}'.format(axis) - return s + ')' - - -def event_to_string(event, state): - s = '{} {}: '.format(event.get_source(), event.get_type_name()) - - if event.type == EV_ABS: - s += axes_to_string(state.get_joystick3d()) + ' ' + \ - axes_to_string(state.get_joystickR3d()) + ' ' + \ - '({:2.0f}, {:2.0f}) '.format(*state.get_hat()) - - if event.type == EV_REL: - s += '({:d}, {:d}) '.format(*state.get_mouse()) + \ - '({:d}, {:d})'.format(*state.get_wheel()) - - if event.type == EV_KEY: - state = 'pressed' if event.value else 'released' - s += '0x{:x} {}'.format(event.code, state) - - return s - - -class JogHandler: - def __init__(self, config): - self.config = config - self.reset() - - - def changed(self): - log.info(axes_to_string(self.axes) + ' x {:d}'.format(self.speed)) - - - def reset(self): - self.axes = [0.0, 0.0, 0.0, 0.0] - self.speed = 3 - self.vertical_lock = 0 - self.horizontal_lock = 0 - - - def clear(self): - self.reset() - self.changed() - - - def get_config(self, name): - if name in self.config: return self.config[name] - return self.config['default'] - - - def event(self, event, state, dev_name): - if event.type not in [EV_ABS, EV_REL, EV_KEY]: return - - config = self.get_config(dev_name) - changed = False - - # Process event - if event.type == EV_ABS and event.code in config['axes']: - old_axes = list(self.axes) - deadband = config['deadband'] - axis = config['axes'].index(event.code) - - self.axes[axis] = event.stream.state.abs[event.code] - self.axes[axis] *= config['dir'][axis] - - value = abs(self.axes[axis]) - if value >= deadband: - sign = -1 if self.axes[axis] < 0 else 1 - delta = value - deadband - range = 1 - deadband - - # Scale the new value to the available range (full range, minus the deadband) - self.axes[axis] = (delta * sign) / range - else: - self.axes[axis] = 0 - - if self.horizontal_lock and axis not in [0, 3]: - self.axes[axis] = 0 - - if self.vertical_lock and axis not in [1, 2]: - self.axes[axis] = 0 - - if old_axes[axis] != self.axes[axis]: changed = True - - elif event.type == EV_KEY and event.code in config['speed']: - old_speed = self.speed - self.speed = config['speed'].index(event.code) + 1 - if self.speed != old_speed: changed = True - - elif event.type == EV_KEY and event.code in config['lock']: - index = config['lock'].index(event.code) - - self.horizontal_lock, self.vertical_lock = False, False - - if event.value: - if index == 0: self.horizontal_lock = True - if index == 1: self.vertical_lock = True - - log.debug(event_to_string(event, state)) - - if changed: self.changed() diff --git a/src/py/inevent/Keys.py b/src/py/inevent/Keys.py deleted file mode 100644 index 449aa97..0000000 --- a/src/py/inevent/Keys.py +++ /dev/null @@ -1,445 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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. - -KEY_ESC = 1 -KEY_1 = 2 -KEY_2 = 3 -KEY_3 = 4 -KEY_4 = 5 -KEY_5 = 6 -KEY_6 = 7 -KEY_7 = 8 -KEY_8 = 9 -KEY_9 = 10 -KEY_0 = 11 -KEY_MINUS = 12 -KEY_EQUAL = 13 -KEY_BACKSPACE = 14 -KEY_TAB = 15 -KEY_Q = 16 -KEY_W = 17 -KEY_E = 18 -KEY_R = 19 -KEY_T = 20 -KEY_Y = 21 -KEY_U = 22 -KEY_I = 23 -KEY_O = 24 -KEY_P = 25 -KEY_LEFTBRACE = 26 -KEY_RIGHTBRACE = 27 -KEY_ENTER = 28 -KEY_LEFTCTRL = 29 -KEY_A = 30 -KEY_S = 31 -KEY_D = 32 -KEY_F = 33 -KEY_G = 34 -KEY_H = 35 -KEY_J = 36 -KEY_K = 37 -KEY_L = 38 -KEY_SEMICOLON = 39 -KEY_APOSTROPHE = 40 -KEY_GRAVE = 41 -KEY_LEFTSHIFT = 42 -KEY_BACKSLASH = 43 -KEY_Z = 44 -KEY_X = 45 -KEY_C = 46 -KEY_V = 47 -KEY_B = 48 -KEY_N = 49 -KEY_M = 50 -KEY_COMMA = 51 -KEY_DOT = 52 -KEY_SLASH = 53 -KEY_RIGHTSHIFT = 54 -KEY_KPASTERISK = 55 -KEY_LEFTALT = 56 -KEY_SPACE = 57 -KEY_CAPSLOCK = 58 -KEY_F1 = 59 -KEY_F2 = 60 -KEY_F3 = 61 -KEY_F4 = 62 -KEY_F5 = 63 -KEY_F6 = 64 -KEY_F7 = 65 -KEY_F8 = 66 -KEY_F9 = 67 -KEY_F10 = 68 -KEY_NUMLOCK = 69 -KEY_SCROLLLOCK = 70 -KEY_KP7 = 71 -KEY_KP8 = 72 -KEY_KP9 = 73 -KEY_KPMINUS = 74 -KEY_KP4 = 75 -KEY_KP5 = 76 -KEY_KP6 = 77 -KEY_KPPLUS = 78 -KEY_KP1 = 79 -KEY_KP2 = 80 -KEY_KP3 = 81 -KEY_KP0 = 82 -KEY_KPDOT = 83 - -KEY_ZENKAKUHANKAKU = 85 -KEY_102ND = 86 -KEY_F11 = 87 -KEY_F12 = 88 -KEY_RO = 89 -KEY_KATAKANA = 90 -KEY_HIRAGANA = 91 -KEY_HENKAN = 92 -KEY_KATAKANAHIRAGANA = 93 -KEY_MUHENKAN = 94 -KEY_KPJPCOMMA = 95 -KEY_KPENTER = 96 -KEY_RIGHTCTRL = 97 -KEY_KPSLASH = 98 -KEY_SYSRQ = 99 -KEY_RIGHTALT = 100 -KEY_LINEFEED = 101 -KEY_HOME = 102 -KEY_UP = 103 -KEY_PAGEUP = 104 -KEY_LEFT = 105 -KEY_RIGHT = 106 -KEY_END = 107 -KEY_DOWN = 108 -KEY_PAGEDOWN = 109 -KEY_INSERT = 110 -KEY_DELETE = 111 -KEY_MACRO = 112 -KEY_MUTE = 113 -KEY_VOLUMEDOWN = 114 -KEY_VOLUMEUP = 115 -KEY_POWER = 116 -KEY_KPEQUAL = 117 -KEY_KPPLUSMINUS = 118 -KEY_PAUSE = 119 - -KEY_KPCOMMA = 121 -KEY_HANGUEL = 122 -KEY_HANJA = 123 -KEY_YEN = 124 -KEY_LEFTMETA = 125 -KEY_RIGHTMETA = 126 -KEY_COMPOSE = 127 - -KEY_STOP = 128 -KEY_AGAIN = 129 -KEY_PROPS = 130 -KEY_UNDO = 131 -KEY_FRONT = 132 -KEY_COPY = 133 -KEY_OPEN = 134 -KEY_PASTE = 135 -KEY_FIND = 136 -KEY_CUT = 137 -KEY_HELP = 138 -KEY_MENU = 139 -KEY_CALC = 140 -KEY_SETUP = 141 -KEY_SLEEP = 142 -KEY_WAKEUP = 143 -KEY_FILE = 144 -KEY_SENDFILE = 145 -KEY_DELETEFILE = 146 -KEY_XFER = 147 -KEY_PROG1 = 148 -KEY_PROG2 = 149 -KEY_WWW = 150 -KEY_MSDOS = 151 -KEY_COFFEE = 152 -KEY_DIRECTION = 153 -KEY_CYCLEWINDOWS = 154 -KEY_MAIL = 155 -KEY_BOOKMARKS = 156 -KEY_COMPUTER = 157 -KEY_BACK = 158 -KEY_FORWARD = 159 -KEY_CLOSECD = 160 -KEY_EJECTCD = 161 -KEY_EJECTCLOSECD = 162 -KEY_NEXTSONG = 163 -KEY_PLAYPAUSE = 164 -KEY_PREVIOUSSONG = 165 -KEY_STOPCD = 166 -KEY_RECORD = 167 -KEY_REWIND = 168 -KEY_PHONE = 169 -KEY_ISO = 170 -KEY_CONFIG = 171 -KEY_HOMEPAGE = 172 -KEY_REFRESH = 173 -KEY_EXIT = 174 -KEY_MOVE = 175 -KEY_EDIT = 176 -KEY_SCROLLUP = 177 -KEY_SCROLLDOWN = 178 -KEY_KPLEFTPAREN = 179 -KEY_KPRIGHTPAREN = 180 - -KEY_F13 = 183 -KEY_F14 = 184 -KEY_F15 = 185 -KEY_F16 = 186 -KEY_F17 = 187 -KEY_F18 = 188 -KEY_F19 = 189 -KEY_F20 = 190 -KEY_F21 = 191 -KEY_F22 = 192 -KEY_F23 = 193 -KEY_F24 = 194 - -KEY_PLAYCD = 200 -KEY_PAUSECD = 201 -KEY_PROG3 = 202 -KEY_PROG4 = 203 -KEY_SUSPEND = 205 -KEY_CLOSE = 206 -KEY_PLAY = 207 -KEY_FASTFORWARD = 208 -KEY_BASSBOOST = 209 -KEY_PRINT = 210 -KEY_HP = 211 -KEY_CAMERA = 212 -KEY_SOUND = 213 -KEY_QUESTION = 214 -KEY_EMAIL = 215 -KEY_CHAT = 216 -KEY_SEARCH = 217 -KEY_CONNECT = 218 -KEY_FINANCE = 219 -KEY_SPORT = 220 -KEY_SHOP = 221 -KEY_ALTERASE = 222 -KEY_CANCEL = 223 -KEY_BRIGHTNESSDOWN = 224 -KEY_BRIGHTNESSUP = 225 -KEY_MEDIA = 226 - -KEY_UNKNOWN = 240 - -BTN_MISC = 0x100 -BTN_0 = 0x100 -BTN_1 = 0x101 -BTN_2 = 0x102 -BTN_3 = 0x103 -BTN_4 = 0x104 -BTN_5 = 0x105 -BTN_6 = 0x106 -BTN_7 = 0x107 -BTN_8 = 0x108 -BTN_9 = 0x109 - -BTN_MOUSE = 0x110 -BTN_LEFT = 0x110 -BTN_RIGHT = 0x111 -BTN_MIDDLE = 0x112 -BTN_SIDE = 0x113 -BTN_EXTRA = 0x114 -BTN_FORWARD = 0x115 -BTN_BACK = 0x116 -BTN_TASK = 0x117 - -BTN_JOYSTICK = 0x120 -BTN_TRIGGER = 0x120 -BTN_THUMB = 0x121 -BTN_THUMB2 = 0x122 -BTN_TOP = 0x123 -BTN_TOP2 = 0x124 -BTN_PINKIE = 0x125 -BTN_BASE = 0x126 -BTN_BASE2 = 0x127 -BTN_BASE3 = 0x128 -BTN_BASE4 = 0x129 -BTN_BASE5 = 0x12a -BTN_BASE6 = 0x12b -BTN_DEAD = 0x12f - -BTN_GAMEPAD = 0x130 -BTN_A = 0x130 -BTN_B = 0x131 -BTN_C = 0x132 -BTN_X = 0x133 -BTN_Y = 0x134 -BTN_Z = 0x135 -BTN_TL = 0x136 -BTN_TR = 0x137 -BTN_TL2 = 0x138 -BTN_TR2 = 0x139 -BTN_SELECT = 0x13a -BTN_START = 0x13b -BTN_MODE = 0x13c -BTN_THUMBL = 0x13d -BTN_THUMBR = 0x13e - -BTN_DIGI = 0x140 -BTN_TOOL_PEN = 0x140 -BTN_TOOL_RUBBER = 0x141 -BTN_TOOL_BRUSH = 0x142 -BTN_TOOL_PENCIL = 0x143 -BTN_TOOL_AIRBRUSH = 0x144 -BTN_TOOL_FINGER = 0x145 -BTN_TOOL_MOUSE = 0x146 -BTN_TOOL_LENS = 0x147 -BTN_TOUCH = 0x14a -BTN_STYLUS = 0x14b -BTN_STYLUS2 = 0x14c -BTN_TOOL_DOUBLETAP = 0x14d -BTN_TOOL_TRIPLETAP = 0x14e - -BTN_WHEEL = 0x150 -BTN_GEAR_DOWN = 0x150 -BTN_GEAR_UP = 0x151 - -KEY_OK = 0x160 -KEY_SELECT = 0x161 -KEY_GOTO = 0x162 -KEY_CLEAR = 0x163 -KEY_POWER2 = 0x164 -KEY_OPTION = 0x165 -KEY_INFO = 0x166 -KEY_TIME = 0x167 -KEY_VENDOR = 0x168 -KEY_ARCHIVE = 0x169 -KEY_PROGRAM = 0x16a -KEY_CHANNEL = 0x16b -KEY_FAVORITES = 0x16c -KEY_EPG = 0x16d -KEY_PVR = 0x16e -KEY_MHP = 0x16f -KEY_LANGUAGE = 0x170 -KEY_TITLE = 0x171 -KEY_SUBTITLE = 0x172 -KEY_ANGLE = 0x173 -KEY_ZOOM = 0x174 -KEY_MODE = 0x175 -KEY_KEYBOARD = 0x176 -KEY_SCREEN = 0x177 -KEY_PC = 0x178 -KEY_TV = 0x179 -KEY_TV2 = 0x17a -KEY_VCR = 0x17b -KEY_VCR2 = 0x17c -KEY_SAT = 0x17d -KEY_SAT2 = 0x17e -KEY_CD = 0x17f -KEY_TAPE = 0x180 -KEY_RADIO = 0x181 -KEY_TUNER = 0x182 -KEY_PLAYER = 0x183 -KEY_TEXT = 0x184 -KEY_DVD = 0x185 -KEY_AUX = 0x186 -KEY_MP3 = 0x187 -KEY_AUDIO = 0x188 -KEY_VIDEO = 0x189 -KEY_DIRECTORY = 0x18a -KEY_LIST = 0x18b -KEY_MEMO = 0x18c -KEY_CALENDAR = 0x18d -KEY_RED = 0x18e -KEY_GREEN = 0x18f -KEY_YELLOW = 0x190 -KEY_BLUE = 0x191 -KEY_CHANNELUP = 0x192 -KEY_CHANNELDOWN = 0x193 -KEY_FIRST = 0x194 -KEY_LAST = 0x195 -KEY_AB = 0x196 -KEY_NEXT = 0x197 -KEY_RESTART = 0x198 -KEY_SLOW = 0x199 -KEY_SHUFFLE = 0x19a -KEY_BREAK = 0x19b -KEY_PREVIOUS = 0x19c -KEY_DIGITS = 0x19d -KEY_TEEN = 0x19e -KEY_TWEN = 0x19f - -KEY_DEL_EOL = 0x1c0 -KEY_DEL_EOS = 0x1c1 -KEY_INS_LINE = 0x1c2 -KEY_DEL_LINE = 0x1c3 - -KEY_FN = 0x1d0 -KEY_FN_ESC = 0x1d1 -KEY_FN_F1 = 0x1d2 -KEY_FN_F2 = 0x1d3 -KEY_FN_F3 = 0x1d4 -KEY_FN_F4 = 0x1d5 -KEY_FN_F5 = 0x1d6 -KEY_FN_F6 = 0x1d7 -KEY_FN_F7 = 0x1d8 -KEY_FN_F8 = 0x1d9 -KEY_FN_F9 = 0x1da -KEY_FN_F10 = 0x1db -KEY_FN_F11 = 0x1dc -KEY_FN_F12 = 0x1dd -KEY_FN_1 = 0x1de -KEY_FN_2 = 0x1df -KEY_FN_D = 0x1e0 -KEY_FN_E = 0x1e1 -KEY_FN_F = 0x1e2 -KEY_FN_S = 0x1e3 -KEY_FN_B = 0x1e4 - -KEY_MAX = 0x1ff diff --git a/src/py/inevent/__init__.py b/src/py/inevent/__init__.py deleted file mode 100644 index 5e6780d..0000000 --- a/src/py/inevent/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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. - -from .InEvent import InEvent -from .JogHandler import JogHandler diff --git a/src/py/inevent/ioctl.py b/src/py/inevent/ioctl.py deleted file mode 100644 index 652ef68..0000000 --- a/src/py/inevent/ioctl.py +++ /dev/null @@ -1,128 +0,0 @@ -################################################################################ -# # -# 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 . # -# # -# 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 # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - -# 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. -# -# IOCTL macros -# -# ioctl command encoding: 32 bits total, command in lower 16 bits, -# size of the parameter structure in the lower 14 bits of the -# upper 16 bits. -# -# Encoding the size of the parameter structure in the ioctl request -# is useful for catching programs compiled with old versions -# and to avoid overwriting user space outside the user buffer area. -# The highest 2 bits are reserved for indicating the ``access mode''. -# NOTE: This limits the max parameter size to 16kB - 1 -# -# The following is for compatibility across the various Linux -# platforms. The generic ioctl numbering scheme doesn't really enforce -# a type field. De facto, however, the top 8 bits of the lower 16 -# bits are indeed used as a type field, so we might just as well make -# this explicit here. - -import struct - - -sizeof = struct.calcsize - -_IOC_NRBITS = 8 -_IOC_TYPEBITS = 8 -_IOC_SIZEBITS = 14 -_IOC_DIRBITS = 2 - -_IOC_NRMASK = (1 << _IOC_NRBITS) - 1 -_IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 -_IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 -_IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 - -_IOC_NRSHIFT = 0 -_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS -_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS -_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS - -_IOC_NONE = 0 -_IOC_WRITE = 1 -_IOC_READ = 2 -_IOC_RW = _IOC_READ | _IOC_WRITE - - -def _IOC(dir, type, nr, size): - return int( - (dir << _IOC_DIRSHIFT) | - (type << _IOC_TYPESHIFT) | - (nr << _IOC_NRSHIFT) | - (size << _IOC_SIZESHIFT)) - -# encode ioctl numbers -def _IO(type, nr): return _IOC(_IOC_NONE, type, nr, 0) -def _IOR(type, nr, fmt): return _IOC(_IOC_READ, type, nr, sizeof(fmt)) -def _IOW(type, nr, fmt): return _IOC(_IOC_WRITE, type, nr, sizeof(fmt)) -def _IOWR(type, nr, fmt): return _IOC(_IOC_RW, type, nr, sizeof(fmt)) -def _IOR_BAD(type, nr, fmt): return _IOC(_IOC_READ, type, nr, sizeof(fmt)) -def _IOW_BAD(type, nr, fmt): return _IOC(_IOC_WRITE, type, nr, sizeof(fmt)) -def _IOWR_BAD(type, nr, fmt): return _IOC(_IOC_RW, type, nr, sizeof(fmt)) - -# decode ioctl numbers -def _IOC_DIR(nr): return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK -def _IOC_TYPE(nr): return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK -def _IOC_NR(nr): return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK -def _IOC_SIZE(nr): return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK - -# for drivers/sound files -IOC_IN = _IOC_WRITE << _IOC_DIRSHIFT -IOC_OUT = _IOC_READ << _IOC_DIRSHIFT -IOC_INOUT = _IOC_RW << _IOC_DIRSHIFT -IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT -IOCSIZE_SHIFT = _IOC_SIZESHIFT -