1

I'm working on a simple overlay program for PC games. It's just a transparent rectangle positioned in the center of the screen but its size is controlled by the user's mouse wheel. So the concept is to simply match the size of the transparent rectangle to the size of the enemy player to calculate his distance.

Unfortunately I cannot make this happen with conventional mouse listeners because the mouse must be focused on the game and the overlay program at the same time. I'm trying JNativeHook, but I can't get my rectangle to update. Any advice?

public class Main extends Application implements NativeMouseWheelListener {

    Rectangle r = new Rectangle();
    int y = 540; 
    int width = 75;
    int height = 180;
    int velocity = 10;

    @Override
    public void start(Stage stage) throws Exception {
         AnchorPane root = new AnchorPane();
         r = rect(); 
         root.getChildren().add(r);
         root.setStyle("-fx-background-color: rgba(0, 0, 0, 0);");

         Scene scene = new Scene(root, 1920, 1080); 
         scene.setFill(null);

         stage.initStyle(StageStyle.TRANSPARENT);
         stage.setScene(scene);
         stage.setX(0);
         stage.setY(0);
         stage.show();
         stage.setAlwaysOnTop(true);
    }

    public void nativeMouseWheelMoved(NativeMouseWheelEvent e) {
        int direction = e.getWheelRotation();
        System.out.println("Mouse Wheel Moved: " + direction);
        r.setY(r.getY() + direction);
    }

    public Rectangle rect() {
        r.setWidth(width);
        r.setHeight(height);
        r.setX(960 - (width/2));
        r.setY(540);
        r.setFill(Color.TRANSPARENT);
        r.setStroke(Color.BLACK);
        return r;
    }

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

    public static void go() {
        try {
            GlobalScreen.registerNativeHook();
        }
        catch (NativeHookException ex) {
            System.err.println("There was a problem registering the native hook.");
            System.err.println(ex.getMessage());
            ex.printStackTrace();
            System.exit(1);
        }

        GlobalScreen.getInstance().addNativeMouseWheelListener(new Main());
    }
}
dzikoysk
  • 1,560
  • 1
  • 15
  • 27
  • 1
    Why do you show `rect()` when it's not used anywhere? `JNativeHook` runs on it's own thread, and you have no synchronization between that thread and the event thread for JavaFX. You say size should be adjusted, but you adjust location in your code. I don't use FX much, so are you sure `AnchorPane` doesn't enforce constraints on it's children, preventing you from changing the size/location? Have you tried a different pane? – Vince Jul 30 '17 at 19:13
  • Well that's basically what I'm asking. How do I go about getting synchronization between the two? – Jay Bartgis Jul 30 '17 at 19:20
  • 1
    That has already been [asked and answered](https://stackoverflow.com/questions/13263201/javafx-thread-synchronization-with-java-thread), tons of documentation on synchronizing with JavaFX. But for all we know, that may not even be the issue. I listed other things in my comment. You need to debug your code (use a debugger). – Vince Jul 30 '17 at 19:29
  • Why are you using JNativeHook? What does it provide that JavaFX doesn't? – M. le Rutte Aug 01 '17 at 08:14
  • the mouse must be focused on the game and the overlay program at the same time. If I used conventional Java: i would literally have to escape out of the game, click into my overlay program, and click back in. It would be completely useless. I have completely legitimate business using a global mouse listener. – Jay Bartgis Aug 06 '17 at 04:59

1 Answers1

4

I highly recommend taking @vince-emigh advice and running JNativeHook on the same thread as your ui environment by using a custom implementation of the AbstractExecutorService as follows.

public class JavaFxDispatchService extends AbstractExecutorService {
    private boolean running = false;

    public JavaFxDispatchService() {
        running = true;
    }

    public void shutdown() {
        running = false;
    }

    public List<Runnable> shutdownNow() {
        running = false;
        return new ArrayList<Runnable>(0);
    }

    public boolean isShutdown() {
        return !running;
    }

    public boolean isTerminated() {
        return !running;
    }

    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return true;
    }

    public void execute(Runnable r) {
        Platform.runLater(r);
    }
}

Then use the class as the event dispatcher:

// Set the event dispatcher to a swing safe executor service.
GlobalScreen.setEventDispatcher(new JavaFxDispatchService());

// Initialize native hook.
try {
    GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
    System.err.println("There was a problem registering the native hook.");
    System.err.println(ex.getMessage());
    ex.printStackTrace();
    System.exit(1);
}

GlobalScreen.addNativeKeyListener(this);

For more information, please see the Thread Safety article in the wiki

Alex Barker
  • 4,316
  • 4
  • 28
  • 47
  • 1
    He's using JavaFX, which JNativeHook doesn't have a dispatch service for. Should include that he'd have to make his own (which is *extremely* easy) that uses `Platform.runLater` opposed to `SwingUtilities.invokeLater`. +1 for proper use of API – Vince Jul 31 '17 at 19:49
  • Thanks for the tip, I don't use JavaFX but I can add a dispatcher for it ;) – Alex Barker Jul 31 '17 at 21:29
  • 4
    I highly recommend editing a JavaFX dispatch service into your post. Although this answer exposes the main point (using an event dispatcher), it's somewhat incomplete due to being Swing instead of JavaFX. You could gain a lot more rep by improving the answer. You'd be surprised at how many people view your posts long after you posted it, and how many *don't* upvote simply due to the remaining obstacles (creating JavaFX support). No need to spoon-feed if you're against it, but at least make the post focused on FX rather than Swing. – Vince Aug 01 '17 at 20:23