Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sketch process does not close when using OpenGL renderers on Mac #2

Closed
codeanticode opened this issue Mar 13, 2021 · 13 comments
Closed

Comments

@codeanticode
Copy link

The following notebook code should reproduce the issue:

import py5
%load_ext py5
%gui osx
def settings():
    py5.size(500, 400, py5.P2D)

def setup():
    py5.background(240)
    py5.rect_mode(py5.CENTER)

def draw():
    py5.fill(py5.random(255), py5.random(255), py5.random(255))
    py5.rect(py5.mouse_x, py5.mouse_y, 10, 10)

py5.run_sketch()
print('the sketch is running!')   

If the users close the sketch window using the close icon or pressing escape, the sketch will close, but the following message will print in the terminal:

NewtNSView::dealloc: softLock still hold @ dealloc!

and the sketch process will stay visible in the taskbar.

@codeanticode
Copy link
Author

codeanticode commented Mar 13, 2021

Strangely enough, when I run the same notebook using the JAVA2D (default) renderer, the sketch window does not show up at all (tried both with the gui osx magic and without it, same result):

import py5
%load_ext py5
%gui osx
def settings():
    py5.size(500, 400, py5.JAVA2D)

def setup():
    py5.background(240)
    py5.rect_mode(py5.CENTER)

def draw():
    py5.fill(py5.random(255), py5.random(255), py5.random(255))
    py5.rect(py5.mouse_x, py5.mouse_y, 10, 10)

py5.run_sketch()
print('the sketch is running!')   

@hx2A
Copy link
Collaborator

hx2A commented Mar 13, 2021

Can you check if the window is created but not focused, and behind another window? On my Mac that code creates the window but does not focus it or bring it to the front. That's another issue, but less important than the OpenGL windows not exiting.

@codeanticode
Copy link
Author

@hx2A the sketch window appears focused the firs time I run the notebook, but if I close it and call run_sketch() again it reappears not focused the second time. In both cases it's in front of all other windows.

@hx2A
Copy link
Collaborator

hx2A commented Mar 19, 2021

I can replicate the problem in a pure Java sketch. This demonstrates that py5's exitActual() implementation is incomplete for Macs. Try running the below sketch in the the Processing 4.0 PDE:

import com.jogamp.newt.Window;
import com.jogamp.newt.Screen;
import com.jogamp.newt.Display;
import com.jogamp.newt.event.KeyListener;
import com.jogamp.newt.event.MouseListener;
import com.jogamp.newt.event.WindowListener;
import com.jogamp.newt.opengl.GLWindow;
import jogamp.opengl.macosx.cgl.MacOSXOnscreenCGLDrawable;
import jogamp.newt.driver.macosx.WindowDriver;


void setup() {
  size(200, 200, P2D);
}

void draw() {
  rect(mouseX, mouseY, 10, 10);
}

void exitActual() {
  System.err.println("in exit actual");
  py5ExitActual();
  System.err.println("is it gone yet???");
  try {
    Thread.sleep(5000);
  }
  catch (InterruptedException e) {
  }
  super.exitActual();
}

public void py5ExitActual() {
  final Object nativeWindow = surface.getNative();
  if (nativeWindow instanceof GLWindow) {
    System.err.println("in GLWindow");
    GLWindow window = (GLWindow) nativeWindow;
    for (int i = 0; i < window.getGLEventListenerCount(); i++) {
      window.disposeGLEventListener(window.getGLEventListener(i), true);
    }
    window.getAnimator().remove(window);

    // this doesn't help
    if (platform == MACOS) {
      final MacOSXOnscreenCGLDrawable drawable = (MacOSXOnscreenCGLDrawable) window.getDelegatedDrawable();
      WindowDriver driver = (WindowDriver) drawable.getNativeSurface();
      WindowDriver.shutdownAll();
      driver.windowDestroyNotify(true);
    }
  }
  if (nativeWindow instanceof Window) {
    System.err.println("in Window");
    Window window = (Window) nativeWindow;

    // first remove the listeners before destroying window
    for (WindowListener l : window.getWindowListeners()) {
      window.removeWindowListener(l);
    }
    for (KeyListener l : window.getKeyListeners()) {
      window.removeKeyListener(l);
    }
    for (MouseListener l : window.getMouseListeners()) {
      window.removeMouseListener(l);
    }

    Screen screen = window.getScreen();
    Display display = screen.getDisplay();
    display.destroy();
    screen.destroy();

    // finally, destroy the window
    try {
      window.destroy();
      System.err.println("window destroyed");
    }
    catch (RuntimeException e) {
      System.err.println("error thrown while destroying window");
      // ignore possible NullPointerException since exiting anyway
    }
    surface.setVisible(false);
  } else if (nativeWindow instanceof processing.awt.PSurfaceAWT.SmoothCanvas) {
    System.err.println("in SmoothCanvas");
    processing.awt.PSurfaceAWT.SmoothCanvas window = (processing.awt.PSurfaceAWT.SmoothCanvas) nativeWindow;
    window.getFrame().dispose();
  } else {
    System.err.println("in else hide");
    surface.setVisible(false);
  }
}

Observe what it does. I override exitActual() to run py5's code for exitActual(). Then it waits 5 seconds and calls the correct exitActual(). When you see the message "is it gone yet???", the window should be effectively destroyed. On Linux it does what it is supposed to. On my Mac, the window still remains when that message is displayed.

I tried adding some Mac specific stuff but it doesn't fix anything. Those functions looked useful though.

@codeanticode
Copy link
Author

codeanticode commented Mar 20, 2021

