Application name javafx on gnome

I am helping develop an application with JavaFX for Linux and OSX. On Linux we cannot have the application name in the gnome top panel. We have an entry point for JavaFX. The window has a nice name, but on gnome we have something like "com.myApp.javaFXMainClass".

I have the same swing problem and I was able to fix it with this code:

// Set name in system menubar for Gnome (and Linux)
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
    try {
        Toolkit xToolkit = Toolkit.getDefaultToolkit();
        Field awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName");
        awtAppClassNameField.setAccessible(true);
        awtAppClassNameField.set(xToolkit, "MyApp"); 
    } catch (Exception e) {
        // TODO
    }
}

      

How do I do this with JavaFX?

+6


source to share


3 answers


This bug was already reported here, but I couldn't get it to work, so ultimately awt

mt was to create an app awt

as a bootstrap for JavaFx

and it worked like a charming



Snippet @gitlab

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;

import javax.swing.*;
import java.awt.*;
import java.lang.reflect.Field;

public class CustomJavaFxAppName {

    private void display() {
        JFrame f = new JFrame("CustomJavaFxAppName");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JFXPanel jfxPanel = new JFXPanel() {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(320, 240);
            }
        };
        initJFXPanel(jfxPanel);
        f.add(jfxPanel);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);

    }

    private void initJFXPanel(JFXPanel jfxPanel) {
        Platform.runLater(() -> {
            javafx.scene.control.Label label = new javafx.scene.control.Label(
                System.getProperty("os.name") + " v"
                + System.getProperty("os.version") + "; Java v"
                + System.getProperty("java.version"));
            StackPane root = new StackPane(label);
            Scene scene = new Scene(root);
            jfxPanel.setScene(scene);
        });

        if (System.getProperty("os.name").toLowerCase().contains("linux")) {
            try {
                Toolkit xToolkit = Toolkit.getDefaultToolkit();
                Field awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName");
                awtAppClassNameField.setAccessible(true);
                awtAppClassNameField.set(xToolkit, "MyApp");
            } catch (Exception ignored) { }
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new CustomJavaFxAppName()::display);
    }
}

      

0


source


package test;

import javafx.application.Preloader;
import javafx.stage.Stage;

public class TestPre extends Preloader {
    @Override
    public void start(Stage stage) throws Exception {
        com.sun.glass.ui.Application.GetApplication().setName("app test");
    }   
}


package test;

import java.io.ByteArrayInputStream;
import java.util.Base64;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Test extends Application {

    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                Test __app = new Test();
                Stage __stage = new Stage();
                __app.start(__stage);
            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);

        Scene scene = new Scene(root, 300, 250);

        String __simage = "iVBORw0K.....";
        ByteArrayInputStream __imgstream = new ByteArrayInputStream(Base64.getDecoder().decode(__simage));
        javafx.scene.image.Image __image = new javafx.scene.image.Image(__imgstream);
        primaryStage.getIcons().add(__image);       

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        com.sun.javafx.application.LauncherImpl.launchApplication(Test.class, TestPre.class, args);
    }

}

      



0


source


Here is a workaround / solution that should be relatively easy to use for anyone packaging their JavaFX application in a jar using some build tool (like Maven, Gradle, Leiningen) that supports Maven dependency style coordinates:

  • One little class fell into your project
  • Two small dependencies
  • Two values ​​in your manifest
  • One extra argument to your Java startup command
  • One environment variable in your startup script

View / get source here: no.andante.george.Agent

(Detailed usage / help built into source.)

Hope this is helpful.
(Let me know if you were able to use it successfully in the comment below.)


Understanding the problem and this solution

The problem is the double hit!

Digging into the OpenJDK source code, you will find in "sun.awt.X11.XToolkit" (which extends "sun.awt.UnixToolkit", which extends "sun.awt.SunToolkit", which extends "java.awt.Toolkit"):

    private static String awtAppClassName = null;

      

If you set this variable to whatever you want (through introspection) early enough (for example, at the beginning of your main

method), your value will be fetched and passed to the underlying GTK system when the Toolkit windowing system is initialized. This is what IntelliJ for example does in com.intellij.ui.AppUIUtil

However, if you dig deeper into the OpenJFX source code, you will find it at com.sun.class.ui.Application:

    private final static String DEFAULT_NAME = "java";
    protected String name = DEFAULT_NAME;

      

Looking at this, your first idea might be to do
com.sun.javafx.application.PlatformImpl.setApplicationName(..)

. But it won't work because it's too late! The variable name

was set on an instance of the class and then passed to the underlying GTK system before your code is called.

So you're trying to use introspection to set the constant two DEFAULT_NAME

what you want and so that the instance of the variable gets used to name

. But that doesn't work either! What for?

Because of the keyword final

(which is the most significant difference between Toolkit and application implementation)!
Because it is final static and is assigned a value "java"

, that value is "inlined" at compile time. Those. it is inserted into the "persistent pool" and any constant DEFAULT_NAME

references are replaced with a persistent pool reference. And so, even if you set a constant at runtime, it will be ignored and any code (in this case, the constructor) will simply set the name

variable to the value that was compiled into the constant pool.

I don't think you can change the constant pool or constructor body through introspection. If you can, I would like to know how!

Enter ASM

But if you can rewrite the actual bytecode of the class before it is loaded by the JVM, then you can do pretty much anything!

So I decided to write a little "Java agent" (think of it as JVM middleware). All it does is look for the APPLICATION_NAME environment variable, and if found, it looks for the com.sun.glass.ui.Application class and slightly transforms it before passing it to the JVM and classloader:

First, the constant is DEFAULT_NAME

set to "APPLICATION_NAME". It then modifies the body of the application constructor so that the variable is name

assigned the value two DEFAULT_NAME

(instead of the value in the constant pool). Finally, it disables the method setName

(by deleting its body).

Hope this is interesting and maybe helpful for someone. Please see (and use) my solution linked at the top of this answer. Feedback is appreciated.

0


source







All Articles