def clear_joystick_events(self, joy=None): """Clear and Return the current joystick events. Args: joy (Joystick) [None]: Joystick to clear events for. If None clear and return all joysticks. Returns: events (dict): Event dictionary of {joystick: {'events': {}, 'buttons': []}} """ with self.event_lock: if joy is None: events = self.joystick_events.copy() for joy in self.joystick_events: self.joystick_events[joy] = { 'events': OrderedDict(), 'buttons': Stash() } else: events = { joy: self.joystick_events.get(joy, { 'events': OrderedDict(), 'buttons': Stash() }) } self.joystick_events[joy] = { 'events': OrderedDict(), 'buttons': Stash() } return events
def __init__(self, event_loop=None, add_joystick=None, remove_joystick=None, handle_key_event=None, alive=None, button_repeater=None, activity_timeout=1 / 30): super().__init__() if alive is None: alive = threading.Event() self.activity_timeout = activity_timeout self._button_repeater = None self.event_loop = event_loop self.joysticks = Stash() self.alive = alive self.proc = None self.worker = None self.event_lock = threading.RLock() self.joystick_events = {} if add_joystick is not None: self.add_joystick = add_joystick if remove_joystick is not None: self.remove_joystick = remove_joystick if handle_key_event is not None: self.handle_key_event = handle_key_event if button_repeater is not None: self.set_button_repeater(button_repeater)
def __getstate__(self): return {'activity_timeout': self.activity_timeout, 'button_repeater': self.button_repeater, 'event_loop': self.event_loop, 'joysticks': Stash(), 'alive': self.alive, 'proc': None, 'worker': None, 'event_buttons': Stash(), 'event_latest': {}, }
def get_joysticks(cls): # Check init if not get_init(): init() return Stash(cls(i) for i in range( sdl2.SDL_NumJoysticks())) # Use identifier not instance id.
def get_joysticks(cls): """Return a list of available joysticks.""" # Check init if not get_init(): init() return Stash(cls(i) for i in range(pygame.joystick.get_count()))
def test_stash(): from pyjoystick.stash import Stash class CustomObject(object): def __init__(self, identifier, index, value): self.identifier = identifier self.index = index self.value = value def __eq__(self, other): if isinstance(other, CustomObject): return super().__eq__(other) return self.identifier == other li = Stash((CustomObject('{}.{}'.format(i, j), j, k) for i in range(3) for j in range(3) for k in range(3))) assert '0.0' in li assert li[0] == li['0.0'], 'Get from index does not match get from __eq__!' # Test pop item = li.pop('0.0') assert item.identifier == '0.0' assert item.index == 0 assert item.value == 0 try: li.pop(50000000) raise AssertionError( 'remove function did not raise a KeyError when not exists!') except KeyError: pass assert li.pop( 50000000, 'Default Value' ) == 'Default Value', 'pop function did not return a default value!' # Test remove item = li.remove('1.0') assert item.identifier == '1.0' assert item.index == 0 assert item.value == 0 try: li.remove(50000000) raise AssertionError( 'remove function did not raise a ValueError when not exists!') except ValueError: pass
def run_event_loop(add_joystick, remove_joystick, handle_key_event, alive=None, refresh_timeout=2, **kwargs): """Main function to run and handle events. Args: add_joystick (callable/function): Called when a new Joystick is found! remove_joystick (callable/function): Called when a Joystick is removed! handle_key_event (callable/function): Called when a new key event occurs! alive (callable/function)[None]: Function to return True to continue running. If None run forever refresh_timeout (int)[2]: Timeout for when to refresh the joysticks. """ if alive is None: alive = lambda: True joysticks = Stash() last_refresh = 0 # For disconnect and reconnect if not get_init(): init() # pygame.event.set_grab(True) while alive(): try: # Refresh timeout if (time.time() - last_refresh) > refresh_timeout: joysticks, new, removed = refresh_joysticks(joysticks) for joy in removed.values(): remove_joystick(joy) for joy in new.values(): add_joystick(joy) last_refresh = time.time() events = get_events() if len(events) > 0: # Check events for event in events: # pygame.event.get(pump=True) key = key_from_event(event) if key is not None: handle_key_event(key) else: time.sleep(0.01) except: pass
def refresh_joysticks(joysticks=None): """Refresh the joystick list and return a list of all of the joysticks, new joysticks, and removed joysticks. Args: joysticks (list/Stash): List of pygame.joystick.Joystick objects. Returns: sticks (list): List of all pygame.joystick.Joystick objects. new (list): List of new joysticks where the names were not in the given list. removed (list): List of removed joysticks that were in the given list of joysticks, but were not found. """ if joysticks is None: joysticks = [] sticks = Stash() new = {} removed = {} with PYGAME_LOCK: pygame.joystick.quit() pygame.joystick.init() for i in range(pygame.joystick.get_count()): joy = Joystick(i) name = joy.get_name() if name in joysticks: sticks.append(joysticks[name]) else: sticks.append(joy) new[name] = joy for joy in joysticks: name = joy.get_name() if name not in sticks: removed[name] = joy return sticks, new, removed
class ThreadEventManager(object): JOYSTICK_PROXY = None def __init__(self, event_loop=None, add_joystick=None, remove_joystick=None, handle_key_event=None, alive=None, button_repeater=None, activity_timeout=1 / 30): super().__init__() if alive is None: alive = threading.Event() self.activity_timeout = activity_timeout self._button_repeater = None self.event_loop = event_loop self.joysticks = Stash() self.alive = alive self.proc = None self.worker = None self.event_lock = threading.RLock() self.joystick_events = {} if add_joystick is not None: self.add_joystick = add_joystick if remove_joystick is not None: self.remove_joystick = remove_joystick if handle_key_event is not None: self.handle_key_event = handle_key_event if button_repeater is not None: self.set_button_repeater(button_repeater) def add_joystick(self, joy): """Save the added joystick.""" pass def remove_joystick(self, joy): """Remove the given joystick.""" pass def handle_key_event(self, key): """Function to handle key event happens""" pass def get_button_repeater(self): """Return the button repeater.""" return self._button_repeater def set_button_repeater(self, value): """Set the button repeater.""" self._button_repeater = value try: self._button_repeater.key_repeated = self._update_key_event except: pass button_repeater = property(get_button_repeater, set_button_repeater) def clear_joystick_events(self, joy=None): """Clear and Return the current joystick events. Args: joy (Joystick) [None]: Joystick to clear events for. If None clear and return all joysticks. Returns: events (dict): Event dictionary of {joystick: {'events': {}, 'buttons': []}} """ with self.event_lock: if joy is None: events = self.joystick_events.copy() for joy in self.joystick_events: self.joystick_events[joy] = { 'events': OrderedDict(), 'buttons': Stash() } else: events = { joy: self.joystick_events.get(joy, { 'events': OrderedDict(), 'buttons': Stash() }) } self.joystick_events[joy] = { 'events': OrderedDict(), 'buttons': Stash() } return events def save_joystick(self, joy): """Save the added joystick.""" if self.JOYSTICK_PROXY: joy = self.JOYSTICK_PROXY(joy) self.joysticks.append(joy) # Event handlers self.clear_joystick_events(joy) # Run the callback handler self.add_joystick(joy) def delete_joystick(self, joy): """Delete the removed joystick.""" try: self.joysticks.remove(joy) except: pass # Run the callback handler self.remove_joystick(joy) try: self.clear_joystick_events(joy) except: pass def save_key_event(self, key): """Save the initial key event.""" try: joy = self.joysticks[key.joystick] key.joystick = joy except: pass # Calculate deadband after a static joystick was set if key.keytype == key.AXIS: dead = getattr(key.joystick, 'deadband', 0) if dead: key.value = deadband(key.value, dead) # Check if axis is still at 0 (No change due to deadband) to reduce number of events try: if key.value == 0 and key.joystick.get_axis(key.number) == 0: return except (AttributeError, IndexError, Exception): pass try: self.button_repeater.set(key) except: pass self._update_key_event(key) def _update_key_event(self, key): """Update the event list from the key event.""" value = key.value joystick = key.joystick with self.event_lock: try: joystick.update_key(key) except: pass if key.keytype == key.AXIS: self.joystick_events[joystick]['events'][key] = value else: self.joystick_events[joystick]['buttons'].append(key) def process_events(self): """Process all of the saved events.""" events = self.clear_joystick_events() for joystick, items in events.items(): for key, value in items['events'].items(): key.value = value # Value needs to be updated for the key. The key is only used as hash self.handle_key_event(key) for key in items['buttons']: self.handle_key_event(key) @contextlib.contextmanager def run_during(self): """Context manager to temporarily run the manager if the manager is not already running.""" if not self.is_running(): self.start() yield self.stop() else: yield def wait(self, conditional=None, timeout=float('inf'), sleep_func=None): """Wait for the given timeout or conditional function to return false. Args: conditional (callable)[None]: Callable that returns True to keep waiting. If raises error stop waiting. timeout (float/int)[float('inf')]: Time for when to stop waiting. sleep_func (callable)[None]: How to sleep and wait (ex. time.sleep(0.001)). If raises error stop waiting. """ if sleep_func is None: sleep_func = lambda: time.sleep(0.01) if conditional is None: conditional = lambda: True elif not callable(conditional): conditional = lambda: conditional start = time.time() try: while (time.time() - start) < timeout and conditional(): sleep_func() except (ValueError, TypeError, Exception): # If any error occurs stop waiting. pass def find_key(self, joysticks=None, key_types=None, timeout=float("inf"), sleep_func=None): """Wait and return the next key that is pressed. Args: joysticks (list/Joystick)[None]: Joystick(s) to allow events for. key_types (list/KeyTypes)[None]: List of key type. Found with Key.KeyTypes.Axis timeout (float/int)[float('inf')]: Timeout to wait. sleep_func (callable)[None]: How to sleep and wait (time.sleep(0.01)). Returns: key (Key)[None]: If found return the first Key else return None. """ if joysticks is None: joysticks = [] elif not isinstance(joysticks, list): try: joysticks = list(joysticks) # convert tuple to list except (TypeError, Exception): joysticks = [joysticks] # Put a single object in a list if key_types is None: key_types = [] data = {'found key': None} def filter_find_key(key): is_joystick = len(joysticks) == 0 or key.joystick in joysticks is_key_type = len(key_types) == 0 or key.has_keytype( key.keytype, key_types) is_valid_value = abs(key.value) > 0.5 if is_joystick and is_key_type and is_valid_value and data[ 'found key'] is None: data['found key'] = key def is_not_found(): return data['found key'] is None # Change the event handler old_handle_key_event, self.handle_key_event = self.handle_key_event, filter_find_key with self.run_during(): self.wait(is_not_found, timeout, sleep_func) # Reset event handler self.handle_key_event = old_handle_key_event return data['found key'] def run(self, event_loop, add_joystick, remove_joystick, handle_key_event, alive=None, button_repeater=None): """Run the an event loop to process SDL Events. Args: event_loop (callable/function): Event loop function to run. add_joystick (callable/function): Called when a new Joystick is found! remove_joystick (callable/function): Called when a Joystick is removed! handle_key_event (callable/function): Called when a new key event occurs! alive (callable/function)[None]: Function to return True to continue running. If None run forever button_repeater (ButtonRepeater): Thread to start which will monitor button keys and trigger repeating. """ if alive is None: alive = lambda: True if button_repeater is not None: button_repeater.start() event_loop(add_joystick, remove_joystick, handle_key_event, alive=alive) def is_running(self): """Return if the event loop is running.""" return self.alive.is_set() def start(self): """Start running the event loop.""" self.stop() self.alive.set() self.proc = threading.Thread(target=self.run, args=(self.event_loop, self.save_joystick, self.delete_joystick, self.save_key_event), kwargs={ 'alive': self.is_running, 'button_repeater': self.button_repeater }, name='pyjoystick-ThreadEventManager') self.proc.daemon = True self.proc.start() self.worker = PeriodicThread(self.activity_timeout, self.process_events, name='pyjoystick-process_events') self.worker.alive = self.alive # stop when this event stops self.worker.daemon = True self.worker.start() return self def stop(self): """Stop running the event loop.""" try: self.alive.clear() except: pass try: self.button_repeater.stop() except (AttributeError, Exception): pass try: # Try pumping an event on queue to break out of the wait condition self.event_loop.stop_event_wait() except: pass try: self.proc.join(0) except: pass self.proc = None try: self.worker.join(0) except: pass self.worker = None try: self.clear_joystick_events() except: pass return self def __enter__(self): if not self.is_running(): self.start() return self def __exit__(self, exc_type, exc_val, exc_tb): self.stop() return exc_type is None def __getstate__(self): return { 'activity_timeout': self.activity_timeout, 'button_repeater': self.button_repeater, 'event_loop': self.event_loop, 'joysticks': Stash(), 'alive': self.alive, 'proc': None, 'worker': None, 'event_buttons': Stash(), 'event_latest': {}, } def __setstate__(self, state): for k, v in state.items(): setattr(self, k, v) if getattr(self, 'event_lock', None) is None: self.event_lock = threading.RLock()