Search code examples
javajavafxmouseeventmouse

Why is JavaFX raising an IllegalStateException when I create a Robot object?


I am creating an application, and I want a feature where when the user presses E, it will set the mouse pointer to the center of the screen (using the moveMouse() method). However, it just gives me an IllegalStateException.

I have tried using a GlassRobot instead (from this question with an accepted answer), but that only gives me an IllegalAccessException instead. What am I doing wrong? Do I need to include something?

Code and Error

IllegalStateException

Note: There is a comment near the bottom

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1082)
Caused by: java.lang.ExceptionInInitializerError
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
    ... 5 more
Caused by: java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = main
    at javafx.graphics/com.sun.glass.ui.Application.checkEventThread(Application.java:447)
    at javafx.graphics/javafx.scene.robot.Robot.<init>(Robot.java:70)
    at com.galactify.tiles/com.galactify.tiles.HelloApplication.<clinit>(HelloApplication.java:37) // This is where the Robot object is created
    ... 11 more

Scene Robot Declaration

private static final Robot robot = new Robot() ;

GlassRobot Declaration

private static final Robot robot = com.sun.glass.ui.Application.GetApplication().createRobot() ;

Methods used to set the mouse pointer to the center when user presses E

static void onKeyReleased(KeyEvent evt) {
        KeyCode key = evt.getCode() ;
        out.println(key + " is released") ;

        switch (key) {
            case F11 -> {
                stage.setFullScreen(!isFullscreen) ;
                isFullscreen = !isFullscreen ;
            }

            case C -> {
                stage.close() ;
                isClosed = true ;
            }

            case E -> setupInventory() ;

        }
    }
static void setupInventory() {
        robot.mouseMove((double) screenWidth / 2, (double) screenHeight / 2) ;
    }

Solution

  • The question you refer to is outdated (don't use GlassRobot)

    The question mentions raising a request to make the GlassRobot functionality public API. This request was completed years ago in JavaFX 11, and the public API is Robot (which you are already using in your question).

    There is no need to use the non-public GlassRobot API and there are numerous reasons not to use it.

    The error message tells you what your issue is

    This operation is permitted on the event thread only; currentThread = main  
    

    You have robot declared as a static member of your application class. Static members are initialized when the class is loaded. Your main application class will be loaded on the main Java thread. But, the error message states the Robot must be loaded on the JavaFX thread.

    How to fix your issue

    Change your declaration to this:

    private Robot robot;
    

    Initialize the robot in your start() method (or some other method that is called on the JavaFX application thread):

    @Override
    public void start(Stage stage) {
        robot = new Robot();
        // other app logic
    }
    

    Advice

    Often it is best to minimize static state and initialization in Java applications. One reason is that if the initialization fails and throws an Exception, then the class will fail to load, which can be quite hard to understand (as you may have discovered here). There are other reasons for avoiding static state. This advice applies mainly to static state. Static methods that don't rely on static state, such as those in the Math class, can sometimes be fine.

    If you want to know more about threading in JavaFX, read the Application lifecycle documentation.