I now realize that you need to sample your mouse position at at least 120Hz in order to 100% correctly display it on a monitor with 60Hz refresh rate - and yes, it’s a Nyquist thing.
Imagine an ideal case - that your mouse were moving uniformly with infinite precision, from left to right, and every 1/60th of a second a lightbulb went off revealing its position for the monitor to represent.
Suppose the mouse is moving at exactly 60 pixels a second. The mouse on the monitor will also move uniformly at 60 pixels a second - but with a delay from 0 to 1/60th of a second depending on the phase difference between the mouse’s pixel transitions and the update frequency of the monitor.
Now suppose that the mouse is moving at 59 pixels a second. What happens now?
Well, the first camera flash goes off at 0s, and the mouse is on pixel 0 . But the second camera flash goes off at 1/60s and the mouse is still on pixel 0 - because it doesn’t hit that second pixel until 1/59s, which is a tiny bit later than 1/60s (1/3540s to be precise). Then the third flash goes off and the mouse is now on pixel 1 and now it continues forward until the next second starts and it loses another pixel.
So the system represents “59 pixels a second” as “60 pixels a second for 59 pixels and then wait”, which rather different.
Now you’re saying, what’s a pixel between friends? Who would notice? And you’ll notice that the “throttle drip” at this speed is only at most one pixel.
But in fact, this is at quite slow mouse speeds. My screen is 2000 pixels wide and I regularly go over half of that in half a second. At 2000 pixels per second, these errors will be 30 times as likely. Your maximum throttle drip is 30 pixels! This means that you could quickly drag something across the screen and end up 30 pixels off!
Now, you don’t quite see that because you quickly correct - but you see the “wild cursor” for 1/60th of a second and it registers as jerky.
Let me tell you that I never use this sort of throttling after a lot of bad experiences. I intercept each and every event, and make quick tests to see if anything has really changed and I can ignore the message (“on change” strategy, I call this). Most of the time you can quickly detect that your actual objects will stay on the same pixels with a small amount of arithmetic and return from the mouse update.
You know, come to think of it I had a similar issue with Juce and a time counter, which I was sampling at 40Hz initially and even moving to 100Hz had… weirdnesses (I think threading issues). Going to an “on-change” mechanism worked really well, all the counters are updated right from my sample player and move extremely smoothly.
And the issue with “throttle drip” was significant there - because there’s a millisecond time display so if you “stop at a point” you might not see exactly the right time.