def event_loop(configs, joy_map, tty_fd): event = SDL_Event() # keep of dict of active joystick devices as a dict of # instance_id -> (config_id, SDL_Joystick object) active_devices = {} # keep an event queue populated with the current active inputs # indexed by joystick index, input type and input index # the values consist of: # - the event list (as taked from the event configuration) # - the number of times event was emitted (repeated) # - the last time when the event was fired # e.g. { event_hash -> ([event_list], repeat_no, last_fire_time) } event_queue = {} # keep track of axis previous values axis_prev_values = {} def handle_new_input(e: SDL_Event, axis_norm_value: int = 0) -> bool: """ Event handling for button press/hat movement/axis movement Only needed when an new input is present Returns True when 'event_queue' is modified with a new event """ dev_index = active_devices[event.jdevice.which][0] if e.type == SDL_JOYBUTTONDOWN: mapped_events = configs[dev_index].get_btn_event( event.jbutton.button) event_index = f'{dev_index}_btn{event.jbutton.button}' elif e.type == SDL_JOYHATMOTION: mapped_events = configs[dev_index].get_hat_event( event.jhat.hat, event.jhat.value) event_index = f'{dev_index}_hat{event.jhat.hat}' elif e.type == SDL_JOYAXISMOTION and axis_norm_value != 0: mapped_events = configs[dev_index].get_axis_event( event.jaxis.axis, axis_norm_value) event_index = f'{dev_index}_axis{event.jaxis.axis}' if mapped_events is not None: event_queue[event_index] = [mapped_events, 0, SDL_GetTicks()] return True return False running = True while running: input_started = False while SDL_PollEvent(byref(event)): if event.type == SDL_QUIT: running = False break if event.type == SDL_JOYDEVICEADDED: stick = joystick.SDL_JoystickOpen(event.jdevice.which) name = joystick.SDL_JoystickName(stick).decode('utf-8') guid = create_string_buffer(33) joystick.SDL_JoystickGetGUIDString( joystick.SDL_JoystickGetGUID(stick), guid, 33) LOG.debug( f'Joystick #{joystick.SDL_JoystickInstanceID(stick)} {name} added' ) conf_found = False # try to find a configuration for the joystick for key, dev_conf in enumerate(configs): if dev_conf.name == str( name) or dev_conf.guid == guid.value.decode(): # Add the matching joystick configuration to the watched list active_devices[joystick.SDL_JoystickInstanceID( stick)] = (key, stick) LOG.debug( f'Added configuration for known device {configs[key]}' ) conf_found = True break # add the default configuration for unknown/un-configured joysticks if not conf_found: LOG.debug( f'Un-configured device "{str(name)}", mapped using generic mapping' ) active_devices[joystick.SDL_JoystickInstanceID(stick)] = ( 0, stick) # if the device has axis inputs, initialize to zero their initial position if joystick.SDL_JoystickNumAxes(stick) > 0: axis_prev_values[joystick.SDL_JoystickInstanceID( stick)] = [ 0 for x in range(joystick.SDL_JoystickNumAxes(stick)) ] continue if event.jdevice.which not in active_devices: continue else: dev_index = active_devices[event.jdevice.which][0] if event.type == SDL_JOYDEVICEREMOVED: joystick.SDL_JoystickClose( active_devices[event.jdevice.which][1]) active_devices.pop(event.jdevice.which, None) axis_prev_values.pop(event.jdevice.which, None) LOG.debug(f'Removed joystick #{event.jdevice.which}') if event.type == SDL_JOYBUTTONDOWN: input_started = handle_new_input(event) if event.type == SDL_JOYBUTTONUP: event_queue.pop(f'{dev_index}_btn{event.jbutton.button}', None) if event.type == SDL_JOYHATMOTION: if event.jhat.value != SDL_HAT_CENTERED: input_started = handle_new_input(event) else: event_queue.pop(f'{dev_index}_hat{event.jhat.hat}', None) if event.type == SDL_JOYAXISMOTION: # check if the axis value went over the deadzone threshold if (abs(event.jaxis.value) > JS_AXIS_DEADZONE) \ != (abs(axis_prev_values[event.jdevice.which][event.jaxis.axis]) > JS_AXIS_DEADZONE): # normalize the axis value to the movement direction or stop the input if abs(event.jaxis.value) <= JS_AXIS_DEADZONE: event_queue.pop(f'{dev_index}_axis{event.jaxis.axis}', None) else: if event.jaxis.value < 0: axis_norm_value = -1 else: axis_norm_value = 1 input_started = handle_new_input( event, axis_norm_value) # store the axis current values for tracking axis_prev_values[event.jdevice.which][ event.jaxis.axis] = event.jaxis.value # process the current events in the queue if len(event_queue): emitted_events = filter_active_events(event_queue) if len(emitted_events): LOG.debug(f'Events emitted: {emitted_events}') # send the events mapped key code(s) to the terminal for k in emitted_events: if k in joy_map: for c in joy_map[k]: fcntl.ioctl(tty_fd, termios.TIOCSTI, c) SDL_Delay(JS_POLL_DELAY)
def test_SDL_JoystickInstanceID(self): for index in range(self.jcount): stick = joystick.SDL_JoystickOpen(index) ret = joystick.SDL_JoystickInstanceID(stick) assert ret > 0 joystick.SDL_JoystickClose(stick)