0

I'm trying to make an overlay for a HTML-based game running in a browser window and created an JFrame which is opaque. I'd like to be able to still play the game whilst having the overlay above the window. I tried some solutions that I've found but those didn't work for me.

I've thought of catching the click-event on my JFrame and "simulating" the click on the game window. But sadly I don't have an idea how thats possible.

My current code is using the JNA libarys to access the position and scale of the window (in my test code Task-Manager). I'm fine with using another libary or something like that, if it's even possible.

Thats my code so far:

import com.sun.jna.platform.DesktopWindow;
import com.sun.jna.platform.WindowUtils;

import javax.swing.*;
import java.awt.*;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        JFrame frame = new JFrame("title");
        frame.setUndecorated(true);
        frame.setBackground(new Color(255, 69, 0, 100));
        frame.setAlwaysOnTop(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Rectangle rect = null;

        while (true) {
            for (DesktopWindow desktopWindow : WindowUtils.getAllWindows(true)) {
                if (desktopWindow.getTitle().contains("Task-Manager")) {
                    rect = desktopWindow.getLocAndSize();
                    frame.setSize(rect.width - 16, rect.height - 8);
                    frame.setLocation(rect.x + 8, rect.y);
                    frame.setVisible(true);
                    Thread.sleep(10);
                }
            }
        }
    }
}
swpalmer
  • 3,890
  • 2
  • 23
  • 31
ElDapo
  • 1
  • 2
  • Redoing events is possible using WINAPI,it will not be something that runs on every platform. As for semitransparent frame, you would have subclass the window (again WINAPI) – Antoniossss Dec 04 '21 at 23:55
  • An overlay is usually a translucent or transparent `BufferedImage` drawn over the top of the drawing `JPanel` (game play area). The `JFrame` is just a holder of one or more `JPanels`. – Gilbert Le Blanc Dec 05 '21 at 07:22

1 Answers1

0

A JFrame is a heavyweight component. There is a window in the host OS GUI system to go with it. The host OS GUI directs mouse events to the window. Perhaps using a lightweight component for your overlay and then disabling mouse events on it would be a better solution.

Your idea of catching the click event and "simulating it" on you game window should be fairly easy. If your JFrame event processing code has a reference to your game engine, it can determine the relative position of the windows and tell your game engine the corresponding point at which it should act as if it received a click. I.e. just call the same method of your game engine for click events that it received normally and also for the simulated ones.

An ugly hack (there is noticeable flicker) would be to hide the window and send the click through with the Robot class... like this:


import java.awt.AWTException;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.SwingUtilities;

public class ClickThrough  extends Frame implements MouseListener, MouseMotionListener {

    private final Robot robot;
    private Color bgColor = new Color(0x80808080, true);
    private Point dragPoint;

    public ClickThrough() throws AWTException {
        setAlwaysOnTop(true);
        robot = new Robot();
    }

    public static void main(String[] args) throws AWTException {
        ClickThrough w = new ClickThrough();
        w.setUndecorated(true);
        w.setSize(200, 100);
        w.setOpacity(0.7f);
        w.addMouseListener(w);
        w.addMouseMotionListener(w);
        w.setVisible(true);
    }

    @Override
    public void paint(Graphics g) {
        int w = getWidth();
        int h = getHeight();
        g.setColor(Color.GRAY);
        g.fillRect(0, 0, w, 16);
        g.setColor(bgColor);
        g.fillRect(0, 16, w, h-16);
        g.setColor(Color.BLACK);
        g.drawString("Go ahead, click on me...", 20, 50);
    }

    private void makeHole(MouseEvent e) {
        // Tried making a shape with a hole where the mouse was clicked,... didn't work (macOS).
        //setShape(windowWithHoleShape);
        setVisible(false);
    }

    private void repairHole(MouseEvent e) {
        //setShape(windowShape);
        setVisible(true);
    }

    @Override
    public void mousePressed(MouseEvent e) {
        Point p = e.getPoint();
        // give it a draggable area at the top
        if (p.y < 16) {
            dragPoint = p;
            return;
        }
        dragPoint = null;
        SwingUtilities.convertPointToScreen(p,this);
        makeHole(e);
        robot.mouseMove(p.x, p.y);
        robot.mousePress(InputEvent.getMaskForButton(e.getButton()));
        repairHole(e);
    }

    @Override public void mouseReleased(MouseEvent e) { }
    @Override public void mouseClicked(MouseEvent e) { }
    @Override public void mouseEntered(MouseEvent e) { }
    @Override public void mouseExited(MouseEvent e) { }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (dragPoint != null) {
            Point p = e.getPoint();
            SwingUtilities.convertPointToScreen(p, this);
            p.translate(-dragPoint.x, -dragPoint.y);
            setLocation(p);
        }
    }

    @Override
    public void mouseMoved(MouseEvent e) { }
}

I tried to see if I could cut a hole in the window by setting the window Shape, but at least on macOS the hole does not allow the mouse events through.

I should also point out that if you switch your GUI framework to JAvaFX, then you have the option of running your HTML-based game UI in a JavaFX WebView, so you can integrate your game and overlay into a single coherent application. You could specifically make your overlay "mouse transparent". IMO that would be a much better approach than hacking around with the mouse events.

swpalmer
  • 3,890
  • 2
  • 23
  • 31
  • My plan is to use HTML in the end to make the GUI good-looking. I don't really get how disabling the mouse event on i.e. a JLabel passes the click event to the window below. Also I don't have acess to the game window. It's independant to my code. I don't want to decompile and deobfuscate the sourcecode of the game. That's the whole thing I'm trying to avoid. I need a way to just "pass" the click "through" the window like stated [HERE](https://stackoverflow.com/questions/57355930/java-jframe-ignore-mouse-interaction). – ElDapo Dec 06 '21 at 00:40
  • @ElDapo Ugh... HTML is for Web Browsers, not game UIs. Disabling the event on a JFrame won't pass the event to the window below. My point is that when you get an event in your game, you call a method to act on it... So just call the same method from the JFrame, even though it is a different window that gets the event - use a reference to the game engine in both places and call the same method in both places. Just adjust the position to account for the Frame coordinates. – swpalmer Dec 06 '21 at 00:53
  • I don't think you get my problem. Let's assume I open "Mozilla Firefox". I start my Java application, it detects Mozilla Firefox, gets the position and size of it and overlays it. I want to render i.e. an transparent image above it but still be able to use it normally. Is there a way to do that? – ElDapo Dec 06 '21 at 20:02
  • @ElDapo Ah.. I assumed that the "game window" was part of your application. You want to allow clicks to go through to an arbitrary application behind the semi-transparent window. That's a different problem indeed. I think I can make something that sort of works... – swpalmer Dec 07 '21 at 19:16
  • @ElDapo I've updated my answer with a hack to pass clicks through... it's not very good. – swpalmer Dec 17 '21 at 06:40
  • When I'm at home, ill defenitely try this. That's a very good idea! You cannot pass a click event through an opaque part of the frame. You'll always click on the frame itself but you can click on the frame, get the click position, cut a transparent hole in the frame, simulate the click at that position and fix the frame. You have to be a genius ;D – ElDapo Dec 17 '21 at 12:03
  • @ElDapo added an additional suggestion that I think is the best strategy for your situation. Use JavaFX instead of AWT/Swing so you can embed the WebView and use a "mouse transparent" overlay all within the same application window. – swpalmer Dec 18 '21 at 17:59