javaswingmousemotionlistener

Temporarily suspend mouse motion listener


I was attempting to create a GLFW infinite mouse mode for Swing and here's my progress so far

package javax.swing.extras;

// AWT Imports
import java.awt.AWTException;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

// Swing Imports
import javax.swing.JFrame;

/**
 * An extension class that allows JFrames to have infinite input as seen in most
 * 3D games.
 */
public class InfiniteMouse {

    /** Image for the blank cursor */
    private static final BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
    /** Blank cursor */
    private static final Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0),
            "blank cursor");

    /** Mouse X */
    private int x;
    /** Mouse Y */
    private int y;

    /** Previous Mouse X */
    private int previousX;
    /** Previous Mouse Y */
    private int previousY;

    /** Center X */
    private int centerX;
    /** Center Y */
    private int centerY;

    /**
     * Create a InfiniteMouse extension for a JFrame
     * 
     * @param f - The JFrame to add the extension
     * @param c - The Container to detect mouse motion
     * @return an instance of InfiniteMouse containing the x and y
     * @throws AWTException if the platform configuration does not allowlow-level
     *                      input control. This exception is always thrown
     *                      whenGraphicsEnvironment.isHeadless() returns true
     */
    public static InfiniteMouse addExtension(JFrame f, Container c) throws AWTException {
        // Create a new instance
        InfiniteMouse infiniteMouse = new InfiniteMouse();

        // Create a final Robot class to later move the mouse to the center
        final Robot robot = new Robot();

        // Store the on-screen center x of the window
        infiniteMouse.centerX = f.getX() + f.getWidth() / 2;
        // Store the on-screen center y of the window
        infiniteMouse.centerY = f.getY() + f.getHeight() / 2;

        // Add a resize listener
        f.addComponentListener(new ComponentAdapter() {
            // If the frame is resized
            public void componentResized(ComponentEvent e) {
                // Update the X position
                infiniteMouse.centerX = f.getX() + f.getWidth() / 2;
                // Update the Y position
                infiniteMouse.centerY = f.getY() + f.getHeight() / 2;
            }
        });
        // Make the cursor invisible
        f.setCursor(blankCursor);

        // Add a mouse listener
        c.addMouseMotionListener(new MouseMotionListener() {

            // If the mouse is moved
            public void mouseMoved(MouseEvent e) {
                // Get the current X
                int currentX = e.getX();
                // Get the current Y
                int currentY = e.getY();

                System.out.println(infiniteMouse.x);

                // Get the mouse X position delta
                infiniteMouse.x += currentX - infiniteMouse.previousX;
                // Get the mouse Y position delta
                infiniteMouse.y += currentY - infiniteMouse.previousY;

                // Remove the listener when moving to the center
                c.removeMouseMotionListener(this);
                // Move the mouse to the center
                robot.mouseMove(infiniteMouse.centerX, infiniteMouse.centerY);
                // Add the listener back
                c.addMouseMotionListener(this);
                
                // Update the previous X
                infiniteMouse.previousX = currentX;
                // Update the previous Y
                infiniteMouse.previousY = currentY;
            }

            @Override
            public void mouseDragged(MouseEvent e) {
            }
        });

        // Return the newly created instance
        return infiniteMouse;
    }

    /**
     * Get the X position
     * 
     * @return the X position
     */
    public int getX() {
        return x;
    }

    /**
     * Get the Y position
     * 
     * @return the Y position
     */
    public int getY() {
        return y;
    }
}

Used in this code

package javax.swing.extras;

import java.awt.AWTException;
import java.awt.Color;
import java.awt.GridBagLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

    public static void main(String[] args) throws AWTException {
        JFrame jf = new JFrame();
        JPanel panel1 = new JPanel();
        
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        jf.setTitle("Window");
        jf.setSize(800,600);
        jf.setLocationRelativeTo(null);

        jf.add(panel1);
        panel1.grabFocus();
        
        InfiniteMouse.addExtension(jf, panel1);
        
        jf.setVisible(true);
        
    }

}

However the issue I face with this code is that the mouse X being debugged in the console is really not increasing, and I suspect it to be the issue with these lines

c.removeMouseMotionListener(this);
robot.mouseMove(infiniteMouse.centerX, infiniteMouse.centerY);
c.addMouseMotionListener(this);

As it stays around the same value unless I tab out and move the JFrame around and move it back into the window. Well, the purpose of this line is to suspend the mouse motion listener so that I can move the mouse back and then resume the listener. Is there a working alternative to this method that I can use? or is it some other issue?


Solution

  • Thanks to gthanop's answer I was able to figure out a solution to the issue. Here is the updated code

    // Add a mouse listener
    c.addMouseMotionListener(new MouseAdapter() {
        // If the mouse is moved
        public void mouseMoved(MouseEvent e) {
            // Get the current X
            int currentX = e.getX();
            // Get the current Y
            int currentY = e.getY();
                    
            // Get the current X on screen
            int currentXOnScreen = f.getX() + currentX;
            // Get the current Y on screen
            int currentYOnScreen = f.getY() + currentY;
                    
            // Get the mouse X position delta
            infiniteMouse.x += (currentXOnScreen - infiniteMouse.centerX) + 8;
            // Get the mouse Y position delta
            infiniteMouse.y += (currentYOnScreen - infiniteMouse.centerY) + 32;
                    
            // Move the mouse to the center
            robot.mouseMove(infiniteMouse.centerX, infiniteMouse.centerY);
        }
    });
    

    I believe that Robot.moveMouse infact does not move the pointer to the specified location but sets the center of the pointer to the specified location maybe something like that. That is why I add 8 to the difference on the X and 32 on the Y. However I am not sure as to what the reason could be but since it works I will just leave it like that.

    This answer provides a solution to the problem in the question. However gthanop's answer provides a lot more detail I highly recommend reading that