You might be interested to know there is a new library template
You can click through it at opacity < 0.05 on Mac (my demo).
It seems to be a well known issue with macos that this doesn’t properly work.
There is e.g. a website where a .jar file is sold that supposedly will solve this problem. However there is actually nothing there as this problem has no proper solution…
I think I can make something similar if this code works properly:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
import static java.awt.GraphicsDevice.WindowTranslucency.*;
class ShapedWindowDemo extends JFrame {
ShapedWindowDemo() {
super("ShapedWindow");
setLayout(new GridBagLayout());
// It is best practice to set the window's shape in
// the componentResized method. Then, if the window
// changes size, the shape will be correctly recalculated.
addComponentListener(new ComponentAdapter() {
// Give the window an elliptical shape.
// If the window is resized, the shape is recalculated here.
@Override
void componentResized(ComponentEvent e) {
setShape(new Ellipse2D.Double(0, 0, getWidth(), getHeight()));
}
}
);
setUndecorated(true);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
// Determine what the GraphicsDevice can support.
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
final boolean isTranslucencySupported =
gd.isWindowTranslucencySupported(TRANSLUCENT);
//If shaped windows aren't supported, exit.
if (!gd.isWindowTranslucencySupported(PERPIXEL_TRANSPARENT)) {
System.err.println("Shaped windows are not supported");
System.exit(0);
}
//If translucent windows aren't supported,
//create an opaque window.
if (!isTranslucencySupported) {
System.out.println(
"Translucency is not supported, creating an opaque window");
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ShapedWindowDemo sw = new ShapedWindowDemo();
sw.getContentPane().setBackground(new Color(0,0,0));
// Display the window.
sw.setVisible(true);
}
}
);
getSurface().setVisible(false);
This should just create an ellipse in the middle of the screen.
When checking if this properly works please also try clicking near the edge of the ellipse to exclude the possibility that it internally makes an rectangular window that places the ellipse inside.
I can’t seem to run this code by itself. Is it a complete Java project like the previous one?
Runs as is on my mac. Gives an elliptical black non-movable window which will not allow click throughs. My personal assessment is that Apple disallowed click throughs likely for a good reason (to keep users from unintended consequences).
Output:
An alternate approach would be to create a ‘halo’ circle around an arrow cursor, but that won’t be easy either.
I’m actually working on something similar:
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
BufferedImage bi;
JWindow windows[]={createPartialWindow(), createPartialWindow(), createPartialWindow(), createPartialWindow()};
JPanel panels[]=new JPanel[4];
int exclusionZoneSize=2;
void setup() {
//Generates the image
bi=new BufferedImage(displayWidth, displayHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2=(Graphics2D) bi.getGraphics();
g2.setColor(new Color(255,0,0,50));
g2.fillRect(0,0,displayWidth, displayHeight);
g2.setColor(new Color(0,0,0));
g2.fillOval(90,90,20,20);
for(int i=0;i<windows.length;i++) panels[i]=(JPanel) windows[i].getContentPane();
new java.util.Timer().schedule(new java.util.TimerTask() {
public void run() {
System.exit(0);
}
}
, 10000);
windows[0].setLocation(0,0);
for(int i=0;i<windows.length;i++) windows[i].setVisible(true);
}
void draw(){
Point p=MouseInfo.getPointerInfo().getLocation();
//Aligns the four windows so that they cover the screen except a little part around the mouse.
panels[0].setPreferredSize(new Dimension(p.x-1-exclusionZoneSize,p.y+exclusionZoneSize));
windows[0].setSize(p.x-1-exclusionZoneSize,p.y+exclusionZoneSize);
windows[1].setLocation(0,p.y+exclusionZoneSize);
panels[1].setPreferredSize(new Dimension(displayWidth,displayHeight-(p.y+exclusionZoneSize)));
windows[1].setSize(displayWidth,displayHeight-(p.y+exclusionZoneSize));
windows[2].setLocation(p.x+exclusionZoneSize,0);
panels[2].setPreferredSize(new Dimension(displayWidth-(p.x+exclusionZoneSize),p.y+exclusionZoneSize));
windows[2].setSize(displayWidth-(p.x+exclusionZoneSize),p.y+exclusionZoneSize);
windows[3].setLocation(p.x-1-exclusionZoneSize,0);
panels[3].setPreferredSize(new Dimension(2*exclusionZoneSize+1,p.y-exclusionZoneSize));
windows[3].setSize(2*exclusionZoneSize+1,p.y-exclusionZoneSize);
}
JWindow createPartialWindow() {
JWindow jw=new JWindow();
JPanel jp=new JPanel() {
@Override
public void paint(Graphics g) {
//Renders the image
Point location=new Point(0,0);
SwingUtilities.convertPointToScreen(location, this);
//println(location.x, location.y, getPreferredSize().width, getPreferredSize().height);
g.drawImage(bi.getSubimage(location.x, location.y, getPreferredSize().width, getPreferredSize().height), 0, 0, null);
}
};
jw.setBackground(new Color(0, 0, 0, 0));
jp.setBackground(new Color(0, 0, 0, 0));
jw.setAlwaysOnTop(true);
jp.setOpaque(false);
jw.setContentPane(jp);
jw.getRootPane().putClientProperty("apply.awt.draggableWindowBackground", false);
jw.pack();
return jw;
}
As one can notice, this code is extreamly laggy but it would theoretically work.
The next thing to try is to try and abuse the setShape command.
To give it a Shape covering the screen but so happening to have hole exactly where the mouse is.
Can anyone test this code? Someone on StackOverflow commented that I should try using setShape and I wrote this code based on that comment.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
BufferedImage bi;
JWindow windows[]={createPartialWindow(), createPartialWindow(), createPartialWindow(), createPartialWindow()};
JPanel panels[]=new JPanel[4];
int exclusionZoneSize=2;
void setup() {
bi=new BufferedImage(displayWidth, displayHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2=(Graphics2D) bi.getGraphics();
g2.setColor(new Color(255,0,0,50));
g2.fillRect(0,0,displayWidth, displayHeight);
g2.setColor(new Color(0,0,0));
g2.fillOval(90,90,20,20);
for(int i=0;i<windows.length;i++) panels[i]=(JPanel) windows[i].getContentPane();
new java.util.Timer().schedule(new java.util.TimerTask() {
public void run() {
System.exit(0);
}
}
, 10000);
windows[0].setLocation(0,0);
windows[0].setVisible(true);
}
void draw(){
Point p=MouseInfo.getPointerInfo().getLocation();
Rectangle window=new Rectangle(0,0,displayWidth,displayHeight);
Rectangle windown=new Rectangle(p.x-exclusionZoneSize,p.y-exclusionZoneSize,exclusionZoneSize<<1|1,exclusionZoneSize<<1|1);
Area a=new Area(window);
a.subtract(new Area(windown));
panels[0].setPreferredSize(new Dimension(displayWidth,displayHeight));
windows[0].setSize(new Dimension(displayWidth,displayHeight));
windows[0].setShape(a);
}
JWindow createPartialWindow() {
JWindow jw=new JWindow();
JPanel jp=new JPanel() {
@Override
public void paint(Graphics g) {
println("Repainting");
//Graphics2D g2=(Graphics2D)g;
//g2.fillOval(40, 40, 20, 20);
Point location=new Point(0,0);
SwingUtilities.convertPointToScreen(location, this);
println(location.x, location.y, getPreferredSize().width, getPreferredSize().height);
g.drawImage(bi.getSubimage(location.x, location.y, getPreferredSize().width, getPreferredSize().height), 0, 0, null);
}
};
jw.setBackground(new Color(0, 0, 0, 0));
jp.setBackground(new Color(0, 0, 0, 0));
jw.setAlwaysOnTop(true);
jp.setOpaque(false);
jw.setContentPane(jp);
jw.getRootPane().putClientProperty("apply.awt.draggableWindowBackground", false);
jw.pack();
return jw;
}
Unable to run second demo on mac.
Do you get any error message? Does the program just do nothing?
This is part of it (much longer); copy to clipboard doesn’t work on mac:
All that shows is the postage stamp size default Processing window which is empty.
This is the weirdest point where this program could fail.
Try replacing:
g.drawImage(bi.getSubimage(location.x, location.y, getPreferredSize().width, getPreferredSize().height), 0, 0, null);
with
g.drawImage(bi, 0, 0, null);
Now it runs without error. Get a beautiful transparent red sheen to my entire screen which is dispatched by the timer. Still have the default Processing window which should be easy to get rid of.
Is it now “click through”? if so I can now update the library to (hopefully) properly function on Mac.
Still not “click through” at any point, including the black dot.
Could you try to increase the exclusionZoneSize variable? When the program runs is there a blank square following the mouse?
Increased exclusionZoneSize from 2 to 200; still no click through in the ‘zone’.
Thats troubleing. It seems that MacOS does create a rectangle around the window in which one can’t click through. So if I wish to fix it I’d probably go down the path of trying to make the four-window solution less laggy.