0

I used transparent click-through overlay based on the answer (that uses jna to enable click-through) https://stackoverflow.com/a/28772306/1093872. However the actual visible overlay is always behind one step, ie. when a repaint() gets called on the component, the update that should have been shown in the previous update is shown.

At first I suspected memory inconsistency issue because of the different thread. But I also tried the sure-to-be-correct approach using SwingWorker, and still faced the same problem.

The overlay only updated after the next call to repaint, although I used a generous 2000ms delay

AWTUtilities.setWindowOpaque(w, false); seems to be causing the problem in some way, because after I comment that out or set it to true, the problem disappears, but unfortunately the window is also not transparent (obviously).

The update is correct after disabling the transparency

I suspect the problem might be something related to double-buffering (the buffer is not swapped after painting only before next paint), but I don't really know. I also tried calling setDoubleBuffered(false) and setDoubleBuffered(true) on the component, but that does not change anything.

Also I realized that the problem isn't related to the jna part, because after removing that, the problem remains exactly the same.

Note that the index printing occurs at the same time as the drawing, so I know the paintComponent gets called in time, but the visible update only happens on the next call to it.

Any ideas on what can be the cause, and how to fix this?

import com.sun.awt.AWTUtilities;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;

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

public class OverlayUpdateTest {

    private static MyJComponent myJComponent;

    public static void main(String[] args) {
        setupOverlayWindow();

        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                myJComponent.increaseIndex();
                myJComponent.repaint();

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private static void setupOverlayWindow() {
        Window w = new Window(null);

        myJComponent = new MyJComponent();
        w.add(myJComponent);
        w.pack();
        w.setLocationRelativeTo(null);
        w.setVisible(true);
        w.setAlwaysOnTop(true);
        /**
         * This sets the background of the window to be transparent.
         */
        AWTUtilities.setWindowOpaque(w, false);
        setTransparent(w);
    }

    private static void setTransparent(Component w) {
        WinDef.HWND hwnd = getHWnd(w);
        int wl = User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_EXSTYLE);
        wl = wl | WinUser.WS_EX_LAYERED | WinUser.WS_EX_TRANSPARENT;
        User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_EXSTYLE, wl);
    }

    /**
     * Get the window handle from the OS
     */
    private static WinDef.HWND getHWnd(Component w) {
        WinDef.HWND hwnd = new WinDef.HWND();
        hwnd.setPointer(Native.getComponentPointer(w));
        return hwnd;
    }

    private static class MyJComponent extends JComponent {
        private int index = 0;

        /**
         * This will draw a black cross on screen.
         */
        protected void paintComponent(Graphics g) {
            g.setColor(Color.BLACK);
            g.fillRect(0, getHeight() / 2 - 10, getWidth(), 20);
            g.fillRect(getWidth() / 2 - 10, 0, 20, getHeight());
            g.setColor(Color.RED);
            g.drawString("i: " + index, 10, getFont().getSize() + 10);
            System.out.println("i: " + index);
        }

        public Dimension getPreferredSize() {
            return new Dimension(100, 100);
        }

        void increaseIndex() {
            index++;
        }
    }
}
Community
  • 1
  • 1
Adam Toth
  • 933
  • 2
  • 13
  • 27

1 Answers1

0

After much trial and error I found a working solution...


Changing Window w = new Window(null); to Window w = new JWindow(); solved the problem. I don't know why, but I'm happy with it.

Adam Toth
  • 933
  • 2
  • 13
  • 27