Description
I had several running Sketches and was moving the windows around when this exception was thrown:
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.remove(Object)" is null
at processing.core.PApplet.dequeueWindowEvents(PApplet.java:9740)
at processing.core.PApplet.handleDraw(PApplet.java:2085)
at processing.opengl.PSurfaceJOGL$DrawListener.display(PSurfaceJOGL.java:840)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:692)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:674)
at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:443)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1293)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:1147)
at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:782)
at com.jogamp.opengl.util.AWTAnimatorImpl.display(AWTAnimatorImpl.java:81)
at com.jogamp.opengl.util.AnimatorBase.display(AnimatorBase.java:453)
at com.jogamp.opengl.util.FPSAnimator$MainTask.run(FPSAnimator.java:178)
at java.base/java.util.TimerThread.mainLoop(Timer.java:566)
at java.base/java.util.TimerThread.run(Timer.java:516)
I see what the problem is. In the code there is this:
/**
* Internal use only: called by Surface objects to queue a position
* event to call windowPositioned() when it's safe, which is after
* the beginDraw() call and before the draw(). Note that this is
* only the notification that the window is in a new position.
*/
public void postWindowMoved(int newX, int newY) {
if (external && !fullScreen) {
frameMoved(newX, newY);
}
windowEventQueue.put("x", newX);
windowEventQueue.put("y", newY);
}
/** Called when the window is moved */
public void windowMoved() { }
private void dequeueWindowEvents() {
if (windowEventQueue.containsKey("x")) {
windowX = windowEventQueue.remove("x");
windowY = windowEventQueue.remove("y"); // line 9740, where exception was thrown
windowMoved();
}
if (windowEventQueue.containsKey("w")) {
// these should already match width/height
//windowResized(windowEventQueue.remove("w"),
// windowEventQueue.remove("h"));
windowEventQueue.remove("w");
windowEventQueue.remove("h");
windowResized();
}
}
According to the comment, that postWindowMoved()
method gets called by Surface objects "after the beginDraw()
call and before the draw()
".
That dequeueWindowEvents()
method gets called in handleDraw()
at a point that is also after the beginDraw()
call and before the draw()
.
Putting both "x" and "y" in windowEventQueue
is not an atomic operation, even though the queue is a ConcurrentHashMap
, which is thread safe. This must have been a fluke exception where postWindowMoved()
and dequeueWindowEvents()
were executing at the same time, and "x" was added but "y" had not.
This is a rare exception and is probably next to impossible to reproduce. Nevertheless, it should be an easy thing to fix, so let's fix it, and I'd like to work on it.
My proposed solution is for windowEventQueue
to store pairs of numbers, for both x & y, and w & h. We can create a simple dummy class Pair
that we put
and remove
from the queue.