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()