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

Detect controller hotplugging #2182

Closed
Getterac7 opened this issue Jul 30, 2014 · 26 comments
Closed

Detect controller hotplugging #2182

Getterac7 opened this issue Jul 30, 2014 · 26 comments

Comments

@Getterac7
Copy link
Contributor

It would be nice to be able to listen for controller (joystick) connect and disconnect events so a user could add a controller and jump in during the middle of a game.

If that is not possible, maybe an API to scan for controller changes manually.

@GreenLightning
Copy link
Contributor

There is the ControllerListener#connect() method. Register a controller listener / adapter for all controllers using Controllers.addListener(...). Here's the wiki page for more information.

@MobiDevelop
Copy link
Member

I think the request is to make it work on Desktop, which as I understand it currently does not.

@Getterac7
Copy link
Contributor Author

Right, this request was to detect newly connected controllers on the desktop. 'Hotplug' support, if you will.
Currently the Controllers.getControllers() list does not update when a new controller is connected (unsure about when an existing controller is disconnected).

@GreenLightning
Copy link
Contributor

I just tested this. The disconnect method is not called, the controller object stays in the global controller list and it just reports the last state of the controller. I would love Hotplug support for desktop as well!

@badlogic badlogic changed the title Request: Detect controller hotplugging Detect controller hotplugging Aug 31, 2014
@ChetRippo
Copy link
Contributor

I'd love to have this too, but it seems like it may be a big hassle to implement and won't come anytime soon. The only Java library that I've gotten to reliably poll for these events is JInput, but in order to do so you have to reinitialize the environment via reflection, which is hacky and performance intensive.

@Cevantime
Copy link

Hi, there. For now (1.5.3 libGdx version), it seems that the libGdx controller for desktop extension has not been rewritten yet. But it is easy to walk around this problem by using reflection. You just have to reinstantiate the com.badlogic.gdx.controllers.desktop.DesktopControllerManager class as your controller manager.
Here is a method you can use in your Game to reload controllers.

