Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import dev.isxander.controlify.driver.steamdeck.SteamDeckDriver;
import dev.isxander.controlify.driver.steamdeck.SteamDeckUtil;
import dev.isxander.controlify.hid.ControllerHIDService;
import dev.isxander.controlify.hid.ControllerSpecify;
import dev.isxander.controlify.hid.HIDDevice;
import dev.isxander.controlify.hid.HIDID;
import dev.isxander.controlify.utils.CUtil;
Expand Down Expand Up @@ -78,6 +79,11 @@ public void tick(boolean outOfFocus) {
SDL_JoystickID jid = event.jdevice.which;
logger.validateIsTrue(jid != null, "event.jdevice.which was null during SDL_EVENT_JOYSTICK_ADDED event");

if (!isAssignedToThisInstance(jid)) {
logger.debugLog("Ignoring hotplugged joystick {} - not assigned to this instance", jid.intValue());
break;
}

logger.debugLog("SDL event: Joystick added: {}", jid.intValue());

UniqueControllerID ucid = new SDLUniqueControllerID(jid);
Expand Down Expand Up @@ -111,12 +117,39 @@ public void tick(boolean outOfFocus) {
super.tick(outOfFocus);
}

private boolean isAssignedToThisInstance(SDL_JoystickID jid) {
ControllerSpecify cfg = ControllerSpecify.get();
if (!cfg.isFilteringEnabled()) return true;

SDL_Joystick tempHandle = SDL_OpenJoystick(jid);
if (tempHandle == null) {
logger.warn("Could not open joystick {} to check serial: {}", jid.intValue(), SDL_GetError());
return false;
}

try {
String serial = SDL_GetJoystickSerial(tempHandle);
if (serial == null) {
logger.warn("Joystick {} reported no serial number - cannot filter by MAC for this device", jid.intValue());
return false;
}
return ControllerSpecify.normalizeMac(serial).equals(ControllerSpecify.normalizeMac(cfg.getAssignedControllerMac()));
} finally {
SDL_CloseJoystick(tempHandle);
}
}

@Override
public void discoverControllers() {
logger.debugLog("Discovering controllers...");

SDL_JoystickID[] joysticks = SDL_GetJoysticks();
for (SDL_JoystickID jid : joysticks) {
if (!isAssignedToThisInstance(jid)) {
logger.debugLog("Skipping joystick {} - not assigned to this instance", jid.intValue());
continue;
}

Optional<ControllerEntity> controllerOpt = tryCreate(
new SDLUniqueControllerID(jid),
fetchTypeFromSDL(jid)
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/dev/isxander/controlify/hid/ControllerSpecify.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package dev.isxander.controlify.hid;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import dev.isxander.controlify.platform.main.PlatformMainUtil;
import dev.isxander.controlify.utils.CUtil;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;

public class ControllerSpecify {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Path CONFIG_PATH = PlatformMainUtil.getConfigDir().resolve("controlify-controllerspecify.json");

private static ControllerSpecify instance;

private String assignedControllerMac = "";

public static ControllerSpecify get() {
if (instance == null) {
instance = load();
}
return instance;
}

private static ControllerSpecify load() {
if (Files.exists(CONFIG_PATH)) {
try {
String json = Files.readString(CONFIG_PATH);
ControllerSpecify loaded = GSON.fromJson(json, ControllerSpecify.class);
if (loaded != null) {
CUtil.LOGGER.log("Loaded ControllerSpecify config: assignedControllerMac='{}'", loaded.assignedControllerMac);
return loaded;
}
} catch (IOException e) {
CUtil.LOGGER.error("Failed to read controlify-controllerspecify.json, using default", e);
}
}

ControllerSpecify fresh = new ControllerSpecify();
fresh.save();
CUtil.LOGGER.log("Created default controlify-controllerspecify.json at {} - set assignedControllerMac to restrict this instance to one controller", CONFIG_PATH);
return fresh;
}

public void save() {
try {
Files.writeString(CONFIG_PATH, GSON.toJson(this));
} catch (IOException e) {
CUtil.LOGGER.error("Failed to write controlify-controllerspecify.json", e);
}
}

public boolean isFilteringEnabled() {
return assignedControllerMac != null && !assignedControllerMac.isBlank();
}

public String getAssignedControllerMac() {
return assignedControllerMac;
}

public void setAssignedControllerMac(String mac) {
this.assignedControllerMac = mac;
save();
}

public static String normalizeMac(String mac) {
if (mac == null) return "";
return mac.replace(":", "").replace("-", "").trim().toLowerCase(Locale.ROOT);
}
}