Я бы хотел получить движения мыши в высоком разрешении и с высокой частотой кадров в OSX.
«Высокая частота кадров» = 60 кадров в секунду или выше (предпочтительно> 120)
«Высокое разрешение» = значения субпикселя
Задача
У меня есть представление opengl, работающее с частотой обновления монитора, поэтому оно составляет ~ 60 кадров в секунду. Я использую мышь, чтобы осмотреться, поэтому спрятал курсор мыши и полагаюсь на дельта-значения мыши.
Проблема в том, что события мыши поступают при слишком низкой частоте кадров, а значения привязываются к целому числу (целые пиксели). Это вызывает «прерывистый» просмотр. Вот визуализация дельта-значений мыши во времени:
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 на один кадр, а затем 1000 на следующий, но затем снова будет 0,000, затем на 2000, а затем снова на 0,000, затем на 3000, 0,000 и т. Д.
Это означает, что представление будет вращать 2000 единиц на один кадр, затем поворачивать на 0,000 единиц на следующий, а затем вращать на 3000 единиц. Это происходит, когда мышь перетаскивают с более или менее постоянной скоростью. Излишне говорить, что это похоже на дерьмо.
Итак, как я могу 1) увеличить частоту кадров мыши? и 2) получить субпиксельные значения?
Пока
Я пробовал следующее:
- (void)mouseMoved:(NSEvent *)theEvent {
CGFloat dx, dy;
dx = [theEvent deltaX];
dy = [theEvent deltaY];
// ...
actOnMouse(dx,dy);
}
Ну, этот был очевиден. dx
здесь число с плавающей запятой, но значения всегда округляются (0,000, 1000 и т. Д.). Это создает график выше.
Поэтому следующим шагом было попытаться нажать на события мыши, прежде чем они войдут в 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 кадрах в секунду. Я все еще получаю диаграмму выше.
Я также попытался установить очень высокую чувствительность мыши, а затем уменьшил значения на моей стороне. Но кажется, что OSX добавляет какое-то ускорение или что-то еще - значения становятся действительно «нестабильными» и, следовательно, непригодными для использования, а скорострельность все еще слишком низкая.
Не повезло, я начал следить за событиями мыши в кроличьей норе, и я прибыл в IOKit. Это страшно для меня. Это безумный шляпник. Документация Apple становится странной и, кажется, говорит: «Если вы так глубоко, все, что вам действительно нужно, это файлы заголовков».
Итак, я читал заголовочные файлы. И я нашел несколько интересных моментов.
В <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;
Видите, там написано положение субпикселя! Хорошо. Затем в строке 73 в <IOKit/hidsystem/IOLLParameter.h>
#define kIOHIDPointerResolutionKey "HIDPointerResolution"
Хм.
В общем, я чувствую, что OSX знает о субпиксельных координатах мыши в глубине, и просто должен быть способ читать необработанные движения мыши в каждом кадре, но я просто не знаю, как получить эти значения .
Вопросы
Эх, так, что я прошу?
- Есть ли способ получить события мыши с высокой частотой кадров в OSX? (Пример кода?)
- Есть ли способ получить субпиксельные координаты мыши в OSX? (Пример кода?)
- Есть ли способ чтения "сырых" дельт мыши в каждом кадре? (Т.е. не полагаться на событие.)
- Или, как мне получить NXEvents или установить HIDParameters? Пример кода? (Так что я могу копать глубже в этом самостоятельно ...)
(простите за длинный пост)