LWJGLMouseInput ArrayOutOfBounds fix

Quick patch, just needed a check for mice with less than 3 buttons like ThinkPad touchpads.



Index: D:/workspace/jme/jme2/src/com/jme/input/lwjgl/LWJGLMouseInput.java
===================================================================
--- D:/workspace/jme/jme2/src/com/jme/input/lwjgl/LWJGLMouseInput.java   (revision 3898)
+++ D:/workspace/jme/jme2/src/com/jme/input/lwjgl/LWJGLMouseInput.java   (working copy)
@@ -118,7 +118,7 @@
     * @see com.jme.input.MouseInput#isButtonDown(int)
     */
    public boolean isButtonDown(int buttonCode) {
-      return buttonPressed[buttonCode];
+      return buttonCode < buttonPressed.length ? buttonPressed[buttonCode] : false;
    }
 
    /**
@@ -473,6 +473,8 @@
    }
    
    public void clearButton(int buttonCode) {
-      buttonPressed[buttonCode] = false;
+      if (buttonCode < buttonPressed.length) {
+         buttonPressed[buttonCode] = false;
+      }
    }
 }

Makes sense.  thanks.

Hi,



I have experienced the same problem with my Dell laptop's touch pad. It only has 2 buttons, but when you press both at the same time it counts as a 3rd button (middle mouse button). This is when I got the ArrayOutOfBounds exception.



Although the patch submitted by the parent of this thread works, it also disables the "middle mouse button" on my touch pad. This is the fix that I made:


### Eclipse Workspace Patch 1.0
#P jme2
Index: src/com/jme/input/lwjgl/LWJGLMouseInput.java
===================================================================
--- src/com/jme/input/lwjgl/LWJGLMouseInput.java   (revision 3929)
+++ src/com/jme/input/lwjgl/LWJGLMouseInput.java   (working copy)
@@ -199,7 +199,15 @@
                boolean pressed = button >= 0
                      && Mouse.getEventButtonState();
                if (button >= 0) {
-                  buttonPressed[button] = pressed;
+                  try {
+                     buttonPressed[button] = pressed;
+                  } catch (ArrayIndexOutOfBoundsException e) {
+                     boolean[] tempArr = buttonPressed;
+                     buttonPressed = new boolean[button + 1];
+                     System.arraycopy(tempArr, 0, buttonPressed, 0,
+                           tempArr.length);
+                     buttonPressed[button] = pressed;
+                  }
                }
 
                int wheelDelta = Mouse.getEventDWheel();



If the ArrayIndexOutOfBounds exception happens, it gets caught and the array gets resized. This will ONLY happen once when the middle mouse button is pressed for the first time, thus it will not impact on performance.

I think it is a better solution than checking every time with 'if' statements as the parent has done. And it also doesn't disable the middle mouse button functionality.

Feedback is welcome.

Oh and hi :) This is my first (of many) post :)

EDIT: Just realized that the lazlohf's patch has already been implemented in the version that I have. Strange. I still got the ArrayOutOfBounds Exception. My patch fixes it though.

Hmm, I'm not sure how you could still get AOOB.  Could you post the exception?

Sure. Here you go:


Jul 24, 2008 10:32:42 PM class jmetest.TutorialGuide.HelloWorld start()
SEVERE: Exception in game loop
java.lang.ArrayIndexOutOfBoundsException: 2
   at com.jme.input.lwjgl.LWJGLMouseInput.update(LWJGLMouseInput.java:202)
   at com.jme.input.InputSystem.update(InputSystem.java:67)
   at com.jme.app.BaseGame.start(BaseGame.java:78)
   at jmetest.TutorialGuide.HelloWorld.main(HelloWorld.java:50)
Jul 24, 2008 10:32:42 PM com.jme.app.BaseSimpleGame cleanup
INFO: Cleaning up resources.
Jul 24, 2008 10:32:42 PM com.jme.app.BaseGame start
INFO: Application ending.

hmm, that + your patch would indicate you don't have the latest, thus the AOOB…  But could you elaborate on the middle mouse button?

In the constructor of LWJGLMouseInput:


buttonPressed = new boolean[Mouse.getButtonCount()];



Mouse.getButtonCount() returns 2. Which is correct, because my Touch Pad only has 2 buttons - a left and right mouse button.

But, if I press both the left and right mouse buttons together at the same time it acts as a middle mouse button. It seems that Mouse.getButtonCount() does not pick this one up. Thus - in theory - I actually have 3 mouse buttons. But Mouse.getButtonCount() thinks that I only have 2.

I think it's probably because the 3rd mouse button is "emulated" by the Dell laptop software/drivers.

All I know is that if I try to use this "middle mouse button", jMe bombs out.

I'm not sure if the lwjgl Mouse.getButtonCount() can be fixed, so here's a patch that will bounds check and resize the buttonPressed array. As a side note, its very expensive to use exceptions for flow control. Its much faster to check the array size with an if statement.



Index: D:/workspace/jme/jme2/src/com/jme/input/lwjgl/LWJGLMouseInput.java
===================================================================
--- D:/workspace/jme/jme2/src/com/jme/input/lwjgl/LWJGLMouseInput.java   (revision 3912)
+++ D:/workspace/jme/jme2/src/com/jme/input/lwjgl/LWJGLMouseInput.java   (working copy)
@@ -118,7 +118,7 @@
     * @see com.jme.input.MouseInput#isButtonDown(int)
     */
    public boolean isButtonDown(int buttonCode) {
-      return buttonCode < buttonPressed.length ? buttonPressed[buttonCode] : false;
+      return getButtonState(buttonCode);
    }
 
    /**
@@ -198,9 +198,7 @@
                int button = Mouse.getEventButton();
                boolean pressed = button >= 0
                      && Mouse.getEventButtonState();
-               if (button >= 0) {
-                  buttonPressed[button] = pressed;
-               }
+               setButtonState(button, pressed);
 
                int wheelDelta = Mouse.getEventDWheel();
 
@@ -233,9 +231,7 @@
          int button = Mouse.getEventButton();
          boolean pressed = button >= 0
                && Mouse.getEventButtonState();
-         if (button >= 0) {
-            buttonPressed[button] = pressed;
-         }
+         setButtonState(button, pressed);
       }
    }
 
@@ -473,8 +469,29 @@
    }
    
    public void clearButton(int buttonCode) {
-      if (buttonCode < buttonPressed.length) {
-         buttonPressed[buttonCode] = false;
+      setButtonState(buttonCode, false);
+   }
+   
+   private boolean getButtonState(int buttonCode) {
+      if (buttonCode < 0) {
+         return false;
       }
+      checkButtonBounds(buttonCode);
+      return buttonPressed[buttonCode];
    }
+   
+   private void setButtonState(int buttonCode, boolean pressed) {
+      if (buttonCode >= 0) {
+         checkButtonBounds(buttonCode);
+         buttonPressed[buttonCode] = pressed;
+      }
+   }
+   
+   private void checkButtonBounds(int buttonCode) {
+      if (buttonCode >= buttonPressed.length) {
+         boolean[] newButtonPressed = new boolean[buttonCode + 1];
+         System.arraycopy(buttonPressed, 0, newButtonPressed, 0, buttonPressed.length);
+         buttonPressed = newButtonPressed;
+      }
+   }
 }

up in svn

lazlohf said:
As a side note, its very expensive to use exceptions for flow control. Its much faster to check the array size with an if statement.


Was wondering about that. Thanks for the tip.