1
0
forked from Clones/Controlify

Battery level warning + update SDL with macOS ARM support

This commit is contained in:
isXander
2023-05-11 16:43:13 +01:00
parent 0e8bf0cc9b
commit 71c7e26587
19 changed files with 283 additions and 69 deletions

View File

@ -0,0 +1,21 @@
package dev.isxander.controlify.controller;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
public enum BatteryLevel {
EMPTY, LOW, MEDIUM, FULL, MAX,
WIRED, UNKNOWN;
public MutableComponent getFriendlyName() {
return Component.translatable("controlify.battery_level." + name().toLowerCase()).withStyle(
switch (this) {
case EMPTY, LOW -> ChatFormatting.RED;
case MEDIUM -> ChatFormatting.YELLOW;
case FULL, MAX -> ChatFormatting.GREEN;
default -> ChatFormatting.WHITE;
}
);
}
}

View File

@ -2,24 +2,11 @@ package dev.isxander.controlify.controller;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.bindings.ControllerBindings;
import dev.isxander.controlify.controller.gamepad.GamepadController;
import dev.isxander.controlify.controller.hid.ControllerHIDService;
import dev.isxander.controlify.controller.joystick.SingleJoystickController;
import dev.isxander.controlify.debug.DebugProperties;
import dev.isxander.controlify.rumble.RumbleCapable;
import dev.isxander.controlify.rumble.RumbleManager;
import dev.isxander.controlify.rumble.RumbleSource;
import dev.isxander.controlify.utils.DebugLog;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import org.hid4java.HidDevice;
import org.lwjgl.glfw.GLFW;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public interface Controller<S extends ControllerState, C extends ControllerConfig> {
@ -48,6 +35,10 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
RumbleManager rumbleManager();
boolean supportsRumble();
default BatteryLevel batteryLevel() {
return BatteryLevel.UNKNOWN;
}
Optional<ControllerHIDService.ControllerHIDInfo> hidInfo();
default boolean canBeUsed() {

View File

@ -17,6 +17,10 @@ public record ControllerType(String friendlyName, String mappingId, String theme
private static Map<HIDIdentifier, ControllerType> typeMap = null;
private static final ResourceLocation hidDbLocation = new ResourceLocation("controlify", "controllers/controller_identification.json5");
public static ControllerType getTypeForHID(HIDIdentifier hid) {
return getTypeMap().getOrDefault(hid, ControllerType.UNKNOWN);
}
public static void ensureTypeMapFilled() {
if (typeMap != null) return;

View File

@ -2,6 +2,7 @@ package dev.isxander.controlify.controller.gamepad;
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.driver.*;
import dev.isxander.controlify.rumble.RumbleManager;
@ -103,6 +104,11 @@ public class GamepadController extends AbstractController<GamepadState, GamepadC
return this.rumbleManager;
}
@Override
public BatteryLevel batteryLevel() {
return drivers.batteryDriver().getBatteryLevel();
}
@Override
public void close() {
uniqueDrivers.forEach(Driver::close);

View File

@ -3,6 +3,9 @@ package dev.isxander.controlify.controller.hid;
import com.mojang.datafixers.util.Pair;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.controller.ControllerType;
import dev.isxander.controlify.controller.sdl2.SDL2NativesManager;
import dev.isxander.controlify.utils.ToastUtils;
import net.minecraft.network.chat.Component;
import org.hid4java.*;
import java.util.*;
@ -15,6 +18,7 @@ public class ControllerHIDService {
private final Queue<Pair<HidDevice, HIDIdentifier>> unconsumedControllerHIDs;
private final Map<String, HidDevice> attachedDevices = new HashMap<>();
private boolean disabled = false;
private boolean firstFetch = true;
// https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/hid-usages#usage-page
private static final Set<Integer> CONTROLLER_USAGE_IDS = Set.of(
0x04, // Joystick
@ -39,7 +43,29 @@ public class ControllerHIDService {
}
}
public ControllerHIDInfo fetchType() {
public ControllerHIDInfo fetchType(int jid) {
if (firstFetch) {
firstFetch = false;
if (isDisabled() && !SDL2NativesManager.isLoaded()) {
if (Controlify.instance().controllerHIDService().isDisabled() && !SDL2NativesManager.isLoaded()) {
ToastUtils.sendToast(
Component.translatable("controlify.error.hid"),
Component.translatable("controlify.error.hid.desc"),
true
);
}
}
}
// 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) {
return new ControllerHIDInfo(ControllerType.UNKNOWN, Optional.empty());
}
@ -52,7 +78,7 @@ public class ControllerHIDService {
return new ControllerHIDInfo(ControllerType.UNKNOWN, Optional.empty());
}
ControllerType type = ControllerType.getTypeMap().getOrDefault(hid.getSecond(), ControllerType.UNKNOWN);
ControllerType type = ControllerType.getTypeForHID(hid.getSecond());
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.getSecond());

View File

@ -6,6 +6,7 @@ import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.Util;
import org.libsdl.SDL;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.nio.channels.Channels;
@ -13,6 +14,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Map;
import static org.libsdl.SDL_Hints.*;
@ -20,10 +22,11 @@ import static org.libsdl.SDL_Hints.*;
public class SDL2NativesManager {
private static final String SDL2_VERSION = "<SDL2_VERSION>";
private static final Map<Target, String> NATIVE_LIBRARIES = Map.of(
new Target(Util.OS.WINDOWS, true), "windows64.dll",
new Target(Util.OS.WINDOWS, false), "window32.dll",
new Target(Util.OS.LINUX, true), "linux64.so"
//new Target(Util.OS.OSX, true), "mac64.dylib"
new Target(Util.OS.WINDOWS, true, false), "windows64.dll",
new Target(Util.OS.WINDOWS, false, false), "window32.dll",
new Target(Util.OS.LINUX, true, false), "linux64.so",
new Target(Util.OS.OSX, true, false), "macosx64.dylib",
new Target(Util.OS.OSX, true, true), "macosxarm64.dylib"
);
private static final String NATIVE_LIBRARY_URL = "https://maven.isxander.dev/releases/dev/isxander/sdl2-jni-natives/%s/".formatted(SDL2_VERSION);
@ -41,6 +44,16 @@ public class SDL2NativesManager {
Path localLibraryPath = Target.CURRENT.getLocalNativePath();
if (Files.notExists(localLibraryPath)) {
if (Files.exists(localLibraryPath.getParent())) {
try(var walk = Files.walk(localLibraryPath.getParent())) {
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEachOrdered(File::delete);
} catch (Exception e) {
Controlify.LOGGER.error("Failed to delete old SDL2 native library", e);
}
}
Controlify.LOGGER.info("Downloading SDL2 native library: " + Target.CURRENT.getArtifactName());
downloadLibrary(localLibraryPath);
}
@ -110,12 +123,13 @@ public class SDL2NativesManager {
return loaded;
}
private record Target(Util.OS os, boolean is64Bit) {
private record Target(Util.OS os, boolean is64Bit, boolean isARM) {
public static final Target CURRENT = Util.make(() -> {
Util.OS os = Util.getPlatform();
boolean is64bit = System.getProperty("os.arch").contains("64");
boolean isARM = System.getProperty("os.arch").contains("arm");
return new Target(os, is64bit);
return new Target(os, is64bit, isARM);
});
public boolean hasNativeLibrary() {