from .g19_keys import (Data, Key)
|
|
from .runnable import Runnable
|
|
|
|
import threading
|
|
import time
|
|
|
|
class InputProcessor(object):
|
|
'''Object to process key presses.'''
|
|
|
|
def process_input(self, inputEvent):
|
|
'''Processes given event.
|
|
|
|
Should return as fast as possible. Any time-consuming processing
|
|
should be done in another thread.
|
|
|
|
@param inputEvent Event to process.
|
|
@return True if event was consumed, or False if ignored.
|
|
|
|
'''
|
|
return False
|
|
|
|
|
|
class InputEvent(object):
|
|
'''Event created by a key press or release.'''
|
|
|
|
def __init__(self, oldState, newState, keysDown, keysUp):
|
|
'''Creates an InputEvent.
|
|
|
|
@param oldState State before event happened.
|
|
@param newState State after event happened.
|
|
@param keysDown Keys newly pressed.
|
|
@param keysUp Kys released by this event.
|
|
|
|
'''
|
|
self.oldState = oldState
|
|
self.newState = newState
|
|
self.keysDown = keysDown
|
|
self.keysUp = keysUp
|
|
|
|
|
|
class State(object):
|
|
'''Current state of keyboard.'''
|
|
|
|
def __init__(self):
|
|
self.__keysDown = set()
|
|
|
|
def _data_to_keys_g_and_m(self, data):
|
|
'''Converts a G/M keys data package to a set of keys defined as
|
|
pressed by it.
|
|
|
|
'''
|
|
if len(data) != 4 or data[0] != 2:
|
|
raise ValueError("not a multimedia key packet: " + str(data))
|
|
empty = 0x400000
|
|
curVal = data[3] << 16 | data[2] << 8 | data[1]
|
|
keys = []
|
|
while curVal != empty:
|
|
foundAKey = False
|
|
for val in Data.gmKeys.keys():
|
|
if val & curVal == val:
|
|
curVal ^= val
|
|
keys.append(Data.gmKeys[val])
|
|
foundAKey = True
|
|
if not foundAKey:
|
|
raise ValueError("incorrect g/m key packet: " +
|
|
str(data))
|
|
|
|
return set(keys)
|
|
|
|
def _data_to_keys_mm(self, data):
|
|
'''Converts a multimedia keys data package to a set of keys defined as
|
|
pressed by it.
|
|
|
|
'''
|
|
if len(data) != 2 or data[0] not in [1, 3]:
|
|
raise ValueError("not a multimedia key packet: " + str(data))
|
|
if data[0] == 1:
|
|
curVal = data[1]
|
|
keys = []
|
|
while curVal:
|
|
foundAKey = False
|
|
for val in Data.mmKeys.keys():
|
|
if val & curVal == val:
|
|
curVal ^= val
|
|
keys.append(Data.mmKeys[val])
|
|
foundAKey = True
|
|
if not foundAKey:
|
|
raise ValueError("incorrect multimedia key packet: " +
|
|
str(data))
|
|
elif data == [3, 1]:
|
|
keys = [Key.WINKEY_SWITCH]
|
|
elif data == [3, 0]:
|
|
keys = []
|
|
else:
|
|
raise ValueError("incorrect multimedia key packet: " + str(data))
|
|
|
|
return set(keys)
|
|
|
|
def _update_keys_down(self, possibleKeys, keys):
|
|
'''Updates internal keysDown set.
|
|
|
|
Updates the current state of all keys in 'possibleKeys' with state
|
|
given in 'keys'.
|
|
|
|
Example:
|
|
Currently set as pressed in self.__keysDown: [A, B]
|
|
possibleKeys: [B, C, D]
|
|
keys: [C]
|
|
|
|
This would set self.__keysDown to [A, C] and return ([C], [B])
|
|
|
|
@param possibleKeys Keys whose state could be given as 'pressed' at the
|
|
same time by 'keys'.
|
|
@param keys Current state of all keys in possibleKeys.
|
|
@return A pair of sets listing newly pressed and newly released keys.
|
|
|
|
'''
|
|
keysDown = set()
|
|
keysUp = set()
|
|
for key in possibleKeys:
|
|
if key in keys:
|
|
if key not in self.__keysDown:
|
|
self.__keysDown.add(key)
|
|
keysDown.add(key)
|
|
else:
|
|
if key in self.__keysDown:
|
|
self.__keysDown.remove(key)
|
|
keysUp.add(key)
|
|
return (keysDown, keysUp)
|
|
|
|
def clone(self):
|
|
'''Returns an exact copy of this state.'''
|
|
state = State()
|
|
state.__keysDown = set(self.__keysDown)
|
|
return state
|
|
|
|
def packet_received_g_and_m(self, data):
|
|
'''Mutates the state by given data packet from G- and M- keys.
|
|
|
|
@param data Data packet received.
|
|
@return InputEvent for data packet, or None if data packet was ignored.
|
|
|
|
'''
|
|
oldState = self.clone()
|
|
evt = None
|
|
if len(data) == 4:
|
|
keys = self._data_to_keys_g_and_m(data)
|
|
keysDown, keysUp = self._update_keys_down(Key.gmKeys, keys)
|
|
newState = self.clone()
|
|
evt = InputEvent(oldState, newState, keysDown, keysUp)
|
|
return evt
|
|
|
|
def packet_received_mm(self, data):
|
|
'''Mutates the state by given data packet from multimedia keys.
|
|
|
|
@param data Data packet received.
|
|
@return InputEvent for data packet.
|
|
|
|
'''
|
|
oldState = self.clone()
|
|
if len(data) != 2:
|
|
raise ValueError("incorrect multimedia key packet: " + str(data))
|
|
keys = self._data_to_keys_mm(data)
|
|
winKeySet = set([Key.WINKEY_SWITCH])
|
|
if data[0] == 1:
|
|
# update state of all mm keys
|
|
possibleKeys = Key.mmKeys.difference(winKeySet)
|
|
keysDown, keysUp = self._update_keys_down(possibleKeys, keys)
|
|
else:
|
|
# update winkey state
|
|
keysDown, keysUp = self._update_keys_down(winKeySet, keys)
|
|
newState = self.clone()
|
|
return InputEvent(oldState, newState, keysDown, keysUp)
|
|
|
|
|
|
class G19Receiver(Runnable):
|
|
'''This receiver consumes all data sent by special keys.'''
|
|
|
|
def __init__(self, g19):
|
|
Runnable.__init__(self)
|
|
self.__g19 = g19
|
|
self.__ips = []
|
|
self.__mutex = threading.Lock()
|
|
self.__state = State()
|
|
|
|
def add_input_processor(self, processor):
|
|
'''Adds an input processor.'''
|
|
self.__mutex.acquire()
|
|
self.__ips.append(processor)
|
|
self.__mutex.release()
|
|
pass
|
|
|
|
def execute(self):
|
|
gotData = False
|
|
processors = self.list_all_input_processors()
|
|
|
|
data = self.__g19.read_multimedia_keys()
|
|
if data:
|
|
evt = self.__state.packet_received_mm(data)
|
|
if evt:
|
|
for proc in processors:
|
|
if proc.process_input(evt):
|
|
break
|
|
else:
|
|
print("mm ignored: ", data)
|
|
gotData = True
|
|
|
|
data = self.__g19.read_g_and_m_keys()
|
|
if data:
|
|
evt = self.__state.packet_received_g_and_m(data)
|
|
if evt:
|
|
for proc in processors:
|
|
if proc.process_input(evt):
|
|
break
|
|
else:
|
|
print("m/g ignored: ", data)
|
|
gotData = True
|
|
|
|
data = self.__g19.read_display_menu_keys()
|
|
if data:
|
|
print("dis: ", data)
|
|
gotData = True
|
|
|
|
if not gotData:
|
|
time.sleep(0.03)
|
|
|
|
def list_all_input_processors(self):
|
|
'''Returns a list of all input processors currently registered to this
|
|
receiver.
|
|
|
|
@return All registered processors. This list is a copy of the internal
|
|
one.
|
|
|
|
'''
|
|
self.__mutex.acquire()
|
|
allProcessors = list(self.__ips)
|
|
self.__mutex.release()
|
|
return allProcessors
|