Координаты мыши с высоким разрешением и высокой частотой кадров на OSX?(Или другое решение?) - PullRequest
7 голосов
/ 22 июля 2011

Я бы хотел получить движения мыши в высоком разрешении и с высокой частотой кадров в 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? Пример кода? (Так что я могу копать глубже в этом самостоятельно ...)

(простите за длинный пост)

Ответы [ 4 ]

7 голосов
/ 26 февраля 2014

(Это очень поздний ответ, но я думаю, что он все еще полезен для других, кто сталкивается с этим.)

Вы пробовали фильтровать ввод с помощью мыши?Это может быть сложно, потому что фильтрация - это компромисс между задержкой и точностью.Однако несколько лет назад я написал статью, в которой объяснял, как я фильтровал движения мыши, и написал статью для сайта разработки игр.Ссылка: http://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtml.

Поскольку этот сайт больше не находится в активной разработке (и может исчезнуть), вот соответствующая выдержка:


Почти во всех случаях фильтрация означает усреднение,Однако, если мы просто усредним движение мыши по времени, мы введем лаг.Как же тогда мы фильтруем без каких-либо побочных эффектов?Ну, мы все еще будем использовать усреднение, но мы сделаем это с некоторым интеллектом.И в то же время мы дадим пользователю точный контроль над фильтрацией, чтобы они могли сами его настроить.

Мы будем использовать нелинейный фильтр усредненного ввода мыши по времени, где более старыйзначения имеют меньшее влияние на отфильтрованный результат.

Как это работает

В каждом кадре, независимо от того, перемещаете ли вы мышь или нет, мы помещаем текущее движение мыши в буфер истории и удаляем самую старую историюзначение.Таким образом, наша история всегда содержит X выборок, где X - это «размер буфера истории», представляющий самые последние сэмплированные движения мыши с течением времени.

Если мы использовали размер буфера истории 10 и стандартное среднее значениевесь буфер, фильтр будет вносить много лагов.Быстрые движения мыши будут отставать на 1/6 секунды на машине с 60FPS.В быстрой игре действия это было бы очень гладко, но фактически непригодно для использования.В этом же сценарии размер буфера истории 2 даст нам очень небольшое отставание, но очень плохую фильтрацию (грубая и резкая реакция игрока.)

Нелинейный фильтр предназначен для борьбы с этим взаимоисключающим сценарием,Идея очень проста.Вместо того, чтобы просто слепо усреднять все значения в буфере истории одинаково, мы усредняем их с весом.Начнем с веса 1,0.Таким образом, первое значение в буфере истории (ввод мыши в текущем кадре) имеет полный вес.Затем мы умножаем этот вес на «модификатор веса» (скажем ... 0,2) и переходим к следующему значению в буфере истории.Чем дальше в прошлое (через наш буфер истории) мы идем, тем значения все меньше влияют на конечный результат.

Для уточнения с модификатором веса 0,5 выборка текущего кадра будетимеют 100% веса, предыдущий образец будет иметь 50% веса, следующий самый старый образец будет иметь 25% веса, следующий будет иметь 12,5% веса и так далее.Если вы построите график, он будет выглядеть как кривая.Поэтому идея модификатора веса состоит в том, чтобы контролировать, насколько резко падает кривая по мере старения образцов в истории.

Уменьшение отставания означает уменьшение модификатора веса.Уменьшение модификатора веса до 0 предоставит пользователю необработанную обратную связь без фильтрации.Увеличение его до 1,0 приведет к тому, что результат будет простым средним значением всех значений в буфере истории.

Мы предложим пользователю две переменные для точного управления: размер буфера истории и модификатор веса.Я склонен использовать буфер истории размером 10 и просто играть с модификатором веса, пока я не буду счастлив.

2 голосов
/ 25 октября 2013

Если вы используете обратные вызовы IOHIDDevice для мыши, вы можете использовать это, чтобы получить двойное значение:

double doubleValue = IOHIDValueGetScaledValue(inIOHIDValueRef, kIOHIDTransactionDirectionTypeOutput);
1 голос
/ 22 июля 2011

Если вам нужно получить доступ к дельта-информации указательного устройства на более низком уровне, чем обеспечивает система диспетчеризации событий, вам, вероятно, придется использовать пользовательские интерфейсы USB .

1 голос
/ 22 июля 2011

Возможность субпиксельных координат существует, потому что Mac OS X разработана, чтобы быть независимой от разрешения.Квадрат аппаратных пикселей 2x2 на экране может представлять один виртуальный пиксель в программном обеспечении, позволяя поместить курсор на (x + 0.5, y + 0.5).

На любом реальном Mac, использующем нормальное масштабирование 1x, вы никогда не увидите субпиксельные координаты.поскольку курсор мыши нельзя переместить в положение дробного пикселя на экране - квант движения мыши составляет ровно 1 пиксель.

...