2011-07-22 13 views
5

Me gustaría obtener movimientos del mouse en alta resolución y un alto framerate en OSX.¿Coordenadas de mouse de alta resolución y alto framerate en OSX? (¿O alguna otra solución?)

"alta tasa de fotogramas" = 60 fps o más (preferentemente> 120)
"alta resolución" valora = subpíxeles

Problema
Tengo una vista OpenGL funcionando aproximadamente a la tasa de refresco del monitor , entonces es ~ 60 fps. Uso el mouse para mirar alrededor, así que he ocultado el cursor del mouse y estoy confiando en los valores delta del mouse.

El problema es que los eventos del mouse vienen a una velocidad de fotogramas demasiado baja, y los valores se ajustan al entero (píxeles enteros). Esto causa una experiencia de visualización "entrecortada". Aquí es una visualización de valores delta de ratón con el tiempo:

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 

Esta es una curva típica (abreviado) creado a partir de que el usuario mueve el ratón un poco a la derecha. Cada x representa el valor deltaX para cada cuadro, y como los valores de deltaX se redondean a números enteros, este gráfico es realmente bastante preciso. Como podemos ver, el valor deltaX será 0.000 un cuadro, y luego 1.000 el siguiente, pero luego será 0.000 nuevamente, y luego 2.000, y luego 0.000 nuevamente, luego 3.000, 0.000, y así sucesivamente.

Esto significa que la vista rotará 2.000 unidades de un cuadro, y luego girará 0.000 unidades al siguiente, y luego girará 3.000 unidades. Esto ocurre mientras el mouse se está arrastrando con una velocidad más o menos constante. No hace falta decir que esto parece una mierda.

Entonces, ¿cómo puedo 1) aumentar la velocidad de fotogramas del evento del mouse? y 2) obtener valores de subpixel?

Hasta ahora
He intentado lo siguiente:

- (void)mouseMoved:(NSEvent *)theEvent { 
    CGFloat dx, dy; 
    dx = [theEvent deltaX]; 
    dy = [theEvent deltaY]; 
    // ... 
    actOnMouse(dx,dy); 
} 

Bueno, éste era obvio. dx aquí es flotante, pero los valores son siempre redondeados (0.000, 1.000 etc.). Esto crea el gráfico de arriba.

Así que el siguiente paso fue tratar de tocar los eventos del mouse antes de que ingresen al WindowServer, pensé. Así que he creado un CGEventTrap:

eventMask = (1 << kCGEventMouseMoved); 
eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 
      0, eventMask, myCGEventCallback, NULL); 
//... 
myCGEventCallback(...){ 
    double dx = CGEventGetDoubleValueField(event, kCGMouseEventDeltaX); 
    double dy = CGEventGetDoubleValueField(event, kCGMouseEventDeltaY); 
} 

Todavía valores son n.000, aunque creo que la tasa de liberación evento es un poco más alto. Pero todavía no es a 60 fps. Todavía obtengo el cuadro de arriba.

También intenté establecer la sensibilidad del mouse realmente alta, y luego reducir los valores de mi lado. Pero parece que OSX agrega algún tipo de aceleración o algo así: los valores se vuelven realmente "inestables" y, en consecuencia, inutilizables, y la velocidad de disparo es aún demasiado baja.

Sin suerte, he comenzado a seguir los eventos del ratón por el agujero del conejo, y he llegado a IOKit. Esto es aterrador para mi Es el sombrerero loco. La documentación de Apple se vuelve rara y parece decir "si estás en el fondo, todo lo que realmente necesitas son archivos de encabezado".

He estado leyendo archivos de encabezado. Y he encontrado algunas cositas interesantes.

En <IOKit/hidsystem/IOLLEvent.h> en la línea 377 está esta estructura:

struct { /* For mouse-down and mouse-up events */ 
    UInt8 subx;  /* sub-pixel position for x */ 
    UInt8 suby;  /* sub-pixel position for y */ 
    // ... 
} mouse; 

Sede, que dice posición de sub-píxel! De acuerdo. A continuación, en la línea 73 en <IOKit/hidsystem/IOLLParameter.h>

#define kIOHIDPointerResolutionKey  "HIDPointerResolution" 

Hmm.

En general, tengo la sensación de que OSX sabe sobre las coordenadas del mouse sub-pixel en el fondo, y simplemente tiene que haber una manera de leer los movimientos crudos del mouse cada fotograma, pero no tengo idea de cómo obtenerlos valores.

Preguntas
Erh, entonces, ¿qué estoy pidiendo?

  • ¿Hay alguna manera de obtener eventos de mouse de alta velocidad en OSX? (Código de ejemplo?)
  • ¿Hay alguna manera de obtener coordenadas de mouse sub-pixel en OSX? (¿Código de ejemplo?)
  • ¿Hay alguna manera de leer los deltas de mouse "en bruto" en cada cuadro? (Ie no confíe en un evento.)
  • O, ¿cómo obtengo NXEvents o configuro HIDParameters? Código de ejemplo? (Para que pueda profundizar en esto por mi cuenta ...)

