Mouse pos relative to frame java windows is wrong - java

I have an application that opens a window and calculates the relative pos to frame as showed in there : How to get mouse pointer location relative to frame . In Linux this works fine, but when running it on Windows, the Y-Coordinate is around 30px to big
(probably the window border height ?). Thanks for help.
import javax.swing.JFrame;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.*;
import java.awt.MouseInfo;
public class Name extends JFrame {
public Name() {
super("Name");
setTitle("Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
setResizable(true);
setVisible(true);
int x;
int y;
while (true){
x = MouseInfo.getPointerInfo().getLocation().x-getX();
y = MouseInfo.getPointerInfo().getLocation().y-getY(); //This is around 30px to big in windows
System.out.println("X : "+Integer.toString(x)+" Y : "+Integer.toString(y));
try { //Update screen every 33 miliseconds = 25 FPS
Thread.sleep(33);
} catch(InterruptedException bug) {
Thread.currentThread().interrupt();
System.out.println(bug);
}
}
}
public static void main(String args[]){
new Name();
}
}
This code compiles without any error and works fine, but for me, it seems that in Windows the Y-Coordinate is around 30px to big.
Note : This is only a simplified version of the real application, so probably the error wont occur here. But I havent got a Windows device at home, so Im not able to test it.

The code is calculating the difference between the mouse position and the top left corner of the window, that is, the position relative to the window. I suppose you want the position relative to the top left corner of the internal frame (content pane), so try
...
while (true){
Point reference = getContentPane().getLocationOnScreen();
x = MouseInfo.getPointerInfo().getLocation().x-reference.x;
y = MouseInfo.getPointerInfo().getLocation().y-reference.y;
...
not tested on Linux...

import javax.swing.JFrame;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.*;
import java.awt.MouseInfo;
import java.awt.Component;
public class Name extends JFrame {
public Name() {
super("Name");
setTitle("Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
setResizable(true);
setContentPane(new Pane());
setVisible(true);
int x;
int y;
Component[] rel;
while (true){
rel = getComponents();
x=MouseInfo.getPointerInfo().getLocation().x-rel[0].getLocationOnScreen().x;
y=MouseInfo.getPointerInfo().getLocation().y-rel[0].getLocationOnScreen().y;
System.out.println("X : "+Integer.toString(x)+" Y : "+Integer.toString(y));
try { //Update screen every 33 miliseconds = 25 FPS
Thread.sleep(33);
} catch(InterruptedException bug) {
Thread.currentThread().interrupt();
System.out.println(bug);
}
}
}
class Pane extends JPanel {
public void paintComponent(Graphics g) { //Here is were you can draw your stuff
g.drawString("Hello World",0,20); //Display text
}
}
public static void main(String args[]){
new Name();
}
}
this did the trick

Related

Is there a way in Java to be able to click through a JFrame?

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);
}
}
}
}
}
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.

Swing custom painting animation stops after reaching half width of frame

