2

I'm trying to disable Windows display scaling (and in other OS) in my JavaFX application. Setting -Dprism.allowhidpi="false" in IntelliJ run configuration VM options works but setting the system property in code does not. I would like to set it in code so it works in any JVM setup like GraalVM / Gluon Substrate.

Is this a bug in JavaFX or how is it used in code? The following example does not work, the stage is scaled if scaling is set in Windows:

public class Main extends Application {

    public static void main(String[] args) {
        System.setProperty("prism.allowhidpi", "false");
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        stage.setWidth(1280);
        stage.setHeight(720);
        Scene scene = new Scene(new Label("SCALE TEST"));
        stage.setScene(scene);
        stage.show();
    }
}

EDIT: As noted in the comments, creating a seperate launcher class which sets the property and then calls the Application class works. However, it does not seem to work with Gluon Substrate. Here are relevant parts of my project. I also added two other prism properties to make sure the new main class is loaded properly in Substrate and the two other properties do indeed work. If I run mvn javafx:run the scaling is turned off, if I run mvn client:build and mvn client:run the scaling is on but everything else is the same.

public class Main {
    public static void main(String[] args) {
        System.setProperty("prism.allowhidpi", "false"); // doesn't work with Substrate
        System.setProperty("prism.lcdtext", "false"); // works
        System.setProperty("prism.subpixeltext", "false"); // works
        MainFXML.run(args);
    }
}

App

public class MainFXML extends Application {
    
    @Override
    public void start(Stage stage) throws Exception {
        String fxmlFile = "/fxml/FXMLDocument.fxml";
        FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlFile));
        Parent root = loader.load();
        root.getStylesheets().add(getClass().getResource("/styles/Style.css").toString());
        FXMLDocumentController gui = (FXMLDocumentController) loader.getController();

        Scene scene = new Scene(root);
        stage.setScene(scene); 
        stage.show();
        stage.setResizable(false);

        // create logic class etc
    }

    public static void run(String[] args) {
        launch(args);
    }
}

Pom

<?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>ank</groupId>
    <artifactId>PROJ</artifactId>
    <name>PROJ</name>

    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <finalName>PROJ</finalName>
        <plugins>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.3</version>
                <configuration>
                    <mainClass>ank.fxml.Main</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.0.2</version>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.gluonhq</groupId>
                <artifactId>client-maven-plugin</artifactId>
                <version>0.1.38</version>
                <configuration>
                    <mainClass>ank.fxml.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.11</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.9.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
    </dependencies>

</project>

I also tried setting properties in the pom-file as nativeImageArgs, but none of them worked.

    <plugin>
        <groupId>com.gluonhq</groupId>
        <artifactId>client-maven-plugin</artifactId>
        <version>0.1.38</version>
        <configuration>
            <mainClass>ank.fxml.Main</mainClass>
            <nativeImageArgs>
                <arg>-Dprism.allowhidpi=false</arg>
                <arg>-Dprism.lcdtext=false</arg>
                <arg>-Dprism.subpixeltext=false</arg>
            </nativeImageArgs>
        </configuration>
    </plugin>
ank
  • 83
  • 5
  • 1
    Just to be sure that JavaFX doesnt kick in "before" you set the property ... you could try to create a new Main class that only does two things: set that property and call your Application main via reflection. I doubt it would change things, but then you are sure that "just setting the prpoerty in code" is useless. – GhostCat Apr 26 '21 at 10:10
  • 3
    Adding the system property to the `main` within `Application` is too late in this case. You can create a Main/Launcher class that doesn't extend Application, apply the property there, and then call your Application class. – José Pereda Apr 26 '21 at 10:14
  • @GhostCat It will make a difference. It has to do with how the "launch a JavaFX application without a main method" feature is implemented. If the main class extends `Application` then the JavaFX run-time is initialized before calling the main method (if there is one). – Slaw Apr 26 '21 at 10:58
  • @José Pereda Thanks, I tested this and it does indeed work. However, in my Gluon Substrate app using the client-maven-plugin it has no effect. Should it work? I updated the mainClass configuration and the reflection list and it builds fine but scaling is still on. – ank Apr 26 '21 at 11:32
  • Did you set the `mainClass` to the new launcher class? – José Pereda Apr 26 '21 at 11:34
  • Yes, set it to the new class which has a main method that sets the property and then calls the old main method in the Application class which just calls launch. – ank Apr 26 '21 at 11:44
  • Native image with the Client plugin should work too (I take you did a new build). Post some code (pom and launcher/main classes)? – José Pereda Apr 26 '21 at 13:16
  • @José Pereda I edited the question to include my code – ank Apr 26 '21 at 14:27
  • About `nativeImageArgs`, those won't work, as those are intended to build the native image. Once you have the image, though, you can use them as regular runtime arguments, like `PROJ.exe -Dprism.allowhidpi=false ...`. – José Pereda Apr 26 '21 at 15:08
  • 1
    @José Pereda I tried running `/.PROJ.exe -Dprism.allowhidpi=false` in PowerShell but scaling is still on. Actually setting `prism.lcdtext` and `prism.subpixeltext` don't work either when running the exe. I made sure I'm getting command line arguments through in general with another little test. – ank Apr 27 '21 at 07:31

2 Answers2

3

After much searching I managed to get this to work, at least on Windows. Instead of using the prism.allowhidpi flag, setting glass.win.uiScale to 100% did the trick to ignore Windows scaling. This worked setting it in code and passing it as an argument.

public class Main {
    public static void main(String[] args) {
        System.setProperty("glass.win.uiScale", "100%"); // instead of allowhidpi
        MainFXML.run(args);
    }
}

Be warned that setting prism.allowhidpi to false will negate the scaling override from what I tested.

aDonut
  • 84
  • 8
  • 1
    Very nice, this seems to be the workaround to use until prism.allowhidpi is fixed for Gluon Substrate. – ank Jun 27 '21 at 17:59
0

It seems that you want to set system environment for your app before GUI start, several ways to do that:

1.use script to start your app

#sh or cmd
#set system environment
#java -jar your_app_jar

2.use a native loader

loader[set system environment]->your app

3.use a java loader with ProcessBuilder to start you app

ProcessBuilder pb = new ProcessBuilder("your app command"); 
Map<String, String> envMap = pb.environment();
envMap.put("propName", "propValue");
pb.start();

4.use JNI or native command plus java agent in you app

//in premain
JNI[set system environment]
or 
invoke native command[set system environment]

5.use "java.lang.ProcessEnvironment" plus java agent to do the trick

//in premain
Class<?> pe = Class.forName("java.lang.ProcessEnvironment");
Method getenv = pe.getDeclaredMethod("getenv", String.class);
getenv.setAccessible(true);
Field props = pe.getDeclaredField("theCaseInsensitiveEnvironment");
props.setAccessible(true);
Map<String, String> env = (Map<String, String>) props.get(null);
env.put("propName", "propValue");
neohope
  • 1,822
  • 15
  • 29