forked from Clones/Controlify
Merge remote-tracking branch 'origin/feature/drivers' into 1.19.x/dev
# Conflicts: # src/main/java/dev/isxander/controlify/controller/gamepad/GamepadController.java
This commit is contained in:
@ -341,11 +341,7 @@ public class Controlify implements ControlifyApi {
|
|||||||
|
|
||||||
if (this.currentController == controller) return;
|
if (this.currentController == controller) return;
|
||||||
|
|
||||||
if (this.currentController != null)
|
|
||||||
this.currentController.close();
|
|
||||||
|
|
||||||
this.currentController = controller;
|
this.currentController = controller;
|
||||||
this.currentController.open();
|
|
||||||
|
|
||||||
if (switchableController == controller) {
|
if (switchableController == controller) {
|
||||||
switchableController = null;
|
switchableController = null;
|
||||||
|
@ -25,7 +25,7 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
|
|||||||
private final String guid;
|
private final String guid;
|
||||||
private final ControllerType type;
|
private final ControllerType type;
|
||||||
|
|
||||||
private final ControllerBindings<S> bindings;
|
protected ControllerBindings<S> bindings;
|
||||||
protected C config, defaultConfig;
|
protected C config, defaultConfig;
|
||||||
|
|
||||||
public AbstractController(int joystickId, ControllerHIDService.ControllerHIDInfo hidInfo) {
|
public AbstractController(int joystickId, ControllerHIDService.ControllerHIDInfo hidInfo) {
|
||||||
@ -37,7 +37,7 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
|
|||||||
this.joystickId = joystickId;
|
this.joystickId = joystickId;
|
||||||
this.guid = GLFW.glfwGetJoystickGUID(joystickId);
|
this.guid = GLFW.glfwGetJoystickGUID(joystickId);
|
||||||
|
|
||||||
if (hidInfo.path().isPresent()) {
|
if (hidInfo.hidDevice().isPresent()) {
|
||||||
this.uid = hidInfo.createControllerUID().orElseThrow();
|
this.uid = hidInfo.createControllerUID().orElseThrow();
|
||||||
this.type = hidInfo.type();
|
this.type = hidInfo.type();
|
||||||
} else {
|
} else {
|
||||||
@ -48,8 +48,6 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
|
|||||||
var joystickName = GLFW.glfwGetJoystickName(joystickId);
|
var joystickName = GLFW.glfwGetJoystickName(joystickId);
|
||||||
String name = type != ControllerType.UNKNOWN || joystickName == null ? type.friendlyName() : joystickName;
|
String name = type != ControllerType.UNKNOWN || joystickName == null ? type.friendlyName() : joystickName;
|
||||||
setName(name);
|
setName(name);
|
||||||
|
|
||||||
this.bindings = new ControllerBindings<>(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String name() {
|
public String name() {
|
||||||
|
@ -15,6 +15,7 @@ import dev.isxander.controlify.utils.DebugLog;
|
|||||||
import net.minecraft.CrashReport;
|
import net.minecraft.CrashReport;
|
||||||
import net.minecraft.CrashReportCategory;
|
import net.minecraft.CrashReportCategory;
|
||||||
import net.minecraft.ReportedException;
|
import net.minecraft.ReportedException;
|
||||||
|
import org.hid4java.HidDevice;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -42,7 +43,6 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
|
|||||||
void updateState();
|
void updateState();
|
||||||
void clearState();
|
void clearState();
|
||||||
|
|
||||||
default void open() {}
|
|
||||||
default void close() {}
|
default void close() {}
|
||||||
RumbleManager rumbleManager();
|
RumbleManager rumbleManager();
|
||||||
|
|
||||||
@ -78,12 +78,13 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
|
|||||||
CrashReportCategory category = crashReport.addCategory("Controller Info");
|
CrashReportCategory category = crashReport.addCategory("Controller Info");
|
||||||
category.setDetail("Joystick ID", joystickId);
|
category.setDetail("Joystick ID", joystickId);
|
||||||
category.setDetail("Controller identification", hidInfo.type());
|
category.setDetail("Controller identification", hidInfo.type());
|
||||||
category.setDetail("HID path", hidInfo.path().orElse("N/A"));
|
category.setDetail("HID path", hidInfo.hidDevice().map(HidDevice::getPath).orElse("N/A"));
|
||||||
throw new ReportedException(crashReport);
|
throw new ReportedException(crashReport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove(Controller<?, ?> controller) {
|
static void remove(Controller<?, ?> controller) {
|
||||||
|
controller.close();
|
||||||
CONTROLLERS.remove(controller.uid(), controller);
|
CONTROLLERS.remove(controller.uid(), controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
package dev.isxander.controlify.controller.gamepad;
|
package dev.isxander.controlify.controller.gamepad;
|
||||||
|
|
||||||
import dev.isxander.controlify.Controlify;
|
|
||||||
import dev.isxander.controlify.InputMode;
|
import dev.isxander.controlify.InputMode;
|
||||||
import dev.isxander.controlify.api.ControlifyApi;
|
import dev.isxander.controlify.api.ControlifyApi;
|
||||||
|
import dev.isxander.controlify.bindings.ControllerBindings;
|
||||||
import dev.isxander.controlify.controller.AbstractController;
|
import dev.isxander.controlify.controller.AbstractController;
|
||||||
import dev.isxander.controlify.controller.hid.ControllerHIDService;
|
import dev.isxander.controlify.controller.hid.ControllerHIDService;
|
||||||
import dev.isxander.controlify.controller.sdl2.SDL2NativesManager;
|
import dev.isxander.controlify.driver.*;
|
||||||
import dev.isxander.controlify.debug.DebugProperties;
|
|
||||||
import dev.isxander.controlify.rumble.RumbleManager;
|
import dev.isxander.controlify.rumble.RumbleManager;
|
||||||
import dev.isxander.controlify.rumble.RumbleSource;
|
import dev.isxander.controlify.rumble.RumbleSource;
|
||||||
import org.libsdl.SDL;
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
import org.lwjgl.glfw.GLFWGamepadState;
|
import org.lwjgl.glfw.GLFWGamepadState;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class GamepadController extends AbstractController<GamepadState, GamepadConfig> {
|
public class GamepadController extends AbstractController<GamepadState, GamepadConfig> {
|
||||||
private GamepadState state = GamepadState.EMPTY;
|
private GamepadState state = GamepadState.EMPTY;
|
||||||
private GamepadState prevState = GamepadState.EMPTY;
|
private GamepadState prevState = GamepadState.EMPTY;
|
||||||
|
|
||||||
private long gamepadPtr;
|
|
||||||
private boolean rumbleSupported, triggerRumbleSupported;
|
|
||||||
private final RumbleManager rumbleManager;
|
private final RumbleManager rumbleManager;
|
||||||
private boolean hasGyro;
|
|
||||||
private GamepadState.GyroState absoluteGyro = GamepadState.GyroState.ORIGIN;
|
private GamepadState.GyroState absoluteGyro = GamepadState.GyroState.ORIGIN;
|
||||||
|
|
||||||
|
private final GamepadDrivers drivers;
|
||||||
|
private final Set<Driver> uniqueDrivers;
|
||||||
|
|
||||||
public GamepadController(int joystickId, ControllerHIDService.ControllerHIDInfo hidInfo) {
|
public GamepadController(int joystickId, ControllerHIDService.ControllerHIDInfo hidInfo) {
|
||||||
super(joystickId, hidInfo);
|
super(joystickId, hidInfo);
|
||||||
if (!GLFW.glfwJoystickIsGamepad(joystickId))
|
if (!GLFW.glfwJoystickIsGamepad(joystickId))
|
||||||
@ -31,10 +31,15 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
|||||||
if (!this.name.startsWith(type().friendlyName()))
|
if (!this.name.startsWith(type().friendlyName()))
|
||||||
setName(GLFW.glfwGetGamepadName(joystickId));
|
setName(GLFW.glfwGetGamepadName(joystickId));
|
||||||
|
|
||||||
|
this.drivers = GamepadDrivers.forController(joystickId, hidInfo.hidDevice());
|
||||||
|
this.uniqueDrivers = drivers.getUniqueDrivers();
|
||||||
|
|
||||||
this.rumbleManager = new RumbleManager(this);
|
this.rumbleManager = new RumbleManager(this);
|
||||||
|
|
||||||
this.defaultConfig = new GamepadConfig();
|
this.defaultConfig = new GamepadConfig();
|
||||||
this.config = new GamepadConfig();
|
this.config = new GamepadConfig();
|
||||||
|
|
||||||
|
this.bindings = new ControllerBindings<>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,23 +56,16 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
|||||||
public void updateState() {
|
public void updateState() {
|
||||||
prevState = state;
|
prevState = state;
|
||||||
|
|
||||||
GamepadState.AxesState rawAxesState = GamepadState.AxesState.fromController(this);
|
uniqueDrivers.forEach(Driver::update);
|
||||||
GamepadState.AxesState axesState = rawAxesState
|
|
||||||
|
BasicGamepadInputDriver.BasicGamepadState basicState = drivers.basicGamepadInputDriver().getBasicGamepadState();
|
||||||
|
GamepadState.AxesState deadzoneAxesState = basicState.axes()
|
||||||
.leftJoystickDeadZone(config().leftStickDeadzoneX, config().leftStickDeadzoneY)
|
.leftJoystickDeadZone(config().leftStickDeadzoneX, config().leftStickDeadzoneY)
|
||||||
.rightJoystickDeadZone(config().rightStickDeadzoneX, config().rightStickDeadzoneY);
|
.rightJoystickDeadZone(config().rightStickDeadzoneX, config().rightStickDeadzoneY);
|
||||||
GamepadState.ButtonState buttonState = GamepadState.ButtonState.fromController(this);
|
|
||||||
|
|
||||||
GamepadState.GyroState gyroDelta = null;
|
GamepadState.GyroState gyroState = drivers.gyroDriver().getGyroState();
|
||||||
if (this.hasGyro) {
|
|
||||||
float[] gyro = new float[3];
|
|
||||||
SDL.SDL_GameControllerGetSensorData(gamepadPtr, SDL.SDL_SENSOR_GYRO, gyro, 3);
|
|
||||||
gyroDelta = new GamepadState.GyroState(gyro[0], gyro[1], gyro[2]);
|
|
||||||
if (DebugProperties.PRINT_GYRO) Controlify.LOGGER.info("Gyro delta: " + gyroDelta);
|
|
||||||
absoluteGyro = absoluteGyro.add(gyroDelta);
|
|
||||||
}
|
|
||||||
SDL.SDL_GameControllerUpdate();
|
|
||||||
|
|
||||||
state = new GamepadState(axesState, rawAxesState, buttonState, gyroDelta, absoluteGyro);
|
state = new GamepadState(deadzoneAxesState, basicState.axes(), basicState.buttons(), gyroState, absoluteGyro);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GamepadState.GyroState absoluteGyroState() {
|
public GamepadState.GyroState absoluteGyroState() {
|
||||||
@ -75,7 +73,7 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasGyro() {
|
public boolean hasGyro() {
|
||||||
return hasGyro;
|
return drivers.gyroDriver().isGyroSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -104,18 +102,12 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
|||||||
strongMagnitude *= strengthMod;
|
strongMagnitude *= strengthMod;
|
||||||
weakMagnitude *= strengthMod;
|
weakMagnitude *= strengthMod;
|
||||||
|
|
||||||
// the duration doesn't matter because we are not updating the gamecontroller state,
|
return drivers.rumbleDriver().rumble(strongMagnitude, weakMagnitude);
|
||||||
// so there is never any SDL check to stop the rumble after the desired time.
|
|
||||||
if (!SDL.SDL_GameControllerRumble(gamepadPtr, (int)(strongMagnitude * 65535.0F), (int)(weakMagnitude * 65535.0F), 0)) {
|
|
||||||
Controlify.LOGGER.error("Could not rumble controller " + name() + ": " + SDL.SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canRumble() {
|
public boolean canRumble() {
|
||||||
return rumbleSupported
|
return drivers.rumbleDriver().isRumbleSupported()
|
||||||
&& config().allowVibrations
|
&& config().allowVibrations
|
||||||
&& ControlifyApi.get().currentInputMode() == InputMode.CONTROLLER;
|
&& ControlifyApi.get().currentInputMode() == InputMode.CONTROLLER;
|
||||||
}
|
}
|
||||||
@ -125,27 +117,8 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
|
|||||||
return this.rumbleManager;
|
return this.rumbleManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open() {
|
|
||||||
if (SDL2NativesManager.isLoaded()) {
|
|
||||||
this.gamepadPtr = SDL.SDL_GameControllerOpen(joystickId);
|
|
||||||
this.rumbleSupported = SDL.SDL_GameControllerHasRumble(gamepadPtr);
|
|
||||||
this.triggerRumbleSupported = SDL.SDL_GameControllerHasRumble(gamepadPtr);
|
|
||||||
if (this.hasGyro = SDL.SDL_GameControllerHasSensor(gamepadPtr, SDL.SDL_SENSOR_GYRO)) {
|
|
||||||
SDL.SDL_GameControllerSetSensorEnabled(gamepadPtr, SDL.SDL_SENSOR_GYRO, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.gamepadPtr = 0;
|
|
||||||
this.rumbleSupported = false;
|
|
||||||
this.hasGyro = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
SDL.SDL_GameControllerClose(gamepadPtr);
|
uniqueDrivers.forEach(Driver::close);
|
||||||
this.gamepadPtr = 0;
|
|
||||||
this.rumbleSupported = false;
|
|
||||||
this.hasGyro = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,6 @@ public final class GamepadState implements ControllerState {
|
|||||||
"gamepadButtons=" + gamepadButtons + ']';
|
"gamepadButtons=" + gamepadButtons + ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public record AxesState(
|
public record AxesState(
|
||||||
float leftStickX, float leftStickY,
|
float leftStickX, float leftStickY,
|
||||||
float rightStickX, float rightStickY,
|
float rightStickX, float rightStickY,
|
||||||
@ -178,23 +177,6 @@ public final class GamepadState implements ControllerState {
|
|||||||
ControllerUtils.deadzone(rightTrigger, deadZone)
|
ControllerUtils.deadzone(rightTrigger, deadZone)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AxesState fromController(GamepadController controller) {
|
|
||||||
if (controller == null)
|
|
||||||
return EMPTY;
|
|
||||||
|
|
||||||
var state = controller.getGamepadState();
|
|
||||||
var axes = state.axes();
|
|
||||||
|
|
||||||
float leftX = axes.get(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X);
|
|
||||||
float leftY = axes.get(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y);
|
|
||||||
float rightX = axes.get(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X);
|
|
||||||
float rightY = axes.get(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y);
|
|
||||||
float leftTrigger = (axes.get(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER) + 1f) / 2f;
|
|
||||||
float rightTrigger = (axes.get(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER) + 1f) / 2f;
|
|
||||||
|
|
||||||
return new AxesState(leftX, leftY, rightX, rightY, leftTrigger, rightTrigger);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record ButtonState(
|
public record ButtonState(
|
||||||
@ -211,32 +193,6 @@ public final class GamepadState implements ControllerState {
|
|||||||
false, false, false, false,
|
false, false, false, false,
|
||||||
false, false
|
false, false
|
||||||
);
|
);
|
||||||
|
|
||||||
public static ButtonState fromController(GamepadController controller) {
|
|
||||||
if (controller == null)
|
|
||||||
return EMPTY;
|
|
||||||
|
|
||||||
var state = controller.getGamepadState();
|
|
||||||
var buttons = state.buttons();
|
|
||||||
|
|
||||||
boolean a = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_A) == GLFW.GLFW_PRESS;
|
|
||||||
boolean b = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_B) == GLFW.GLFW_PRESS;
|
|
||||||
boolean x = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_X) == GLFW.GLFW_PRESS;
|
|
||||||
boolean y = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_Y) == GLFW.GLFW_PRESS;
|
|
||||||
boolean leftBumper = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER) == GLFW.GLFW_PRESS;
|
|
||||||
boolean rightBumper = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) == GLFW.GLFW_PRESS;
|
|
||||||
boolean back = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_BACK) == GLFW.GLFW_PRESS;
|
|
||||||
boolean start = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_START) == GLFW.GLFW_PRESS;
|
|
||||||
boolean guide = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_GUIDE) == GLFW.GLFW_PRESS;
|
|
||||||
boolean dpadUp = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) == GLFW.GLFW_PRESS;
|
|
||||||
boolean dpadDown = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) == GLFW.GLFW_PRESS;
|
|
||||||
boolean dpadLeft = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT) == GLFW.GLFW_PRESS;
|
|
||||||
boolean dpadRight = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT) == GLFW.GLFW_PRESS;
|
|
||||||
boolean leftStick = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB) == GLFW.GLFW_PRESS;
|
|
||||||
boolean rightStick = buttons.get(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB) == GLFW.GLFW_PRESS;
|
|
||||||
|
|
||||||
return new ButtonState(a, b, x, y, leftBumper, rightBumper, back, start, guide, dpadUp, dpadDown, dpadLeft, dpadRight, leftStick, rightStick);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record GyroState(float pitch, float yaw, float roll) {
|
public record GyroState(float pitch, float yaw, float roll) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package dev.isxander.controlify.controller.hid;
|
package dev.isxander.controlify.controller.hid;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
import dev.isxander.controlify.Controlify;
|
import dev.isxander.controlify.Controlify;
|
||||||
import dev.isxander.controlify.controller.ControllerType;
|
import dev.isxander.controlify.controller.ControllerType;
|
||||||
import org.hid4java.*;
|
import org.hid4java.*;
|
||||||
@ -11,7 +12,7 @@ public class ControllerHIDService {
|
|||||||
private final HidServicesSpecification specification;
|
private final HidServicesSpecification specification;
|
||||||
private HidServices services;
|
private HidServices services;
|
||||||
|
|
||||||
private final Queue<HIDIdentifierWithPath> unconsumedControllerHIDs;
|
private final Queue<Pair<HidDevice, HIDIdentifier>> unconsumedControllerHIDs;
|
||||||
private final Map<String, HidDevice> attachedDevices = new HashMap<>();
|
private final Map<String, HidDevice> attachedDevices = new HashMap<>();
|
||||||
private boolean disabled = false;
|
private boolean disabled = false;
|
||||||
// https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/hid-usages#usage-page
|
// https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/hid-usages#usage-page
|
||||||
@ -41,19 +42,19 @@ public class ControllerHIDService {
|
|||||||
public ControllerHIDInfo fetchType() {
|
public ControllerHIDInfo fetchType() {
|
||||||
doScanOnThisThread();
|
doScanOnThisThread();
|
||||||
|
|
||||||
HIDIdentifierWithPath hid = unconsumedControllerHIDs.poll();
|
Pair<HidDevice, HIDIdentifier> hid = unconsumedControllerHIDs.poll();
|
||||||
if (hid == null) {
|
if (hid == null) {
|
||||||
Controlify.LOGGER.warn("No controller found via USB hardware scan! This prevents identifying controller type.");
|
Controlify.LOGGER.warn("No controller found via USB hardware scan! This prevents identifying controller type.");
|
||||||
return new ControllerHIDInfo(ControllerType.UNKNOWN, Optional.empty());
|
return new ControllerHIDInfo(ControllerType.UNKNOWN, Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerType type = ControllerType.getTypeMap().getOrDefault(hid.identifier(), ControllerType.UNKNOWN);
|
ControllerType type = ControllerType.getTypeMap().getOrDefault(hid.getSecond(), ControllerType.UNKNOWN);
|
||||||
if (type == ControllerType.UNKNOWN)
|
if (type == ControllerType.UNKNOWN)
|
||||||
Controlify.LOGGER.warn("Controller found via USB hardware scan, but it was not found in the controller identification database! (HID: {})", hid.identifier());
|
Controlify.LOGGER.warn("Controller found via USB hardware scan, but it was not found in the controller identification database! (HID: {})", hid.getSecond());
|
||||||
|
|
||||||
unconsumedControllerHIDs.removeIf(h -> hid.path().equals(h.path()));
|
unconsumedControllerHIDs.removeIf(h -> hid.getFirst().getPath().equals(h.getFirst().getPath()));
|
||||||
|
|
||||||
return new ControllerHIDInfo(type, Optional.of(hid.path()));
|
return new ControllerHIDInfo(type, Optional.of(hid.getFirst()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisabled() {
|
public boolean isDisabled() {
|
||||||
@ -75,7 +76,7 @@ public class ControllerHIDService {
|
|||||||
// add an unconsumed identifier that can be removed if not disconnected
|
// add an unconsumed identifier that can be removed if not disconnected
|
||||||
HIDIdentifier identifier = new HIDIdentifier(attachedDevice.getVendorId(), attachedDevice.getProductId());
|
HIDIdentifier identifier = new HIDIdentifier(attachedDevice.getVendorId(), attachedDevice.getProductId());
|
||||||
if (isController(attachedDevice))
|
if (isController(attachedDevice))
|
||||||
unconsumedControllerHIDs.add(new HIDIdentifierWithPath(attachedDevice.getPath(), identifier));
|
unconsumedControllerHIDs.add(new Pair<>(attachedDevice, identifier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +91,7 @@ public class ControllerHIDService {
|
|||||||
removeList.add(deviceId);
|
removeList.add(deviceId);
|
||||||
|
|
||||||
// remove device from unconsumed list
|
// remove device from unconsumed list
|
||||||
unconsumedControllerHIDs.removeIf(device -> this.attachedDevices.get(deviceId).getPath().equals(device.path()));
|
unconsumedControllerHIDs.removeIf(device -> this.attachedDevices.get(deviceId).getPath().equals(device.getFirst().getPath()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,16 +106,12 @@ public class ControllerHIDService {
|
|||||||
boolean isGenericDesktopControlOrGameControl = device.getUsagePage() == 0x1 || device.getUsagePage() == 0x5;
|
boolean isGenericDesktopControlOrGameControl = device.getUsagePage() == 0x1 || device.getUsagePage() == 0x5;
|
||||||
boolean isSelfIdentifiedController = CONTROLLER_USAGE_IDS.contains(device.getUsage());
|
boolean isSelfIdentifiedController = CONTROLLER_USAGE_IDS.contains(device.getUsage());
|
||||||
|
|
||||||
return ControllerType.getTypeMap().containsKey(new HIDIdentifier(device.getVendorId(), device.getProductId()))
|
return isControllerType || (isGenericDesktopControlOrGameControl && isSelfIdentifiedController);
|
||||||
|| (isGenericDesktopControlOrGameControl && isSelfIdentifiedController);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record ControllerHIDInfo(ControllerType type, Optional<String> path) {
|
public record ControllerHIDInfo(ControllerType type, Optional<HidDevice> hidDevice) {
|
||||||
public Optional<String> createControllerUID() {
|
public Optional<String> createControllerUID() {
|
||||||
return path.map(p -> UUID.nameUUIDFromBytes(p.getBytes())).map(UUID::toString);
|
return hidDevice.map(HidDevice::getPath).map(p -> UUID.nameUUIDFromBytes(p.getBytes())).map(UUID::toString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record HIDIdentifierWithPath(String path, HIDIdentifier identifier) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.google.gson.JsonElement;
|
|||||||
import dev.isxander.controlify.Controlify;
|
import dev.isxander.controlify.Controlify;
|
||||||
import dev.isxander.controlify.InputMode;
|
import dev.isxander.controlify.InputMode;
|
||||||
import dev.isxander.controlify.api.ControlifyApi;
|
import dev.isxander.controlify.api.ControlifyApi;
|
||||||
|
import dev.isxander.controlify.bindings.ControllerBindings;
|
||||||
import dev.isxander.controlify.controller.AbstractController;
|
import dev.isxander.controlify.controller.AbstractController;
|
||||||
import dev.isxander.controlify.controller.hid.ControllerHIDService;
|
import dev.isxander.controlify.controller.hid.ControllerHIDService;
|
||||||
import dev.isxander.controlify.controller.joystick.mapping.RPJoystickMapping;
|
import dev.isxander.controlify.controller.joystick.mapping.RPJoystickMapping;
|
||||||
@ -31,6 +32,12 @@ public class SingleJoystickController extends AbstractController<JoystickState,
|
|||||||
|
|
||||||
this.config = new JoystickConfig(this);
|
this.config = new JoystickConfig(this);
|
||||||
this.defaultConfig = 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -117,13 +124,6 @@ public class SingleJoystickController extends AbstractController<JoystickState,
|
|||||||
return this.rumbleManager;
|
return this.rumbleManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open() {
|
|
||||||
this.ptrJoystick = SDL2NativesManager.isLoaded() ? SDL.SDL_JoystickOpen(joystickId) : 0;
|
|
||||||
this.rumbleSupported = SDL2NativesManager.isLoaded() && SDL.SDL_JoystickHasRumble(this.ptrJoystick);
|
|
||||||
this.rumbleManager = new RumbleManager(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
SDL.SDL_JoystickClose(ptrJoystick);
|
SDL.SDL_JoystickClose(ptrJoystick);
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||||
|
|
||||||
|
public interface BasicGamepadInputDriver extends Driver {
|
||||||
|
|
||||||
|
BasicGamepadState getBasicGamepadState();
|
||||||
|
|
||||||
|
record BasicGamepadState(GamepadState.AxesState axes, GamepadState.ButtonState buttons) {
|
||||||
|
public static final BasicGamepadState EMPTY = new BasicGamepadState(GamepadState.AxesState.EMPTY, GamepadState.ButtonState.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicGamepadInputDriver UNSUPPORTED = new BasicGamepadInputDriver() {
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicGamepadState getBasicGamepadState() {
|
||||||
|
return BasicGamepadState.EMPTY;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
8
src/main/java/dev/isxander/controlify/driver/Driver.java
Normal file
8
src/main/java/dev/isxander/controlify/driver/Driver.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
public interface Driver {
|
||||||
|
void update();
|
||||||
|
|
||||||
|
default void close() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.glfw.GLFWGamepadState;
|
||||||
|
|
||||||
|
public class GLFWGamepadDriver implements BasicGamepadInputDriver {
|
||||||
|
private final int jid;
|
||||||
|
|
||||||
|
private BasicGamepadState state = new BasicGamepadState(GamepadState.AxesState.EMPTY, GamepadState.ButtonState.EMPTY);
|
||||||
|
|
||||||
|
public GLFWGamepadDriver(int jid) {
|
||||||
|
this.jid = jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
GLFWGamepadState state = GLFWGamepadState.create();
|
||||||
|
GLFW.glfwGetGamepadState(jid, state);
|
||||||
|
|
||||||
|
GamepadState.AxesState axes = new GamepadState.AxesState(
|
||||||
|
state.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_X),
|
||||||
|
state.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y),
|
||||||
|
state.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X),
|
||||||
|
state.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y),
|
||||||
|
state.axes(GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER),
|
||||||
|
state.axes(GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER)
|
||||||
|
);
|
||||||
|
GamepadState.ButtonState buttons = new GamepadState.ButtonState(
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_A) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_B) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_X) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_Y) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_BACK) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_START) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_GUIDE) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB) == GLFW.GLFW_PRESS,
|
||||||
|
state.buttons(GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB) == GLFW.GLFW_PRESS
|
||||||
|
);
|
||||||
|
|
||||||
|
this.state = new BasicGamepadState(axes, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicGamepadState getBasicGamepadState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
import dev.isxander.controlify.controller.sdl2.SDL2NativesManager;
|
||||||
|
import org.hid4java.HidDevice;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public record GamepadDrivers(BasicGamepadInputDriver basicGamepadInputDriver, GyroDriver gyroDriver, RumbleDriver rumbleDriver) {
|
||||||
|
public Set<Driver> getUniqueDrivers() {
|
||||||
|
Set<Driver> drivers = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||||
|
drivers.addAll(List.of(basicGamepadInputDriver, gyroDriver, rumbleDriver));
|
||||||
|
return drivers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GamepadDrivers forController(int jid, Optional<HidDevice> hid) {
|
||||||
|
BasicGamepadInputDriver basicGamepadInputDriver = new GLFWGamepadDriver(jid);
|
||||||
|
GyroDriver gyroDriver = GyroDriver.UNSUPPORTED;
|
||||||
|
RumbleDriver rumbleDriver = RumbleDriver.UNSUPPORTED;
|
||||||
|
|
||||||
|
if (SDL2NativesManager.isLoaded()) {
|
||||||
|
SDL2GamepadDriver sdl2Driver = new SDL2GamepadDriver(jid);
|
||||||
|
gyroDriver = sdl2Driver;
|
||||||
|
rumbleDriver = sdl2Driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
// broken
|
||||||
|
if (hid.isPresent() && SteamDeckDriver.isSteamDeck(hid.get()) && false) {
|
||||||
|
gyroDriver = new SteamDeckDriver(hid.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GamepadDrivers(basicGamepadInputDriver, gyroDriver, rumbleDriver);
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/dev/isxander/controlify/driver/GyroDriver.java
Normal file
25
src/main/java/dev/isxander/controlify/driver/GyroDriver.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||||
|
|
||||||
|
public interface GyroDriver extends Driver {
|
||||||
|
GamepadState.GyroState getGyroState();
|
||||||
|
|
||||||
|
boolean isGyroSupported();
|
||||||
|
|
||||||
|
GyroDriver UNSUPPORTED = new GyroDriver() {
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GamepadState.GyroState getGyroState() {
|
||||||
|
return GamepadState.GyroState.ORIGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGyroSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
public interface RumbleDriver extends Driver {
|
||||||
|
boolean rumble(float strongMagnitude, float weakMagnitude);
|
||||||
|
|
||||||
|
boolean isRumbleSupported();
|
||||||
|
|
||||||
|
RumbleDriver UNSUPPORTED = new RumbleDriver() {
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean rumble(float strongMagnitude, float weakMagnitude) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRumbleSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
import dev.isxander.controlify.Controlify;
|
||||||
|
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||||
|
import dev.isxander.controlify.debug.DebugProperties;
|
||||||
|
import org.libsdl.SDL;
|
||||||
|
|
||||||
|
public class SDL2GamepadDriver implements GyroDriver, RumbleDriver {
|
||||||
|
private final long ptrGamepad;
|
||||||
|
private GamepadState.GyroState gyroDelta;
|
||||||
|
private final boolean isGyroSupported, isRumbleSupported;
|
||||||
|
|
||||||
|
public SDL2GamepadDriver(int jid) {
|
||||||
|
this.ptrGamepad = SDL.SDL_GameControllerOpen(jid);
|
||||||
|
this.isGyroSupported = SDL.SDL_GameControllerHasSensor(ptrGamepad, SDL.SDL_SENSOR_GYRO);
|
||||||
|
this.isRumbleSupported = SDL.SDL_GameControllerHasRumble(ptrGamepad);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
if (isGyroSupported()) {
|
||||||
|
float[] gyro = new float[3];
|
||||||
|
SDL.SDL_GameControllerGetSensorData(ptrGamepad, SDL.SDL_SENSOR_GYRO, gyro, 3);
|
||||||
|
gyroDelta = new GamepadState.GyroState(gyro[0], gyro[1], gyro[2]);
|
||||||
|
if (DebugProperties.PRINT_GYRO) Controlify.LOGGER.info("Gyro delta: " + gyroDelta);
|
||||||
|
}
|
||||||
|
SDL.SDL_GameControllerUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean rumble(float strongMagnitude, float weakMagnitude) {
|
||||||
|
// duration of 0 is infinite
|
||||||
|
if (!SDL.SDL_GameControllerRumble(ptrGamepad, (int)(strongMagnitude * 65535.0F), (int)(weakMagnitude * 65535.0F), 0)) {
|
||||||
|
Controlify.LOGGER.error("Could not rumble controller: " + SDL.SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GamepadState.GyroState getGyroState() {
|
||||||
|
return gyroDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGyroSupported() {
|
||||||
|
return isGyroSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRumbleSupported() {
|
||||||
|
return isRumbleSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
SDL.SDL_GameControllerClose(ptrGamepad);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,234 @@
|
|||||||
|
package dev.isxander.controlify.driver;
|
||||||
|
|
||||||
|
import dev.isxander.controlify.Controlify;
|
||||||
|
import dev.isxander.controlify.controller.gamepad.GamepadState;
|
||||||
|
import dev.isxander.controlify.controller.hid.HIDIdentifier;
|
||||||
|
import org.hid4java.HidDevice;
|
||||||
|
|
||||||
|
public class SteamDeckDriver implements GyroDriver, BasicGamepadInputDriver {
|
||||||
|
private static final int cInputRecordLen = 8; // Number of bytes that are read from the hid device per 1 byte of HID
|
||||||
|
private static final int cByteposInput = 4; // Position in the raw hid data where HID data byte is
|
||||||
|
private static final byte[] startMarker = new byte[] { 0x01, 0x00, 0x09, 0x40 }; // Beginning of every Steam deck HID frame
|
||||||
|
|
||||||
|
private final HidDevice hidDevice;
|
||||||
|
|
||||||
|
private GamepadState.GyroState gyroDelta = GamepadState.GyroState.ORIGIN;
|
||||||
|
private BasicGamepadState basicGamepadState = new BasicGamepadState(GamepadState.AxesState.EMPTY, GamepadState.ButtonState.EMPTY);
|
||||||
|
|
||||||
|
public SteamDeckDriver(HidDevice hidDevice) {
|
||||||
|
this.hidDevice = hidDevice;
|
||||||
|
this.hidDevice.open();
|
||||||
|
this.hidDevice.setNonBlocking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
sendSomething();
|
||||||
|
|
||||||
|
byte[] data = new byte[64];
|
||||||
|
int readCnt = hidDevice.read(data);
|
||||||
|
|
||||||
|
if (readCnt == 0) {
|
||||||
|
Controlify.LOGGER.warn("No data available.");
|
||||||
|
}
|
||||||
|
if (readCnt == -1) {
|
||||||
|
Controlify.LOGGER.warn("Error reading data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkData(data, readCnt)) return;
|
||||||
|
|
||||||
|
Frame frame = Frame.fromBytes(data);
|
||||||
|
System.out.println(frame);
|
||||||
|
readFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSomething() {
|
||||||
|
hidDevice.getFeatureReport(new byte[]{ (byte) 0x89 }, (byte) 0x0);
|
||||||
|
hidDevice.write(new byte[]{ (byte) 0x89 }, 2, (byte) 0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFrame(Frame frame) {
|
||||||
|
gyroDelta = new GamepadState.GyroState(
|
||||||
|
frame.gyroAxisFrontToBack,
|
||||||
|
frame.gyroAxisTopToBottom,
|
||||||
|
frame.gyroAxisRightToLeft
|
||||||
|
);
|
||||||
|
|
||||||
|
basicGamepadState = new BasicGamepadState(
|
||||||
|
new GamepadState.AxesState(
|
||||||
|
frame.leftStickX,
|
||||||
|
frame.leftStickY,
|
||||||
|
frame.rightStickX,
|
||||||
|
frame.rightStickY,
|
||||||
|
frame.l2Analog,
|
||||||
|
frame.r2Analog
|
||||||
|
),
|
||||||
|
new GamepadState.ButtonState(
|
||||||
|
((frame.buttons1BitMap >> 7) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 5) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 6) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 4) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 3) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 2) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 12) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 14) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 13) & 1) == 1,
|
||||||
|
false, false, false, false,
|
||||||
|
((frame.buttons1BitMap >> 1) & 1) == 1,
|
||||||
|
((frame.buttons1BitMap >> 0) & 1) == 1
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkData(byte[] data, int readCnt) {
|
||||||
|
int first4Bytes = 0xFFFF0002;
|
||||||
|
int first4BytesAlt = 0xFFFF0001;
|
||||||
|
|
||||||
|
boolean inputFail = readCnt < data.length;
|
||||||
|
boolean startMarkerFail = false;
|
||||||
|
|
||||||
|
if (!inputFail) {
|
||||||
|
startMarkerFail = first4Bytes(data) != first4Bytes;
|
||||||
|
if (startMarkerFail && first4Bytes(data) == first4BytesAlt) {
|
||||||
|
startMarkerFail = false;
|
||||||
|
|
||||||
|
for (int i = cByteposInput, j = 0; j < startMarker.length; j++, i += cInputRecordLen) {
|
||||||
|
if (data[i] != startMarker[j]) {
|
||||||
|
startMarkerFail = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !inputFail && !startMarkerFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int first4Bytes(byte[] data) {
|
||||||
|
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GamepadState.GyroState getGyroState() {
|
||||||
|
return gyroDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicGamepadState getBasicGamepadState() {
|
||||||
|
return basicGamepadState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGyroSupported() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
hidDevice.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/kmicki/SteamDeckGyroDSU/blob/574745406011cc2433fc6f179446ecc836180aa4/inc/sdgyrodsu/sdhidframe.h
|
||||||
|
private record Frame(
|
||||||
|
int header,
|
||||||
|
int increment,
|
||||||
|
|
||||||
|
// Buttons 1:
|
||||||
|
// .0 - R2 full pull
|
||||||
|
// .1 - L2 full pull
|
||||||
|
// .2 - R1
|
||||||
|
// .3 - L1
|
||||||
|
// .4 - Y
|
||||||
|
// .5 - B
|
||||||
|
// .6 - X
|
||||||
|
// .7 - A
|
||||||
|
// .12 - Select
|
||||||
|
// .13 - STEAM
|
||||||
|
// .14 - Start
|
||||||
|
// .15 - L5
|
||||||
|
// .16 - R5
|
||||||
|
// .17 - L trackpad click
|
||||||
|
// .18 - R trackpad click
|
||||||
|
// .19 - L trackpad touch
|
||||||
|
// .20 - R trackpad touch
|
||||||
|
// .22 - L3
|
||||||
|
// .26 - R3
|
||||||
|
int buttons1BitMap,
|
||||||
|
|
||||||
|
// Buttons 2:
|
||||||
|
// .9 - L4
|
||||||
|
// .10 - R4
|
||||||
|
// .14 - L3 touch
|
||||||
|
// .15 - R3 touch
|
||||||
|
// .18 - (...)
|
||||||
|
int buttons2BitMap,
|
||||||
|
|
||||||
|
short leftTrackpadX,
|
||||||
|
short leftTrackpadY,
|
||||||
|
short rightTrackpadX,
|
||||||
|
short rightTrackpadY,
|
||||||
|
|
||||||
|
short accelAxisRightToLeft,
|
||||||
|
short accelAxisTopToBottom,
|
||||||
|
short accelAxisFrontToBack,
|
||||||
|
|
||||||
|
short gyroAxisRightToLeft,
|
||||||
|
short gyroAxisTopToBottom,
|
||||||
|
short gyroAxisFrontToBack,
|
||||||
|
|
||||||
|
short unknown1,
|
||||||
|
short unknown2,
|
||||||
|
short unknown3,
|
||||||
|
short unknown4,
|
||||||
|
|
||||||
|
short l2Analog,
|
||||||
|
short r2Analog,
|
||||||
|
short leftStickX,
|
||||||
|
short leftStickY,
|
||||||
|
short rightStickX,
|
||||||
|
short rightStickY,
|
||||||
|
|
||||||
|
short leftTrackpadPushForce,
|
||||||
|
short rightTrackpadPushForce,
|
||||||
|
short leftStickTouchCoverage,
|
||||||
|
short rightStickTouchCoverage
|
||||||
|
) {
|
||||||
|
// i love github copilot
|
||||||
|
public static Frame fromBytes(byte[] bytes) {
|
||||||
|
return new Frame(
|
||||||
|
(bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3],
|
||||||
|
(bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7],
|
||||||
|
(bytes[8] << 24) | (bytes[9] << 16) | (bytes[10] << 8) | bytes[11],
|
||||||
|
(bytes[12] << 24) | (bytes[13] << 16) | (bytes[14] << 8) | bytes[15],
|
||||||
|
(short) ((bytes[16] << 8) | bytes[17]),
|
||||||
|
(short) ((bytes[18] << 8) | bytes[19]),
|
||||||
|
(short) ((bytes[20] << 8) | bytes[21]),
|
||||||
|
(short) ((bytes[22] << 8) | bytes[23]),
|
||||||
|
(short) ((bytes[24] << 8) | bytes[25]),
|
||||||
|
(short) ((bytes[26] << 8) | bytes[27]),
|
||||||
|
(short) ((bytes[28] << 8) | bytes[29]),
|
||||||
|
(short) ((bytes[30] << 8) | bytes[31]),
|
||||||
|
(short) ((bytes[32] << 8) | bytes[33]),
|
||||||
|
(short) ((bytes[34] << 8) | bytes[35]),
|
||||||
|
(short) ((bytes[36] << 8) | bytes[37]),
|
||||||
|
(short) ((bytes[38] << 8) | bytes[39]),
|
||||||
|
(short) ((bytes[40] << 8) | bytes[41]),
|
||||||
|
(short) ((bytes[42] << 8) | bytes[43]),
|
||||||
|
(short) ((bytes[44] << 8) | bytes[45]),
|
||||||
|
(short) ((bytes[46] << 8) | bytes[47]),
|
||||||
|
(short) ((bytes[48] << 8) | bytes[49]),
|
||||||
|
(short) ((bytes[50] << 8) | bytes[51]),
|
||||||
|
(short) ((bytes[52] << 8) | bytes[53]),
|
||||||
|
(short) ((bytes[54] << 8) | bytes[55]),
|
||||||
|
(short) ((bytes[56] << 8) | bytes[57]),
|
||||||
|
(short) ((bytes[58] << 8) | bytes[59]),
|
||||||
|
(short) ((bytes[60] << 8) | bytes[61]),
|
||||||
|
(short) ((bytes[62] << 8) | bytes[63])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSteamDeck(HidDevice hid) {
|
||||||
|
return hid.getVendorId() == 0x28DE && hid.getProductId() == 0x1205;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package dev.isxander.controlify.mixins.core;
|
|||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
import dev.isxander.controlify.Controlify;
|
import dev.isxander.controlify.Controlify;
|
||||||
|
import dev.isxander.controlify.controller.Controller;
|
||||||
import dev.isxander.controlify.gui.screen.BetaNoticeScreen;
|
import dev.isxander.controlify.gui.screen.BetaNoticeScreen;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.components.toasts.SystemToast;
|
import net.minecraft.client.gui.components.toasts.SystemToast;
|
||||||
@ -67,4 +68,9 @@ public abstract class MinecraftMixin {
|
|||||||
});
|
});
|
||||||
return resourceReload;
|
return resourceReload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "close", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/telemetry/ClientTelemetryManager;close()V"))
|
||||||
|
private void onMinecraftClose(CallbackInfo ci) {
|
||||||
|
Controller.CONTROLLERS.values().forEach(Controller::close);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user