I was trying to answer a question related to moving a ball across the screen while changing its color over time, however I came through a weird bug, (most probably in my code) and while asking this question I came to a related question but that question is using a Client-Server architecture while mine is simply a Swing app running itself.
What is happening is that when the circle / ball, however you want to name it, reaches the half width of the JPanel or JFrame it becomes invisible or stops.
At first I thought it could be my JPanel being badly positioned, but I added a Border to it, so I could see its dimensions, but it's showing the whole border around the whole space of the JFrame.
Next I thought it could be some arithmetical problem, so I decided to make the ball larger and smaller than what I was originally painting it, giving me the same result, and having the same issue when I enlarge or reduce the window's size.
To get the following output I needed to change the increment by 9 instead of 10 that I was adding originally, because if I change it to 10 it becomes invisible:
The below code produces the above output:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ChangingColorBall {
private JFrame frame;
private Timer timer;
private BallPane ballPane;
public static void main(String[] args) {
SwingUtilities.invokeLater(new ChangingColorBall()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
ballPane = new BallPane();
timer = new Timer(100, e -> {
ballPane.increaseX();
});
ballPane.setBorder(BorderFactory.createLineBorder(Color.RED));
frame.add(ballPane);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
timer.start();
}
#SuppressWarnings("serial")
class BallPane extends JPanel {
private int x;
private static final int Y = 50;
private static final int SIZE = 20;
private Color color;
private Random r;
public void increaseX() {
x += 9;
r = new Random();
color = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
revalidate();
repaint();
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(color);
// g2d.fill(new Ellipse2D.Double(x, Y, SIZE, SIZE));
g2d.fillOval(x, Y, SIZE, SIZE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
}
}
I also thought it could be something related to the Shapes API, and decided to change it to fillOval as well with the same results, I can't post a GIF yet, but will add it later if necessary.
I'm working under macOS Sierra 10.12.6 (16G29) on a MacBook Pro (13'' Retina display, early 2015) compiling and running it under Java 1.8
I'll test this code later as well on my own PC and not my work's Mac, however, could this be a bug related to Swing's API or a bug in my own code? If so, what am I doing wrong? Since it doesn't seem clear to me
The issue is that you are inadvertently overriding the getX() method defined in JComponent in your BallPane class.
As a result the x coordinate of the the JPanel whenever accessed by getX() is also changing as getX() now returns your field x which is defining how the ball moves and thus resulting in this behavior. You should either remove the method getX() from BallPane or rename it.

Full Screen Exclusive Mode Key Input with just a Window

I am working on a small game using full screen exclusive and I need to be able to receive keyboard input from the player.
In my program, I have a Window which I set to Full Screen Exclusive, and a rendering loop.
Window creation:
private void initialize() {
//This is used for my game loop...
running = true;
//Create the instance variable 'window' here.
window = new Window(null);
//Ignoring OS paint requests...
window.setIgnoreRepaint(true);
//Set the window to full screen exclusive.
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
...
Game Loop:
private void loop() {
Graphics graphics = window.getGraphics();
graphics.setColor(Color.CYAN);
graphics.fillRect(0, 0, 1920, 1080);
graphics.dispose();
}
My Imports:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
Now, this works fine. Just as it should. It renders a cyan colored rectangle, across my screen, however, I need to be able to detect when the user hits keys e.g. hitting Escape to close the program, and such. I don't know how to do this. :(
I have tried adding a KeyListener to my window (didn't work).
I have tried adding a JPanel to my window and adding a Listener to that (also didn't work).
I have tried requesting focus on my JPanel and doing the same thing above.
I have tried making a JFrame with a KeyListener then passing it into my window's constructor.
I have tried passing the same JFrame with Key Bindings to my window, rather than a KeyListener.
Obviously, none of the above has worked. (Errors weren't thrown, I simply couldn't get the program to output the text in my sysouts when I pressed a key or exit the program using System.exit(int);) I have taken out everything that didn't work for me from the above code; I currently have a window and a game loop. If there are any other ways for me to get Key Input in full screen exclusive, please inform me. (I feel as if there is a conventional way specifically for full screen exclusive, but I haven't found one yet.) Or if you believe there is a way to get one of the methods I have tried to work, (maybe you believe I did something wrong), please let me know. (I am getting somewhat desperate at this point).
Example using Key Bindings. Really simple, also demonstrates the use of DisplayMode if you're so inclined, but once it's running, simply press and hold space, it will update, release it, it will update. Double click to close ;)
import java.awt.DisplayMode;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame("Test");
f.setUndecorated(true);
f.add(new TestPane());
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GraphicsDevice device = GraphicsEnvironment
.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if (device.isFullScreenSupported()) {
device.setFullScreenWindow(f);
if (device.isDisplayChangeSupported()) {
try {
List<DisplayMode> matchingModes = new ArrayList<>(25);
DisplayMode[] modes = device.getDisplayModes();
for (DisplayMode mode : modes) {
if (mode.getWidth() == 1280 && mode.getHeight() == 720) {
matchingModes.add(mode);
}
}
if (!matchingModes.isEmpty()) {
for (DisplayMode mode : matchingModes) {
try {
device.setDisplayMode(mode);
System.out.println(mode.getWidth() + "x" + mode.getHeight() + " " + mode.getBitDepth() + " # " + mode.getRefreshRate());
break;
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
System.err.println("!! No matching modes available");
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.err.println("Change display mode not supported");
}
} else {
System.err.println("Full screen not supported");
}
}
public static class TestPane extends JPanel {
private boolean spaced = false;
public TestPane() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
requestFocusInWindow(true);
if (e.getClickCount() == 2) {
SwingUtilities.windowForComponent(TestPane.this).dispose();
}
}
});
InputMap im = getInputMap();
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "spaced-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "spaced-released");
am.put("spaced-pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
spaced = true;
repaint();
}
});
am.put("spaced-released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
spaced = false;
repaint();
}
});
requestFocusInWindow(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String text = getWidth() + "x" + getHeight();
FontMetrics fm = g.getFontMetrics();
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = (getHeight() - fm.getHeight()) / 2;
g.drawString(text, x, y + fm.getAscent());
GraphicsDevice device = GraphicsEnvironment
.getLocalGraphicsEnvironment().getDefaultScreenDevice();
DisplayMode mode = device.getDisplayMode();
text = mode.getWidth() + "x" + mode.getHeight() + " " + mode.getBitDepth() + " # " + mode.getRefreshRate();
x = (getWidth() - fm.stringWidth(text)) / 2;
y += fm.getHeight();
g.drawString(text, x, y + fm.getAscent());
text = "Spaced [" + spaced + "]";
x = (getWidth() - fm.stringWidth(text)) / 2;
y += fm.getHeight();
g.drawString(text, x, y + fm.getAscent());
}
}
}
I think the default event handling for keystrokes is that you could only get them from a focusable component, i.e. a text-field. However, referencing this post, you could try using adding a custom KeyEventDispatcher (which I think is the underlying event-handling for AWT) to the KeyboardFocusManager.

