PS3 - Secondary window problem with WindowListener

Download this code as an archived sketch

I am working on updating G4P for PS3 and I have managed to create multiple windows with either JAVA2D, P2D or P3D renderers. The problem is that the secondary Window created by Processing, when closed exits the application. In the existing version of G4P the user can specify what to do when the user clicks on the red cross. Either keep the window open, close the window or exit the application and I am trying to do the same thing for PS3.

So the code below is a cut down version to demonstrate the problem. In line 16 the user can specify which of these 3 actions is used. The CLOSE_WINDOW and EXIT_APP work as expected but the KEEP_OPEN option still allows the window to close. :(

In the G3WindowA class the intAWTlisteners() method removes all existing WindowListeners and then adds one of its own (inner class WindowListenerAWT).

This is exactly the same approach I used in the last version of G4P which worked fine in 2.2.1

Any help would be appreciated.

Main sketch tab

// These are used by the secondary window to test mouse event
int x, y;
float a;

public void settings() {
  size(100, 100);
}

public void setup() {
  // Creates a window with a JAVA2D renderer 
  // (do not change renderers in this test)
  G3WindowA g3w = G3WindowA.getWindow("My window", 20, 40, 480, 320, JAVA2D);
  // KEEP_OPEN - ignore attempt to close window (default action) <br>
  // CLOSE_WINDOW - close this window, if it is the main window it causes the app to exit <br>
  // EXIT_APP - exit the app, this will cause all windows to close. <br>
  g3w.setActionOnClose(G3WindowA.KEEP_OPEN);
  g3w.addDrawHandler(this, "g3wDraw");
}

public void draw() {
  background(0);
  ellipse(mouseX, mouseY, 20, 20);
}     

// Draw and mouse event handlers for secondary window
public void g3wDraw(PApplet app, GWinData data) {
  app.background(255);
  app.fill(0);
  app.ellipse(app.mouseX, app.mouseY, 10, 10);
}

G3WindowA.java tab

import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.lang.reflect.Method;

import processing.core.PApplet;

public final class G3WindowA extends PApplet {

  /**
   * Factory method to create and start a new window
   * 
   * @param title text to appear in frame title bar
   * @param px horizontal position of top-left corner
   * @param py vertical position of top-left corner
   * @param w width of drawing surface
   * @param h height of surface
   * @param r renderer e.g. JAVA2D, P3D etc.
   * @return the window created (in case the user wants its.
   */
  public static G3WindowA getWindow(String title, int px, int py, int w, int h, String r) {
    G3WindowA g3w = new G3WindowA(title, w, h, r);
    String loc = "--location=" + px + "," + py;
    String className = g3w.getClass().getName();
    String[] args = { loc, className};
    PApplet.runSketch(args, g3w);
    return g3w;
  }

  /** The object to handle the draw event */
  private Object drawHandlerObject = null;
  /** The method in drawHandlerObject to execute */
  private Method drawHandlerMethod = null;
  /** the name of the method to handle the event */
  private String drawHandlerMethodName;

  //*********************************************************
  // Copied from GConstants
  public static final int EXIT_APP       = 0x00000f01;
  public static final int CLOSE_WINDOW     = 0x00000f02;
  public static final int KEEP_OPEN       = 0x00000f03;
  //**********************************************************

  static final int _INVALID = 0;
  static final int _AWT = 1;      // JAVA2D
  static final int _NEWT = 2;    // P2D or P3D

  private int renderer_type = _INVALID;

  private int actionOnClose = KEEP_OPEN;

  public GWinData data;

  private final int w, h;
  private final String r, title;

  private WindowListener awtAdapter = new WindowListenerAWT();

  private G3WindowA(String title, int w, int h, String renderer) {
    this.title = title;
    this.w = w;
    this.h = h;
    this.r = renderer;
    renderer_type = _INVALID;
    if (renderer.equals(JAVA2D))
      renderer_type = _AWT;
    else if (renderer.equals(P2D) || renderer.equals(P3D))
      renderer_type = _NEWT;
    System.out.println(renderer_type);
    registerMethod("draw", this);
  }

  public void settings() {
    size(w, h, r);
  }

  public void setup() {
    surface.setTitle(title); // does not like this in settings  
    Object obj = surface.getNative();
    System.out.println(obj.getClass().getName());
    switch(renderer_type) {
    case _AWT:
      processing.awt.PSurfaceAWT.SmoothCanvas awtCanvas = (processing.awt.PSurfaceAWT.SmoothCanvas) obj;
      //System.out.println(awtCanvas.getFrame().getClass().getName());
      frame = awtCanvas.getFrame();
      intAWTlisteners();
      break;
    }
  }

  // Remove all WindowListeners and add own own
  private void intAWTlisteners() {
    frame = ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
    for (WindowListener l : frame.getWindowListeners())
      frame.removeWindowListener(l);
    frame.addWindowListener(awtAdapter);
  }

  public void draw() {
    pushMatrix();
    if (drawHandlerObject != null) {
      try {
        drawHandlerMethod.invoke(drawHandlerObject, new Object[] { this, data });
      } 
      catch (Exception e) {
        //  ERROR
      }
    }
    popMatrix();
  }

  /**
   * Attempt to add the 'draw' handler method. 
   * The default event handler is a method that returns void and has two
   * parameters PApplet and GWinData
   * 
   * @param obj the object to handle the event
   * @param methodName the method to execute in the object handler class
   */
  public void addDrawHandler(Object obj, String methodName) {
    try {
      drawHandlerMethod = obj.getClass().getMethod(methodName, new Class<?>[] {PApplet.class, GWinData.class } );
      drawHandlerObject = obj;
      drawHandlerMethodName = methodName;
    } 
    catch (Exception e) {
      drawHandlerObject = null;
      //GMessenger.message(NONEXISTANT, new Object[] {this, methodName, new Class<?>[] { PApplet.class, GWinData.class } } );
    }
  }

  /**
   * This sets what happens when the users attempts to close the window. <br>
   * There are 3 possible actions depending on the value passed. <br>
   * KEEP_OPEN - ignore attempt to close window (default action) <br>
   * CLOSE_WINDOW - close this window, if it is the main window it causes the app to exit <br>
   * EXIT_APP - exit the app, this will cause all windows to close. <br>
   * @param action the required close action
   */
  public void setActionOnClose(int action) {
    actionOnClose = action;
  }

  /**
   * Window adapter class that remembers the window it belongs to so
   * it can be used to mark it for closure if required.
   * 
   * @author Peter Lager
   */
  public class WindowListenerAWT implements WindowListener {

    @Override
      public void windowClosing(WindowEvent evt) {
      System.out.println("CLOSING AWT");
      switch(actionOnClose) {
        case EXIT_APP:
          System.out.println("EXIT");
        System.exit(0);
        break;
      case CLOSE_WINDOW:
        System.out.println("CLOSE");
        noLoop();
        unregisterMethod("draw", this);
        dispose();
        break;
      default:
        System.out.println("KEEP OPEN");
      }
    }

    @Override
      public void windowOpened(WindowEvent e) {
    }

    @Override
      public void windowClosed(WindowEvent e) {
      System.out.println("AWT WINDOW CLOSED");
    }

    @Override
      public void windowIconified(WindowEvent e) {
    }

    @Override
      public void windowDeiconified(WindowEvent e) {
    }

    @Override
      public void windowActivated(WindowEvent e) {
    }

    @Override
      public void windowDeactivated(WindowEvent e) {
    }
  }
}

GWinData.java tab

public class GWinData {
}

Answers

  • edited September 2015 Answer ✓

    You have to set the JFrame's close action via setDefaultCloseOperation() otherwise it will alway close after firing all windowClosing events.

    In G3WindowA.java import JFrame:

    import javax.swing.JFrame;
    

    And change setActionOnClose() to something like:

    public void setActionOnClose(int action) {
        frame = ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
        if(action == KEEP_OPEN)
            ((JFrame)frame).setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        else
            ((JFrame)frame).setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        actionOnClose = action;
    }
    

    And everything should work as expected. :)

  • Thanks very much that worked like a charm =D>

  • No problem! Great work btw.! :)

Sign In or Register to comment.