0

I'm creating an autoclick program. It will probably need some fixes, but I have a major problem: My program does not detect when I press the defined key, so it does not start clicking.

package fr.didier.autoclicker;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.awt.*;
import java.awt.event.InputEvent;
import java.util.Random;

public class AutoClicker extends Application {
    private int minCps;
    private int maxCps;
    private Thread clickThread;
    private boolean running = false;
    private boolean paused = false;
    private Random random = new Random();
    private KeyCode triggerKey = KeyCode.UNDEFINED;
    private TextField minCpsField;
    private TextField maxCpsField;
    private Button startButton;
    private Button stopButton;
    private Button chooseKeyButton;
    private Label keyLabel;

    public AutoClicker() {
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        minCpsField = new TextField();
        maxCpsField = new TextField();
        chooseKeyButton = new Button("Choose key");
        keyLabel = new Label("Key: not chosen");

        chooseKeyButton.setOnAction(e -> {
            keyLabel.setText("Press a key to choose as trigger");
            primaryStage.getScene().setOnKeyPressed(event -> {
                if (event.isControlDown() || event.isAltDown() || event.isShiftDown()) {
                    keyLabel.setText("Please choose a key that is not already in use by the operating system or other applications");
                } else {
                    triggerKey = event.getCode();
                    keyLabel.setText("Key: " + triggerKey);
                    primaryStage.getScene().setOnKeyPressed(null);
                }
            });
        });

        HBox minCpsBox = new HBox(new Label("Min CPS: "), minCpsField);
        HBox maxCpsBox = new HBox(new Label("Max CPS: "), maxCpsField);
        HBox keyBox = new HBox(chooseKeyButton, keyLabel);
        VBox root = new VBox(minCpsBox, maxCpsBox, keyBox);

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

        primaryStage.setScene(scene);
        primaryStage.show();

        primaryStage.getScene().setOnKeyPressed(event -> {
            if (event.getCode() == triggerKey) {
                if (!running) {
                    if (minCpsField.getText().isEmpty() || maxCpsField.getText().isEmpty()) {
                        keyLabel.setText("Please set the min and max CPS values before starting the autoclick");
                    } else {
                        minCps = Integer.parseInt(minCpsField.getText());
                        maxCps = Integer.parseInt(maxCpsField.getText());
                        start(triggerKey);
                    }
                } else {
                    if (!paused) {
                        pause();
                    } else {
                        resume();
                    }
                }
            }
        });

        primaryStage.getScene().setOnKeyReleased(event -> {
            if (event.getCode().equals(triggerKey)) {
                if (running) {
                    pause();
                }
            }
        });
    }

