"高帧率" = 60 fps或更高(最好> 120)
"高分辨率" = 子像素值
问题是,鼠标事件以太低的帧率到来,并且值被捕捉为整数(整个像素)。这会导致“不连续”的查看体验。以下是随时间变化的鼠标增量值的可视化:
mouse delta X
^ xx
2 | x x x x xx
| x x x x xx x x x
0 |x-x-x--xx-x-x-xx--x-x----x-xx-x-----> frame
|
-2 |
v
这是用户将鼠标向右移动一点所创建的典型(缩短后的)曲线。每个x代表每帧的deltaX值,由于deltaX值被舍入为整数,因此此图实际上相当准确。我们可以看到,deltaX值在一帧中将为0.000,然后在下一帧中为1.000,但接着又会变成0.000,然后是2.000,再次变成0.000,然后是3.000,0.000等等。
这意味着在鼠标以更或少恒定的速度拖动时,视图将在一帧中旋转2.000单位,然后在下一帧中旋转0.000单位,然后旋转3.000单位。不用说,这看起来很糟糕。
那么,我该如何 1) 增加鼠标事件的帧率?和 2) 获取亚像素值?
到目前为止,我尝试了以下内容:
- (void)mouseMoved:(NSEvent *)theEvent {
CGFloat dx, dy;
dx = [theEvent deltaX];
dy = [theEvent deltaY];
// ...
actOnMouse(dx,dy);
}
很显然,这个问题很容易解决。这里的dx
是一个浮点数,但其值总是四舍五入的(0.000、1.000等)。这就产生了上面的图形。
所以下一步我想尝试在鼠标事件进入WindowServer之前截取它们。于是我创建了一个CGEventTrap:
eventMask = (1 << kCGEventMouseMoved);
eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
0, eventMask, myCGEventCallback, NULL);
//...
myCGEventCallback(...){
double dx = CGEventGetDoubleValueField(event, kCGMouseEventDeltaX);
double dy = CGEventGetDoubleValueField(event, kCGMouseEventDeltaY);
}
数值仍然是n.000
,尽管我认为事件触发率略微更高。但它仍未达到60 fps。我仍然得到上面的图表。
我也尝试将鼠标灵敏度设置得非常高,然后在我的端口上缩小值。但似乎OSX添加了某种加速或其他东西,数值变得非常“不稳定”,因此无法使用,并且开火速度仍然太低。
运气不好的是,我一直在跟随兔子洞中的鼠标事件,并抵达了IOKit。这对我来说很可怕。这是疯帽子。苹果文档变得奇怪,并似乎在说“如果你深入到这个级别,你只需要头文件”。
所以我一直在读取头文件。我发现了一些有趣的细节。
在<IOKit/hidsystem/IOLLEvent.h>
的第377行中有这个结构:
struct { /* For mouse-down and mouse-up events */
UInt8 subx; /* sub-pixel position for x */
UInt8 suby; /* sub-pixel position for y */
// ...
} mouse;
看,它说子像素位置!好的。然后在<IOKit/hidsystem/IOLLParameter.h>
的第73行。
#define kIOHIDPointerResolutionKey "HIDPointerResolution"
嗯。
总的来说,我感觉OSX在深层次上了解子像素鼠标坐标,只是必须有一种方法可以每帧读取原始鼠标移动值,但我不知道如何获取这些值。
问题
那么,我在问什么?
- 是否有一种在OSX中获取高帧率鼠标事件的方法?(示例代码?)
- 是否有一种在OSX中获取子像素鼠标坐标的方法?(示例代码?)
- 是否有一种每帧读取“原始”鼠标增量的方法?(即不依赖于事件。)
- 或者,如何获取NXEvents或设置HIDParameters?示例代码?(这样我就可以自己深入研究了...)
(很抱歉发了这么长的帖子)