forked from Clones/Controlify
➕ Implement SDL controller identification when hid4java is unavailable (macOS ARM)
This commit is contained in:
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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"));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
91
src/main/java/dev/isxander/controlify/hid/HIDDevice.java
Normal file
91
src/main/java/dev/isxander/controlify/hid/HIDDevice.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package dev.isxander.controlify.controller.hid;
|
||||
package dev.isxander.controlify.hid;
|
||||
|
||||
public record HIDIdentifier(int vendorId, int productId) {
|
||||
}
|
@ -166,7 +166,7 @@
|
||||
|
||||
"controlify.sdl2_onboarding.title": "Controlify Native Library",
|
||||
"controlify.sdl2_onboarding.message": "Many features in Controlify require an extra library that needs to be downloaded for your system. If you do not download this library, you will lose access to many features such as: controller vibration, gyroscope control, better controller identification. This is a seamless process and will only take a few seconds. If you choose no, you may change your mind later in Controlify settings, but you won't have access to these features in the meantime.",
|
||||
"controlify.sdl2_onboarding.message_mac": "Because you are on macOS, this library is required for any sort of controller identification. Without it, all controllers will be unidentified. This will severely impact on user experience if you choose not to download them.",
|
||||
"controlify.sdl2_onboarding.message_mac": "Because you are on macOS ARM, this library is required for any sort of controller identification. Without it, all controllers will be unidentified. This will severely impact on user experience if you choose not to download them.",
|
||||
"controlify.sdl2_onboarding.question": "Would you like to download them?",
|
||||
|
||||
"controlify.controller_theme.default": "Default",
|
||||
|
Reference in New Issue
Block a user