How to get screensize with GraalVM 21 when running in no-console mode

95 views Asked by At

QUESTION:
Is there any way to detect the screen size on GraalVM in no-console mode?

FINDINGS:

  • I am getting screen size using the class GraphicsEnvironment initialized by java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment()
  • when I tried to run the code on jar mode, it is working.
  • when I tried to run the code on GraalVM native mode, it is working (com.tugalsan.blg.gvm.graphicsdevice.exe).
  • when I tried to run the code on GraalVM native mode, without console (com.tugalsan.blg.gvm.graphicsdevice.noconsole.exe).
    • on console nothing appears (no log, no error)
    • on log file, logs appears only until the initialization of GraphicsEnvironment.
    • Hence it crashes silently.

STEPS TO REPRODUCE: (project link)

create project file: D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\src\main\java\com\tugalsan\blg\gvm\graphicsdevice\Main.java

package com.tugalsan.blg.gvm.graphicsdevice;

import java.awt.GraphicsEnvironment;
import static java.lang.System.out;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;

public class Main {

    public static void log(CharSequence funcName, Object... oa) {
        try {
            var lstStr = Arrays.stream(oa)
                    .map(o -> String.valueOf(o))
                    .collect(Collectors.toCollection(ArrayList::new));
            var str = funcName + " -> " + lstStr + "\n";
            out.print(str);
            Files.writeString(file, str, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    final public static Path file = FileSystems.getDefault().getPath(Main.class.getPackageName() + ".log").toAbsolutePath();

    public static void main(String... s) {
        try {
            Files.deleteIfExists(file);
            log("main", "#1");
            var localGraphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
            log("main", "#2");
        } catch (Exception e) {
            log("main", e);
        }
    }
}

create project file: D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\src\main\java\module-info.java

module com.tugalsan.blg.gvm.graphicsdevice {
    requires java.desktop;
}

create project file: D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.tugalsan</groupId>
    <artifactId>com.tugalsan.blg.gvm.graphicsdevice</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>        
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>21</maven.compiler.release>
        <exec.mainClass>com.tugalsan.blg.gvm.graphicsdevice.Main</exec.mainClass>
    </properties>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.6.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>${exec.mainClass}</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <release>21</release>
                    <enablePreview>true</enablePreview>
                    <compilerArgs>
                        <arg>--add-modules</arg>
                        <arg>jdk.incubator.vector</arg>
                        <!-- <arg>-XX:+EnableDynamicAgentLoading -->
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

run bat file: D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\compileJar.bat

d:
cd D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice
cmd /c mvnd clean install -DskipTests -q versions:display-dependency-updates 

run bat file: D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\compileAgent.bat

java -agentlib:native-image-agent=config-output-dir=config --enable-preview --add-modules jdk.incubator.vector -jar target/com.tugalsan.blg.gvm.graphicsdevice-1.0-SNAPSHOT-jar-with-dependencies.jar

run bat file: D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\compileNative.bat

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
native-image -H:ConfigurationFileDirectories=config -march=compatibility --enable-url-protocols=http,https --enable-preview --add-modules jdk.incubator.vector -jar target\com.tugalsan.blg.gvm.graphicsdevice-1.0-SNAPSHOT-jar-with-dependencies.jar com.tugalsan.blg.gvm.graphicsdevice

run bat file: D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\compileNoConsole.bat

copy com.tugalsan.blg.gvm.graphicsdevice.exe com.tugalsan.blg.gvm.graphicsdevice.noconsole.exe
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\bin\Hostx64\x64\editbin.exe" /SUBSYSTEM:WINDOWS com.tugalsan.blg.gvm.graphicsdevice.noconsole.exe

CONSOLE OUT

Microsoft Windows [Version 10.0.22621.2428]
(c) Microsoft Corporation. All rights reserved.

Clink v1.5.9.411d0f
Copyright (c) 2012-2018 Martin Ridgers
Portions Copyright (c) 2020-2023 Christopher Antos
https://github.com/chrisant996/clink

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>compileJar.bat

d:

cd D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice

cmd /c mvnd clean install -DskipTests -q versions:display-dependency-updates

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>compileAgent.bat

java -agentlib:native-image-agent=config-output-dir=config --enable-preview --add-modules jdk.incubator.vector -jar target/com.tugalsan.blg.gvm.graphicsdevice-1.0-SNAPSHOT-jar-with-dependencies.jar
WARNING: Using incubator modules: jdk.incubator.vector
main -> [#1]
main -> [#2]

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>compileNative.bat

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
**********************************************************************
** Visual Studio 2022 Developer Command Prompt v17.7.3
** Copyright (c) 2022 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
WARNING: Using incubator modules: jdk.incubator.vector
========================================================================================================================
GraalVM Native Image: Generating 'com.tugalsan.blg.gvm.graphicsdevice' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                                   (11,7s @ 0,15GB)
 Java version: 21.0.1+12, vendor version: Oracle GraalVM 21.0.1+12.1
 Graal compiler: optimization level: 2, target machine: compatibility, PGO: ML-inferred
 C compiler: cl.exe (microsoft, x64, 19.37.32822)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
 1 user-specific feature(s):
 - com.oracle.svm.thirdparty.gson.GsonFeature
------------------------------------------------------------------------------------------------------------------------
Build resources:
 - 20,82GB of memory (66,3% of 31,41GB system memory, determined at start)
 - 16 thread(s) (100,0% of 16 available processor(s), determined at start)
[2/8] Performing analysis...  [******]                                                                  (11,5s @ 0,52GB)
    6.723 reachable types   (80,1% of    8.392 total)
    9.551 reachable fields  (55,2% of   17.314 total)
   34.508 reachable methods (55,0% of   62.750 total)
    2.211 types,   114 fields, and 1.798 methods registered for reflection
       97 types,   120 fields, and    87 methods registered for JNI access
       0 foreign downcalls registered
        4 native libraries: crypt32, ncrypt, version, winhttp
[3/8] Building universe...                                                                               (1,8s @ 0,70GB)
[4/8] Parsing methods...      [**]                                                                       (3,7s @ 0,61GB)
[5/8] Inlining methods...     [***]                                                                      (0,6s @ 0,66GB)
[6/8] Compiling methods...    [*****]                                                                   (24,9s @ 0,83GB)
[7/8] Layouting methods...    [**]                                                                       (2,5s @ 0,92GB)
[8/8] Creating image...       [**]                                                                       (3,0s @ 0,64GB)
  17,91MB (54,74%) for code area:    19.468 compilation units
  14,53MB (44,42%) for image heap:  206.679 objects and 50 resources
 279,28kB ( 0,83%) for other data
  32,71MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
  12,01MB java.base                                            4,83MB byte[] for code metadata
   2,74MB svm.jar (Native Image)                               2,52MB byte[] for java.lang.String
   1,25MB java.desktop                                         1,45MB java.lang.String
 367,37kB java.rmi                                             1,15MB java.lang.Class
 266,37kB java.naming                                        636,93kB byte[] for general heap data
 257,91kB jdk.crypto.ec                                      503,70kB heap alignment
 180,64kB com.oracle.svm.svm_enterprise                      380,02kB byte[] for reflection metadata
 164,87kB java.logging                                       315,14kB com.oracle.svm.core.hub.DynamicHubCompanion
 135,25kB jdk.naming.dns                                     267,41kB java.util.HashMap$Node
 105,20kB jdk.crypto.mscapi                                  195,44kB c.o.svm.core.hub.DynamicHub$ReflectionMetadata
 338,36kB for 16 more packages                                 2,34MB for 1723 more object types
                              Use '-H:+BuildReport' to create a report with more details.
------------------------------------------------------------------------------------------------------------------------
Security report:
 - Binary includes Java deserialization.
 - Use '--enable-sbom' to embed a Software Bill of Materials (SBOM) in the binary.
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 PGO:  Use Profile-Guided Optimizations ('--pgo') for improved throughput.
 INIT: Adopt '--strict-image-heap' to prepare for the next GraalVM release.
 HEAP: Set max heap for improved and more predictable memory usage.
 QBM:  Use the quick build mode ('-Ob') to speed up builds during development.
------------------------------------------------------------------------------------------------------------------------
                        3,8s (6,2% of total time) in 515 GCs | Peak RSS: 1,73GB | CPU load: 6,73
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\awt.dll (jdk_library)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\com.tugalsan.blg.gvm.graphicsdevice.exe (executable)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\fontmanager.dll (jdk_library)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\freetype.dll (jdk_library)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\java.dll (jdk_library_shim)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\javaaccessbridge.dll (jdk_library)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\jawt.dll (jdk_library)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\jvm.dll (jdk_library_shim)
 D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice\lcms.dll (jdk_library)
========================================================================================================================
Finished generating 'com.tugalsan.blg.gvm.graphicsdevice' in 1m 0s.

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>compileNoConsole.bat

copy com.tugalsan.blg.gvm.graphicsdevice.exe com.tugalsan.blg.gvm.graphicsdevice.noconsole.exe
        1 file(s) copied.

"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\bin\Hostx64\x64\editbin.exe" /SUBSYSTEM:WINDOWS com.tugalsan.blg.gvm.graphicsdevice.noconsole.exe
Microsoft (R) COFF/PE Editor Version 14.37.32822.0
Copyright (C) Microsoft Corporation.  All rights reserved.


D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>com.tugalsan.blg.gvm.graphicsdevice.exe
main -> [#1]
main -> [#2]

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>copy com.tugalsan.blg.gvm.graphicsdevice.log con:
main -> [#1]
main -> [#2]
        1 file(s) copied.

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>com.tugalsan.blg.gvm.graphicsdevice.noconsole.exe

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>copy com.tugalsan.blg.gvm.graphicsdevice.log con:
main -> [#1]
        1 file(s) copied.

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>java -version
java version "21.0.1" 2023-10-17
Java(TM) SE Runtime Environment Oracle GraalVM 21.0.1+12.1 (build 21.0.1+12-jvmci-23.1-b19)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21.0.1+12.1 (build 21.0.1+12-jvmci-23.1-b19, mixed mode, sharing)

D:\git\blg\com.tugalsan.blg.gvm.graphicsdevice>
1

There are 1 answers

0
Tugalsan Karabacak On

Not an answer, but just a fix:

I think the only way to do is, calling a console app from non-console app, there console app will pop up, detect screen size, print it to console and disappear. At least the main non-console app can run hidden.

On my computer, nothing popped up, but it may vary according to speed of cpu.

Here is a working implementation:

package com.tugalsan.blg.gvm.graphicsdevice;

import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import static java.lang.System.out;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;

public class Main {

    public static void main(String... s) {
        try {
            if (s.length == 1 && Objects.equals(s[0], "size")) {//WHEN CALLED AS CONSOLE, return size
                out.print(size());
                System.exit(0);
            }
            delLogFile();
            if (!fileExists(fileSize)) {
                log("main", "fileSize not exists, will call console app...", fileSize);
                var process = Runtime.getRuntime().exec(fileConsoleApp + " size");
                process.waitFor();
                String strSize;
                try (var is = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    strSize = is.lines().collect(Collectors.joining("\n"));
                }
                txt2File(strSize, fileSize);
            }
            var strSize = file2txt(fileSize);
            log("main", "size retrived from fileSize", fileSize, strSize);
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    final public static Path fileSize = FileSystems.getDefault().getPath(Main.class.getPackageName() + ".size").toAbsolutePath();
    final public static Path fileConsoleApp = FileSystems.getDefault().getPath(Main.class.getPackageName() + ".exe").toAbsolutePath();

    //UTILITY FUNC SIZE
    @Deprecated //Warning: GRAALVM NO-CONSOLE NOT WORKING!!!
    public static Rectangle size() {
        var rectangle = new Rectangle(0, 0, 0, 0);
        for (var graphicsDevice : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
            rectangle = rectangle.union(graphicsDevice.getDefaultConfiguration().getBounds());
        }
        return rectangle;
    }

    //UTILITY FUNC FILE
    public static boolean fileExists(Path file) {
        return !Files.isDirectory(file) && Files.exists(file);
    }

    public static void txt2File(CharSequence source, Path dest) {
        try {
            Files.writeString(dest, source, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String file2txt(Path source) {
        try {
            return Files.readString(source);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //UTILITY FUNC LOG
    public static void delLogFile() {
        try {
            Files.deleteIfExists(fileLog);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void log(CharSequence funcName, Object... oa) {
        try {
            var lstStr = Arrays.stream(oa)
                    .map(o -> String.valueOf(o))
                    .collect(Collectors.toCollection(ArrayList::new));
            var str = funcName + " -> " + lstStr + "\n";
            out.print(str);
            Files.writeString(fileLog, str, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    final public static Path fileLog = FileSystems.getDefault().getPath(Main.class.getPackageName() + ".log").toAbsolutePath();
}