perf(#271): RipplePresenter マウスモニターを画面外除外 + 33ms 間引き#283
Conversation
NSEvent.addGlobalMonitorForEvents はマウスの全移動(画面外含む)を受信するが、 従来実装では毎イベントを Task で MainActor にホップしていた。 - コールバック前に NSEvent.mouseLocation と CACurrentMediaTime() をキャプチャ - Task 内で前回受理から 33ms 未満の場合は早期リターン(≈30Hz 上限) - screenRect.contains() を handleMouseLocation より前に評価し、 画面外イベントをオーバーレイ更新なしに棄却
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reached
More reviews will be available in 41 minutes and 41 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI のクリーンビルドで、ネストした Task クロージャ内の stored property 参照が "explicit use of 'self'" コンパイルエラーになっていた。 グローバルモニターのコールバックはメインスレッドで配送されるため、 MainActor.assumeIsolated で同期実行し Task を廃止。これにより: - 画面外/間引きで弾かれるイベントは Task を一切生成しない (issue #271 の「Task 生成前に return」「Task 生成頻度に上限」を満たす) - ネストクロージャの self キャプチャ曖昧性が解消されコンパイルが通る 処理本体を processGlobalMouseMove() に抽出。画面外イベントは mouseInScreen を false に戻して即 return、画面内は 33ms 間引きの上で handleMouseLocation へ。
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
codecov/patch が新規 processGlobalMouseMove() 未到達で 80% を下回っていた。 グローバルモニターの実 NSEvent サンプルはユニットテストで再現できないため、 ロジックを (location, time) を受け取る processMouseMove(at:time:) に抽出し、 デフォルト引数でライブ値、テストでは決定的な値を注入できるようにした。 - 画面外サンプルで mouseInScreen がリセットされ idle ripple が出ないこと - 画面内サンプルで ripple が生成されること - 33ms 未満の連続サンプルが間引かれ、33ms 超過で再処理されること を直接検証。未到達は start() 内 assumeIsolated クロージャ1行のみ。
Extract the monitor closure body into a nonisolated handleGlobalMouseMove() so the MainActor.assumeIsolated hop is unit-testable; the global monitor itself cannot be fired from a test. Add a test driving the bridge with a zero screenRect to exercise the hop and the exclusion guard. Only the never-invoked monitor registration closure now remains uncovered.
…-monitor-throttle
Closes #271
変更内容
NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved)は接続されている全ディスプレイのマウス移動を受信する。従来実装では毎イベントをTask { @MainActor }でホップしていたため、マウスを素早く動かしたとき(連続 500Hz+ 相当)にオーバーレイ画面外のイベントも含めて大量の Task が積まれる問題があった。修正
Sources/Presenters/Wallpaper/RipplePresenter.swiftNSEvent.mouseLocationとCACurrentMediaTime()を Task 生成前に取得し、Task クロージャに値として渡すcallTime - lastMouseTaskTime >= 0.033を確認し、前回受理から 33ms 未満のイベントは早期リターンscreenRect.contains(location)をhandleMouseLocationより先に評価し、オーバーレイ画面外のマウス移動でリップル計算が走らないようにするテスト追加
Tests/PresentersTests/RipplePresenterTests.swiftrejects point outside screenRect and clears mouseInScreen (#271)— 画面外座標を渡すとmouseInScreenがリセットされ idle tick でリップルが生成されないことを確認accepts point inside screenRect and spawns ripple (#271)— 画面内座標を渡すとリップルが生成されることを確認