My quick &dirty solution - probably developers us this to add an official version
I created a new file remoteinput.py and added the following lines into __init__.py
try:
import kivy.input.providers.remoteinput
except:
err = 'Input: RemoteInput is not supported by your version of linux'
Logger.exception(err)
here the remoteinput.py
<code>
# coding utf-8
'''
Native support for remotes input from the linux kernel
==================================================
Support starts from 2.6.32-ubuntu, or 2.6.34.
To configure remoteinput, add this to your configuration::
[input]
# devicename = remoteinput,/dev/input/eventXX
# example with remote at Raspberry GPIO
remote = remoteinput,/dev/input/event5
.. note::
You must have read access to the input event.
'''
import os
from kivy.input.motionevent import MotionEvent
__all__ = ('RemoteInputMotionEventProvider', 'RemoteMotionEvent')
# late imports
Window = None
Keyboard = None
class RemoteMotionEvent(MotionEvent):
pass
if 'KIVY_DOC' in os.environ:
# documentation hack
RemoteInputMotionEventProvider = None
else:
import threading
import collections
import struct
import fcntl
from kivy.input.provider import MotionEventProvider
from kivy.input.factory import MotionEventFactory
from kivy.logger import Logger
#
# This part is taken from linux-source-2.6.32/include/linux/input.h
#
# value types
EV_UP = 0x00
EV_DN = 0x01
EV_REP = 0x02
# Event types
EV_SYN = 0x00
EV_REM = 0x04
EVIOCGNAME =
2147501318 # if a button shows the warning: 'not recognized by SDL' this is causes by the KEY_ name (e.g. KEY_SETUP) occurs because SDL tries to process IR-codes directly. The button still works here - this are the codes from the ir-keytable .toml file
remote_buttons = {
0x3b0c: 'KEY_POWER',
0x3b58: 'KEY_UP',
0x3b59: 'KEY_DOWN',
0x3b5a: 'KEY_LEFT',
0x3b5b: 'KEY_RIGHT',
0x3b92: 'KEY_HOME',
0x3b5c: 'KEY_OK',
0x3b69: 'KEY_SEARCH',
0x3b19: 'KEY_FAVORITES',
0x3b2c: 'KEY_PLAYPAUSE',
0x3b20: 'KEY_NEXT',
0x3b83: 'KEY_BACK',
0x3b21: 'KEY_PREVIOUS',
0x3b0d: 'KEY_MUTE',
0x3b31: 'KEY_STOP',
0x3b10: 'KEY_VOLUMEUP',
0x3b11: 'KEY_VOLUMEDOWN',
0x3b00: 'KEY_0',
0x3b01: 'KEY_1',
0x3b02: 'KEY_2',
0x3b03: 'KEY_3',
0x3b04: 'KEY_4',
0x3b05: 'KEY_5',
0x3b06: 'KEY_6',
0x3b07: 'KEY_7',
0x3b08: 'KEY_8',
0x3b09: 'KEY_9',
0x3b16: 'KEY_SCROLLUP',
0x3b17: 'KEY_SCROLLDOWN',
0x3b94: 'KEY_LIBRARY',
0x3b15: 'KEY_CLEAR',
0x3b47: 'KEY_SLEEP',
0x3b1d: 'KEY_REPEAT',
0x3b1c: 'KEY_SHUFFLE',
0x3b98: 'KEY_BRIGHTNESS', # press may show warning 'not recognized by SDL' but works
0x3b1b: 'KEY_RADIO',
0x3b4b: 'KEY_AUX',
0x3b1a: 'KEY_ONLINE_SERVICES',
0x3ba5: 'KEY_SNOOZE',
0x3be6: 'KEY_CLOCK',
0x3b4c: 'KEY_DOCKING',
0x3b54: 'KEY_SETUP', # press may show warning 'not recognized by SDL' but works
0x3b6d: 'KEY_ALARM',
0x3ba4: 'KEY_NOW_PLAYING',
0x3b8c: 'KEY_CAPSLOCK',
0x3b53: 'KEY_SOUND_MENU',
0x3ba6: 'KEY_LIVINGSOUND',
0x3ba7: 'KEY_FULLSOUND',
0x3baa: 'KEY_NEUTRAL',
}
# sizeof(struct input_event)
struct_input_event_sz = struct.calcsize('LLHHi')
struct_input_absinfo_sz = struct.calcsize('iiiiii')
sz_l = struct.calcsize('Q')
class RemoteInputMotionEventProvider(MotionEventProvider): #HIDInput
remote_code = 0
def __init__(self, device, args):
super(RemoteInputMotionEventProvider, self).__init__(device, args)
global Window, Keyboard
if Window is None:
from kivy.core.window import Window
if Keyboard is None:
from kivy.core.window import Keyboard
for code in remote_buttons:
Keyboard.keycodes.update({remote_buttons[code].lower(): code}) # add the remote buttons and codes into keycodes list
self.input_fn = None
self.default_ranges = dict()
# split arguments
args = args.split(',')
if not args:
Logger.error('RemoteInput: Filename missing in configuration')
Logger.error('RemoteInput: Use /dev/input/event0 for example')
return None
# read filename
self.input_fn = args[0]
Logger.info('RemoteInput: Read event from <%s>' % self.input_fn)
# read parameters
for arg in args[1:]:
if arg == '':
continue
arg = arg.split('=')
# ensure it's a key = value
if len(arg) != 2:
Logger.error('RemoteInput: invalid parameter '
'%s, not in key=value format.' % arg)
continue
# ensure the key exist
key, value = arg
if key not in HIDInputMotionEventProvider.options:
Logger.error('RemoteInput: unknown %s option' % key)
continue
# ensure the value
try:
self.default_ranges[key] = int(value)
except ValueError:
err = 'RemoteInput: invalid value "%s" for "%s"' % (
key, value)
Logger.error(err)
continue
# all good!
Logger.info('RemoteInput: Set custom %s to %d' % (
key, int(value)))
def start(self):
if self.input_fn is None:
return
self.uid = 0
self.queue = collections.deque()
self.dispatch_queue = []
self.thread = threading.Thread(
name=self.__class__.__name__,
target=self._thread_run,
kwargs=dict(
queue=self.queue,
input_fn=self.input_fn,
device=self.device))
self.thread.daemon = True
self.thread.start()
def _thread_run(self, **kwargs):
input_fn = kwargs.get('input_fn')
queue = self.queue
dispatch_queue = self.dispatch_queue
device = kwargs.get('device')
def process_as_remote(tv_sec, tv_usec, ev_type, ev_code, ev_value):
if ev_type == EV_SYN:
return
if ev_type == EV_REM: # first event delivers the ir-key-code
self.remote_code = ev_value
if ev_value not in remote_buttons:
Logger.warn('RemoteInput: unhandled remote code: {}'.format(ev_code))
return
if not 0 <= ev_value <= 2: # we can have value 2 for press&hold button
return
z = remote_buttons[self.remote_code]
if z.lower() not in Keyboard.keycodes:
# or if it is not in this LUT
Logger.warn('RemoteInput: unhandled button: {}'.
format(z))
return
keycode = Keyboard.keycodes[z.lower()]
if ev_value == EV_DN: # button down
dispatch_queue.append(('key_down', (keycode, ev_code, z.lower() ,[])))
elif ev_value == EV_UP:
dispatch_queue.append(('key_up', (keycode, ev_code, z.lower() ,[])))
elif ev_value == EV_REP: # for key hold pressed (does not work - see below)
dispatch_queue.append(('key_down', (keycode, ev_code, z.lower() ,[]))) # repeat key_down (key-repeat throws error:
# kivy._event.EventDispatcher.dispatchKeyError: 'on_key_repeat'
# open the input
fd = open(input_fn, 'rb')
# get the controller name (EVIOCGNAME)
device_name = fcntl.ioctl(fd, EVIOCGNAME + (256 << 16),
" " * 256).decode().strip()
Logger.info('RemoteInput: using <%s>' % device_name)
# read until the end
while fd:
data = fd.read(struct_input_event_sz)
if len(data) < struct_input_event_sz:
break
# extract each event
for i in range(int(len(data) / struct_input_event_sz)):
ev = data[i * struct_input_event_sz:]
# extract timeval + event infos
infos = struct.unpack('LLHHi', ev[:struct_input_event_sz])
process_as_remote(*infos)
def update(self,dispatch_fn):
# dispatch all events from threads
dispatch_queue = self.dispatch_queue
n = len(dispatch_queue)
for name, args in dispatch_queue[:n]:
if name == 'key_down':
if not Window.dispatch('on_key_down', *args):
Window.dispatch('on_keyboard', *args)
elif name == 'key_up':
Window.dispatch('on_key_up', *args)
elif name == 'key_repeat': # as said above - repeat does not work, however we see multiple key_down messages (without key_up in between)
Window.dispatch('on_key_repeat', *args)
del dispatch_queue[:n]
try:
while True:
event_type, touch = self.queue.popleft()
dispatch_fn(event_type, touch)
except:
pass
MotionEventFactory.register('remoteinput', RemoteInputMotionEventProvider)
</code>