将鼠标移动限制在 OS X 范围内的区域而不会出现抖动

Constrain mouse movement to region in OS X without jitter

我想将鼠标移动限制在 OS X 10.11 屏幕的特定矩形区域。我修改了 MouseTools(下方)中的一些代码来执行此操作,但是当您点击屏幕边缘时它会出现抖动。我怎样才能摆脱这种抖动?

// gcc -Wall constrain.cpp -framework ApplicationServices -o constrain

#include <ApplicationServices/ApplicationServices.h>

int main (int argc, const char * argv[]) {
    if(argc != 5) {
        printf("Usage: constrain left top right bottom\n");
        return 0;
    }
    int leftBound = strtol(argv[1], NULL, 10);
    int topBound = strtol(argv[2], NULL, 10);
    int rightBound = strtol(argv[3], NULL, 10);
    int bottomBound = strtol(argv[4], NULL, 10);
    CGEventTapLocation tapLocation = kCGHIDEventTap;
    CGEventSourceRef sourceRef = CGEventSourceCreate(kCGEventSourceStatePrivate);
    while(true) {
        CGEventRef mouseEvent = CGEventCreate(NULL);
        CGPoint mouseLoc = CGEventGetLocation(mouseEvent);
        int x = mouseLoc.x, y = mouseLoc.y;
        if(x < leftBound || x > rightBound || y < topBound || y > bottomBound) {
            if(x < leftBound) x = leftBound;
            if(x > rightBound) x = rightBound;
            if(y < topBound) y = topBound;
            if(y > bottomBound) y = bottomBound;
            CGEventRef moveMouse = CGEventCreateMouseEvent(sourceRef, kCGEventMouseMoved, CGPointMake(x, y), 0);
            CGEventPost(tapLocation, moveMouse);
            CFRelease(moveMouse);
        }
        CFRelease(mouseEvent);
        usleep(8*1000); // 8ms, ~120fps
    }
    CFRelease(sourceRef);
    return 0;
}

以下是我尝试过的一些其他方法:Receiving, Filtering, and Modifying Mouse Events shows how to create a callback for mouse movements using CGEventTapCreate. In the code it says "We can change aspects of the mouse event. For example, we can use CGEventSetLocation(event, newLocation)." Unfortunately this doesn't actually change the mouse location (complete example here). The only ways I've been able to change the mouse location are by doing a CGEventPost or CGWarpMouseCursorPosition. I also tried using kCGHIDEventTap instead of kCGSessionEventTap, and I checked that I had super user permissions and accessibility enabled for the app. It seems like it should work because there is another example of modifying key presses 可以正常工作。我没有使用 CGEventSetLocation,而是尝试使用 CGEventSetIntegerValueField(就像在键盘示例中一样)将 x 和 y 增量设置为 0,但这并没有改变任何东西。

最后,我也尝试在CGEventCallback中使用CGEventPostCGWarpMouseCursorPosition,“隐形边框”仍然比上面的代码更抖动。

我已经在 Mac 驱动程序中为 Wine 实现了这个。有点复杂。

方法是通过调用 CGAssociateMouseAndMouseCursorPosition(false) 基本上禁用鼠标光标的移动。如果约束矩形是光标当前位置的 1x1 矩形,那么这里就可以实现你想要的。

最重要的是,我然后使用事件点击来观察鼠标移动事件。这些不会改变位置,但具有用户尝试执行的操作的增量。然后我使用 CGWarpMouseCursorPosition() 根据这些增量移动光标并在传递它们之前调整事件。

请注意,鼠标事件中的位置信息没有意义。它会陈旧。一些事件将在您上次经线之前排队,因此有一个旧位置。您需要跟踪光标所在的位置。当您将它与鼠标解除关联时,它会从它所在的位置开始,并在您扭曲它时发生变化。您将增量添加到该位置(而不是事件位置),将其剪辑到约束矩形,并将其用作事件和扭曲的新位置。对于下一个事件,您将从这个新位置开始。等等

还有一个不幸的皱纹。 CGWarpMouseCursorPosition() 影响后续鼠标移动事件的增量值。基本上,您使光标变形的距离会添加到在变形后排队的第一个鼠标移动事件中。如果未选中,这将有效地使用户的鼠标移动与每个事件一起增加一倍,从而导致失控的反馈循环,并且光标将跳到一个极端或另一个极端。

因此,您必须记录您执行的扭曲、执行它们的时间以及它们改变位置的程度。然后,当事件通过水龙头时,您将它们的时间与扭曲时间进行比较。对于早于当前事件的任何扭曲,您从事件的增量中减去它们的位置变化并将它们从列表中删除。