4

I am looking for a way to capture or trap the mouse in a window after it has entered that window much like a mouse is trapped in a virtual machine window until a user presses CTRL+ALT+DEL or release the mouse in some other manner. How do I make this happen in Java? Going full screen is not an option.

EDIT:

Here is some SSCCE for ya. This code will trap your mouse in the window. To get out you just have to within the generated frame and move directly to the close button. If you will notice when your mouse tries to leave it automatically goes back to (0,0). What I need to know is how do I get it to go back to the coordinates where it exited from. I tried getX() and getY() in place of (0,0) but the robot does not return the mouse there (I think the response time is to slow). I also have had the robot move the mouse back to crosshair.x and crosshair.y but this (as well as the others) still allows the mouse to escape if the user clicks at the right moment. Any thoughts?

Main Class:

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferStrategy;
import java.awt.image.MemoryImageSource;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class Game extends JFrame implements MouseMotionListener, MouseListener{
 private int windowWidth = 640;
 private int windowHeight = 480;
 private Crosshair crosshair;
 public static void main(String[] args) {
 new Game();
 }
 public Game() {
 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 this.setSize(windowWidth, windowHeight);
 this.setResizable(false);
 this.setLocation(0,0);
 this.setVisible(true);
 this.createBufferStrategy(2);
 addMouseMotionListener(this);
 addMouseListener(this);
 initGame();
 while(true) {
 long start = System.currentTimeMillis();
 gameLoop();
 while(System.currentTimeMillis()-start < 5) {
 //empty while loop
 }
 }
 }
 private void initGame() {
 hideCursor();
 crosshair = new Crosshair (windowWidth/2, windowHeight/2);
 }
 private void gameLoop() {
 //game logic
 drawFrame();
 }
 private void drawFrame() {
 BufferStrategy bf = this.getBufferStrategy();
 Graphics g = (Graphics)bf.getDrawGraphics();
 try {
 g = bf.getDrawGraphics();
 Color darkBlue = new Color(0x010040);
 g.setColor(darkBlue);
 g.fillRect(0, 0, windowWidth, windowHeight);
 drawCrossHair(g);
 } finally {
 g.dispose();
 }
 bf.show();
 Toolkit.getDefaultToolkit().sync();
 }
 private void drawCrossHair(Graphics g){
 Color yellow = new Color (0xEDFF62);
 g.setColor(yellow);
 g.drawOval(crosshair.x, crosshair.y, 40, 40);
 g.fillArc(crosshair.x + 10, crosshair.y + 21 , 20, 20, -45, -90);
 g.fillArc(crosshair.x - 1, crosshair.y + 10, 20, 20, -135, -90);
 g.fillArc(crosshair.x + 10, crosshair.y - 1, 20, 20, -225, -90);
 g.fillArc(crosshair.x + 21, crosshair.y + 10, 20, 20, -315, -90);
 }
 @Override
 public void mouseDragged(MouseEvent e) {
 //empty method
 }
 @Override
 public void mouseMoved(MouseEvent e) {
 crosshair.x = e.getX();
 crosshair.y = e.getY();
 }
 private void hideCursor() {
 int[] pixels = new int[16 * 16];
 Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
 Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "invisiblecursor");
 getContentPane().setCursor(transparentCursor);
 }
 public void mouseExited(MouseEvent e) {
 System.out.println("Event: " + e);
 try {
 Robot robot = new Robot();
 robot.mouseMove(0, 0);// When I use (getX(),getY()) instead of (0,0) the robot will not move the mouse at all even though getX() and getY() are the coordinates I want the mouse to be moved to. Also the mouse can still escape, even when crosshair.x and crosshair.y are used as the coordinates. It seems that robot is too slow.
 }
 catch (AWTException ex) {
 ex.printStackTrace();
 }
 }
 public void mouseEntered(MouseEvent e){
 }
 public void mousePressed(MouseEvent e) {
 }
 public void mouseReleased(MouseEvent e) {
 }
 public void mouseClicked(MouseEvent e) {
 }
}

Another Class:

public class Crosshair{
 public int x;
 public int y;
 public Crosshair(int x, int y) {
 this.x = x;
 this.y = y;
 }
}
asked Mar 20, 2010 at 3:34
2
  • 1
    Doing this would REALLY annoy me if I were the user. Commented Mar 20, 2010 at 4:01
  • I know what you are saying but it is necessary because I have set up the Hotas Cougar (a joystick) to emulate mouse movements, and I have written my code so the crosshairs are controlled by mouse movements. I plan on putting in some function that allows the window to "release" the mouse in an user friendly manner. Commented Mar 20, 2010 at 5:06

3 Answers 3

5

I guess you could use a Global Event Listener to listen for mouse entered events for the frame. Then on a mouse exited event I suppose you could use the Robot to reset the location of the mouse once it leaves the frame.

answered Mar 20, 2010 at 3:42
Sign up to request clarification or add additional context in comments.

6 Comments

