diff --git a/build.gradle.kts b/build.gradle.kts index 21d536f..eaf5d85 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -47,6 +47,13 @@ dependencies { implementation(libs.mixin.extras) annotationProcessor(libs.mixin.extras) include(libs.mixin.extras) + + implementation(libs.hid4java) + include(libs.hid4java) +} + +machete { + } tasks { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9fd9628..c0c13b2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ fabric_api = "0.73.3+1.19.4" mixin_extras = "0.2.0-beta.1" yet_another_config_lib = "2.2.0+update.1.19.4-SNAPSHOT" mod_menu = "6.0.0-beta.1" +hid4java = "0.7.0" [libraries] minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } @@ -23,6 +24,7 @@ fabric_api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fab mixin_extras = { module = "com.github.llamalad7:mixinextras", version.ref = "mixin_extras" } yet_another_config_lib = { module = "dev.isxander:yet-another-config-lib", version.ref = "yet_another_config_lib" } mod_menu = { module = "com.terraformersmc:modmenu", version.ref = "mod_menu" } +hid4java = { module = "org.hid4java:hid4java", version.ref = "hid4java" } [plugins] loom = { id = "fabric-loom", version.ref = "loom" } diff --git a/src/main/java/dev/isxander/controlify/Controlify.java b/src/main/java/dev/isxander/controlify/Controlify.java index c92193c..c8e4372 100644 --- a/src/main/java/dev/isxander/controlify/Controlify.java +++ b/src/main/java/dev/isxander/controlify/Controlify.java @@ -5,6 +5,7 @@ import dev.isxander.controlify.compatibility.screen.ScreenProcessorProvider; import dev.isxander.controlify.config.ControlifyConfig; import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.ControllerState; +import dev.isxander.controlify.controller.hid.ControllerHIDService; import dev.isxander.controlify.event.ControlifyEvents; import dev.isxander.controlify.ingame.InGameInputHandler; import dev.isxander.controlify.mixins.feature.virtualmouse.MouseHandlerAccessor; @@ -24,51 +25,65 @@ public class Controlify { private InGameInputHandler inGameInputHandler; private VirtualMouseHandler virtualMouseHandler; private InputMode currentInputMode; + private ControllerHIDService controllerHIDService; private final ControlifyConfig config = new ControlifyConfig(); public void onInitializeInput() { Minecraft minecraft = Minecraft.getInstance(); + inGameInputHandler = new InGameInputHandler(Controller.DUMMY); // initialize with dummy controller before connection in case of no controllers + controllerHIDService = new ControllerHIDService(); + // find already connected controllers for (int i = 0; i < GLFW.GLFW_JOYSTICK_LAST; i++) { if (GLFW.glfwJoystickPresent(i)) { - setCurrentController(Controller.byId(i)); - LOGGER.info("Controller found: " + currentController.name()); + int jid = i; + controllerHIDService.awaitNextDevice(device -> { + setCurrentController(Controller.create(jid, device)); + LOGGER.info("Controller found: " + currentController.name()); + }); } } + controllerHIDService.start(); + // load after initial controller discovery config().load(); // listen for new controllers GLFW.glfwSetJoystickCallback((jid, event) -> { if (event == GLFW.GLFW_CONNECTED) { - setCurrentController(Controller.byId(jid)); - LOGGER.info("Controller connected: " + currentController.name()); - this.setCurrentInputMode(InputMode.CONTROLLER); + controllerHIDService.awaitNextDevice(device -> { + setCurrentController(Controller.create(jid, device)); + LOGGER.info("Controller connected: " + currentController.name() + " (" + device.getPath() + ")"); + this.setCurrentInputMode(InputMode.CONTROLLER); - config().load(); // load config again if a configuration already exists for this controller - config().save(); // save config if it doesn't exist + config().load(); // load config again if a configuration already exists for this controller + config().save(); // save config if it doesn't exist + + minecraft.getToasts().addToast(SystemToast.multiline( + minecraft, + SystemToast.SystemToastIds.PERIODIC_NOTIFICATION, + Component.translatable("controlify.toast.controller_connected.title"), + Component.translatable("controlify.toast.controller_connected.description") + )); + }); - minecraft.getToasts().addToast(SystemToast.multiline( - minecraft, - SystemToast.SystemToastIds.PERIODIC_NOTIFICATION, - Component.translatable("controlify.toast.controller_connected.title"), - Component.translatable("controlify.toast.controller_connected.description") - )); } else if (event == GLFW.GLFW_DISCONNECTED) { var controller = Controller.CONTROLLERS.remove(jid); - setCurrentController(Controller.CONTROLLERS.values().stream().filter(Controller::connected).findFirst().orElse(null)); - LOGGER.info("Controller disconnected: " + controller.name()); - this.setCurrentInputMode(currentController == null ? InputMode.KEYBOARD_MOUSE : InputMode.CONTROLLER); + if (controller != null) { + setCurrentController(Controller.CONTROLLERS.values().stream().filter(Controller::connected).findFirst().orElse(null)); + LOGGER.info("Controller disconnected: " + controller.name()); + this.setCurrentInputMode(currentController == null ? InputMode.KEYBOARD_MOUSE : InputMode.CONTROLLER); - minecraft.getToasts().addToast(SystemToast.multiline( - minecraft, - SystemToast.SystemToastIds.PERIODIC_NOTIFICATION, - Component.translatable("controlify.toast.controller_disconnected.title"), - Component.translatable("controlify.toast.controller_disconnected.description", controller.name()) - )); + minecraft.getToasts().addToast(SystemToast.multiline( + minecraft, + SystemToast.SystemToastIds.PERIODIC_NOTIFICATION, + Component.translatable("controlify.toast.controller_disconnected.title"), + Component.translatable("controlify.toast.controller_disconnected.description", controller.name()) + )); + } } }); diff --git a/src/main/java/dev/isxander/controlify/bindings/Bind.java b/src/main/java/dev/isxander/controlify/bindings/Bind.java index db81e57..544ae7d 100644 --- a/src/main/java/dev/isxander/controlify/bindings/Bind.java +++ b/src/main/java/dev/isxander/controlify/bindings/Bind.java @@ -28,12 +28,10 @@ public enum Bind { private final BiFunction state; private final String identifier; - private final ResourceLocation textureLocation; Bind(BiFunction state, String identifier) { this.state = state; this.identifier = identifier; - this.textureLocation = new ResourceLocation("controlify", "textures/gui/buttons/xbox/" + identifier + ".png"); } Bind(Function state, String identifier) { @@ -48,8 +46,8 @@ public enum Bind { return identifier; } - public ResourceLocation textureLocation() { - return textureLocation; + public ResourceLocation textureLocation(Controller controller) { + return new ResourceLocation("controlify", "textures/gui/buttons/" + controller.config().theme.id(controller) + "/" + identifier + ".png"); } public static Bind fromIdentifier(String identifier) { diff --git a/src/main/java/dev/isxander/controlify/bindings/ControllerTheme.java b/src/main/java/dev/isxander/controlify/bindings/ControllerTheme.java new file mode 100644 index 0000000..aa4227a --- /dev/null +++ b/src/main/java/dev/isxander/controlify/bindings/ControllerTheme.java @@ -0,0 +1,28 @@ +package dev.isxander.controlify.bindings; + +import dev.isxander.controlify.controller.Controller; +import dev.isxander.yacl.api.NameableEnum; +import net.minecraft.network.chat.Component; + +import java.util.function.Function; + +public enum ControllerTheme implements NameableEnum { + AUTO(c -> c.type().theme().id(c)), + XBOX_ONE(c -> "xbox"), + DUALSHOCK4(c -> "dualshock4"); + + private final Function idGetter; + + ControllerTheme(Function idGetter) { + this.idGetter = idGetter; + } + + public String id(Controller controller) { + return idGetter.apply(controller); + } + + @Override + public Component getDisplayName() { + return Component.translatable("controlify.controller_theme." + name().toLowerCase()); + } +} diff --git a/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java b/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java index 6546a5e..b7af1f4 100644 --- a/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java +++ b/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java @@ -57,7 +57,7 @@ public class ControlifyConfig { for (var controller : Controller.CONTROLLERS.values()) { // `add` replaces if already existing // TODO: find a better way to identify controllers, GUID will report the same for multiple controllers of the same model - newControllerData.add(controller.guid(), generateControllerConfig(controller)); + newControllerData.add(controller.uid(), generateControllerConfig(controller)); } controllerData = newControllerData; @@ -84,7 +84,7 @@ public class ControlifyConfig { JsonObject controllers = object.getAsJsonObject("controllers"); if (controllers != null) { for (var controller : Controller.CONTROLLERS.values()) { - var settings = controllers.getAsJsonObject(controller.guid()); + var settings = controllers.getAsJsonObject(controller.uid()); if (settings != null) { applyControllerConfig(controller, settings); } diff --git a/src/main/java/dev/isxander/controlify/config/gui/BindButtonController.java b/src/main/java/dev/isxander/controlify/config/gui/BindButtonController.java index ddc9c52..74066a0 100644 --- a/src/main/java/dev/isxander/controlify/config/gui/BindButtonController.java +++ b/src/main/java/dev/isxander/controlify/config/gui/BindButtonController.java @@ -19,9 +19,11 @@ import org.lwjgl.glfw.GLFW; public class BindButtonController implements Controller { private final Option option; + private final dev.isxander.controlify.controller.Controller controller; - public BindButtonController(Option option) { + public BindButtonController(Option option, dev.isxander.controlify.controller.Controller controller) { this.option = option; + this.controller = controller; } @Override @@ -52,7 +54,7 @@ public class BindButtonController implements Controller { if (awaitingControllerInput) { textRenderer.drawShadow(matrices, awaitingText, getDimension().xLimit() - textRenderer.width(awaitingText) - getXPadding(), getDimension().centerY() - textRenderer.lineHeight / 2f, 0xFFFFFF); } else { - ButtonRenderer.drawButton(control.option().pendingValue(), matrices, getDimension().xLimit() - ButtonRenderer.BUTTON_SIZE / 2, getDimension().centerY(), ButtonRenderer.BUTTON_SIZE); + ButtonRenderer.drawButton(control.option().pendingValue(), control.controller, matrices, getDimension().xLimit() - ButtonRenderer.BUTTON_SIZE / 2, getDimension().centerY(), ButtonRenderer.BUTTON_SIZE); } } diff --git a/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java b/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java index 23275a4..96ebd3a 100644 --- a/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java +++ b/src/main/java/dev/isxander/controlify/config/gui/YACLHelper.java @@ -2,11 +2,13 @@ package dev.isxander.controlify.config.gui; import dev.isxander.controlify.Controlify; import dev.isxander.controlify.bindings.Bind; +import dev.isxander.controlify.bindings.ControllerTheme; import dev.isxander.controlify.config.GlobalSettings; import dev.isxander.controlify.controller.Controller; import dev.isxander.controlify.controller.ControllerConfig; import dev.isxander.yacl.api.*; import dev.isxander.yacl.gui.controllers.cycling.CyclingListController; +import dev.isxander.yacl.gui.controllers.cycling.EnumController; import dev.isxander.yacl.gui.controllers.slider.FloatSliderController; import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController; import dev.isxander.yacl.gui.controllers.string.StringController; @@ -102,6 +104,13 @@ public class YACLHelper { .tooltip(Component.translatable("controlify.gui.right_trigger_threshold.tooltip")) .binding(def.rightTriggerActivationThreshold, () -> config.rightTriggerActivationThreshold, v -> config.rightTriggerActivationThreshold = v) .controller(opt -> new FloatSliderController(opt, 0, 1, 0.05f, v -> Component.literal(String.format("%.0f%%", v*100)))) + .build()) + .option(Option.createBuilder(ControllerTheme.class) + .name(Component.translatable("controlify.gui.controller_theme")) + .tooltip(Component.translatable("controlify.gui.controller_theme.tooltip")) + .binding(def.theme, () -> config.theme, v -> config.theme = v) + .controller(EnumController::new) + .instant(true) .build()); category.group(configGroup.build()); @@ -111,7 +120,7 @@ public class YACLHelper { controlsGroup.option(Option.createBuilder(Bind.class) .name(control.name()) .binding(control.defaultBind(), control::currentBind, control::setCurrentBind) - .controller(BindButtonController::new) + .controller(opt -> new BindButtonController(opt, controller)) .build()); } category.group(controlsGroup.build()); diff --git a/src/main/java/dev/isxander/controlify/controller/Controller.java b/src/main/java/dev/isxander/controlify/controller/Controller.java index 0a9a3dc..1f726e3 100644 --- a/src/main/java/dev/isxander/controlify/controller/Controller.java +++ b/src/main/java/dev/isxander/controlify/controller/Controller.java @@ -1,7 +1,10 @@ package dev.isxander.controlify.controller; import dev.isxander.controlify.bindings.ControllerBindings; +import dev.isxander.controlify.bindings.ControllerTheme; +import dev.isxander.controlify.controller.hid.HIDIdentifier; import dev.isxander.controlify.event.ControlifyEvents; +import org.hid4java.HidDevice; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWGamepadState; @@ -11,12 +14,14 @@ import java.util.Objects; public final class Controller { public static final Map CONTROLLERS = new HashMap<>(); - public static final Controller DUMMY = new Controller(-1, "DUMMY", "DUMMY", false); + public static final Controller DUMMY = new Controller(-1, "DUMMY", "DUMMY", false, "DUMMY", ControllerType.UNKNOWN); - private final int id; + private final int joystickId; private final String guid; private final String name; private final boolean gamepad; + private final String uid; + private final ControllerType type; private ControllerState state = ControllerState.EMPTY; private ControllerState prevState = ControllerState.EMPTY; @@ -24,11 +29,13 @@ public final class Controller { private final ControllerBindings bindings = new ControllerBindings(this); private ControllerConfig config = new ControllerConfig(); - public Controller(int id, String guid, String name, boolean gamepad) { - this.id = id; + public Controller(int joystickId, String guid, String name, boolean gamepad, String uid, ControllerType type) { + this.joystickId = joystickId; this.guid = guid; this.name = name; this.gamepad = gamepad; + this.uid = uid; + this.type = type; } public ControllerState state() { @@ -63,24 +70,32 @@ public final class Controller { } public boolean connected() { - return GLFW.glfwJoystickPresent(id); + return GLFW.glfwJoystickPresent(joystickId); } GLFWGamepadState getGamepadState() { GLFWGamepadState state = GLFWGamepadState.create(); if (gamepad) - GLFW.glfwGetGamepadState(id, state); + GLFW.glfwGetGamepadState(joystickId, state); return state; } public int id() { - return id; + return joystickId; } public String guid() { return guid; } + public String uid() { + return uid; + } + + public ControllerType type() { + return type; + } + public String name() { if (config().customName != null) return config().customName; @@ -112,7 +127,7 @@ public final class Controller { return Objects.hash(guid); } - public static Controller byId(int id) { + public static Controller create(int id, HidDevice device) { if (id > GLFW.GLFW_JOYSTICK_LAST) throw new IllegalArgumentException("Invalid joystick id: " + id); if (CONTROLLERS.containsKey(id)) @@ -120,10 +135,16 @@ public final class Controller { String guid = GLFW.glfwGetJoystickGUID(id); boolean gamepad = GLFW.glfwJoystickIsGamepad(id); - String name = gamepad ? GLFW.glfwGetGamepadName(id) : GLFW.glfwGetJoystickName(id); - if (name == null) name = Integer.toString(id); + String fallbackName = gamepad ? GLFW.glfwGetGamepadName(id) : GLFW.glfwGetJoystickName(id); + String uid = device.getPath(); + ControllerType type = ControllerType.getTypeForHID(new HIDIdentifier(device.getVendorId(), device.getProductId())); + String name = type != ControllerType.UNKNOWN || fallbackName == null ? type.friendlyName() : fallbackName; + int tries = 1; + while (CONTROLLERS.values().stream().map(Controller::name).anyMatch(name::equals)) { + name = type.friendlyName() + " (" + tries++ + ")"; + } - Controller controller = new Controller(id, guid, name, gamepad); + Controller controller = new Controller(id, guid, name, gamepad, uid, type); CONTROLLERS.put(id, controller); return controller; diff --git a/src/main/java/dev/isxander/controlify/controller/ControllerConfig.java b/src/main/java/dev/isxander/controlify/controller/ControllerConfig.java index 57330a8..a5d27bc 100644 --- a/src/main/java/dev/isxander/controlify/controller/ControllerConfig.java +++ b/src/main/java/dev/isxander/controlify/controller/ControllerConfig.java @@ -1,5 +1,7 @@ package dev.isxander.controlify.controller; +import dev.isxander.controlify.bindings.ControllerTheme; + public class ControllerConfig { public static final ControllerConfig DEFAULT = new ControllerConfig(); @@ -20,5 +22,7 @@ public class ControllerConfig { public float virtualMouseSensitivity = 1f; + public ControllerTheme theme = ControllerTheme.AUTO; + public String customName = null; } diff --git a/src/main/java/dev/isxander/controlify/controller/ControllerType.java b/src/main/java/dev/isxander/controlify/controller/ControllerType.java new file mode 100644 index 0000000..397c61e --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controller/ControllerType.java @@ -0,0 +1,63 @@ +package dev.isxander.controlify.controller; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import dev.isxander.controlify.bindings.ControllerTheme; +import dev.isxander.controlify.controller.hid.HIDIdentifier; + +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +public enum ControllerType { + UNKNOWN("Unknown Controller", ControllerTheme.XBOX_ONE), + XBOX_ONE("Xbox Controller", ControllerTheme.XBOX_ONE), + XBOX_360("Xbox 360 Controller", ControllerTheme.XBOX_ONE), + DUALSHOCK4("PS4 Controller", ControllerTheme.DUALSHOCK4); + + private static final Gson GSON = new GsonBuilder().setLenient().create(); + private static Map typeMap = null; + + private final String friendlyName; + private final ControllerTheme theme; + + ControllerType(String friendlyName, ControllerTheme theme) { + this.friendlyName = friendlyName; + this.theme = theme; + } + + public String friendlyName() { + return friendlyName; + } + + public ControllerTheme theme() { + return theme; + } + + public static ControllerType getTypeForHID(HIDIdentifier hid) { + if (typeMap != null) return typeMap.getOrDefault(hid, UNKNOWN); + + typeMap = new HashMap<>(); + try { + try (var hidDb = ControllerType.class.getResourceAsStream("/hiddb.json5")) { + var json = GSON.fromJson(new InputStreamReader(hidDb), JsonObject.class); + for (var type : ControllerType.values()) { + if (!json.has(type.name().toLowerCase())) continue; + + var themeJson = json.getAsJsonObject(type.name().toLowerCase()); + + int vendorId = themeJson.get("vendor").getAsInt(); + for (var productIdEntry : themeJson.getAsJsonArray("product")) { + int productId = productIdEntry.getAsInt(); + typeMap.put(new HIDIdentifier(vendorId, productId), type); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return typeMap.getOrDefault(hid, UNKNOWN); + } +} diff --git a/src/main/java/dev/isxander/controlify/controller/hid/ControllerHIDService.java b/src/main/java/dev/isxander/controlify/controller/hid/ControllerHIDService.java new file mode 100644 index 0000000..64f5b75 --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controller/hid/ControllerHIDService.java @@ -0,0 +1,68 @@ +package dev.isxander.controlify.controller.hid; + +import dev.isxander.controlify.Controlify; +import org.hid4java.*; +import org.hid4java.event.HidServicesEvent; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.Set; +import java.util.function.Consumer; + +public class ControllerHIDService implements HidServicesListener { + // https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/hid-usages#usage-page + private static final Set CONTROLLER_USAGE_IDS = Set.of( + 0x04, // Joystick + 0x05, // Gamepad + 0x08 // Multi-axis Controller + ); + + private final HidServicesSpecification specification; + private final Queue> deviceQueue; + + public ControllerHIDService() { + this.deviceQueue = new ArrayDeque<>(); + + this.specification = new HidServicesSpecification(); + specification.setAutoStart(false); + specification.setScanInterval(2000); // long interval, so we can guarantee this runs after GLFW hook + } + + public void start() { + var services = HidManager.getHidServices(specification); + services.addHidServicesListener(this); + + services.start(); + } + + public void awaitNextDevice(Consumer consumer) { + deviceQueue.add(consumer); + } + + @Override + public void hidDeviceAttached(HidServicesEvent event) { + var device = event.getHidDevice(); + + if (isController(device)) { + if (deviceQueue.peek() != null) { + deviceQueue.poll().accept(event.getHidDevice()); + } + } + } + + private boolean isController(HidDevice device) { + var isGenericDesktopControlOrGameControl = device.getUsagePage() == 0x1 || device.getUsagePage() == 0x5; + var isController = CONTROLLER_USAGE_IDS.contains(device.getUsage()); + return isGenericDesktopControlOrGameControl && isController; + } + + @Override + public void hidDeviceDetached(HidServicesEvent event) { + + } + + @Override + public void hidFailure(HidServicesEvent event) { + + } +} diff --git a/src/main/java/dev/isxander/controlify/controller/hid/HIDIdentifier.java b/src/main/java/dev/isxander/controlify/controller/hid/HIDIdentifier.java new file mode 100644 index 0000000..d3f82ed --- /dev/null +++ b/src/main/java/dev/isxander/controlify/controller/hid/HIDIdentifier.java @@ -0,0 +1,4 @@ +package dev.isxander.controlify.controller.hid; + +public record HIDIdentifier(int vendorId, int productId) { +} diff --git a/src/main/java/dev/isxander/controlify/gui/ButtonRenderer.java b/src/main/java/dev/isxander/controlify/gui/ButtonRenderer.java index d121f01..4f27501 100644 --- a/src/main/java/dev/isxander/controlify/gui/ButtonRenderer.java +++ b/src/main/java/dev/isxander/controlify/gui/ButtonRenderer.java @@ -3,13 +3,14 @@ package dev.isxander.controlify.gui; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import dev.isxander.controlify.bindings.Bind; +import dev.isxander.controlify.controller.Controller; import net.minecraft.client.gui.GuiComponent; public class ButtonRenderer { public static final int BUTTON_SIZE = 22; - public static void drawButton(Bind button, PoseStack poseStack, int x, int y, int size) { - RenderSystem.setShaderTexture(0, button.textureLocation()); + public static void drawButton(Bind button, Controller controller, PoseStack poseStack, int x, int y, int size) { + RenderSystem.setShaderTexture(0, button.textureLocation(controller)); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); GuiComponent.blit(poseStack, x - size / 2, y - size / 2, 0, 0, BUTTON_SIZE, BUTTON_SIZE, BUTTON_SIZE, BUTTON_SIZE); diff --git a/src/main/resources/assets/controlify/lang/en_us.json b/src/main/resources/assets/controlify/lang/en_us.json index 93eeb77..e7c4d55 100644 --- a/src/main/resources/assets/controlify/lang/en_us.json +++ b/src/main/resources/assets/controlify/lang/en_us.json @@ -25,6 +25,8 @@ "controlify.gui.left_trigger_threshold.tooltip": "How far the left trigger needs to be pushed before registering as pressed.", "controlify.gui.right_trigger_threshold": "Right Trigger Threshold", "controlify.gui.right_trigger_threshold.tooltip": "How far the right trigger needs to be pushed before registering as pressed.", + "controlify.gui.controller_theme": "Controller Theme", + "controlify.gui.controller_theme.tooltip": "The theme to use for rendering controller buttons.", "controlify.gui.group.controls": "Controls", "controlify.gui.group.controls.tooltip": "Adjust the controller controls.", @@ -43,6 +45,10 @@ "controlify.toast.controller_disconnected.title": "Controller Disconnected", "controlify.toast.controller_disconnected.description": "'%s' was disconnected.", + "controlify.controller_theme.auto": "Auto", + "controlify.controller_theme.xbox_one": "Xbox", + "controlify.controller_theme.dualshock4": "PS4", + "controlify.binding.controlify.jump": "Jump", "controlify.binding.controlify.sneak": "Sneak", "controlify.binding.controlify.attack": "Attack", diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_bumper_left.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_bumper_left.png deleted file mode 100644 index a8e61b4..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_bumper_left.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_bumper_right.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_bumper_right.png deleted file mode 100644 index 378c324..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_bumper_right.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_down.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_down.png deleted file mode 100644 index 80980c1..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_down.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_left.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_left.png deleted file mode 100644 index 518f3f6..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_left.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_right.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_right.png deleted file mode 100644 index efa1a2b..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_right.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_up.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_up.png deleted file mode 100644 index a856341..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_dpad_up.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_down.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_down.png deleted file mode 100644 index 906300f..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_down.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_left.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_left.png deleted file mode 100644 index d8c2780..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_left.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_right.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_right.png deleted file mode 100644 index bb60df9..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_right.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_up.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_up.png deleted file mode 100644 index e03a123..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_face_button_up.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_left_trigger.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_left_trigger.png deleted file mode 100644 index ff2f695..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_left_trigger.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_right_trigger.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_right_trigger.png deleted file mode 100644 index 8d40b06..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_right_trigger.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_select_button.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_select_button.png deleted file mode 100644 index 19604e2..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_select_button.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_stick_left.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_stick_left.png deleted file mode 100644 index b403672..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_stick_left.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_stick_right.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_stick_right.png deleted file mode 100644 index 9726759..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_stick_right.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_touchpad.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_touchpad.png deleted file mode 100644 index 0997317..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock/ps4_touchpad.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/a_button.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/a_button.png new file mode 100644 index 0000000..3f36fa8 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/a_button.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/b_button.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/b_button.png new file mode 100644 index 0000000..3ffc30a Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/b_button.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/back.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/back.png new file mode 100644 index 0000000..f8d6f74 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/back.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_down.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_down.png new file mode 100644 index 0000000..59682a2 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_down.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_left.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_left.png new file mode 100644 index 0000000..268dafa Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_left.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_right.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_right.png new file mode 100644 index 0000000..f56807f Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_right.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_up.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_up.png new file mode 100644 index 0000000..b253225 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/dpad_up.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_bumper.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_bumper.png new file mode 100644 index 0000000..42ffb2f Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_bumper.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_stick.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_stick.png new file mode 100644 index 0000000..22a24ba Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_stick.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_trigger.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_trigger.png new file mode 100644 index 0000000..2e5a8f1 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/left_trigger.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_bumper.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_bumper.png new file mode 100644 index 0000000..1ccabb8 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_bumper.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_stick.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_stick.png new file mode 100644 index 0000000..a415a10 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_stick.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_trigger.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_trigger.png new file mode 100644 index 0000000..a0e295e Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/right_trigger.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/start.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/start.png new file mode 100644 index 0000000..9d73671 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/start.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/x_button.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/x_button.png new file mode 100644 index 0000000..93d6629 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/x_button.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/y_button.png b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/y_button.png new file mode 100644 index 0000000..5203598 Binary files /dev/null and b/src/main/resources/assets/controlify/textures/gui/buttons/dualshock4/y_button.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/left_bumper_big.png b/src/main/resources/assets/controlify/textures/gui/buttons/xbox/left_bumper_big.png deleted file mode 100644 index 194a408..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/left_bumper_big.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/left_trigger_big.png b/src/main/resources/assets/controlify/textures/gui/buttons/xbox/left_trigger_big.png deleted file mode 100644 index e7fc042..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/left_trigger_big.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/right_bumper_big.png b/src/main/resources/assets/controlify/textures/gui/buttons/xbox/right_bumper_big.png deleted file mode 100644 index 6bc380e..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/right_bumper_big.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/right_trigger_big.png b/src/main/resources/assets/controlify/textures/gui/buttons/xbox/right_trigger_big.png deleted file mode 100644 index 01c9651..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/right_trigger_big.png and /dev/null differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/start.png b/src/main/resources/assets/controlify/textures/gui/buttons/xbox/start.png index 359e589..ee7deac 100644 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/start.png and b/src/main/resources/assets/controlify/textures/gui/buttons/xbox/start.png differ diff --git a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/xbox_dpad.png b/src/main/resources/assets/controlify/textures/gui/buttons/xbox/xbox_dpad.png deleted file mode 100644 index b6494e2..0000000 Binary files a/src/main/resources/assets/controlify/textures/gui/buttons/xbox/xbox_dpad.png and /dev/null differ diff --git a/src/main/resources/hiddb.json5 b/src/main/resources/hiddb.json5 new file mode 100644 index 0000000..865580d --- /dev/null +++ b/src/main/resources/hiddb.json5 @@ -0,0 +1,30 @@ +// THIS FILE IS PARSED BY LENIENT GSON PARSER AND IS NOT JSON5 COMPLIANT! +{ + "xbox_one": { + "vendor": 1118, // 0x45e + "friendly_name": "Xbox One Controller", + "product": [ + 767, // 0x2ff + 746, // 0x2ea + 2834, // 0xb12 + 733, // 0x2dd + 739, // 0x2e3 + 742, // 0x2e6 + 765, // 0x2fd + 721, // 0x2d1 + 649, // 0x289 + 514, // 0x202 + 645, // 0x285 + 648 // 0x288 + ] + }, + "dualshock4": { + "vendor": 1356, // 0x54c + "friendly_name": "PS4 Controller", + "product": [ + 1476, // 0x5c4 + 2508, // 0x9cc + 2976 // 0xba0 + ] + } +}