motion of rectangle by keyboard

I made the following code to move a rectangle using arrowkeys of keyboard. The "keyPressed" function does not seem to be working properly.Infact, i don't think it is even getting called when a key is pressed bcz when i tried to print some text when a key is pressed, it was not getting printed.All i see in the output window is a stationary rectangle fixed at the top left corner of the window.Here is my code....pls help me...i need it desperately
import javax.swing.JFrame;
public class Main
{
public static void main(String args[])
{
JFrame window=new JFrame();
window.setSize(600,400);
window.setTitle("window");
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingComponent DC=new drawingComponent();
window.add(DC);
}
}
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JComponent;
import javax.swing.Timer;
public class drawingComponent extends JComponent implements ActionListener,KeyListener
{
Timer t=new Timer(2000,this);//moving after 5 milliseconds
static int x=0;
static int y=0;
private static int velx=0;
private static int vely=0;
public drawingComponent()
{
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
System.out.println("tr1");
}
public void paintComponent(Graphics g)
{
Graphics2D g2=(Graphics2D) g;
Rectangle rect1=new Rectangle(x,y,50,30);
g2.setColor(Color.RED);
g2.fill(rect1);
System.out.println("tr2");
}
public void actionPerformed(ActionEvent e) //inbuilt fncn f actionListener(interface) which needs to be created
{
x+=velx; //changing values
y+=vely;
System.out.println("tr3");
repaint(); //inbuilt fncn to repeat the paintComponent method
}
public void keyPressed(KeyEvent e)
{
int code=e.getKeyCode();
if(code==KeyEvent.VK_UP)
{ velx=0; vely=-1;repaint(); }
if(code==KeyEvent.VK_DOWN)
{ velx=0; vely=1; repaint(); }
if(code==KeyEvent.VK_LEFT)
{vely=0; velx=-1; repaint(); }
if(code==KeyEvent.VK_RIGHT)
{vely=0; velx=1; repaint();}
}
public void keyTyped(KeyEvent e)
{}
public void keyReleased(KeyEvent e)
{}
}
Welcome to the wonderful world of KeyListeners...
While you have set the component focusable, you've not requested that the component be focused.
You could try calling requestFocusInWindow, but he this raises the question of when to call it.
You could call it within the constructor, but because the component doesn't belong to a valid visible component yet, the call may fail. You could override the components addNotify method and add the call to it, after you call super.addNotify, but the requestFocusInWindow method doesn't gurentee that focus will be given the component
Instead, you could simply avoid all this hassle and use the key bindings API instead, which will give you control over the level of focus require for key events to be triggered
As a side note, you should call setVisible on your frame after you've set up the UI completely

Mouse Pointer Problem in Java Swing

I have created the following simple Java Swing program which outputs a 3*3 square in the window every time the user clicks their mouse. The squares remain in the window even if the user clicks more than once. The program compiles and runs just fine, however, when one clicks in the window the square is drawn far below where the mouse pointer is. I've been racking my brain over this one for a while -- what can I change here to get the square to appear exactly with the pointer on each click? Many thanks for any help!
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class ClickCloud extends JComponent {
final ArrayList<Point2D> points = new ArrayList<Point2D>();
public void addPoint(Point2D a) {
points.add(a);
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < points.size(); i++) {
Point2D aPoint = points.get(i);
g2.draw(new Rectangle2D.Double(aPoint.getX(), aPoint.getY(), 3, 3));
}
}
public static void main(String[] args) {
final ClickCloud cloud = new ClickCloud();
JFrame aFrame = new JFrame();
class ClickListen implements MouseListener {
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
cloud.addPoint(arg0.getPoint());
cloud.repaint();
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
aFrame.setSize(500, 500);
aFrame.add(cloud);
aFrame.addMouseListener(new ClickListen());
aFrame.setVisible(true);
}
}
You're adding the MouseListener to the JFrame, but displaying the results in the JComponent and relative to the JComponent. So the location of the Point clicked will be relative to the JFrame's coordinates, but then displayed relative to the JComponent's coordinates which will shift things down by the distance of the title bar. Instead simply add the MouseListener to the same component that is responsible for displaying the results so that the display and clicking coordinates match:
aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
aFrame.setSize(500, 500);
aFrame.add(cloud);
//!! aFrame.addMouseListener(new ClickListen()); // !! Removed
cloud.addMouseListener(new ClickListen()); // !! added
aFrame.setVisible(true);
By the way: Thanks for creating and posting a decent SSCCE as this makes it so much easier to analyse and solve your problem.

Categories