From 355952783c1984b6004d2f7329b850bd45063b50 Mon Sep 17 00:00:00 2001 From: Diego Waxemberg Date: Sun, 29 Dec 2013 22:56:12 -0500 Subject: [PATCH] fixed memory leak caused by creating many DefaultControllerEnvironment objects --- .../input/gamepad/GamepadListener.java | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/com/limelight/input/gamepad/GamepadListener.java b/src/com/limelight/input/gamepad/GamepadListener.java index 076513d..553530f 100644 --- a/src/com/limelight/input/gamepad/GamepadListener.java +++ b/src/com/limelight/input/gamepad/GamepadListener.java @@ -1,6 +1,9 @@ package com.limelight.input.gamepad; -import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.LinkedList; import java.util.logging.Level; import java.util.logging.Logger; @@ -17,6 +20,8 @@ import net.java.games.input.ControllerEnvironment; public class GamepadListener { private static Thread listenerThread; private static NvConnection conn; + private static Field controllers; + private static Method scan; /** * starts a thread to listen to controllers @@ -36,22 +41,26 @@ public class GamepadListener { * This is really janky, but it is currently the only way to rescan for controllers. * The DefaultControllerEnvironment class caches the results of scanning and if a controller is * unplugged or plugged in, it will not detect it. Since DefaultControllerEnvironment is package-protected - * we have to use reflections in order to manually instantiate a new instance to ensure there is no caching. - * Supposedly Aaron is going to fix JInput and we will have the ability to rescan soon! + * we have to use reflections in order to manually get access to the private field caching the controllers + * and the method to scan for controllers */ try { //#allthejank - Constructor construct = null; - - Class defEnv = ControllerEnvironment.getDefaultEnvironment().getClass(); - construct = defEnv.getDeclaredConstructor(); - construct.setAccessible(true); - ControllerEnvironment defaultEnv = null; + ControllerEnvironment defaultEnv = ControllerEnvironment.getDefaultEnvironment(); + //must be called so the controllers array list will not be null + defaultEnv.getControllers(); + + controllers = defaultEnv.getClass().getDeclaredField("controllers"); + controllers.setAccessible(true); + + scan = defaultEnv.getClass().getDeclaredMethod("scanControllers", (Class[])null); + scan.setAccessible(true); while(!isInterrupted()) { - defaultEnv = (ControllerEnvironment)construct.newInstance(); + rescanControllers(defaultEnv); + Controller[] ca = defaultEnv.getControllers(); LinkedList gamepads = new LinkedList(); @@ -88,6 +97,30 @@ public class GamepadListener { return false; } + /* + * Janky method that clears the controller environment's list of controllers and tells it to scan for new ones. + * #allthejank + */ + @SuppressWarnings("rawtypes") + private static void rescanControllers(ControllerEnvironment defaultEnv) { + try { + ((ArrayList)controllers.get(defaultEnv)).clear(); + scan.invoke(defaultEnv, (Object[])null); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + /** * Stops listening for controllers, ie. stops the thread if it is running */