2011-07-22 15 views
5

Chciałbym uzyskać ruchy myszy w wysokiej rozdzielczości i wysokiej szybkości klatek na OSX.Współrzędne myszy wysokiej rozdzielczości i wysokiej szybkości klatek na sekundę w systemie OSX? (Lub inne rozwiązanie?)

"High framerate" = 60 fps lub wyższe (najlepiej> 120)
"wysokiej rozdzielczości" Wartości = subpiksel

Problem
Mam widoku OpenGL działa na temat częstotliwości odświeżania monitora , więc wynosi ~ 60 fps. Używam myszy, aby się rozejrzeć, więc ukryłem kursor myszy i polegam na wartościach delta myszy.

Problem polega na tym, że zdarzenia myszy pojawiają się przy zbyt niskiej liczbie klatek na sekundę, a wartości są przyciągane do liczby całkowitej (całe piksele). Powoduje to "wzburzone" wrażenia wizualne. Oto wizualizacja wartości delta mysz w czasie:

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 

To jest typowy (skrócona) krzywa tworzona od użytkownika przesuwając mysz trochę w prawo. Każde x reprezentuje wartość deltaX dla każdej klatki, a ponieważ wartości deltaX są zaokrąglane do liczb całkowitych, ten wykres jest rzeczywiście dość dokładny. Jak widać, wartość deltaX wyniesie 0,000 jednej klatki, a następnie 1.000 następnej, ale potem znów będzie 0.000, a następnie 2.000, a następnie 0.000 ponownie, następnie 3.000, 0.000 i tak dalej.

Oznacza to, że widok obróci 2.000 jednostek o jedną klatkę, a następnie obróci o 0,000 jednostek w następnej, a następnie obróci 3.000 jednostek. Dzieje się tak, gdy mysz jest przeciągana z mniej więcej stałą prędkością. Nie ma nic do powiedzenia, to wygląda na gówno.

Jak mogę 1) zwiększyć liczbę klatek na sekundę myszy? i 2) uzyskać wartości podpikseli?

Dotychczas
Próbowałem następujące:

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

dobrze, to jedno było oczywiste. dx tutaj jest float, ale wartości są zawsze zaokrąglone (0.000, 1.000 itd.). Spowoduje to utworzenie powyższego wykresu.

Następnym krokiem było spróbowanie stuknięcia zdarzeń myszy przed wejściem do WindowServera, pomyślałem. Więc stworzyliśmy CGEventTrap:

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

Wciąż wartości są n.000, chociaż uważam, że szybkość wypalania zdarzeń jest nieco wyższa. Ale nadal nie jest to 60 klatek na sekundę. Nadal dostaję powyższy wykres.

Próbowałem także ustawić czułość myszy naprawdę wysoko, a następnie skalować wartości w dół po mojej stronie. Ale wydaje się, że OSX dodaje jakieś przyspieszenie lub coś - wartości stają się naprawdę "niestabilne", a w konsekwencji nieużyteczne, a tempo pożaru jest wciąż zbyt niskie.

Bez powodzenia zacząłem śledzić zdarzenia myszy w króliczej dziurze i dotarłem do IOKit. To dla mnie przerażające. To szalony kapelusznik. Dokumentacja Apple robi się dziwna i zdaje się mówić: "jeśli jesteś w tym głęboko, wszystko, czego naprawdę potrzebujesz, to pliki nagłówkowe".

Więc czytałem pliki nagłówkowe. I znalazłem kilka ciekawych ciekawostek.

W <IOKit/hidsystem/IOLLEvent.h> na linii 377 nie ma tej struktury:

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

See, mówi pozycja subpiksel! Ok. Następnie na linii 73 w <IOKit/hidsystem/IOLLParameter.h>

#define kIOHIDPointerResolutionKey  "HIDPointerResolution" 

Hmm.

Podsumowując, mam wrażenie, że OSX wie o głębokich współrzędnych myszy pod-pikselem i musi być po prostu sposób na odczytanie surowych ruchów myszy w każdej klatce, ale nie mam pojęcia, jak je uzyskać wartości.

Pytania
Erh, więc, o co proszę?

  • Czy istnieje sposób na uzyskanie wysokiej liczby klatek na sekundę w systemie OSX? (Przykładowy kod?)
  • Czy istnieje sposób uzyskiwania współrzędnych myszy pod-pikseli w systemie OSX? (Przykładowy kod?)
  • Czy istnieje sposób odczytu "surowych" delt myszy w każdej klatce? (Tj. nie polegać na zdarzeniu.)
  • Lub, w jaki sposób uzyskać NXEvents lub ustawić HIDParameters? Przykładowy kod? (Więc mogę sięgnąć głębiej do tego na własną rękę ...)

(Przepraszam za długi post)

Odpowiedz

1

Możliwość współrzędnych subpikseli istnieje, ponieważ Mac OS X przeznaczony jest do rozdzielczości niezależne. Kwadrat 2x2 pikseli sprzętowych na ekranie może reprezentować pojedynczy wirtualny piksel w oprogramowaniu, umożliwiając umieszczenie kursora na (x + 0.5, y + 0.5).

