Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX screencapture headless exception on OSX

I'm converting my old java app from swing to javafx and I'm running into a problem.

I'm using the following code to capture screenshots:

 public ScreenCapper() {
    ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    gs = ge.getScreenDevices();

    try {
        robot = new Robot(gs[gs.length-1]);
    } catch (AWTException e) {
        LOGGER.getInstance().ERROR("Error creating screenshot robot instance!");
    }
}

public Color capture() {
    Rectangle bounds;

    mode = gs[0].getDisplayMode();
    bounds = new Rectangle(0, 0, mode.getWidth(), mode.getHeight());
    //......
}

This works fine when running the application under Windows. However when running under OSX in get the following exception:

Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:403)
at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.awt.HeadlessException
at sun.java2d.HeadlessGraphicsEnvironment.getScreenDevices(HeadlessGraphicsEnvironment.java:72)
at be.beeles_place.roggbiv.utils.ScreenCapper.<init>(ScreenCapper.java:33)
at be.beeles_place.roggbiv.modes.AverageColorMode.start(AverageColorMode.java:31)
at be.beeles_place.roggbiv.modes.ColorModeContext.startCurrentColorMode(ColorModeContext.java:28)
at be.beeles_place.roggbiv.controller.RoggbivController.<init>(RoggbivController.java:42)
at be.beeles_place.roggbiv.RoggbivMain.start(RoggbivMain.java:67)
at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:319)
at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:215)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:76)

This I think has todo with javafx appearently running is headless mode on OSX, as the following debug warnings suggest:

013-03-10 10:44:03.795 java[1912:5903] *** WARNING: Method userSpaceScaleFactor in class NSView is deprecated on 10.7 and later. It should not be used in new applications. Use convertRectToBacking: instead. 
2013-03-10 10:44:05.472 java[1912:707] [JRSAppKitAWT markAppIsDaemon]: Process manager already initialized: can't fully enable headless mode.

Is there any way to get this to work? Or another way to capture screenshots that does not conflict with OSX?

full code @ https://github.com/beele/Roggbiv

like image 702
Beele Avatar asked Mar 10 '13 09:03

Beele


1 Answers

JavaFX doesn't use AWT stack, so it's not being started in pure JavaFX application. Due to threads handling specifics AWT is being run in headless mode on Mac then requested from JavaFX.

There are next options to solve that:

  1. Use some voodoo magic to initialize AWT -- in static initialization run java.awt.Toolkit.getDefaultToolkit(); EDIT this worked only in older JavaFX, sorry

  2. Better options would be to opt out of using AWT from JavaFX. You can use next functionality to make screenshots: http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#snapshot%28javafx.util.Callback,%20javafx.scene.SnapshotParameters,%20javafx.scene.image.WritableImage%29

  3. EDIT As Alexander pointed out another way is to run AWT code in a separate VM. To achieve that you can refactor your screenshot functionality to a separate class and call it from JavaFX app by:

        new ProcessBuilder(
              System.getProperty("java.home") + "/bin/java", 
              "-cp", "classpath", 
              "my.apps.DoScreenshot"
        ).start();
    

    This app can store screenshot to a filesystem. If you need to do screenshots often and met performance issues you can run that separate app once and communicate with it through socket.

  4. Use com.sun.glass.ui.Robot instead of AWTRobot

like image 182
Sergey Grinev Avatar answered Sep 26 '22 06:09

Sergey Grinev