    public void start(KeyCode triggerKey) {
        this.triggerKey = triggerKey;
        running = true;
        clickThread = new Thread(() -> {
            while (running) {
                while (!paused) {
                    try {
                        int delay = 1000 / (random.nextInt(maxCps - minCps + 1) + minCps);
                        Thread.sleep(delay);

                        Robot robot = new Robot();
                        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                    } catch (InterruptedException | AWTException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        clickThread.start();
    }


    public void stop() {
        running = false;
        paused = false;
        clickThread = null;
    }

    public void pause() {
        paused = true;
    }

    public void resume() {
        paused = false;
    }
}

At first I made several types of checks to know if the touch was well-defined, then taken into account. I tried to add the triggerKey argument in the start method; I also made tests with Start/Stop buttons which are still present in my code but which allowed me to see if my autoclick was clicking well (and yes it does click well, I made a correct equation, which realizes the number of clicks I want well in theory since I can't really test it at 100% currently).

I made a console message when the autoclick clicks, but no console message is sent so the autoclick doesn't work, I made it in the loop that handles clicks after the line that uses the Robot class

JuninOP
  • 11
  • 4
  • In the button handler for choosing the key, you call `primaryStage.getScene().setOnKeyPressed(null);`, so there is no handler to listen for the key presses. – James_D Jan 13 '23 at 16:17
  • Off-topic: 1. Don't mix AWT and JavaFX. Use the [JavaFX Robot](https://openjfx.io/javadoc/19/javafx.graphics/javafx/scene/robot/Robot.html) instead of the AWT one. 2. Your threading is broken. You can only access the robot from the appropriate UI thread (the FX Application Thread if you switch to the JavaFX Robot). Use the Animation API instead of a background thread. See https://stackoverflow.com/a/60685975/2189127 – James_D Jan 13 '23 at 16:22
  • Indeed you have just thank you I will make tests because I tried to replace by the triggerKey but it does not seem to work – JuninOP Jan 13 '23 at 16:22
  • Sorry I don't understand your last comment, so it's not just a "null" problem? – JuninOP Jan 13 '23 at 16:25
  • Setting the event handler to null is causing the problem you observe. There are other ways in which your code is broken which are not relevant to that problem. – James_D Jan 13 '23 at 16:28

1 Answers1

2

Your event handler for the "Choose key" button sets the key pressed handler for the scene to null, so no further key presses will be detected. Put the event handler you need in a variable, and set it from there.

Note that you should not mix JavaFX and AWT: use the JavaFX Robot instead of the AWT robot, and you should replace the threading code with Animation API to ensure the robot calls are being made from the correct thread. See https://stackoverflow.com/a/60685975/2189127 for details.

Here's a version that properly detects the key press. I have commented out the code that is broken but that is unrelated to the question.

public class AutoClicker extends Application {
    private int minCps;
    private int maxCps;
    private Thread clickThread;
    private boolean running = false;
    private boolean paused = false;
    private Random random = new Random();
    private KeyCode triggerKey = KeyCode.UNDEFINED;
    private TextField minCpsField;
    private TextField maxCpsField;
    private Button startButton;
    private Button stopButton;
    private Button chooseKeyButton;
    private Label keyLabel;

    public AutoClicker() {
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        minCpsField = new TextField();
        maxCpsField = new TextField();
        chooseKeyButton = new Button("Choose key");
        keyLabel = new Label("Key: not chosen");

        EventHandler<KeyEvent> keyEventEventHandler = event -> {
            if (event.getCode() == triggerKey) {
                if (!running) {
                    if (minCpsField.getText().isEmpty() || maxCpsField.getText().isEmpty()) {
                        keyLabel.setText("Please set the min and max CPS values before starting the autoclick");
                    } else {
                        minCps = Integer.parseInt(minCpsField.getText());
                        maxCps = Integer.parseInt(maxCpsField.getText());
                        start(triggerKey);
                    }
                } else {
                    if (!paused) {
                        pause();
                    } else {
                        resume();
                    }
                }
            }
        };

        chooseKeyButton.setOnAction(e -> {
            keyLabel.setText("Press a key to choose as trigger");
            primaryStage.getScene().setOnKeyPressed(event -> {
                if (event.isControlDown() || event.isAltDown() || event.isShiftDown()) {
                    keyLabel.setText("Please choose a key that is not already in use by the operating system or other applications");
                } else {
                    triggerKey = event.getCode();
                    keyLabel.setText("Key: " + triggerKey);
                    primaryStage.getScene().setOnKeyPressed(keyEventEventHandler);
                }
            });
        });

        HBox minCpsBox = new HBox(new Label("Min CPS: "), minCpsField);
        HBox maxCpsBox = new HBox(new Label("Max CPS: "), maxCpsField);
        HBox keyBox = new HBox(chooseKeyButton, keyLabel);
        VBox root = new VBox(minCpsBox, maxCpsBox, keyBox);

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

        primaryStage.setScene(scene);
        primaryStage.show();



        primaryStage.getScene().setOnKeyReleased(event -> {
            if (event.getCode().equals(triggerKey)) {
                if (running) {
                    pause();
                }
            }
        });
    }

    public void start(KeyCode triggerKey) {
        this.triggerKey = triggerKey;
        running = true;
        System.out.println("Start...");

        // Replace all this code with Animation API and JAvaFX Robot

//        clickThread = new Thread(() -> {
//            while (running) {
//                while (!paused) {
//                    try {
//                        int delay = 1000 / (random.nextInt(maxCps - minCps + 1) + minCps);
//                        Thread.sleep(delay);
//
//                        Robot robot = new Robot();
//                        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
//                        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
//                    } catch (InterruptedException | AWTException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }
//        });
//        clickThread.start();
    }


    // Similarly, use Animation API to control the pause/resume functionality:

    public void stop() {
//        running = false;
//        paused = false;
//        clickThread = null;
    }

    public void pause() {
//        paused = true;
    }

    public void resume() {
//        paused = false;
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Thank you I understand much better I will make the changes it may take time because I did not know the Animation API and I think I will struggle a little on the replacement of AWT by JavaFX for the management of Thread but thank you again – JuninOP Jan 13 '23 at 16:33
  • To do what you say I have to recreate my program completely: because the Animation API is used for graphical animation, while the use of AWT's Robot class is for simulating system inputs In fact you are right but I am not sure to succeed in doing this – JuninOP Jan 13 '23 at 16:51
  • @JuninOP Again, don't mix AWT and JavaFX. Replace the *threading* with the Animation API. Replace the *AWT Robot* with the JavaFX Robot. – James_D Jan 13 '23 at 18:22