Ok, not sure that's the correct solution for mac would be but if you reduce py5ExitActual() to the following:

public void py5ExitActual() {
  final Object nativeWindow = surface.getNative();
  if (nativeWindow instanceof GLWindow) {
    System.err.println("in GLWindow");
    GLWindow window = (GLWindow) nativeWindow;
    for (int i = 0; i < window.getGLEventListenerCount(); i++) {
      window.disposeGLEventListener(window.getGLEventListener(i), true);
    }
    window.getAnimator().remove(window);

    System.err.println("About to destroy GLWindow");
    window.destroy();
    System.err.println("GLWindow destroyed");
  }
}

The window actually disappears without the delay, although it seems that it never gets to print "is it gone yet???". In fact the output in the console is the following:

in exit actual
in GLWindow
About to destroy GLWindow
in exit actual
in GLWindow

so some weird thing is happening where the GLWindow.destroy() call triggers another visit to exitActual() apparently... but the window does close :-)

@hx2A
Copy link
Collaborator

hx2A commented Mar 20, 2021

Hmmm, that is interesting and is consistent with things I've observed while working through this problem.

The recursiveness can be eliminated with a flag:

boolean exitActualCalled = false;

void exitActual() {
  System.err.println("in exit actual");

  if (exitActualCalled) {
    System.err.println("exiting exitActual early");
    return;
  } else {
    exitActualCalled = true;
  }
  py5ExitActual();
  System.err.println("is it gone yet???");
  try {
    Thread.sleep(5000);
  }
  catch (InterruptedException e) {
  }
  super.exitActual();
}

And now the window goes away right away, but remains on the taskbar.

Is it possible that the thing on the taskbar is because of the running JVM and not the window itself? (and is "taskbar" the correct term for those icons on the bottom of my mac screen?) Further experimentation suggests that this may be the case.

I incorporated this belief back into Sketch.java and created a new branch exitactual_macos for you to look at. It seems the extra code for destroying the screen and display objects are also necessary to shutdown the sketch properly. There is no recursiveness and the window consistently goes away right away. It isn't going away because of a surface.setVisible(false); call. :-)

I can continue using Jupyter and launch new sketches one after another. The new sketches can have different sizes, different renderers, not have a draw method, etc. It all works as expected.

I can run multiple Sketches via class mode. Critically, those multiple sketches have only one icon in the taskbar, not two.

The windows seem to be destroyed properly, and if I'm wrong about that, nothing is stopping me from using this as I'd like to. I do believe the thing in the taskbar is because of the JVM and not the window. That might be wrong, but everything I observe is consistent with that belief.

There is this NewtNSView::dealloc: softLock still hold @ dealloc! message that keeps popping up, but I can live with that. I'd suggest that perhaps there's something about jpype and the way %gui osx works that holds on to references somewhere. Perhaps this will be improved later when they fix the jpype bug and %gui osx is no longer necessary? What is the softLock it is referring to? I remember seeing that somewhere in the JOGL documentation I was looking at.

@hx2A
Copy link
Collaborator

hx2A commented Mar 20, 2021

I do believe the thing in the taskbar is because of the JVM and not the window.

Well that can't be true because the default renderer does not show up on the taskbar at all. But I remember you saying that the OpenGL renderers use different Java libraries than the default renderer, so perhaps it is related to that.

@hx2A
Copy link
Collaborator

hx2A commented Mar 20, 2021

I also observe that Sketches launched from the PDE always end up on the taskbar, regardless of what renderer is used. If I run multiple sketches from multiple editors, I get multiple icons in the taskbar. But the PDE is launching the sketches in a separate process and killing the JVM, so this is not a good comparison.

@hx2A
Copy link
Collaborator

hx2A commented Mar 20, 2021

@hx2A
Copy link
Collaborator

hx2A commented Mar 20, 2021

Curiously, commenting out PApplet's ThinkDifferent calls seems to change nothing. Does that code work correctly?

Nevertheless, it is possible that Processing is doing something special on Macs to get the Sketch added as a dock icon (I have learned the correct terminology :-) ) but does nothing to remove it, because the PDE is killing the JVM on sketch exit and special removal code was never implemented.

The comments do suggest that something special is needed to manage the dock icon.

@codeanticode
Copy link
Author

I think the most important trick it that code is registering the quit handler (here) to properly handle the situation when the users presses Cmd+Q. Setting the doc icon image is another platform specific bit, but looks quite simple (just this).

I tested the gene viewer using the exitactual_macos branch, and the behavior of the sketch window is acceptable I think. I can close it and re-open it without any errors. The only two minor issues that you already mentioned are the Processing icon that remains in the doc after closing the window, and the NewtNSView::dealloc: warning message in the terminal. I can leave with those for now, don't seem to be stoppers.

@hx2A
Copy link
Collaborator

hx2A commented Mar 21, 2021

Great. I also think the current functionality on Macs is now acceptable. I will continue doing some investigation but won't totally beat my head against a wall over this because the remaining problems might magically go away once the jpype issues are resolved.

I would like to properly document the Mac issues and workarounds (ie using %gui osx, etc) so the correct expectations are set. This won't take a lot of time though.

I had a productive weekend and just about finished the other things I wanted to include in the next release. I expect to do a release by EOD Tuesday. Thanks a bunch for your help investigating the Mac problems!

@hx2A
Copy link
Collaborator

hx2A commented Mar 23, 2021

I am closing this out but have created issues #3 #4 and #5 to track the residual known OSX problems.

The important fix discussed in this thread will be released today.

@hx2A hx2A closed this as completed Mar 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants