libgdx / packr

Packages your JAR, assets and a JVM for distribution on Windows, Linux and Mac OS X
Apache License 2.0
2.56k stars 171 forks source link

Splash screen in Packr on macOS #216

Open hvdwolf opened 2 years ago

hvdwolf commented 2 years ago

Hi, Build platform: Ubuntu focal fossa 20.0.4.3 LTS Test platform: macOS Catalina and Big Sur Packr: release version 4.0.0 build jdk: adoptopenjdk 11.0.x packed jre: adoptopenjdk 11.0.x

(This refers also to issue #65 :Some ambiguity with vmargs in config.json)

I build my java jar for Linux, Windows and macOS with the splashscreen functionality from java itself, as that is the easiest. On macOS I use packr v4 recently (I could not make it work with v3, maybe my fault). Before that I created a bundle manually and use a script to start the app with java -jar jExifToolGUI.jar with some vmargs. That showed the splashscreen. (my app is jExifToolGUI in case you're interested or in case it matters) Now with pack 4.0.0, even with my builtin java splashscreen, it doesn't work. If I double-click the the app, it doesn't show the splash screen. If I do an open jExifToolGUI.app, it doesn't show the splash screen. If I do an ./jExifToolGUI.app/Contents/MacOS/jexiftoolgui, it doesn't show the splash screen. If I do a 'java -jar jExifToolGUI.app/Contents/Resources/jExifToolGUI.jar`, I do get the splash screen.

I already created a jexiftoolgui.json with or without a vmargs splash:my_splash_screen and splash:my_splash_screen.png (although the latter with extension should not be needed) and adding the png to my bundle, but that doesn't work either, which is of course in ling with issue #65. I understand the explanation in issue #65, but why does the Packr binary block my java builtin splash screen?

(It does work on Windows (using launch4j), it works using a .deb and an AppImage, or simply using java -jar ...)

karlsabo commented 2 years ago

Are you creating the splash screen yourself in code doing something like final SplashScreen splash = SplashScreen.getSplashScreen();? Or is it only the -splash argument?

I'm not sure a -splash argument gets passed to the JVM shared library when it's loaded.

karlsabo commented 2 years ago

Have you tried running the packr executable with the arguments -c --verbose to see what gets printed out?

hvdwolf commented 2 years ago

The splash is created by adding the splash screen in the jar and add an entry to the manifest. The manifest is created from the build.gradle.kts

val jar by tasks.getting(Jar::class) {
    manifest {
        attributes["Main-Class"] = project.ext["mainClassName"] as String
        attributes["Build-Timestamp"] = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(Date())
        attributes["Created-By"] = "Gradle ${gradle.gradleVersion}"
        attributes["Author"] = "Harry van der Wolf"
        attributes["SplashScreen-Image"] = "icons/jexiftoolgui-splashlogo.png"
        attributes["Multi-Release"] = "true"
    }
}

So it is not coded. I also tried with the vmargs option by specifying the logo --vmargs splash:jexiftoolgui-splashlogo.png, using many both jar internal as normal disk paths to try it out. I tried to code it this morning using the SplashScreen splash = SplashScreen.getSplashScreen();, but somehow I can't make that work. In my initial Application.java I end with SwingUtilities.invokeLater(mainScreen::createAndShowGUI); as "it should be done". This however prevents me from using the splash screen as I get "suddenly" nullpointer exceptions in my main thread crashing my application. When using the same code from a plain simple java app it works fine. I have not sorted that out yet.

EDIT: sorry, forgot to mention the -c --verbose options. I copied the relevant part

Using default configuration file jexiftoolgui.json ...
Changing working directory to /Users/harryvanderwolf/Downloads/java/packr/jExifToolGUI.app/Contents/Resources ...
Loading JVM runtime library ...
fileSearch found libjli! filename=jre/lib/jli/libjli.dylib, lastFileSearch=
Loading libjli=/Users/harryvanderwolf/Downloads/java/packr/jExifToolGUI.app/Contents/Resources/jre/lib/jli/libjli.dylib
Passing VM options ...
isZgcSupported()=1, hasJsonValue(jsonRoot, "useZgcIfSupportedOs", sajson::TYPE_TRUE)=0
  # -Xmx2G
  # -Dlogback.configurationFile=logback.xml
  # -splash:jexiftoolgui-splashlogo.png
Passing VM options:
  -Xmx2G
  -Dlogback.configurationFile=logback.xml
  -splash:jexiftoolgui-splashlogo.png
Creating Java VM ...
Passing command line arguments ...
Loading JAR file ...
Adding 1 classpaths ...
  # jExifToolGUI.jar
hvdwolf commented 2 years ago

I now coded it with both a URL from resource in my jar, which doesn't work. I also coded it now by using the SplashScreen splash = SplashScreen.getSplashScreen(); with using the vmargs parameter, but that doesn't work either. The second perhaps because that is that blocking issue between java and macOS.

karlsabo commented 2 years ago

I'm not sure if this is accurate, but there's an old StackOverflow that says the splash screen is created by native code: https://stackoverflow.com/questions/32215515/how-java-splashscreen-works.

If that's true, it would explain why it doesn't work, packr only loads the JVM shared library and executes your main method.

I think the best chance you have is to show a splash screen yourself from your main method like they did in #65.

karlsabo commented 2 years ago

It could also be similar to an issue jpackage had: https://bugs.openjdk.java.net/browse/JDK-8250611. Maybe loading the splashscreen shared library would fix it. You could try updating the C code to load those libraries, or try out jpackage.

hvdwolf commented 2 years ago

I already tried jpackage for jdk16 and jdk17. Exactly the same behavior as Packr, with the disadvantage of jpackage that Packr is platform independent and jpackage is not. And with regard to the "early loading" as described. That is exactly what I was doing: load the splash screen from the manifest using the native C-launcher before the JVM loads. I guess the jpackage launcher and Packr launcher are slightly different from the manifest invoked launcher.

I have seen the solution in #65, but my UI loads diferently via the SwingUtilities.invokeLater scheduler. I actually hate to write a special code block only for a splash screen and only for macOS. The manifest solution is such an elegant solution and when doing a java -jar whatever.jar it simply works. Maybe it might be better to do some of my initialization in the background using an executor to show my screen faster.

Executor executor = Executors.newSingleThreadExecutor();
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    .................
                }
            }
});

I really appreciate your feedback and thinking along, but I think I will "park" this for some time and look at other ways. This already took my whole day only for a splash screen. I will stay with Packr and abandon my manual bundle, as the Packr method delivers a real bundle instead of my script in a bundle starting java with a jar. If I find something different from #65 I will certainly share the snippet.