2011-07-22 68 views
5

我想在OSX上獲得高分辨率和高幀率的鼠標移動。OSX上的高分辨率和高幀率鼠標座標? (或其他解決方案?)

「高幀率」 = 60幀或更高(優選> 120)
「高分辨率」 =子像素值

問題
我有在約顯示器的刷新速率運行一個OpenGL視圖,所以它是~60 fps。我使用鼠標環顧四周,因此我隱藏了鼠標光標,並依賴於鼠標增量值。

問題是鼠標事件的幀速率太低,並且數值被捕捉爲整數(整像素)。這會導致「波濤洶涌」的觀看體驗。這裏的鼠標增量值的可視化隨時間:

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,然後是0.000,然後是2.000,然後是0.000,然後是3.000,0.000等等。

這意味着視圖將一幀旋轉2.000個單位,然後旋轉0.000個單位,然後旋轉3.000個單位。當鼠標以大致恆定的速度拖動時,會發生這種情況。無情地說,這看起來像廢話。

那麼,我該如何1)增加鼠標的事件幀率?和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。這對我來說很可怕。這是瘋子。蘋果的文檔變得很奇怪,似乎在說「如果你深深地思考,你真正需要的是頭文件」。

所以我一直在閱讀頭文件。我發現了一些有趣的花絮。上線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; 

看到,它說,子像素位置!好。然後在線73 <IOKit/hidsystem/IOLLParameter.h>

#define kIOHIDPointerResolutionKey  "HIDPointerResolution" 

嗯。總而言之,我感受到了OSX深入瞭解亞像素鼠標座標的感覺,並且每一幀都必須有一種讀取原始鼠標移動的方式,但我不知道如何獲得這些信息值。

問題
呃,那麼,我在問什麼?

  • 有沒有辦法在OSX中獲得高幀速率的鼠標事件? (示例代碼?)
  • 有沒有辦法在OSX中獲取子像素鼠標座標? (示例代碼?)
  • 有沒有讀「生」鼠標每個幀的方式? (即不是依賴於一個事件。)
  • 或者,我如何獲得NXEvents或設置HIDParameters?示例代碼? (這樣我就可以更深入地瞭解這對我自己的......)

(對不起,長職位)

回答

1

子像素座標的可能性是存在的,因爲Mac OS X上的設計是與分辨率無關。屏幕上2x2個硬件像素的正方形可以代表軟件中的單個虛擬像素,允許將光標置於(x + 0.5, y + 0.5)。使用正常的1倍縮放,你永遠不會看到子像素座標,因爲鼠標光標無法移動到屏幕上的分數像素位置

在任何實際的Mac - 鼠標移動的量子恰恰是1個像素。

1

如果您需要在比事件派發系統提供的級別更低的級別訪問指針設備增量信息,那麼您可能需要使用user-space USB APIs

1

如果您使用的是IOHIDDevice回調的鼠標,你可以用它來得到雙重價值:

double doubleValue = IOHIDValueGetScaledValue(inIOHIDValueRef, kIOHIDTransactionDirectionTypeOutput); 
5

(這是一個非常晚的答案,而是一個我認爲仍然是有用的其他人)

您是否嘗試過濾鼠標輸入?這可能會很棘手,因爲過濾往往是滯後和精確性之間的折衷。然而,多年前我寫了一篇文章,解釋我如何過濾鼠標移動,併爲遊戲開發網站撰寫文章。鏈接是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的歷史記錄緩衝區大小,只需使用重量修飾符,直到我很高興。