Skip to content

NullPointerException thrown in PApplet.dequeueWindowEvents() #918

Closed

Description

@hx2A

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.

Activity

  1. self-assigned this
    on Jan 16, 2025
  2. added a commit that references this issue on Jan 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions