joystick axis rendering (no textures), improve config error handling and fix joystick deadzones being unordered
@ -125,7 +125,10 @@ public class ControllerBindings<T extends ControllerState> {
|
||||
public void fromJson(JsonObject json) {
|
||||
for (var binding : registry().values()) {
|
||||
var bind = json.get(binding.id().toString()).getAsJsonObject();
|
||||
if (bind == null) continue;
|
||||
if (bind == null) {
|
||||
Controlify.LOGGER.warn("Unknown control: " + binding.id() + " in config file. Skipping!");
|
||||
continue;
|
||||
}
|
||||
binding.setCurrentBind(IBind.fromJson(bind, controller));
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ public enum GamepadBind implements IBind<GamepadState> {
|
||||
public void draw(PoseStack matrices, int x, int centerY, Controller<GamepadState, ?> controller) {
|
||||
ResourceLocation texture;
|
||||
if (((GamepadConfig)controller.config()).theme == BuiltinGamepadTheme.DEFAULT) {
|
||||
texture = new ResourceLocation("controlify", "textures/gui/gamepad_buttons/" + controller.type().identifier() + "/" + identifier + ".png");
|
||||
texture = new ResourceLocation("controlify", "textures/gui/gamepad/" + controller.type().identifier() + "/" + identifier + ".png");
|
||||
} else {
|
||||
texture = textureLocations.get(((GamepadConfig)controller.config()).theme);
|
||||
}
|
||||
|
@ -32,14 +32,10 @@ public interface IBind<S extends ControllerState> {
|
||||
case JoystickButtonBind.BIND_ID -> JoystickButtonBind.fromJson(json, joystick);
|
||||
case JoystickHatBind.BIND_ID -> JoystickHatBind.fromJson(json, joystick);
|
||||
case JoystickAxisBind.BIND_ID -> JoystickAxisBind.fromJson(json, joystick);
|
||||
default -> {
|
||||
Controlify.LOGGER.error("Unknown bind type: " + type);
|
||||
yield new EmptyBind<>();
|
||||
}
|
||||
default -> throw new IllegalStateException("Unknown bind type for joystick: " + type);
|
||||
};
|
||||
}
|
||||
|
||||
Controlify.LOGGER.error("Could not parse bind for controller: " + controller.name());
|
||||
return new EmptyBind<>();
|
||||
throw new IllegalStateException("Unknown controller type: " + controller.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,18 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickController;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickState;
|
||||
import dev.isxander.controlify.controller.joystick.mapping.JoystickMapping;
|
||||
import dev.isxander.controlify.controller.joystick.mapping.UnmappedJoystickMapping;
|
||||
import dev.isxander.controlify.gui.DrawSize;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -35,22 +40,32 @@ public class JoystickAxisBind implements IBind<JoystickState> {
|
||||
|
||||
@Override
|
||||
public void draw(PoseStack matrices, int x, int centerY, Controller<JoystickState, ?> controller) {
|
||||
var font = Minecraft.getInstance().font;
|
||||
font.drawShadow(matrices, getTempButtonName(), x + 1.5f, centerY - font.lineHeight / 2f, 0xFFFFFF);
|
||||
if (controller != joystick) return;
|
||||
JoystickMapping mapping = joystick.mapping();
|
||||
|
||||
String type = joystick.type().identifier();
|
||||
String axis = mapping.axis(axisIndex).identifier();
|
||||
String direction = mapping.axis(axisIndex).getDirectionIdentifier(axisIndex, this.direction);
|
||||
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/axis_" + axis + "_" + direction + ".png");
|
||||
|
||||
RenderSystem.setShaderTexture(0, texture);
|
||||
RenderSystem.setShaderColor(1, 1, 1, 1);
|
||||
GuiComponent.blit(matrices, x, centerY - 11, 0, 0, 22, 22, 22, 22);
|
||||
|
||||
if (mapping instanceof UnmappedJoystickMapping) {
|
||||
var text = Integer.toString(axisIndex + 1);
|
||||
var font = Minecraft.getInstance().font;
|
||||
GuiComponent.drawCenteredString(matrices, font, text, x + 11, centerY - font.lineHeight / 2, 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DrawSize drawSize() {
|
||||
var font = Minecraft.getInstance().font;
|
||||
return new DrawSize(font.width(getTempButtonName()) + 3, font.lineHeight);
|
||||
}
|
||||
int width = 22;
|
||||
if (joystick.mapping() instanceof UnmappedJoystickMapping)
|
||||
width = Math.max(width, Minecraft.getInstance().font.width(Integer.toString(axisIndex + 1)));
|
||||
|
||||
private Component getTempButtonName() {
|
||||
var axis = joystick.mapping().axis(axisIndex);
|
||||
return Component.empty()
|
||||
.append(axis.name())
|
||||
.append(" ")
|
||||
.append(axis.getDirectionName(axisIndex, direction));
|
||||
return new DrawSize(width, 22);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,13 +1,16 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickController;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickState;
|
||||
import dev.isxander.controlify.gui.DrawSize;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -29,19 +32,20 @@ public class JoystickButtonBind implements IBind<JoystickState> {
|
||||
|
||||
@Override
|
||||
public void draw(PoseStack matrices, int x, int centerY, Controller<JoystickState, ?> controller) {
|
||||
var font = Minecraft.getInstance().font;
|
||||
if (controller != joystick) return;
|
||||
|
||||
font.drawShadow(matrices, getTempButtonName(), x + 1.5f, centerY - font.lineHeight / 2f, 0xFFFFFF);
|
||||
String type = joystick.type().identifier();
|
||||
String button = joystick.mapping().button(buttonIndex).identifier();
|
||||
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/button_" + button + ".png");
|
||||
|
||||
RenderSystem.setShaderTexture(0, texture);
|
||||
RenderSystem.setShaderColor(1, 1, 1, 1);
|
||||
GuiComponent.blit(matrices, x, centerY - 11, 0, 0, 22, 22, 22, 22);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DrawSize drawSize() {
|
||||
var font = Minecraft.getInstance().font;
|
||||
return new DrawSize(font.width(getTempButtonName()) + 3, font.lineHeight);
|
||||
}
|
||||
|
||||
private Component getTempButtonName() {
|
||||
return joystick.mapping().button(buttonIndex).name();
|
||||
return new DrawSize(22, 22);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,13 +1,16 @@
|
||||
package dev.isxander.controlify.bindings;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickController;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickState;
|
||||
import dev.isxander.controlify.gui.DrawSize;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -31,21 +34,30 @@ public class JoystickHatBind implements IBind<JoystickState> {
|
||||
|
||||
@Override
|
||||
public void draw(PoseStack matrices, int x, int centerY, Controller<JoystickState, ?> controller) {
|
||||
var font = Minecraft.getInstance().font;
|
||||
font.drawShadow(matrices, getTempButtonName(), x + 1.5f, centerY - font.lineHeight / 2f, 0xFFFFFF);
|
||||
if (controller != joystick) return;
|
||||
|
||||
String type = joystick.type().identifier();
|
||||
String button = joystick.mapping().button(hatIndex).identifier();
|
||||
String direction = "centered";
|
||||
if (hatState.isUp())
|
||||
direction = "up";
|
||||
else if (hatState.isDown())
|
||||
direction = "down";
|
||||
else if (hatState.isLeft())
|
||||
direction = "left";
|
||||
else if (hatState.isRight())
|
||||
direction = "right";
|
||||
|
||||
var texture = new ResourceLocation("controlify", "textures/gui/joystick/" + type + "/hat" + button + "_" + direction + ".png");
|
||||
|
||||
RenderSystem.setShaderTexture(0, texture);
|
||||
RenderSystem.setShaderColor(1, 1, 1, 1);
|
||||
GuiComponent.blit(matrices, x, centerY - 11, 0, 0, 22, 22, 22, 22);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DrawSize drawSize() {
|
||||
var font = Minecraft.getInstance().font;
|
||||
return new DrawSize(font.width(getTempButtonName()) + 3, font.lineHeight);
|
||||
}
|
||||
|
||||
private Component getTempButtonName() {
|
||||
return Component.empty()
|
||||
.append(joystick.mapping().hat(hatIndex).name())
|
||||
.append(" ")
|
||||
.append(hatState.getDisplayName());
|
||||
return new DrawSize(22, 22);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -105,8 +105,14 @@ public class ControlifyConfig {
|
||||
}
|
||||
|
||||
private void applyControllerConfig(Controller<?, ?> controller, JsonObject object) {
|
||||
controller.setConfig(GSON, object.getAsJsonObject("config"));
|
||||
controller.bindings().fromJson(object.getAsJsonObject("bindings"));
|
||||
try {
|
||||
controller.setConfig(GSON, object.getAsJsonObject("config"));
|
||||
controller.bindings().fromJson(object.getAsJsonObject("bindings"));
|
||||
} catch (Exception e) {
|
||||
Controlify.LOGGER.error("Failed to load controller data for " + controller.uid() + ". Resetting to default!", e);
|
||||
controller.resetConfig();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
public GlobalSettings globalSettings() {
|
||||
|
@ -27,6 +27,7 @@ import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@ -190,7 +191,8 @@ public class YACLHelper {
|
||||
.collect(Collectors.toMap(
|
||||
i -> joystick.mapping().axis(i).identifier(),
|
||||
i -> i,
|
||||
(x, y) -> x
|
||||
(x, y) -> x,
|
||||
LinkedHashMap::new
|
||||
))
|
||||
.values();
|
||||
var jsCfg = joystick.config();
|
||||
|
@ -97,6 +97,11 @@ public abstract class AbstractController<S extends ControllerState, C extends Co
|
||||
return this.defaultConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConfig() {
|
||||
this.config = defaultConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(Gson gson, JsonElement json) {
|
||||
C newConfig = gson.fromJson(json, new TypeToken<C>(getClass()){}.getType());
|
||||
|
@ -5,6 +5,7 @@ import com.google.gson.JsonElement;
|
||||
import dev.isxander.controlify.bindings.ControllerBindings;
|
||||
import dev.isxander.controlify.controller.gamepad.GamepadController;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickController;
|
||||
import dev.isxander.controlify.debug.DebugProperties;
|
||||
import org.hid4java.HidDevice;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
@ -24,6 +25,7 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
|
||||
|
||||
C config();
|
||||
C defaultConfig();
|
||||
void resetConfig();
|
||||
void setConfig(Gson gson, JsonElement json);
|
||||
|
||||
ControllerType type();
|
||||
@ -39,7 +41,7 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
|
||||
return CONTROLLERS.get(joystickId);
|
||||
}
|
||||
|
||||
if (GLFW.glfwJoystickIsGamepad(joystickId)) {
|
||||
if (GLFW.glfwJoystickIsGamepad(joystickId) && !DebugProperties.FORCE_JOYSTICK) {
|
||||
GamepadController controller = new GamepadController(joystickId, device);
|
||||
CONTROLLERS.put(joystickId, controller);
|
||||
return controller;
|
||||
@ -94,6 +96,11 @@ public interface Controller<S extends ControllerState, C extends ControllerConfi
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConfig() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(Gson gson, JsonElement json) {
|
||||
|
||||
|
@ -6,17 +6,12 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class JoystickConfig extends ControllerConfig {
|
||||
private final Map<String, Float> deadzones;
|
||||
private Map<String, Float> deadzones;
|
||||
|
||||
private transient JoystickController controller;
|
||||
|
||||
public JoystickConfig(JoystickController controller) {
|
||||
this.controller = controller;
|
||||
deadzones = new HashMap<>();
|
||||
for (int i = 0; i < controller.axisCount(); i++) {
|
||||
if (controller.mapping().axis(i).requiresDeadzone())
|
||||
deadzones.put(controller.mapping().axis(i).identifier(), 0.2f);
|
||||
}
|
||||
setup(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -35,7 +30,14 @@ public class JoystickConfig extends ControllerConfig {
|
||||
return deadzones.getOrDefault(controller.mapping().axis(axis).identifier(), 0.2f);
|
||||
}
|
||||
|
||||
void setController(JoystickController controller) {
|
||||
void setup(JoystickController controller) {
|
||||
this.controller = controller;
|
||||
if (this.deadzones == null) {
|
||||
deadzones = new HashMap<>();
|
||||
for (int i = 0; i < controller.axisCount(); i++) {
|
||||
if (controller.mapping().axis(i).requiresDeadzone())
|
||||
deadzones.put(controller.mapping().axis(i).identifier(), 0.2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,6 @@ public class JoystickController extends AbstractController<JoystickState, Joysti
|
||||
@Override
|
||||
public void setConfig(Gson gson, JsonElement json) {
|
||||
super.setConfig(gson, json);
|
||||
this.config.setController(this);
|
||||
this.config.setup(this);
|
||||
}
|
||||
}
|
||||
|
@ -133,9 +133,8 @@ public class DataJoystickMapping implements JoystickMapping {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDirectionName(int axis, JoystickAxisBind.AxisDirection direction) {
|
||||
var directionId = axisNames().get(ids.indexOf(axis)).get(direction.ordinal());
|
||||
return Component.translatable("controlify.joystick_mapping." + typeId() + ".axis." + identifier() + "." + directionId);
|
||||
public String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction) {
|
||||
return this.axisNames().get(ids.indexOf(axis)).get(direction.ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,6 +143,8 @@ public class DataJoystickMapping implements JoystickMapping {
|
||||
public Component name() {
|
||||
return Component.translatable("controlify.joystick_mapping." + typeId() + ".button." + identifier());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private record HatMapping(String identifier, String typeId) implements Hat {
|
||||
|
@ -21,7 +21,7 @@ public interface JoystickMapping {
|
||||
|
||||
boolean isAxisResting(float value);
|
||||
|
||||
Component getDirectionName(int axis, JoystickAxisBind.AxisDirection direction);
|
||||
String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction);
|
||||
}
|
||||
|
||||
interface Button {
|
||||
|
@ -24,36 +24,36 @@ public class UnmappedJoystickMapping implements JoystickMapping {
|
||||
private record UnmappedAxis(int axis) implements Axis {
|
||||
|
||||
@Override
|
||||
public String identifier() {
|
||||
public String identifier() {
|
||||
return "axis-" + axis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component name() {
|
||||
return Component.translatable("controlify.joystick_mapping.unmapped.axis", axis + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresDeadzone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float modifyAxis(float value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAxisResting(float value) {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDirectionName(int axis, JoystickAxisBind.AxisDirection direction) {
|
||||
return Component.translatable("controlify.joystick_mapping.unmapped.axis_direction." + direction.name().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component name() {
|
||||
return Component.translatable("controlify.joystick_mapping.unmapped.axis", axis + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresDeadzone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float modifyAxis(float value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAxisResting(float value) {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDirectionIdentifier(int axis, JoystickAxisBind.AxisDirection direction) {
|
||||
return direction.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
private record UnmappedButton(int button) implements Button {
|
||||
@Override
|
||||
public String identifier() {
|
||||
|
@ -0,0 +1,12 @@
|
||||
package dev.isxander.controlify.debug;
|
||||
|
||||
public class DebugProperties {
|
||||
// Renders debug overlay for vmouse snapping
|
||||
public static final boolean DEBUG_SNAPPING = boolProp("controlify.debug.snapping", false);
|
||||
// Forces all gamepads to be treated as a regular joystick
|
||||
public static final boolean FORCE_JOYSTICK = boolProp("controlify.debug.force_joystick", false);
|
||||
|
||||
private static boolean boolProp(String name, boolean def) {
|
||||
return Boolean.parseBoolean(System.getProperty(name, Boolean.toString(def)));
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import com.mojang.datafixers.util.Pair;
|
||||
import dev.isxander.controlify.Controlify;
|
||||
import dev.isxander.controlify.InputMode;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.debug.DebugProperties;
|
||||
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
|
||||
import dev.isxander.controlify.event.ControlifyEvents;
|
||||
import dev.isxander.controlify.mixins.feature.virtualmouse.KeyboardHandlerAccessor;
|
||||
@ -26,7 +27,6 @@ import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
|
||||
public class VirtualMouseHandler {
|
||||
private static final boolean DEBUG_SNAPPING = FabricLoader.getInstance().isDevelopmentEnvironment();
|
||||
private static final ResourceLocation CURSOR_TEXTURE = new ResourceLocation("controlify", "textures/gui/virtual_mouse.png");
|
||||
|
||||
private double targetX, targetY;
|
||||
@ -201,7 +201,7 @@ public class VirtualMouseHandler {
|
||||
public void renderVirtualMouse(PoseStack matrices) {
|
||||
if (!virtualMouseEnabled) return;
|
||||
|
||||
if (DEBUG_SNAPPING) {
|
||||
if (DebugProperties.DEBUG_SNAPPING) {
|
||||
for (var snapPoint : snapPoints) {
|
||||
GuiComponent.fill(matrices, snapPoint.position().x() - snapPoint.range(), snapPoint.position().y() - snapPoint.range(), snapPoint.position().x() + snapPoint.range(), snapPoint.position().y() + snapPoint.range(), 0x33FFFFFF);
|
||||
GuiComponent.fill(matrices, snapPoint.position().x() - 1, snapPoint.position().y() - 1, snapPoint.position().x() + 1, snapPoint.position().y() + 1, snapPoint.equals(lastSnappedPoint) ? 0xFFFFFF00 : 0xFFFF0000);
|
||||
|
@ -1,25 +1,25 @@
|
||||
// THIS FILE IS PARSED BY LENIENT GSON PARSER AND IS NOT JSON5 COMPLIANT!
|
||||
[
|
||||
{
|
||||
"name": "Xbox One Controller",
|
||||
"identifier": "xbox_one",
|
||||
|
||||
"vendor": 1118, // 0x45e
|
||||
"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
|
||||
]
|
||||
},
|
||||
// {
|
||||
// "name": "Xbox One Controller",
|
||||
// "identifier": "xbox_one",
|
||||
//
|
||||
// "vendor": 1118, // 0x45e
|
||||
// "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
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
"name": "Dualshock 4 Controller",
|
||||
"identifier": "dualshock4",
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 241 B |
Before Width: | Height: | Size: 205 B After Width: | Height: | Size: 205 B |
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 281 B |
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 296 B |
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 256 B After Width: | Height: | Size: 256 B |
Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 288 B |
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 242 B |
Before Width: | Height: | Size: 251 B After Width: | Height: | Size: 251 B |
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 361 B After Width: | Height: | Size: 361 B |
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 372 B |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 282 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 282 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |