1
0
forked from Clones/Controlify

Implement SDL controller identification when hid4java is unavailable (macOS ARM)

This commit is contained in:
isXander
2023-07-10 18:01:55 +01:00
parent 23d65cb89d
commit 45e859bdb1
19 changed files with 141 additions and 43 deletions

View File

@ -3,7 +3,6 @@ package dev.isxander.controlify;
import com.mojang.blaze3d.Blaze3D;
import dev.isxander.controlify.api.ControlifyApi;
import dev.isxander.controlify.api.entrypoint.ControlifyEntrypoint;
import dev.isxander.controlify.driver.SteamDeckDriver;
import dev.isxander.controlify.gui.controllers.ControllerBindHandler;
import dev.isxander.controlify.gui.screen.ControllerCarouselScreen;
import dev.isxander.controlify.controller.Controller;
@ -16,7 +15,7 @@ import dev.isxander.controlify.reacharound.ReachAroundHandler;
import dev.isxander.controlify.reacharound.ReachAroundMode;
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
import dev.isxander.controlify.config.ControlifyConfig;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.api.event.ControlifyEvents;
import dev.isxander.controlify.gui.guide.InGameButtonGuide;
import dev.isxander.controlify.ingame.InGameInputHandler;

View File

@ -3,10 +3,11 @@ package dev.isxander.controlify;
import com.google.common.collect.ImmutableList;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.gamepad.GamepadController;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.controller.joystick.CompoundJoystickController;
import dev.isxander.controlify.controller.joystick.SingleJoystickController;
import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.hid.HIDDevice;
import dev.isxander.controlify.utils.DebugLog;
import dev.isxander.controlify.utils.Log;
import net.minecraft.CrashReport;
@ -54,7 +55,7 @@ public final class ControllerManager {
CrashReportCategory category = crashReport.addCategory("Controller Info");
category.setDetail("Joystick ID", joystickId);
category.setDetail("Controller identification", hidInfo.type());
category.setDetail("HID path", hidInfo.hidDevice().map(HidDevice::getPath).orElse("N/A"));
category.setDetail("HID path", hidInfo.hidDevice().map(HIDDevice::path).orElse("N/A"));
category.setDetail("HID service status", Controlify.instance().controllerHIDService().isDisabled() ? "Disabled" : "Enabled");
category.setDetail("GLFW name", Optional.ofNullable(GLFW.glfwGetJoystickName(joystickId)).orElse("N/A"));
throw new ReportedException(crashReport);

View File

@ -6,7 +6,7 @@ import com.google.gson.JsonElement;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.ControllerManager;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.utils.Log;
import org.apache.commons.lang3.SerializationUtils;

View File

@ -3,7 +3,7 @@ package dev.isxander.controlify.controller;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.rumble.RumbleManager;
import dev.isxander.controlify.rumble.RumbleSource;

View File

@ -1,7 +1,7 @@
package dev.isxander.controlify.controller;
import com.google.common.collect.ImmutableMap;
import dev.isxander.controlify.controller.hid.HIDIdentifier;
import dev.isxander.controlify.hid.HIDIdentifier;
import dev.isxander.controlify.utils.Log;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;

View File

@ -4,7 +4,7 @@ import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.controller.AbstractController;
import dev.isxander.controlify.controller.BatteryLevel;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.driver.*;
import dev.isxander.controlify.rumble.RumbleManager;
@ -44,7 +44,7 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
this.defaultConfig = new GamepadConfig();
this.config = new GamepadConfig();
if (hidInfo.hidDevice().map(hid -> SteamDeckDriver.isSteamDeck(hid.getVendorId(), hid.getProductId())).orElse(false)) {
if (hidInfo.hidDevice().map(hid -> SteamDeckDriver.isSteamDeck(hid.vendorID(), hid.productID())).orElse(false)) {
this.defaultConfig.mixedInput = true;
this.config.mixedInput = true;
}

View File

@ -5,7 +5,7 @@ import com.google.gson.Gson;
import com.google.gson.JsonElement;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.controller.ControllerType;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.controller.joystick.mapping.JoystickMapping;
import dev.isxander.controlify.controller.joystick.mapping.RPJoystickMapping;
import dev.isxander.controlify.rumble.RumbleCapable;

View File

@ -4,7 +4,7 @@ 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.controller.hid.ControllerHIDService;
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;

View File

@ -130,7 +130,7 @@ public class SDL2NativesManager {
return initialised;
}
private record Target(Util.OS os, boolean is64Bit, boolean isARM) {
public record Target(Util.OS os, boolean is64Bit, boolean isARM) {
public static final Target CURRENT = Util.make(() -> {
Util.OS os = Util.getPlatform();
@ -155,5 +155,9 @@ public class SDL2NativesManager {
.resolve("controlify-natives")
.resolve(getArtifactName());
}
public boolean isMacArm() {
return os == Util.OS.OSX && isARM;
}
}
}

View File

@ -3,8 +3,8 @@ package dev.isxander.controlify.driver;
import com.google.common.collect.Sets;
import dev.isxander.controlify.controller.sdl2.SDL2NativesManager;
import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.hid.HIDDevice;
import dev.isxander.controlify.utils.Log;
import org.hid4java.HidDevice;
import java.util.*;
@ -28,7 +28,7 @@ public record GamepadDrivers(BasicGamepadInputDriver basicGamepadInputDriver, Gy
}
}
public static GamepadDrivers forController(int jid, Optional<HidDevice> hid) {
public static GamepadDrivers forController(int jid, Optional<HIDDevice> hid) {
GLFWGamepadDriver glfwDriver = new GLFWGamepadDriver(jid);
BasicGamepadInputDriver basicGamepadInputDriver = glfwDriver;
@ -50,7 +50,7 @@ public record GamepadDrivers(BasicGamepadInputDriver basicGamepadInputDriver, Gy
}
// TODO: Fix Steam Deck driver
if (hid.isPresent() && SteamDeckDriver.isSteamDeck(hid.get().getVendorId(), hid.get().getProductId()) && false) {
if (hid.isPresent() && hid.get().supportsCommunication() && SteamDeckDriver.isSteamDeck(hid.get().vendorID(), hid.get().productID()) && false) {
gyroDriver = new SteamDeckDriver(hid.get());
}

View File

@ -1,9 +1,8 @@
package dev.isxander.controlify.driver;
import dev.isxander.controlify.controller.gamepad.GamepadState;
import dev.isxander.controlify.controller.hid.HIDIdentifier;
import dev.isxander.controlify.hid.HIDDevice;
import dev.isxander.controlify.utils.Log;
import org.hid4java.HidDevice;
import java.util.Arrays;
@ -12,16 +11,15 @@ public class SteamDeckDriver implements GyroDriver, BasicGamepadInputDriver {
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 final HIDDevice hidDevice;
private int interval = 0;
private GamepadState.GyroState gyroDelta = new GamepadState.GyroState();
private BasicGamepadState basicGamepadState = new BasicGamepadState(GamepadState.AxesState.EMPTY, GamepadState.ButtonState.EMPTY);
public SteamDeckDriver(HidDevice hidDevice) {
public SteamDeckDriver(HIDDevice hidDevice) {
this.hidDevice = hidDevice;
this.hidDevice.open();
this.hidDevice.setNonBlocking(true);
}
@Override
@ -50,7 +48,7 @@ public class SteamDeckDriver implements GyroDriver, BasicGamepadInputDriver {
}
private void keepAlive() {
hidDevice.sendFeatureReport(new byte[0], (byte) 8);
//hidDevice.sendFeatureReport(new byte[0], (byte) 8);
}
private void readFrame(Frame frame) {

View File

@ -514,7 +514,7 @@ public class ControllerConfigScreenFactory {
return opt;
}));
} else {
boolean isSteamDeck = gamepad != null && gamepad.hidInfo().map(hid -> hid.hidDevice().map(d -> SteamDeckDriver.isSteamDeck(d.getVendorId(), d.getProductId())).orElse(false)).orElse(false);
boolean isSteamDeck = gamepad != null && gamepad.hidInfo().map(hid -> hid.hidDevice().map(d -> SteamDeckDriver.isSteamDeck(d.vendorID(), d.productID())).orElse(false)).orElse(false);
gyroGroup.option(LabelOption.create(Component.translatable(!isSteamDeck ? "controlify.gui.group.gyro.no_gyro.tooltip" : "controlify.gui.group.gyro.no_gyro_steamdeck.tooltip").withStyle(ChatFormatting.RED)));
}

View File

@ -1,6 +1,7 @@
package dev.isxander.controlify.gui.screen;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.controller.sdl2.SDL2NativesManager;
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
@ -25,9 +26,9 @@ public class SDLOnboardingScreen extends ConfirmScreen {
Util.make(() -> {
var message = Component.translatable("controlify.sdl2_onboarding.message");
// if (Util.getPlatform() == Util.OS.OSX) {
// message.append("\n").append(Component.translatable("controlify.sdl2_onboarding.message_mac").withStyle(ChatFormatting.RED));
// }
if (SDL2NativesManager.Target.CURRENT.isMacArm()) {
message.append("\n").append(Component.translatable("controlify.sdl2_onboarding.message_mac").withStyle(ChatFormatting.RED));
}
message.append("\n\n").append(Component.translatable("controlify.sdl2_onboarding.question"));

View File

@ -1,4 +1,4 @@
package dev.isxander.controlify.controller.hid;
package dev.isxander.controlify.hid;
import com.mojang.datafixers.util.Pair;
import dev.isxander.controlify.Controlify;
@ -8,6 +8,8 @@ import dev.isxander.controlify.utils.Log;
import dev.isxander.controlify.utils.ToastUtils;
import net.minecraft.network.chat.Component;
import org.hid4java.*;
import org.libsdl.SDL;
import org.lwjgl.glfw.GLFW;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
@ -58,16 +60,18 @@ public class ControllerHIDService {
}
}
// if (SDL2NativesManager.isLoaded()) {
// int vid = SDL.SDL_JoystickGetDeviceVendor(jid);
// int pid = SDL.SDL_JoystickGetDeviceProduct(jid);
//
// if (vid != 0 && pid != 0) {
// return new ControllerHIDInfo(ControllerType.getTypeForHID(new HIDIdentifier(vid, pid)), Optional.empty());
// }
// }
if (disabled) {
if (SDL2NativesManager.isLoaded()) {
int vid = SDL.SDL_JoystickGetDeviceVendor(jid);
int pid = SDL.SDL_JoystickGetDeviceProduct(jid);
String path = GLFW.glfwGetJoystickGUID(jid);
if (vid != 0 && pid != 0) {
Log.LOGGER.info("Using SDL to identify controller type.");
return new ControllerHIDInfo(ControllerType.getTypeForHID(new HIDIdentifier(vid, pid)), Optional.of(new HIDDevice.IDOnly(vid, pid, path)));
}
}
return new ControllerHIDInfo(ControllerType.UNKNOWN, Optional.empty());
}
@ -85,7 +89,7 @@ public class ControllerHIDService {
unconsumedControllerHIDs.removeIf(h -> hid.getFirst().getPath().equals(h.getFirst().getPath()));
return new ControllerHIDInfo(type, Optional.of(hid.getFirst()));
return new ControllerHIDInfo(type, Optional.of(new HIDDevice.Hid4Java(hid.getFirst())));
}
public boolean isDisabled() {
@ -133,7 +137,7 @@ public class ControllerHIDService {
}
public void unconsumeController(ControllerHIDInfo hid) {
hid.hidDevice.ifPresent(device -> attachedDevices.remove(device.getPath()));
hid.hidDevice.ifPresent(device -> attachedDevices.remove(device.path()));
}
private boolean isController(HidDevice device) {
@ -144,9 +148,9 @@ public class ControllerHIDService {
return isControllerType || (isGenericDesktopControlOrGameControl && isSelfIdentifiedController);
}
public record ControllerHIDInfo(ControllerType type, Optional<HidDevice> hidDevice) {
public record ControllerHIDInfo(ControllerType type, Optional<HIDDevice> hidDevice) {
public Optional<String> createControllerUID() {
return hidDevice.map(HidDevice::getPath).map(p -> UUID.nameUUIDFromBytes(p.getBytes())).map(UUID::toString);
return hidDevice.map(HIDDevice::path).map(p -> UUID.nameUUIDFromBytes(p.getBytes())).map(UUID::toString);
}
}
}

View File

@ -0,0 +1,91 @@
package dev.isxander.controlify.hid;
public sealed interface HIDDevice permits HIDDevice.Hid4Java, HIDDevice.IDOnly {
int vendorID();
int productID();
String path();
boolean supportsCommunication();
void open();
void close();
int read(byte[] buffer);
int write(byte[] buffer, int packetLength, byte reportId);
final class Hid4Java implements HIDDevice {
private final org.hid4java.HidDevice hidDevice;
public Hid4Java(org.hid4java.HidDevice hidDevice) {
this.hidDevice = hidDevice;
}
@Override
public int vendorID() {
return hidDevice.getVendorId();
}
@Override
public int productID() {
return hidDevice.getProductId();
}
@Override
public String path() {
return hidDevice.getPath();
}
@Override
public boolean supportsCommunication() {
return true;
}
@Override
public void open() {
hidDevice.open();
hidDevice.setNonBlocking(true);
}
@Override
public void close() {
hidDevice.close();
}
@Override
public int read(byte[] buffer) {
return hidDevice.read(buffer);
}
@Override
public int write(byte[] buffer, int packetLength, byte reportId) {
return hidDevice.write(buffer, packetLength, reportId);
}
}
record IDOnly(int vendorID, int productID, String path) implements HIDDevice {
@Override
public boolean supportsCommunication() {
return false;
}
@Override
public void open() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
throw new UnsupportedOperationException();
}
@Override
public int read(byte[] buffer) {
throw new UnsupportedOperationException();
}
@Override
public int write(byte[] buffer, int packetLength, byte reportId) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -1,4 +1,4 @@
package dev.isxander.controlify.controller.hid;
package dev.isxander.controlify.hid;
public record HIDIdentifier(int vendorId, int productId) {
}