Robot.mouseMove(1000,1000) 将鼠标移动到屏幕上的随机位置...为什么?

Robot.mouseMove(1000,1000) moves mouse to random locations on screen... why?

public class MoveCursor extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws Exception {
        Button b = new Button("X");
        b.setOnAction((event) -> {
            try {
                new Robot().mouseMove(1000, 1000);
            } catch (AWTException ex) {
                Logger.getLogger(MoveCursor.class.getName()).log(Level.SEVERE, null, ex);
            }
        });
        stage.setScene(new Scene(b));
        stage.show();
    }
}
  1. 这应该将鼠标光标移动到 my/your 屏幕上的 1000x1000 位置(即距离我屏幕的绝对 0x0 坐标 1000 像素...并且应该结束 总是在同一个位置)。 ... 它不会... 这取决于按钮的位置。为什么?

  2. 这是什么原因造成的?

...这曾经在旧笔记本电脑上工作。即 windows 10、1 个 intel 显卡、1 个 nvidia 显卡、1920x1080 显示器。

我目前正在使用 Windows 10、2 个 SLI 显卡,分辨率为 3840x2160,缩放比例为 175%。

调整比例因子似乎没有任何作用。

...我也在用jdk8

使用 -Dsun.java2d.dpiaware=true 或 -Dsun.java2d.dpiaware=false 虚拟机选项似乎没有任何作用。

[edit] ...对于问题重复问题...它不是那个的重复。该问题的修复是无用的。

这行不通!

 for(int count = 0;(MouseInfo.getPointerInfo().getLocation().getX() != x || 
        MouseInfo.getPointerInfo().getLocation().getY() != y) &&
        count < 100; count++) {
    new Robot().mouseMove(x, y);
  }

是的,这是高分辨率屏幕的一个已知错误。有关详细信息,请参阅 https://bugs.openjdk.java.net/browse/JDK-8186063

最好的解决方案是 运行 循环执行命令,直到到达正确的坐标。

这种事情的代码如下:

 for(int count = 0;(MouseInfo.getPointerInfo().getLocation().getX() != x || 
        MouseInfo.getPointerInfo().getLocation().getY() != y) &&
        count < 100; count++) {
    new Robot().mouseMove(x, y);
  }

请注意,此代码将最大迭代次数设置为 100 以防止无限循环,因此值得在循环后立即执行另一次检查以确定它是否在正确的位置。

Sub-optimal,糟糕的解决方案...但它 WORKS...而且我在这个问题上浪费的时间比我应该浪费的时间太多了。

import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.LONG;
import com.sun.jna.platform.win32.WinUser.INPUT;
import java.awt.MouseInfo;
import java.awt.Point;

public class Mouse {

    public static final int MOUSEEVENTF_MOVE = 1;

    public static void _winEvent_mi_move(int x, int y) {
        mouseAction(x, y, MOUSEEVENTF_MOVE);
    }

    public static void mouseAction(int x, int y, int flags) {
        INPUT input = new INPUT();
        input.type = new DWORD(INPUT.INPUT_MOUSE);
        input.input.setType("mi");
        if (x != -1) {
            input.input.mi.dx = new LONG(x);
        }
        if (y != -1) {
            input.input.mi.dy = new LONG(y);
        }
        input.input.mi.time = new DWORD(0);
        input.input.mi.dwExtraInfo = new ULONG_PTR(0);
        input.input.mi.dwFlags = new DWORD(flags);
        User32.INSTANCE.SendInput(new DWORD(1), new INPUT[]{input}, input.size());
    }

    public static void forceMove(int x, int y) {
        init_abs_move_0_0:
        {
            Point ip = MouseInfo.getPointerInfo().getLocation();
            _winEvent_mi_move(-ip.x, -ip.y);
        }
        moveX:
        {
            while (MouseInfo.getPointerInfo().getLocation().x < x - 1) {
                _winEvent_mi_move(1, 0);
            }
        }
        moveY:
        {
            while (MouseInfo.getPointerInfo().getLocation().y < y - 1) {
                _winEvent_mi_move(0, 1);
            }
        }
        System.out.println(MouseInfo.getPointerInfo().getLocation().toString());
    }

