예제 #1
0
    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
예제 #2
0
    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)
예제 #3
0
 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': {},
             }
예제 #4
0
    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.
예제 #5
0
    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()))
예제 #6
0
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
예제 #7
0
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
예제 #8
0
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
예제 #9
0
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()