private Array<Controller> reloadControllers(){
    String controllersClassName = "com.badlogic.gdx.controllers.Controllers";
    try {
        //very hacky and dirty method to update controllers
        Class controllersClass = Class.forName(controllersClassName);
        Field managersField = controllersClass.getDeclaredField("managers");
        managersField.setAccessible(true);
        ObjectMap<Application, ControllerManager> managers = (ObjectMap<Application, ControllerManager>) managersField.get(null);
        managersField.setAccessible(false);
        String desktopControllerManagerClassName = "com.badlogic.gdx.controllers.desktop.DesktopControllerManager";
        managers.put(Gdx.app, (ControllerManager) Class.forName(desktopControllerManagerClassName).newInstance());
Field runnablesField = Class.forName("com.badlogic.gdx.backends.lwjgl.LwjglApplication").getDeclaredField("runnables");
        runnablesField.setAccessible(true);
        Array<Runnable> runnables = (Array<Runnable>) runnablesField.get(Gdx.app);
        runnablesField.setAccessible(false);
        runnables.removeIndex(runnables.size-1);
    } catch (ClassNotFoundException ex) {
        Logger.getLogger(Gamepad.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchFieldException ex) {
        Logger.getLogger(Gamepad.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SecurityException ex) {
        Logger.getLogger(Gamepad.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalArgumentException ex) {
        Logger.getLogger(Gamepad.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        Logger.getLogger(Gamepad.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        Logger.getLogger(Gamepad.class.getName()).log(Level.SEVERE, null, ex);
    }
    return Controllers.getControllers();
}

This is of course very dirty, but you're not supposed to invoke this method each time the render method is called (you better not!). Use a Timer for this :

@Override
public void create () {
        //... just add this at the end of your create method for instance
    if(Gdx.app.getType() == Application.ApplicationType.Desktop){
        Timer.schedule(new Timer.Task() {

            @Override
            public void run() {
                reloadControllers();
            }
        }, 0.5f, 0.5f);
    }
}

EFIT: after further researches, it seems that the DesktopManager post a runnable to libGdx that we have to clean up after reinstantiate it. It has been added to the previous code... and it's even more horrible

@bobbyrne01
Copy link

Would love this functionality. Want to avoid hacks that have been mentioned previously.

@derrick56007
Copy link

Same here. Listening for controller connects/disconnects without doing anything similar to the above would be great.

@badlogic
Copy link
Member

badlogic commented Jan 5, 2016

This has been fixed in the new LWJGL 3 backend.

@badlogic badlogic closed this as completed Jan 5, 2016
@the-coding-fox
Copy link

Can you explain how to get this working?
The connect() and disconnect() methods are not firing on desktop and I have the most recent version of libgdx.

@Cevantime
Copy link

You have to put the '1.7.3-SNAPSHOT' as gdxVersion in your build.gradle file.
You also have to change dependencies for desktop :

compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"

becomes

compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"

Then reload the project. In your DesktopLauncher class, you have to replace the regular LwjglApplicationConfiguration and LwjglApplication classes by the new Lwjgl3ApplicationConfiguration and Lwjgl3Application classes. The name of the controller manager is also different : Lwjgl3ControllerManager.
It's worth noting that you can't add listeners directly to your controllers. You'll have to add your listeners directly to an instance of Lwjgl3ControllerManager (and you'll have to declare the instance).

Lwjgl3ControllerManager manager = new Lwjgl3ControllerManager();
manager.addListener(myListener);

I guess the implementation is not really finished yet but it's testable

@Jack5496
Copy link

Thanks, tired the dirty way, but lets see how this works

@Jack5496
Copy link

hmm still got an Error.
Chanced to '1.7.3.....' reloaded, took a small bit of time and had to reimport things but no problem,
but Eclipse still cant find class Lwjgl3ApplicationConfiguration

@Cevantime
Copy link

Okay, my mistake. I forgot to mention some other modifcations in the build.gradle file (I edited my previous post)

@the-coding-fox
Copy link

But the current GDX version is 1.9.0. How is it that I have to load 1.7.3-SNAPSHOT to get it working?

@the-coding-fox
Copy link

I followed the instructions from here: https://github.com/libgdx/libgdx/wiki/Controllers

Simply check the "Controllers" checkbox in the gdx-setup app. For LWJGL 3, replace the dependency to gdx-controllers-desktop with gdx-controllers-lwjgl3 and remove the compile "com.badlogicgames.gdx:gdx-controllers-platform:$gdxVersion:natives-desktop" dependency in your desktop project.

It seems to load LWJGL3 just for the controller (which is better than loading it for the whole project), but I get an exception "Error creating controller manager: com.badlogic.gdx.controllers.desktop.DesktopControllerManager"

I did also test doing the changes you mentioned before, and I get the same problem. I would rather not change the whole project to LWJGL3 because it doesn't have the ability to set the window's icon.

@badlogic
Copy link
Member

You will have to use the LWJGL 3 backend in connection with the LWJGL 3
controller backend.
On Jan 30, 2016 4:28 AM, "Alexander" notifications@github.com wrote:

I followed the instructions from here:
https://github.com/libgdx/libgdx/wiki/Controllers

Simply check the "Controllers" checkbox in the gdx-setup app. For LWJGL 3,
replace the dependency to gdx-controllers-desktop with
gdx-controllers-lwjgl3 and remove the compile
"com.badlogicgames.gdx:gdx-controllers-platform:$gdxVersion:natives-desktop"
dependency in your desktop project.

It seems to load LWJGL3 just for the controller (which is better than
loading it for the whole project), but I get an exception "Error creating
controller manager:
com.badlogic.gdx.controllers.desktop.DesktopControllerManager"

I did also test doing the changes you mentioned before, and I get the same
problem. I would rather not change the whole project to LWJGL3 because it
doesn't have the ability to set the window's icon.


Reply to this email directly or view it on GitHub
#2182 (comment).

@the-coding-fox
Copy link

I've tested using LWJGL3 for both the backend and the controller, the program runs now, but the disconnect() method still does not get called when I unplug the controller.

These are the dependencies I'm using for desktop. I've tried both 1.9.0 and 1.7.3-SNAPSHOT for the $gdxVersion.

    dependencies {
        compile project(":core")
        compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
        compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
        compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
        compile "com.badlogicgames.gdx:gdx-tools:$gdxVersion"
        compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion"
    }

@badlogic
Copy link
Member

What OS?
On Jan 30, 2016 11:05 PM, "Alexander" notifications@github.com wrote:

I've tested using LWJGL3 for both the backend and the controller, the
program runs now, but the disconnect() method still does not get called
when I unplug the controller.

These are the dependencies I'm using for desktop. I've tried both 1.9.0
and 1.7.3-SNAPSHOT for the $gdxVersion.

dependencies {
    compile project(":core")
    compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
    compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
    compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
    compile "com.badlogicgames.gdx:gdx-tools:$gdxVersion"
    compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion"
}


Reply to this email directly or view it on GitHub
#2182 (comment).

@the-coding-fox
Copy link

The device is using Windows 8.1

@CreamyCookie
Copy link
Contributor

@the-coding-fox Is this still an issue?

@Cevantime
Copy link

As far as i know, lwjgl3 backend has not become the default implementation of lwjgl of libgdx yet (1.9.3 version). You still have to adapt your dependencies to make hotplugging work. I guess the issue will be quiet fixed when lwjgl3 will be default (and stable) and hotplugging working out of the box !

@fatalcubez
Copy link

So how can I get the lwjgl3 backend working for my libGDX project? I'm on version 1.9.3. I've tried changing the gradle build to include the lwjgl3 under desktop dependencies, but the controller api still fails to recognize that I have the lwjgl3 backend.

@CreamyCookie
Copy link
Contributor

CreamyCookie commented Aug 10, 2016

@fatalcubez Have you done everything here to enable lwjgl3: #3673 ?

You also need to replace gdx-controllers-desktop with
gdx-controllers-lwjgl3 and remove the
compile "com.badlogicgames.gdx:gdx-controllers-platform:$gdxVersion:natives-desktop"
dependency in your desktop project.

@fatalcubez
Copy link

So I set everything up, but now I'm getting this exception:

Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Users\Scott\AppData\Local\Temp\libgdxScott\68069333\lwjgl.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary1(ClassLoader.java:1965)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1890)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1851)
at java.lang.Runtime.load0(Runtime.java:795)
at java.lang.System.load(System.java:1062)
at org.lwjgl.system.Library$1.load(Library.java:25)
at org.lwjgl.system.Library$1.load(Library.java:22)
at org.lwjgl.system.Library.loadLibrary(Library.java:196)
at org.lwjgl.system.Library.loadSystem(Library.java:90)
at org.lwjgl.system.Library.(Library.java:48)
at org.lwjgl.system.MemoryUtil.(MemoryUtil.java:56)
at org.lwjgl.system.libffi.Closure.(Closure.java:57)
at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.initializeGlfw(Lwjgl3Application.java:66)
at com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application.(Lwjgl3Application.java:75)
at com.fullspectrum.game.desktop.DesktopLauncher.main(DesktopLauncher.java:26)

From what I've read about people who are having a similar issue but with different library dll files, the suggestion to change the JVM to be 32-bit. However, I was wondering if there is a fix for this so I can keep everything 64-bit. Thanks!

@fatalcubez
Copy link

For anyone who is wondering, it turns out that if you have the new lwjgl3 as well as the old lwjgl versions then you'll get this error. The source of my problem was that I was using gdx-tools which relies on the older lwjgl versions. Once I got rid of that in the gradle build and refreshed my project, the old lwjgl jars went away and the program ran without problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests