fenrir/src/fenrirscreenreader/inputDriver/evdevDriver.py

395 lines
17 KiB
Python
Raw Normal View History

#!/bin/python
# -*- coding: utf-8 -*-
# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributers.
_evdevAvailable = False
_udevAvailable = False
_evdevAvailableError = ''
_udevAvailableError = ''
try:
import evdev
from evdev import InputDevice, UInput
_evdevAvailable = True
except Exception as e:
_evdevAvailableError = str(e)
try:
import pyudev
_udevAvailable = True
except Exception as e:
_udevAvailableError = str(e)
import time
from select import select
import multiprocessing
from multiprocessing.sharedctypes import Value
from ctypes import c_bool
from fenrirscreenreader.core.eventData import fenrirEventType
from fenrirscreenreader.core import inputData
from fenrirscreenreader.core import debug
from fenrirscreenreader.core.inputDriver import inputDriver
2018-03-28 11:08:15 -04:00
class driver(inputDriver):
def __init__(self):
inputDriver.__init__(self)
self._manager = multiprocessing.Manager()
self.iDevices = {}
self.iDevicesFD = self._manager.list()
self.uDevices = {}
self.gDevices = {}
self.iDeviceNo = 0
self.watchDog = Value(c_bool, True)
def initialize(self, environment):
self.env = environment
2018-03-26 03:10:05 -04:00
self.env['runtime']['inputManager'].setShortcutType('KEY')
global _evdevAvailable
global _udevAvailable
2018-05-23 09:00:56 -04:00
self._initialized = _evdevAvailable and _udevAvailable
if not self._initialized:
global _evdevAvailableError
2018-05-23 09:00:56 -04:00
global _udevAvailableError
currError = ' '
if not _evdevAvailable:
currError += _evdevAvailableError
if not _udevAvailable:
currError += ' ' + _udevAvailableError
self.env['runtime']['debug'].writeDebugOut('InputDriver:' + currError, debug.debugLevel.ERROR)
return
2018-05-22 19:12:31 -04:00
2018-05-23 09:00:56 -04:00
self.env['runtime']['processManager'].addCustomEventThread(self.plugInputDeviceWatchdogUdev)
self.env['runtime']['processManager'].addCustomEventThread(self.inputWatchdog)
def plugInputDeviceWatchdogUdev(self,active , eventQueue):
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
2018-05-23 08:44:15 -04:00
#monitor.filter_by(subsystem='input')
monitor.start()
while active.value:
2018-05-18 05:40:05 -04:00
validDevices = []
2018-05-23 08:51:35 -04:00
device = monitor.poll(1)
2018-05-16 18:13:21 -04:00
while device:
try:
if not '/sys/devices/virtual/input/' in device.sys_path:
2018-05-20 01:43:26 -04:00
if device.device_node:
2018-05-23 08:51:35 -04:00
validDevices.append(str(device.device_node))
2018-05-16 18:13:21 -04:00
except:
2018-05-13 15:46:51 -04:00
pass
2018-05-23 09:00:56 -04:00
try:
device = monitor.poll(0.1)
except:
device = None
2018-05-18 05:40:05 -04:00
if validDevices:
eventQueue.put({"Type":fenrirEventType.PlugInputDevice,"Data":validDevices})
return time.time()
def plugInputDeviceWatchdogTimer(self, active):
2018-03-28 12:11:03 -04:00
time.sleep(10)
2018-05-18 05:54:43 -04:00
return None
def inputWatchdog(self,active , eventQueue):
2018-04-15 09:43:03 -04:00
try:
while active.value:
2018-05-16 19:01:17 -04:00
r, w, x = select(self.iDevices, [], [], 0.7)
2018-04-15 09:43:03 -04:00
for fd in r:
event = None
foreward = False
eventFired = False
try:
event = self.iDevices[fd].read_one()
except:
self.removeDevice(fd)
while(event):
self.env['input']['eventBuffer'].append( [self.iDevices[fd], self.uDevices[fd], event])
if event.type == evdev.events.EV_KEY:
if event.code != 0:
currMapEvent = self.mapEvent(event)
if not currMapEvent:
foreward = True
if not isinstance(currMapEvent['EventName'], str):
foreward = True
if not foreward or eventFired:
if currMapEvent['EventState'] in [0,1,2]:
eventQueue.put({"Type":fenrirEventType.KeyboardInput,"Data":currMapEvent.copy()})
eventFired = True
else:
if not event.type in [0,4]:
foreward = True
event = self.iDevices[fd].read_one()
if foreward and not eventFired:
self.writeEventBuffer()
self.clearEventBuffer()
except Exception as e:
self.env['runtime']['debug'].writeDebugOut("INPUT WATCHDOG CRASH: "+str(e),debug.debugLevel.ERROR)
def handleInputEvent(self, event):
return
def writeEventBuffer(self):
if not self._initialized:
return
for iDevice, uDevice, event in self.env['input']['eventBuffer']:
try:
if uDevice:
if self.gDevices[iDevice.fd]:
self.writeUInput(uDevice, event)
except Exception as e:
pass
def writeUInput(self, uDevice, event):
if not self._initialized:
return
uDevice.write_event(event)
uDevice.syn()
2018-05-18 05:49:43 -04:00
def updateInputDevices(self, newDevices = None, init = False):
if init:
self.removeAllDevices()
2018-05-20 01:43:26 -04:00
2018-05-18 07:54:08 -04:00
deviceFileList = None
2018-05-20 01:43:26 -04:00
2018-05-18 05:49:43 -04:00
if newDevices and not init:
deviceFileList = newDevices
else:
deviceFileList = evdev.list_devices()
if len(deviceFileList) == self.iDeviceNo:
2018-05-18 07:54:08 -04:00
return
if not deviceFileList:
return
2018-05-20 01:43:26 -04:00
mode = self.env['runtime']['settingsManager'].getSetting('keyboard', 'device').upper()
2018-05-18 07:54:08 -04:00
iDevicesFiles = []
for device in self.iDevices:
iDevicesFiles.append(self.iDevices[device].fn)
2018-05-18 05:49:43 -04:00
eventType = evdev.events
for deviceFile in deviceFileList:
try:
2018-05-21 15:26:43 -04:00
self.env['runtime']['debug'].writeDebugOut('loop start',debug.debugLevel.ERROR)
2018-05-18 05:49:43 -04:00
if not deviceFile:
continue
if deviceFile == '':
continue
if deviceFile in iDevicesFiles:
2018-05-20 01:43:26 -04:00
continue
2018-05-21 15:26:43 -04:00
self.env['runtime']['debug'].writeDebugOut('open(deviceFile)',debug.debugLevel.ERROR)
try:
2018-05-23 09:49:08 -04:00
with open(deviceFile) as f:
pass
except Exception as e:
self.env['runtime']['debug'].writeDebugOut("Not readable Inputdevice : " + deviceFile +' ' + str(e),debug.debugLevel.ERROR)
continue
# 3 pos absolute
# 2 pos relative
# 1 Keys
2018-05-21 15:26:43 -04:00
self.env['runtime']['debug'].writeDebugOut('currDevice = evdev.InputDevice(deviceFile)',debug.debugLevel.ERROR)
2018-05-20 01:43:26 -04:00
try:
currDevice = evdev.InputDevice(deviceFile)
except:
continue
2018-05-21 15:26:43 -04:00
self.env['runtime']['debug'].writeDebugOut('naming',debug.debugLevel.ERROR)
2018-05-20 01:43:26 -04:00
try:
if currDevice.name.upper() in ['','SPEAKUP','PY-EVDEV-UINPUT']:
continue
if currDevice.phys.upper() in ['','SPEAKUP','PY-EVDEV-UINPUT']:
continue
if 'BRLTTY' in currDevice.name.upper():
2018-05-21 17:14:40 -04:00
continue
self.env['runtime']['debug'].writeDebugOut('loaded name:'+ str(currDevice.name),debug.debugLevel.ERROR)
2018-05-20 01:43:26 -04:00
except:
pass
2018-05-21 15:26:43 -04:00
self.env['runtime']['debug'].writeDebugOut('cap = currDevice.capabilities()',debug.debugLevel.ERROR)
cap = currDevice.capabilities()
2018-05-21 15:26:43 -04:00
self.env['runtime']['debug'].writeDebugOut('cap = currDevice.capabilities() fin',debug.debugLevel.ERROR)
if mode in ['ALL','NOMICE']:
if eventType.EV_KEY in cap:
2018-05-21 17:14:40 -04:00
self.env['runtime']['debug'].writeDebugOut('eventType.EV_KEY in cap NoOfCaps: '+ str(cap) ,debug.debugLevel.ERROR)
if 116 in cap[eventType.EV_KEY] and len(cap[eventType.EV_KEY]) < 10:
self.env['runtime']['debug'].writeDebugOut('Device Skipped (has 116):' + currDevice.name,debug.debugLevel.INFO)
continue
2018-05-13 15:27:29 -04:00
if len(cap[eventType.EV_KEY]) < 60:
self.env['runtime']['debug'].writeDebugOut('Device Skipped (< 60 keys):' + currDevice.name,debug.debugLevel.INFO)
2018-05-20 01:43:26 -04:00
continue
if mode == 'ALL':
2018-05-14 14:03:39 -04:00
self.addDevice(currDevice)
self.env['runtime']['debug'].writeDebugOut('Device added (ALL):' + self.iDevices[currDevice.fd].name, debug.debugLevel.INFO)
elif mode == 'NOMICE':
if not ((eventType.EV_REL in cap) or (eventType.EV_ABS in cap)):
2018-05-14 14:03:39 -04:00
self.addDevice(currDevice)
self.env['runtime']['debug'].writeDebugOut('Device added (NOMICE):' + self.iDevices[currDevice.fd].name,debug.debugLevel.INFO)
else:
2018-05-22 12:55:03 -04:00
self.env['runtime']['debug'].writeDebugOut('Device Skipped (NOMICE):' + currDevice.name,debug.debugLevel.INFO)
else:
self.env['runtime']['debug'].writeDebugOut('Device Skipped (no EV_KEY):' + currDevice.name,debug.debugLevel.INFO)
elif currDevice.name.upper() in mode.split(','):
2018-05-14 14:03:39 -04:00
self.addDevice(currDevice)
2018-05-21 15:26:43 -04:00
self.env['runtime']['debug'].writeDebugOut('Device added (Name):' + self.iDevices[currDevice.fd].name,debug.debugLevel.INFO)
self.env['runtime']['debug'].writeDebugOut('loop end',debug.debugLevel.ERROR)
except Exception as e:
2018-05-20 01:43:26 -04:00
self.env['runtime']['debug'].writeDebugOut("Device Skipped (Exception): " + deviceFile +' ' + currDevice.name +' '+ str(e),debug.debugLevel.INFO)
self.iDeviceNo = len(evdev.list_devices())
self.updateMPiDevicesFD()
2018-05-20 01:43:26 -04:00
def updateMPiDevicesFD(self):
2018-05-16 17:40:36 -04:00
try:
for fd in self.iDevices:
if not fd in self.iDevicesFD:
self.iDevicesFD.append(fd)
for fd in self.iDevicesFD:
if not fd in self.iDevices:
self.iDevicesFD.remove(fd)
except:
pass
def mapEvent(self, event):
if not self._initialized:
return None
if not event:
return None
mEvent = inputData.inputEvent
try:
mEvent['EventName'] = evdev.ecodes.keys[event.code]
mEvent['EventValue'] = event.code
mEvent['EventSec'] = event.sec
mEvent['EventUsec'] = event.usec
mEvent['EventState'] = event.value
mEvent['EventType'] = event.type
return mEvent
except Exception as e:
return None
def getLedState(self, led = 0):
if not self.hasIDevices():
return False
# 0 = Numlock
# 1 = Capslock
# 2 = Rollen
for fd, dev in self.iDevices.items():
if led in dev.leds():
return True
return False
def toggleLedState(self, led = 0):
if not self.hasIDevices():
return False
ledState = self.getLedState(led)
for i in self.iDevices:
# 17 LEDs
if 17 in self.iDevices[i].capabilities():
if ledState == 1:
self.iDevices[i].set_led(led , 0)
else:
self.iDevices[i].set_led(led , 1)
def grabAllDevices(self):
if not self._initialized:
return
for fd in self.iDevices:
2018-05-16 18:24:57 -04:00
self.grabDevice(fd)
2018-05-11 16:21:30 -04:00
def ungrabAllDevices(self):
if not self._initialized:
2018-05-22 15:42:57 -04:00
return
2018-05-11 16:21:30 -04:00
for fd in self.iDevices:
2018-05-13 17:16:22 -04:00
self.ungrabDevice(fd)
2018-05-16 18:24:57 -04:00
2018-05-14 08:13:29 -04:00
def createUInputDev(self, fd):
if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'):
self.uDevices[fd] = None
return
2018-05-14 08:13:29 -04:00
try:
test = self.uDevices[fd]
return
except KeyError:
2018-05-14 14:03:39 -04:00
self.uDevices[fd] = None
2018-05-14 08:26:57 -04:00
if self.uDevices[fd] != None:
2018-05-14 08:13:29 -04:00
return
try:
2018-05-11 16:17:38 -04:00
self.uDevices[fd] = UInput.from_device(self.iDevices[fd])
except Exception as e:
try:
self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: compat fallback: ' + str(e),debug.debugLevel.WARNING)
dev = self.iDevices[fd]
cap = dev.capabilities()
del cap[0]
self.uDevices[fd] = UInput(
cap,
dev.name,
)
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: init Uinput not possible: ' + str(e),debug.debugLevel.ERROR)
2018-05-14 08:13:29 -04:00
return
2018-05-14 14:20:28 -04:00
def addDevice(self, newDevice):
2018-05-16 18:52:35 -04:00
self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: device added: ' + str(newDevice.fd) + ' ' +str(newDevice),debug.debugLevel.INFO)
2018-05-16 17:40:36 -04:00
self.iDevices[newDevice.fd] = newDevice
self.gDevices[newDevice.fd] = False
2018-05-14 14:20:28 -04:00
self.createUInputDev(newDevice.fd)
def grabDevice(self, fd):
if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'):
return
try:
self.iDevices[fd].grab()
2018-05-22 15:42:57 -04:00
self.gDevices[fd] = True
self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: grab device ('+ str(self.iDevices[fd].name) + ')',debug.debugLevel.INFO)
2018-05-17 15:57:54 -04:00
except IOError:
self.gDevices[fd] = True
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: grabing not possible: ' + str(e),debug.debugLevel.ERROR)
2018-05-13 17:16:22 -04:00
def ungrabDevice(self,fd):
2018-05-11 16:21:30 -04:00
if not self.env['runtime']['settingsManager'].getSettingAsBool('keyboard', 'grabDevices'):
return
try:
2018-05-14 14:03:39 -04:00
self.gDevices[fd] = False
2018-05-22 15:42:57 -04:00
self.iDevices[fd].ungrab()
self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: ungrab device ('+ str(self.iDevices[fd].name) + ')',debug.debugLevel.INFO)
2018-05-11 16:21:30 -04:00
except:
pass
def removeDevice(self,fd):
2018-05-16 18:52:35 -04:00
self.env['runtime']['debug'].writeDebugOut('InputDriver evdev: device removed: ' + str(fd) + ' ' +str(self.iDevices[fd]),debug.debugLevel.INFO)
self.clearEventBuffer()
try:
2018-05-13 17:16:22 -04:00
self.ungrabDevice(fd)
except:
pass
try:
self.iDevices[fd].close()
except:
pass
try:
self.uDevices[fd].close()
except:
pass
try:
del(self.iDevices[fd])
except:
pass
try:
del(self.uDevices[fd])
except:
pass
try:
del(self.gDevices[fd])
except:
pass
2018-05-14 14:03:39 -04:00
self.updateMPiDevicesFD()
def hasIDevices(self):
if not self._initialized:
return False
if not self.iDevices:
return False
if len(self.iDevices) == 0:
return False
return True
def removeAllDevices(self):
if not self.hasIDevices():
return
devices = self.iDevices.copy()
for fd in devices:
self.removeDevice(fd)
self.iDevices.clear()
self.uDevices.clear()
2018-05-16 17:40:36 -04:00
self.gDevices.clear()
self.iDeviceNo = 0