    public static void main(String[] args) {
        forceMove(1000, 1000);
        forceMove(2000, 1500);
    }

}

理论上,可以通过使用 awt.Robot 实例逐像素移动某物来实现相同的结果...这似乎效果更好。

是的...我确实尝试根据鼠标光标的当前位置和所有内容来计算指针目的地的相对目标位置...但它仍然基于难以获取有关是否或不是 DPI 缩放和 MS 决定在 WPI 和 GDI 输入事件处理例程中实施的任何废话。

...将它直接移动到 0x0 然后 1000x1000 会导致光标位置由 (2000x1400) 定义...没有时间也没有耐心弄明白。 ...解决方案有效。故事结束。

我写了一个 "adjust" 函数,它完成了肮脏的工作。 注意:如果它必须调整超过 30 像素,那么它就会终止。 您可以拨打电话:

如果你有和我一样的错误,代码修复如下:

Adjust:638:598>507:537
Adjust:638:598>670:613
Adjust:638:598>630:595
Adjust:638:598>640:598
Move: 638 598 Click:true

moveAndClick(1000,1000,true);

private void moveAndClick(int x, int y, boolean click) throws AWTException {

    robot.mouseMove(x, y);

    adjustMouse(x, y);

    if (click) {
        robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
        robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
    }
    System.out.println("Move: " + x + " " + y + " Click:" + click);

}

