我希望在OSX上以高分辨率和高帧率进行鼠标移动。
“高帧率”= 60fps或更高(优选地> 120)
“高分辨率”=子像素值
问题
我有一个关于显示器刷新率的opengl视图,所以它是~60 fps。我用鼠标环顾四周,所以我隐藏了鼠标光标,我依赖鼠标delta值。
问题是鼠标事件的帧速率太低,值被捕捉到整数(整个像素)。这会导致“波涛汹涌”的观看体验。这是随时间变化的鼠标增量值的可视化:
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个单位。当鼠标以或多或少的恒定速度拖动时会发生这种情况。不用说,这看起来像垃圾。
那么,我怎样才能增加鼠标的事件帧率? 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。这对我来说很可怕。这是疯狂的帽子。 Apple文档很奇怪,似乎在说“如果你内心深处,你真正需要的就是头文件”。
所以我一直在阅读头文件。我发现了一些有趣的花絮。
在第377行的<IOKit/hidsystem/IOLLEvent.h>
中有这个结构:
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>
#define kIOHIDPointerResolutionKey "HIDPointerResolution"
嗯。
总而言之,我觉得OSX深入了解亚像素鼠标坐标,并且必须有一种方法来读取每帧的原始鼠标移动,但我只是不知道如何获得这些值
问题
嗯,那么,我要求的是什么?
(很抱歉很长)
答案 0 :(得分:7)
(这是一个非常晚的答案,但我认为对于偶然发现这个问题的其他人来说仍然有用。)
您是否尝试过滤鼠标输入?这可能很棘手,因为过滤往往是滞后和精确度之间的权衡。然而,几年前我写了一篇文章,解释了我如何过滤鼠标移动并为游戏开发网站写了一篇文章。链接为http://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtml。
由于该网站不再处于积极开发状态(可能会消失),因此这里有相关的摘录:
几乎在所有情况下,过滤都意味着平均。但是,如果我们简单地平均鼠标随时间的移动,我们将引入滞后。那么,我们如何在不引入任何副作用的情况下进行过滤?好吧,我们仍然会使用平均值,但我们会用一些智能来做。同时,我们会让用户对过滤进行精细控制,以便他们自行调整。
我们将使用平均鼠标输入的非线性滤波器,其中较旧的值对滤波结果的影响较小。
工作原理
每一帧,无论您是否移动鼠标,我们都会将当前鼠标移动到历史缓冲区中并删除最早的历史值。所以我们的历史总是包含X个样本,其中X是“历史缓冲区大小”,表示随着时间的推移最近采样的鼠标移动。
如果我们使用10的历史缓冲区大小和整个缓冲区的标准平均值,那么过滤器会引入很多延迟。在60FPS机器上,快速鼠标移动将落后1/6秒。在快速动作游戏中,这将非常流畅,但几乎无法使用。在同一场景中,历史缓冲区大小为2会给我们带来很小的延迟,但过滤效果非常差(玩家反应粗糙和生涩。)
非线性过滤器旨在打击这种互斥的场景。这个想法非常简单。我们不是仅仅平均地平均历史缓冲区中的所有值,而是将它们与权重进行平均。我们从1.0开始。因此历史缓冲区中的第一个值(当前帧的鼠标输入)具有完全权重。然后我们将这个权重乘以“权重修正符”(比如说...... 0.2)并继续前进到历史缓冲区中的下一个值。我们进一步回溯(通过我们的历史缓冲区),这些值对最终结果的权重(影响力)越来越小。
详细说明,使用0.5的重量调整值,当前帧的样本将具有100%的权重,之前的样本将具有50%的权重,下一个最旧的样本将具有25%的权重,下一个将具有12.5%的权重和等等。如果你绘制图形,它看起来像一条曲线。因此,权重修改器背后的想法是控制曲线在历史中的样本变老时曲线下降的程度。
减少滞后意味着减少重量调整器。将权重修改器减少到0将为用户提供原始的,未经过滤的反馈。将其增加到1.0将导致结果是历史缓冲区中所有值的简单平均值。
我们将为用户提供两个用于精细控制的变量:历史缓冲区大小和权重修饰符。我倾向于使用10的历史缓冲区大小,并且只使用权重修饰符直到我很高兴。
答案 1 :(得分:2)
如果您使用鼠标的IOHIDDevice回调,您可以使用它来获得双倍值:
double doubleValue = IOHIDValueGetScaledValue(inIOHIDValueRef, kIOHIDTransactionDirectionTypeOutput);
答案 2 :(得分:1)
存在子像素坐标的可能性,因为Mac OS X被设计为独立于分辨率。屏幕上的2x2硬件像素的正方形可以代表软件中的单个虚拟像素,允许将光标放在(x + 0.5, y + 0.5)
。
在使用普通1x缩放的任何实际Mac上,您将永远不会看到子像素坐标,因为鼠标光标无法移动到屏幕上的小数位像素位置 - 鼠标移动量恰好为1像素。
答案 3 :(得分:1)
如果您需要在比事件调度系统提供的更低级别访问指针设备增量信息,那么您可能需要使用user-space USB APIs。