JDK-8176813 : Mac: Failure to exit full-screen programmatically in some cases
  • Type: Bug
  • Component: javafx
  • Sub-Component: window-toolkit
  • Affected Version: 8,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2017-03-15
  • Updated: 2025-05-08
  • Resolved: 2025-05-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
jfx25 b16Fixed
Related Reports
Relates :  
Relates :  
Description
There are two problems that can happen when programmatically entering and then exiting full-screen mode. They are likely related to each other, so I am filing one bug for both symptoms. We can split one of the two out into a separate bug later if necessary.

Symptom #1 -- stage.setFullScreen(false) will not exit full-screen if done before the window has finished the animation for expanding the window to fill the screen.

To reproduce this, run the existing test.javafx.stage.RestoreStagePositionTest#testUfullscreenPosition unit test. Because of the short sleep time between submitting the runLater that enters full-screen and submitting the runLater that exits full-screen, the exit is called before the animation is done. It seems that glass does not handle this case gracefully. The window will fail to exit full-screen mode and the test will fail.

java.lang.AssertionError: Assertion failed: excpected 300.0, was: 0.0
	at RestoreStagePositionTest2.fail(RestoreStagePositionTest2.java:62)
	at RestoreStagePositionTest2.assertEquals(RestoreStagePositionTest2.java:78)
	at RestoreStagePositionTest2.testUfullscreenPosition(RestoreStagePositionTest2.java:141)
	at RestoreStagePositionTest2.main(RestoreStagePositionTest2.java:52)


Symptom #2 -- stage.setFullScreen(false) will cause an NPE in leaveNestedEventLoop if done after the window has finished the animation for expanding the window to fill the screen.

To reproduce this, modify the "Thread.sleep(400)" to "Thread.sleep(2000)" and run the test. It may be necessary to click in the window before the exception is delivered.

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
	at javafx.graphics/com.sun.glass.ui.Application.leaveNestedEventLoop(Application.java:540)
	at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoopImpl(Native Method)
	at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoop(MacApplication.java:109)
	at javafx.graphics/com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:511)
	at javafx.graphics/com.sun.glass.ui.mac.MacView._exitFullscreen(Native Method)
	at javafx.graphics/com.sun.glass.ui.View.exitFullscreen(View.java:792)
	at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.applyFullScreen(WindowStage.java:720)
	at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.setFullScreen(WindowStage.java:756)
	at javafx.graphics/javafx.stage.Stage.setFullScreen(Stage.java:666)
	at RestoreStagePositionTest2.lambda$testUfullscreenPosition$4(RestoreStagePositionTest2.java:136)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417)
	at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)

Comments
Changeset: 498b7e4c Branch: master Author: Martin Fox <mfox@openjdk.org> Date: 2025-05-02 02:31:20 +0000 URL: https://git.openjdk.org/jfx/commit/498b7e4cc56d91d6a0caf6a9a93691b0195e997b
02-05-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jfx/pull/1797 Date: 2025-04-28 17:47:50 +0000
28-04-2025

I've seen multiple failures while working on this bug. Most stem from the same underlying problem: the nested event loop that glass spins up during the fullscreen transition is allowing runLater runnables to fire at unexpected times. - In the test case the runnable that turns fullscreen off arrives during the animation. At that point the Window hasn't been put into the fullscreen state so the change to the fullscreen property is a no-op. This means the window is still fullscreen when the test harness tries to tear everything down. - We can enter a deadlock state if we try to hide a fullscreen window. When setView is called (with a null view) glass tries to kick the window out of fullscreen. This fires up the fullscreen event loop which allows a pulse to execute. The pulse then waits forever for the previous draw cycle to finish. I'm assuming this is because this entire process happens while the render lock is held. Again, the core problem is that a pulse runLater runnable is firing at an unexpected time, namely inside runWithRenderLock. A possible work around is to take the window out of fullscreen before the render lock is engaged (see setVisible in WindowStage, there's already some prologue code before the render lock that deals with stuff like this). - tkExit schedules some runLater runnables and those can be fired by the fullscreen event loop as the window is being hidden. One runnable asks the Application if any event loops are running and the Application says yes so the runnable calls exitAllNestedEventLoops. But the fullscreen event loop was setup by calling directly into Application and was never entered into the eventLoopMap. In fact, the eventLoopMap is null so exitAllNestedEventLoops throws an NPE.
28-04-2025

I agree with Alexander that this patch looks like it is just masking the symptom rather than fixing the problem. One further comment. The following is an indication that something has gone wrong with the thread state management of the Toolkit: > Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: Not on FX application thread; currentThread = JavaFX Application Thread This should not be possible unless there was either a problem recording the JavaFX application thread or if it was subsequently changed (e.g., as part of shutdown).
08-08-2017