private void adjustMouse(int x, int y) {

    int realX = MouseInfo.getPointerInfo().getLocation().x;
    int realY = MouseInfo.getPointerInfo().getLocation().y;

    int aX = x;
    int aY = y;

    int count = 0;

    while (realX != x || realY != y) {
        System.out.println("Adjust:" + x + ":" + y + ">" + realX + ":" + realY + "");

        aX = realX > x ? aX-- : aX++;
        aY = realY > y ? aY-- : aY++;

        robot.mouseMove(x, y);
        try {  // you can remove this part 
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;

        realX = MouseInfo.getPointerInfo().getLocation().x;
        realY = MouseInfo.getPointerInfo().getLocation().y;
        if (count > 30) {  // you can remove or increase this part
            System.exit(0);
        }
    }


}

产量:

@ system.start - mouse position : java.awt.Point[x=1540,y=1462]
Adjust:1000:1000>191:307
Adjust:1000:1000>2212:2039
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
Adjust:1000:1000>2500:2159
Adjust:1000:1000>0:0
@ system.exit - mouse position : java.awt.Point[x=2500,y=2159]

底部是一个完整但略带沼泽感的虚拟鼠标三角洲解决方案。不需要库,在 Java 8/9/11 Windows 10 上进行了测试,桌面缩放比例为 200%。


This Tejas Shah 的解决方案对我有效 99%。 (我还在找我的下巴,它一定在某处。)

这是我评论的 Java 改编自我怀疑是 Kotlin 的东西。它在我的 Java 8 和 9 的 4K 200% 桌面上工作。我的对角线鼠标移动循环看起来仍然 wee 有点奇怪,但是鼠标移动到你想要的 99%而不是跳来跳去。

不是100%!关于Java8,当我在Swing线程上每隔20ms一直把鼠标放在一个固定的位置时,鼠标有时会如果我以某种方式移动它,则关闭 1 个像素。这可能是一个四舍五入的问题。 (使用下面的 Math.round 并不能解决它。)同样,在 Java 9 上,鼠标要么在那个地方,要么一直在那个地方和一个像素之间跳到 left/top那个地方。

要获得正确的比例因子(例如 2 在 Windows 系统上以 200% 比例缩放),您可以使用我的蹩脚但非常精细的 ,它也支持自定义图形缩放、鼠标缩放等,并在 Java 8&9(或其他)100% 和 200%(或其他)缩放之间保持一致。

final private Robot robot;


private void mouseMoveFixed(final int x,
                            final int y) {

    final double scaleFactor = GUIScaling.GUISCALINGFACTOR_SYSTEM; // If your Windows system is at 200% scaling, put a 2 here.

    final double modFactor = 1 - (1 / scaleFactor);

    final Point origin = MouseInfo.getPointerInfo().getLocation();

    final int deltaX = x - origin.x;
    final int deltaY = y - origin.y;

    final int finalX = (int) (x - deltaX * modFactor); // I don't know if this needs to be rounded.
    final int finalY = (int) (y - deltaY * modFactor); // I couldn't spot a difference if Math.round() was used.

    robot.mouseMove(finalX, finalY);
}

以后,如果你需要一个“虚拟鼠标”增量,只需调用这个静态方法:

JavaRobotMouseDelta.calcVirtualMouseDelta()

它将 return 屏幕中心的增量并将鼠标设置到该位置。如果没有要 returned 的增量,方法 returns null.

如果桌面缩放不是 100%,该方法将根据 Java 8、9 和 11+ 使用不同的方法。所有这些都不精确,因此会给鼠标带来轻微的沼泽感。不过聊胜于无,都封装好了。 尽情享受吧!

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;




/**
 * [v1, 2022-01-15 12!00 UTC] by Dreamspace President
 * <p>
 * Uses 2 values from my GUIScaling class, see 
 */
final public class JavaRobotMouseDelta {


    /**
     * @return NULL if there is no delta and thus no action should be taken.
     */
    public static Dimension calcVirtualMouseDelta() {

        final int centerX;
        final int centerY;

        int deltaX;
        int deltaY;

        {
            final PointerInfo pointerInfo = MouseInfo.getPointerInfo();

            final float halfScaled = (float) (2 * GUIScaling.GUISCALINGFACTOR_FONTINCUSTOMGRAPHICSCONTEXT);

            final DisplayMode displayMode = pointerInfo.getDevice().getDisplayMode();
            centerX = Math.round(displayMode.getWidth() / halfScaled);
            centerY = Math.round(displayMode.getHeight() / halfScaled);

            final Point mouseLocation = pointerInfo.getLocation();
            deltaX = mouseLocation.x - centerX;
            deltaY = mouseLocation.y - centerY;
        }


        if (GUIScaling.GUISCALINGFACTOR_SYSTEM == 1) {

            if (deltaX == 0 && deltaY == 0) {
                // Ignoring mouse move event caused by Robot centering call.
                return null;
            }

            ROBOT.mouseMove(centerX, centerY);

            return new Dimension(deltaX, deltaY);

        } else {


            // The following comments and logic are for desktop scaling 200%.
            // I did not yet bother trying to figure this out for other values.


            // Java 11 etc. finally have a FIXED Robot.mouseMove()! HOWEVER: You will be blindfolded,
            // so you get a pixel offset of 1 every once in a while because moving the mouse from center to the LEFT OR TOP
            // will INSTANTLY cause a mouse event with a new position. Moving it right or bottom will NOT cause an event,
            // because e.g. a 3840x2160 (twice 1920x1080) display will ALL THROUGHOUT inside Java have HALVED coordinates,
            // e.g. you set window width 800, reality will be 1600, but you will have getWidth 800, and the mouse coordinates
            // will also be treated like this - EVEN FOR THE ROBOT!

            // Java 8/9 (and below) have a broken Robot.mouseMove() under Windows!=100%, but thanks to Tejas Shah's solution below,
            // we can work around that. Without JNA or looping tricks.

            // HOWEVER: We need to lose 1 pixel of motion. And we need to thoroughly rape the resulting delta DEPENDING ON JAVA VERSION.

            // Rant: I want an additional mouseMove method that uses TRUE pixels. And I want access to the HARDWARE MOUSE DELTA,
            // scaled by system (acceleration) as well as unscaled. And I want access to scanCode (KeyEvent) without Reflection
            // and on ALL systems, not just Windows! ... Rant over.

            if (deltaX < 0) {
                deltaX += 1;
            } else if (deltaX > 0) {
                deltaX -= 1;
            }
            if (deltaY < 0) {
                deltaY += 1;
            } else if (deltaY > 0) {
                deltaY -= 1;
            }


            if (deltaX == 0 && deltaY == 0) {
                // Ignoring mouse move event caused by Robot centering call.
                return null;
            }

            final int deltaXRaped;
            final int deltaYRaped;

            if (JAVAVERSIONMAJOR >= 11) {

                deltaXRaped = deltaX < 0 ? deltaX : Math.round(deltaX * (1 + RAND.nextFloat() * 0.75f));
                deltaYRaped = deltaY < 0 ? deltaY : Math.round(deltaY * (1 + RAND.nextFloat() * 0.75f));

                ROBOT.mouseMove(centerX, centerY);

            } else if (JAVAVERSIONMAJOR >= 9) {

                deltaXRaped = deltaX > 0 ? deltaX : Math.round(deltaX * (1 + RAND.nextFloat() * 0.75f));
                deltaYRaped = deltaY > 0 ? deltaY : Math.round(deltaY * (1 + RAND.nextFloat() * 0.75f));

                mouseMoveFixed(centerX, centerY);

            } else {

                deltaXRaped = deltaX < 0 ? deltaX : Math.round(deltaX * (1 + RAND.nextFloat() * 0.75f));
                deltaYRaped = deltaY < 0 ? deltaY : Math.round(deltaY * (1 + RAND.nextFloat() * 0.75f));

                mouseMoveFixed(centerX, centerY);

            }

            return new Dimension(deltaXRaped, deltaYRaped);

        }

    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // UTILITIES NEEDED ABOVE.
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    final public static int JAVAVERSIONMAJOR = getJavaVersionMajor();

    final public static Robot ROBOT = getRobot();

    final public static Random RAND = new Random();


    private static Robot getRobot() {

        try {
            return new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
            System.exit(-1);
            throw new Error();
        }
    }


    // From  (Aaron Digulla)
    private static int getJavaVersionMajor() {

        String version = System.getProperty("java.version");
        if (version.startsWith("1.")) {
            version = version.substring(2, 3);
        } else {
            int dot = version.indexOf(".");
            if (dot != -1) {
                version = version.substring(0, dot);
            }
        }
        return Integer.parseInt(version);
    }


    // Converted from the ?Kotlin? original at https://gist.github.com/tejashah88/201daacada3a785f86b8cf069ead63f5 (Tejas Shah)
    public static void mouseMoveFixed(final int x,
                                      final int y) {

        final double scaleFactor = GUIScaling.GUISCALINGFACTOR_SYSTEM; // If your Windows system is at 200% scaling, put a 2 here.

        final double modFactor = 1 - (1 / scaleFactor);

        final Point origin = MouseInfo.getPointerInfo().getLocation();

        final int deltaX = x - origin.x;
        final int deltaY = y - origin.y;

        int finalX = (int) (x - deltaX * modFactor); // I don't know if this needs to be rounded.
        int finalY = (int) (y - deltaY * modFactor); // I couldn't spot a difference if Math.round() was used.

        ROBOT.mouseMove(finalX, finalY);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // BONUS, YOU CAN DELETE THIS.
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    final private static Cursor INVISIBLE_CURSOR;

    static {
        final BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
        INVISIBLE_CURSOR = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg,
                                                                          new Point(0, 0),
                                                                          "invisibleCursor");
    }


    public static void setMouseBusy(final Component windowOrComponent) {

        if (windowOrComponent != null) {
            windowOrComponent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        }
    }


    public static void setMouseIdle(final Component windowOrComponent) {

        if (windowOrComponent != null) {
            windowOrComponent.setCursor(Cursor.getDefaultCursor());
        }
    }


    public static void setMouseInvisible(final Component windowOrComponent) {

        if (windowOrComponent != null) {
            windowOrComponent.setCursor(INVISIBLE_CURSOR);
        }
    }


    public static void setMouseCrossHair(final Component windowOrComponent) {

        if (windowOrComponent != null) {
            windowOrComponent.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        }
    }

}