(Lo siento por largo post)

Respuesta

1

La posibilidad de coordenadas subpíxeles existe porque Mac OS X está diseñado para ser independiente de la resolución. Un cuadrado de píxeles de hardware de 2x2 en una pantalla podría representar un solo píxel virtual en el software, permitiendo que el cursor se coloque en (x + 0.5, y + 0.5).

En cualquier Mac real que utilice escala normal 1x, nunca verá coordenadas de subpíxeles porque el cursor del mouse no se puede mover a una posición de píxeles fraccionarios en la pantalla; el movimiento del mouse es exactamente 1 píxel.

1

Si necesita obtener acceso a la información delta del dispositivo del puntero en un nivel inferior al que proporciona el sistema de despacho de eventos, entonces probablemente necesite usar el user-space USB APIs.

1

Si está utilizando las devoluciones de llamada IOHIDDevice para el ratón se puede utilizar esto para conseguir un doble valor:

double doubleValue = IOHIDValueGetScaledValue(inIOHIDValueRef, kIOHIDTransactionDirectionTypeOutput); 
5

(Esta es una respuesta muy tarde, pero que creo que sigue siendo útil para otros que tropezar con esto.)

¿Has intentado filtrar la entrada del mouse? Esto puede ser complicado porque el filtrado tiende a ser una compensación entre el retraso y la precisión. Sin embargo, hace años escribí un artículo que explicaba cómo filtraba los movimientos de mi mouse y escribía un artículo para un sitio de desarrollo de juegos. El enlace es http://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtml.

Desde ese sitio ya no está en desarrollo activo (y puede desaparecer) aquí es un extracto relevante:


En casi todos los casos, medios de filtración promedio. Sin embargo, si simplemente promediamos el movimiento del mouse a lo largo del tiempo, presentaremos lag.¿Cómo, entonces, filtramos sin introducir ningún efecto secundario? Bueno, todavía usaremos un promedio, pero lo haremos con algo de inteligencia. Y al mismo tiempo, le daremos al usuario un control preciso sobre el filtrado para que ellos puedan ajustarlo ellos mismos.

Usaremos un filtro no lineal de entrada de mouse promedio a lo largo del tiempo, donde los valores anteriores tienen menos influencia sobre el resultado filtrado.

Cómo funciona

Cada cuadro, si se mueve el ratón o no, que pone el movimiento actual del ratón en un búfer de historial y eliminar el valor de la historia más antigua. Por lo tanto, nuestra historia siempre contiene X muestras, donde X es el "tamaño del búfer de historial", que representa los movimientos del mouse muestreados más recientes a lo largo del tiempo.

Si utilizamos un tamaño de búfer de historial de 10, y un promedio estándar de todo el búfer, el filtro introduciría un gran retraso. Los movimientos rápidos del mouse se retrasarían 1/6 de segundo en una máquina de 60 FPS. En un juego de acción rápida, esto sería muy fácil, pero prácticamente inutilizable. En el mismo escenario, un tamaño de búfer de historial de 2 nos daría muy poco retraso, pero un filtrado muy pobre (reacciones de jugador ásperas y espasmódicas)

El filtro no lineal está destinado a combatir este escenario mutuamente exclusivo. La idea es muy simple. En lugar de promediar ciegamente todos los valores en el búfer de historia por igual, los promediamos con un peso. Comenzamos con un peso de 1.0. De modo que el primer valor en el búfer de historial (la entrada de mouse del fotograma actual) tiene todo su peso. Luego multiplicamos este peso por un "modificador de peso" (digamos ... 0.2) y avanzamos al siguiente valor en el buffer de historial. Cuanto más atrás en el tiempo (a través de nuestro buffer de historial) vamos, los valores tienen cada vez menos peso (influencia) en el resultado final.

Para elaborar, con un modificador de peso de 0.5, la muestra del marco actual tendría 100% de peso, la muestra anterior tendría 50% de peso, la siguiente muestra más vieja tendría 25% de peso, la siguiente tendría 12.5% ​​de peso y así. Si grafica esto, parece una curva. Entonces, la idea detrás del modificador de peso es controlar qué tan bruscamente la curva disminuye a medida que las muestras en el historial envejecen.

Reducir el retraso significa disminuir el modificador de peso. Reducir el modificador de peso a 0 proporcionará al usuario comentarios crudos y sin filtrar. Aumentarlo a 1.0 hará que el resultado sea un promedio simple de todos los valores en el búfer de historial.

Ofreceremos al usuario dos variables para un control preciso: el tamaño del búfer de historial y el modificador de peso. Tiendo a usar un tamaño de búfer de historial de 10, y solo juego con el modificador de peso hasta que esté contento.

Cuestiones relacionadas