package dev.isxander.controlify.controller.joystick; import com.google.gson.Gson; import com.google.gson.JsonElement; import dev.isxander.controlify.bindings.ControllerBindings; import dev.isxander.controlify.controller.AbstractController; import dev.isxander.controlify.hid.ControllerHIDService; import dev.isxander.controlify.controller.joystick.mapping.RPJoystickMapping; import dev.isxander.controlify.controller.joystick.mapping.JoystickMapping; import dev.isxander.controlify.controller.sdl2.SDL2NativesManager; import dev.isxander.controlify.rumble.RumbleManager; import dev.isxander.controlify.rumble.RumbleSource; import dev.isxander.controlify.utils.Log; import org.libsdl.SDL; import java.util.Objects; public class SingleJoystickController extends AbstractController implements JoystickController { private JoystickState state = JoystickState.EMPTY, prevState = JoystickState.EMPTY; private final JoystickMapping mapping; private final long ptrJoystick; private RumbleManager rumbleManager; private boolean rumbleSupported; public SingleJoystickController(int joystickId, ControllerHIDService.ControllerHIDInfo hidInfo) { super(joystickId, hidInfo); this.mapping = Objects.requireNonNull(RPJoystickMapping.fromType(this)); this.config = new JoystickConfig(this); this.defaultConfig = new JoystickConfig(this); this.ptrJoystick = SDL2NativesManager.isLoaded() ? SDL.SDL_JoystickOpen(joystickId) : 0; this.rumbleSupported = SDL2NativesManager.isLoaded() && SDL.SDL_JoystickHasRumble(this.ptrJoystick); this.rumbleManager = new RumbleManager(this); this.bindings = new ControllerBindings<>(this); this.config.validateRadialActions(bindings); this.defaultConfig.validateRadialActions(bindings); } @Override public JoystickState state() { return state; } @Override public JoystickState prevState() { return prevState; } @Override public void updateState() { prevState = state; state = JoystickState.fromJoystick(this, joystickId); } @Override public void clearState() { this.state = JoystickState.empty(this); } @Override public JoystickMapping mapping() { return mapping; } @Override public int axisCount() { return mapping().axes().length; } @Override public int buttonCount() { return mapping.buttons().length; } @Override public int hatCount() { return mapping.hats().length; } @Override public int joystickId() { return joystickId; } @Override public void setConfig(Gson gson, JsonElement json) { super.setConfig(gson, json); this.config.setup(this); } @Override public boolean setRumble(float strongMagnitude, float weakMagnitude) { if (!supportsRumble()) return false; // the duration doesn't matter because we are not updating the joystick state, // so there is never any SDL check to stop the rumble after the desired time. if (!SDL.SDL_JoystickRumbleTriggers(ptrJoystick, (int)(strongMagnitude * 65535.0F), (int)(weakMagnitude * 65535.0F), 1)) { Log.LOGGER.error("Could not rumble controller " + name() + ": " + SDL.SDL_GetError()); return false; } return true; } @Override public boolean supportsRumble() { return rumbleSupported; } @Override public RumbleManager rumbleManager() { return this.rumbleManager; } @Override public void close() { if (ptrJoystick != 0) SDL.SDL_JoystickClose(ptrJoystick); this.rumbleSupported = false; this.rumbleManager = null; } }