Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to hotplugging on Desktop. #31

Merged
merged 2 commits into from
Apr 27, 2023
Merged
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ allprojects {
appName = 'gdx-controllers'
gdxVersion = '1.9.11'
roboVMVersion = '2.3.16'
jamepadVersion = '2.0.20.0'
jamepadVersion = '2.26.4.0'

isReleaseBuild = {
return project.hasProperty("RELEASE")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ public JamepadControllerManager() {
monitor.run();

Gdx.app.addLifecycleListener(new JamepadShutdownHook(controllerManager));
Gdx.app.postRunnable(monitor);
MrStahlfelge marked this conversation as resolved.
Show resolved Hide resolved

nativeLibInitialized = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ public class JamepadController implements Controller {
}

private final CompositeControllerListener compositeControllerListener = new CompositeControllerListener();
private final ControllerIndex controllerIndex;
private final IntMap<Boolean> buttonState = new IntMap<>();
private final IntMap<Float> axisState = new IntMap<>();
private final String uuid;
private final String name;
private ControllerIndex controllerIndex;
private boolean connected = true;
private Boolean canVibrate = null;
private long vibrationEndMs;
Expand All @@ -43,6 +44,7 @@ public class JamepadController implements Controller {
public JamepadController(ControllerIndex controllerIndex) {
this.controllerIndex = controllerIndex;
this.uuid = UUID.randomUUID().toString();
this.name = getInitialName();
initializeState();
}

Expand All @@ -51,7 +53,7 @@ public boolean getButton(final int buttonCode) {
try {
ControllerButton button = toButton(buttonCode);
return button != null && controllerIndex.isButtonPressed(button);
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
}
return false;
Expand All @@ -67,26 +69,36 @@ public float getAxis(final int axisCode) {
} else {
return controllerIndex.getAxisState(axis);
}
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
}
return 0f;
}

@Override
public String getName() {
private String getInitialName() {
try {
return controllerIndex.getName();
MrStahlfelge marked this conversation as resolved.
Show resolved Hide resolved
} catch (ControllerUnpluggedException e) {
setDisconnected();
} catch (ControllerUnpluggedException | NullPointerException e) {
// this is only called in the constructor, so disconnecting here wouldn't make sense
}
return "Unknown";
}

private void setDisconnected() {
@Override
public String getName() {
return name;
}

public void setControllerIndex(ControllerIndex controllerIndex) {
this.controllerIndex = controllerIndex;
}

public void setDisconnected() {
if (connected) {
connected = false;
logger.info("Failed querying controller at index: " + controllerIndex.getIndex());
if (controllerIndex != null) {
logger.info("Failed querying controller at index: " + controllerIndex.getIndex());
}
compositeControllerListener.disconnected(this);
}
}
Expand Down Expand Up @@ -165,7 +177,7 @@ public boolean canVibrate() {
if (canVibrate == null) {
try {
canVibrate = controllerIndex.canVibrate();
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
}
}
Expand All @@ -185,7 +197,7 @@ public void startVibration(int duration, float strength) {
vibrationEndMs = TimeUtils.millis() + duration;
canVibrate = true;
}
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
}
}
Expand All @@ -212,7 +224,7 @@ public boolean supportsPlayerIndex() {
public int getPlayerIndex() {
try {
return controllerIndex.getPlayerIndex();
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
return PLAYER_IDX_UNSET;
}
Expand All @@ -222,7 +234,7 @@ public int getPlayerIndex() {
public void setPlayerIndex(int index) {
try {
controllerIndex.setPlayerIndex(index);
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
}
}
Expand All @@ -243,7 +255,7 @@ public int getMaxButtonIndex() {
while (maxButtonIndex > 0 && !controllerIndex.isButtonAvailable(CODE_TO_BUTTON.get(maxButtonIndex))) {
maxButtonIndex--;
}
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
}

Expand All @@ -261,7 +273,7 @@ public int getAxisCount() {
while (axisCount > 0 && !controllerIndex.isAxisAvailable(CODE_TO_AXIS.get(axisCount - 1))) {
axisCount--;
}
} catch (ControllerUnpluggedException e) {
} catch (ControllerUnpluggedException | NullPointerException e) {
setDisconnected();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,107 @@

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.controllers.ControllerListener;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.controllers.desktop.JamepadControllerManager;
import com.studiohartman.jamepad.ControllerIndex;
import com.studiohartman.jamepad.ControllerManager;
import com.studiohartman.jamepad.ControllerUnpluggedException;

public class JamepadControllerMonitor implements Runnable {
private final ControllerManager controllerManager;
private final ControllerListener listener;
private final IntMap<Tuple> indexToController = new IntMap<>();
//temporary array for cleaning disconnected controllers
private final IntArray disconnectedControllers = new IntArray();
private final IntMap<Tuple> indexToController
= new IntMap<>(JamepadControllerManager.jamepadConfiguration.maxNumControllers);
// temporary array for delaying connect messages
private final Array<JamepadController> connectedControllers = new Array<JamepadController>();

public JamepadControllerMonitor(ControllerManager controllerManager, ControllerListener listener) {
this.controllerManager = controllerManager;
this.listener = listener;

reconcileControllers();
}

@Override
public void run() {
controllerManager.update();
boolean controllersChanged = controllerManager.update();

if (controllersChanged) {
reconcileControllers();
}

checkForNewControllers();
update();

Gdx.app.postRunnable(this);
}

private void checkForNewControllers() {
private void reconcileControllers() {
// Break old connections to help detect disconnected objects later.
for (Tuple tuple : indexToController.values()) {
JamepadController controller = tuple.controller;
tuple.index = null;
controller.setControllerIndex(null);
}

// Get already-connected controllers paired with their existing objects.
// Create objects for new controllers, but don't send connect messages yet.
connectedControllers.clear();
int numControllers = JamepadControllerManager.jamepadConfiguration.maxNumControllers;
for (int i = 0; i < numControllers; i++) try {
ControllerIndex controllerIndex = controllerManager.getControllerIndex(i);

if (!indexToController.containsKey(controllerIndex.getIndex()) && controllerIndex.isConnected()) {
Tuple tuple1 = new Tuple(controllerIndex);
tuple1.controller.addListener(listener);

indexToController.put(controllerIndex.getIndex(), tuple1);
listener.connected(tuple1.controller);
try {
int instanceID = controllerIndex.getDeviceInstanceID();
if (indexToController.containsKey(instanceID)) {
// Pre-existing controller, pair with existing object.
Tuple tuple1 = indexToController.get(instanceID);
tuple1.index = controllerIndex;
tuple1.controller.setControllerIndex(controllerIndex);
} else {
// New controller. Create new object, and store it for connect message later.
Tuple tuple1 = new Tuple(controllerIndex);
indexToController.put(instanceID, tuple1);
connectedControllers.add(tuple1.controller);
}
} catch (ControllerUnpluggedException e) {
// controller not connected, no need to pair it
}
} catch (ArrayIndexOutOfBoundsException t) {
// more controllers connected than we can handle according to our config
}

// Remove disconnected objects.
IntMap.Values<Tuple> values = indexToController.values();
while (values.hasNext()) {
Tuple tuple = values.next();
if (tuple.index == null) {
tuple.controller.setDisconnected();
values.remove();
}
}

// Set up listeners for new controllers and send connect messages.
for (JamepadController controller: connectedControllers) {
controller.addListener(listener);
listener.connected(controller);
}
}

private void update() {
disconnectedControllers.clear();
for (Tuple tuple : indexToController.values()) {
IntMap.Values<Tuple> values = indexToController.values();
while (values.hasNext()) {
Tuple tuple = values.next();
JamepadController controller = tuple.controller;
boolean connected = controller.update();

if (!connected) {
disconnectedControllers.add(tuple.index.getIndex());
values.remove();
}
}

for (int i = 0; i < disconnectedControllers.size; i++) {
indexToController.remove(disconnectedControllers.get(i));
}
}

private class Tuple {
public final ControllerIndex index;
public ControllerIndex index;
public final JamepadController controller;

public Tuple(ControllerIndex index) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ public void disconnected(Controller controller) {
}
};

refreshControllersList();

Controllers.addListener(new ControllerAdapter() {
@Override
public void connected(final Controller controller) {
Expand Down Expand Up @@ -226,6 +224,8 @@ public void changed(ChangeEvent event, Actor actor) {
stage.addActor(table);

Gdx.input.setInputProcessor(stage);

refreshControllersList();
}

private void addAxisLabels() {
Expand Down Expand Up @@ -293,6 +293,9 @@ private void refreshControllersList() {
if (name.length() > 30)
name = name.substring(0, 28) + "...";
controllerNames.add(name);

// avoid adding the listener to the same controller twice
controller.removeListener(controllerListener);
controller.addListener(controllerListener);
}
controllerList.setItems(controllerNames);
Expand Down