Na każdym Macu używając normalnego 1x skalowania, nigdy nie zobaczysz współrzędnych podpikseli, ponieważ kursor myszy nie może zostać przesunięty do ułamkowej pozycji piksela na ekranie - kwant ruchu myszy wynosi dokładnie 1 piksel.

1

Jeśli potrzebujesz uzyskać dostęp do informacji o urządzeniu delta wskaźnika na niższym poziomie niż zapewnia system dyspozytorski zdarzeń, prawdopodobnie będziesz potrzebował użyć user-space USB APIs.

1

Jeśli używasz callbacków IOHIDDevice dla myszy można to wykorzystać, aby uzyskać podwójną wartość:

double doubleValue = IOHIDValueGetScaledValue(inIOHIDValueRef, kIOHIDTransactionDirectionTypeOutput); 
5

(jest to bardzo późno odpowiedź, ale taki, który moim zdaniem jest nadal użyteczny dla innych, potknąć się o to.)

Czy próbowałeś filtrować wejście myszy? Może to być trudne, ponieważ filtrowanie jest kompromisem pomiędzy opóźnieniem a precyzją. Jednak wiele lat temu napisałem artykuł wyjaśniający, w jaki sposób odfiltrowałem ruchy myszy i napisałem artykuł na temat strony poświęconej tworzeniu gier. Link to http://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtml.

Ponieważ ta strona nie jest już pod aktywnego rozwoju (może odejść) jest tu istotny fragment:


W prawie każdym przypadku, filtrowanie oznacza uśrednianie. Jednakże, jeśli po prostu średnią ruch myszy w czasie, wprowadzimy opóźnienie.Jak zatem filtrujemy bez wprowadzania jakichkolwiek efektów ubocznych? Cóż, nadal będziemy używać uśredniania, ale zrobimy to z pewną inteligencją. Jednocześnie zapewnimy użytkownikowi dokładną kontrolę nad filtrowaniem, aby mogli samodzielnie dostosować ustawienia.

Będziemy używać nieliniowego filtra uśrednionej myszy z czasem, gdzie starsze wartości mają mniejszy wpływ na wynik filtrowany.

Jak to działa

każdej klatce, czy przesunąć mysz lub nie, kładziemy bieżący ruch myszy w buforze historii i usunąć najstarszy wartość historii. Tak więc nasza historia zawsze zawiera próbki X, gdzie X jest "wielkością bufora historii", reprezentującą najnowsze ruchy myszy w czasie.

Jeśli użyjemy bufora historii o wielkości 10 i standardowej średniej całego bufora, filtr wprowadzi dużo opóźnienia. Szybkie ruchy myszki pozostawałyby w tyle za 1/6 sekundy na maszynie 60FPS. W szybkiej grze akcji byłoby to bardzo płynne, ale praktycznie bezużyteczne. W tym samym scenariuszu rozmiar bufora historycznego równy 2 dałby nam bardzo małe opóźnienie, ale bardzo słabe filtrowanie (szorstkie i gwałtowne reakcje odtwarzacza).

Filtr nieliniowy ma na celu zwalczanie tego wykluczającego się wzajemnie scenariusza. Pomysł jest bardzo prosty. Zamiast ślepo uśredniać wszystkie wartości w buforze historii w równym stopniu, oceniamy je na podstawie wagi. Zaczynamy od wagi 1,0. Tak więc pierwsza wartość w buforze historii (wejście myszy w bieżącej ramce) ma pełną wagę. Następnie pomnożymy tę wagę przez "modyfikator wagi" (powiedzmy ... 0,2) i przejdźmy do następnej wartości w buforze historii. Im dalej cofamy się w czasie (poprzez nasz bufor historyczny), tym wartości mają coraz mniejszy ciężar (wpływ) na końcowy wynik.

Aby opracować, z modyfikatorem wagi 0,5, próbka bieżącej ramki miałaby 100% wagę, poprzednia próbka miałaby 50% wagi, następna najstarsza próbka miałaby 25% wagi, następna miałaby 12,5% masy i tak dalej. Jeśli to wyrysujesz, wygląda na krzywą. Ideą modyfikatora wagi jest kontrolowanie, jak ostro spada krzywa, gdy próbki w historii stają się starsze.

Zmniejszenie opóźnienia oznacza zmniejszenie modyfikatora masy. Zmniejszenie modyfikatora wagi do 0 zapewni użytkownikowi surowe, niefiltrowane sprzężenie zwrotne. Zwiększenie go do 1.0 spowoduje, że wynik będzie prostą średnią wszystkich wartości w buforze historii.

Oferujemy użytkownikowi dwie zmienne do dokładnej kontroli: rozmiar bufora historii i modyfikator masy. Zwykle używam bufora historii o wielkości 10 i po prostu gram z modyfikatorem wagi, dopóki nie będę szczęśliwy.

Powiązane problemy