Regarding http://cr.openjdk.java.net/~aniyogi/8176813/webrev.00/ What about first part of the issue? It looks like that avoiding NPE only masking the issue, because I am still able to get following exceptions after the fix Exception in thread "JavaFX Application Thread" java.lang.NullPointerException at javafx.graphics/com.sun.glass.ui.Screen.initScreens(Screen.java:412) at javafx.graphics/com.sun.glass.ui.Screen.notifySettingsChanged(Screen.java:381) at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoopImpl(Native Method) at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoop(MacApplication.java:112) at javafx.graphics/com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:559) at javafx.graphics/com.sun.glass.ui.mac.MacWindow._setView(Native Method) at javafx.graphics/com.sun.glass.ui.Window.setView(Window.java:418) at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.lambda$setScene$1(WindowStage.java:295) at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithRenderLock(QuantumToolkit.java:399) at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.setScene(WindowStage.java:291) at javafx.graphics/javafx.stage.Window$12.invalidated(Window.java:1142) at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110) at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145) at javafx.graphics/javafx.stage.Window.setShowing(Window.java:1186) at javafx.graphics/javafx.stage.Window.hide(Window.java:1211) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418) at java.base/java.security.AccessController.doPrivileged(Native Method) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417) at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96) or (just move mouse over stage) Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: Not on FX application thread; currentThread = JavaFX Application Thread at javafx.graphics/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:246) at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:415) at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3799) at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604) at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1874) at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613) at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397) at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295) at java.base/java.security.AccessController.doPrivileged(Native Method) at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434) at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:381) at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433) at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556) at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942) at javafx.graphics/com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127) at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoopImpl(Native Method) at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoop(MacApplication.java:112) at javafx.graphics/com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:559) at javafx.graphics/com.sun.glass.ui.mac.MacWindow._setView(Native Method) at javafx.graphics/com.sun.glass.ui.Window.setView(Window.java:418) at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.lambda$setScene$1(WindowStage.java:295) at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithRenderLock(QuantumToolkit.java:399) at javafx.graphics/com.sun.javafx.tk.quantum.WindowStage.setScene(WindowStage.java:291) at javafx.graphics/javafx.stage.Window$12.invalidated(Window.java:1142) at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110) at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145) at javafx.graphics/javafx.stage.Window.setShowing(Window.java:1186) at javafx.graphics/javafx.stage.Window.hide(Window.java:1211) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418) at java.base/java.security.AccessController.doPrivileged(Native Method) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417) at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
07-08-2017

enterNestedEventLoopImpl looks unreachable. Any better way to debug the null value? What is the expected default value in such cases?
13-07-2017

It isn't unreachable. enterNestedEventLoopImpl is called from Application::enterNestedEventLoop. When entering full screen on Mac, the call stack is: MacApplication::_enterNestedEventLoopImpl [native] MacApplication::_enterNestedEventLoop Application::enterNestedEventLoop GlassApplication::enterFullScreenExitingLoop [native] GlassViewDelegate::enterFullscreenWithAnimate [native] When exiting full screen, the call stack is similar: ... GlassApplication::enterFullScreenExitingLoop [native] GlassViewDelegate::exitFullscreenWithAnimate [native] I suspect what is happening in the failing case (just a guess) is that the problem occurs when exit fullscreen is called while still in the nested event loop for the enter full screen animation.
13-07-2017

As part of this fix, the following unit tests must be re-enabled (after verifying that they work correctly). test.javafx.scene.RestoreSceneSizeTest#testUnfullscreenSize test.javafx.stage.RestoreStagePositionTest#testUfullscreenPosition The tests will also need to be fixed up (if they haven't already been by the time this bug is fixed) to have a longer delay before exiting full-screen to make sure that the full-screen has happened already on Mac.
16-03-2017

Another simliar unit test is also failing due to this bug: test.javafx.scene.RestoreSceneSizeTest#testUnfullscreenSize java.lang.AssertionError: at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.assertTrue(Assert.java:43) at org.junit.Assert.assertFalse(Assert.java:68) at org.junit.Assert.assertFalse(Assert.java:79) at test.javafx.scene.RestoreSceneSizeTest.testUnfullscreenSize(RestoreSceneSizeTest.java:112)
15-03-2017

Attached a standalone version of RestoreStagePositionTest that does not depend on JUnit.
15-03-2017

Note: in both cases the Platform.exit() call fails to properly exit the FX toolkit.
15-03-2017