Ok camick, I read through your blog (very nice) and I set up a robot inside a mouseExited event. The robot moves the mouse back to a set of absolute coordinates, but I would like the robot to move the mouse back to the EXACT place where it exited. I used getX() and getY() but the robot will not move the mouse back there (I think the response time is too slow). I have also used the coordinates of my crosshair (crosshair.x and crosshair.y) but even then the mouse can escape if the user clicks at the right time. Any thoughts?
You should be using event.getX() and event.getY(). Also these points are relative to the frame, whereas the Robot needs uses the location relative to the screen. You can use methods found in the SwingUtilities class to do the conversion. And you probably need to adjust these points since the mouseExited event will not necessarily generate points at the bounds of the frame, Depending on how fast the mouse is dragged.
Regarding the mouse escaping and clicking on another component I'm not sure how to solve the problem (although I couldn't duplicate it, I guess I'm too slow). Anyway when I run you demo, my CPU jumps up to 50% because of you infinite while loop. This is obviously not the way to structure a game. Maybe if the CPU wan't being hogged the Robot would respond faster. Game looping can generally be controlled by a Swing Timer so you don't hog the CPU. I'm also not sure where you got your code from. Swing is double buffered so there should be no need to use a BufferStrategy.
I modified the code from this tutorial: gamedeveloperskills.com/?p=52 When I run my code on my computer my CPU usage only jumps to 13% (but I have a pretty powerful machine). I had already tried e.getX and e.getY but with less than satisfactor results (because if I move quick I can have the mouse into one of my other monitors before the event registers, so the coordinates of the event are actually in the other monitor I am referring to). Using the actual crosshair coordinates (crosshair.x and crosshair.y) seems to give me the best results, but mouse can still escape.
It looks like controlling the crosshair with the mouse events driven by my joystick isn't going to work the way I want. I guess I will have to go back and try to use some sort of joystick input instead, unless you have any better ideas. Just a reminder, my joystick is the Hotas Cougar (I can generate almost any mouse or keyboard input from the joystick, right now I just have it acting like a mouse).
|
3

Well, I don't pretend to be a game developer but the code presented in the link is terrible (from what I can tell) for a simple "pong game". I copied the code and got it running so that the ball bounces around.

On my computer the CPU is 50% because of the infinite while loop that wastes CPU time to provide the animation for ball movement. The simple change to use a Thread that sleeps for 5ms rather than hog the CPU for 5ms, causes the CPU usage to drop down to about 18%.

I then changed the code to do the ball animation on a Swing panel. The code is easier to write and the CPU usage dropped to 1% and the ball animation was much faster.

Here is my final version. You should be able to swap back to using the buffered strategy by not adding the panel to the frame and then by invoking the drawFrame() method again.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import javax.swing.*;
public class Game3 extends JFrame {
 /**
 * @author Georgi Khomeriki
 */
 private Ball ball;
 // added this
 private JPanel gamePanel;
 private int windowWidth = 800;
 private int windowHeight = 600;
 public static void main(String[] args) {
 new Game3();
 }
 public Game3() {
 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 this.setSize(windowWidth, windowHeight);
 this.setResizable(false);
 this.setLocation(100, 100);
 this.setVisible(true);
 this.createBufferStrategy(2);
 initGame3();
/*
 while(true) {
 long start = System.currentTimeMillis();
 gameLoop();
 while(System.currentTimeMillis()-start < 5) {
 //do nothing
 }
 }
*/
 Thread thread = new Thread(new Runnable()
 {
 public void run()
 {
 while(true)
 {
 gameLoop();
 try { Thread.sleep(5); }
 catch(Exception e) {}
 }
 }
 });
 thread.start();
 }
 private void initGame3() {
 // all you're game variables should be initialized here
 ball = new Ball(windowWidth/2, windowHeight/2, 5, 5);
 // added these lines
 gamePanel = new GamePanel();
 gamePanel.setBackground(Color.BLACK);
 add(gamePanel);
 }
 private void gameLoop() {
 // your game logic goes here
// move the ball
 ball.x = ball.x + ball.dx;
 ball.y = ball.y + ball.dy;
 // change the direction of the ball if it hits a wall
 if(ball.x <= 0 || ball.x >= windowWidth-40)
 ball.dx = -ball.dx;
 if(ball.y <= 0 || ball.y >= windowHeight-40)
 ball.dy = -ball.dy;
 // changed to following to use Swing instead of buffer strategy
// drawFrame();
 gamePanel.repaint();
 }
 private void drawFrame() {
 // code for the drawing goes here
 BufferStrategy bf = this.getBufferStrategy();
 Graphics g = null;
 try {
 g = bf.getDrawGraphics();
 // clear the back buffer (just draw a big black rectangle over it)
 g.setColor(Color.BLACK);
 g.fillRect(0, 0, windowWidth, windowHeight);
drawBall(g);
 } finally {
 // It is best to dispose() a Graphics object when done with it.
 g.dispose();
 }
 // Shows the contents of the backbuffer on the screen.
 bf.show();
 //Tell the System to do the Drawing now, otherwise it can take a few extra ms until
 //Drawing is done which looks very jerky
 Toolkit.getDefaultToolkit().sync();
 }
 private void drawBall(Graphics g) {
 g.setColor(Color.GREEN);
 g.fillOval(ball.x, ball.y, 40, 40);
 }
 // added this
 class GamePanel extends JPanel
 {
 protected void paintComponent(Graphics g)
 {
 super.paintComponent(g);
 drawBall(g);
 }
 }
 class Ball {
 public int x;
 public int y;
 public int dx;
 public int dy;
 public Ball(int x, int y, int dx, int dy) {
 this.x = x;
 this.y = y;
 this.dx = dx;
 this.dy = dy;
 }
 }
}
answered Mar 21, 2010 at 2:40

1 Comment

I have some other stuff to catch up on, but I will definitley look at this later this week. Thanks!
1

As camickr mentions, you can do this using the Robot. It's generally something that people would recommend against, but here's a pretty good Robot primer to get you started:

http://www.developer.com/java/other/article.php/2212401/Introduction-to-the-Java-Robot-Class-in-Java.htm

answered Mar 20, 2010 at 3:43

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.