diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-20 22:39:00 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-20 22:39:00 +0300 |
commit | 982b0250255624e6bdf8782dcd2e7cb512f0bd4d (patch) | |
tree | 3540f25f88b3b3a5704bc3a822abfba15eacb085 /src/ui/touch.c | |
parent | eaaecf50a5ec64e9c344d195a3a74c7a46c8797c (diff) |
iOS: Fixed momentum scroll timing
The scrolling stutters were being caused by timing and not slow text rendering. Now the momentum scroll is locked to display refresh rate.
Diffstat (limited to 'src/ui/touch.c')
-rw-r--r-- | src/ui/touch.c | 46 |
1 files changed, 31 insertions, 15 deletions
diff --git a/src/ui/touch.c b/src/ui/touch.c index 4498efae..8a532821 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -99,6 +99,8 @@ struct Impl_TouchState { | |||
99 | iArray *touches; | 99 | iArray *touches; |
100 | iArray *pinches; | 100 | iArray *pinches; |
101 | iArray *moms; | 101 | iArray *moms; |
102 | double stepDurationMs; | ||
103 | double momFrictionPerStep; | ||
102 | double lastMomTime; | 104 | double lastMomTime; |
103 | iInt2 currentTouchPos; /* for emulating SDL_GetMouseState() */ | 105 | iInt2 currentTouchPos; /* for emulating SDL_GetMouseState() */ |
104 | }; | 106 | }; |
@@ -107,10 +109,15 @@ static iTouchState *touchState_(void) { | |||
107 | static iTouchState state_; | 109 | static iTouchState state_; |
108 | iTouchState *d = &state_; | 110 | iTouchState *d = &state_; |
109 | if (!d->touches) { | 111 | if (!d->touches) { |
110 | d->touches = new_Array(sizeof(iTouch)); | 112 | d->touches = new_Array(sizeof(iTouch)); |
111 | d->pinches = new_Array(sizeof(iPinch)); | 113 | d->pinches = new_Array(sizeof(iPinch)); |
112 | d->moms = new_Array(sizeof(iMomentum)); | 114 | d->moms = new_Array(sizeof(iMomentum)); |
113 | d->lastMomTime = SDL_GetTicks(); | 115 | d->lastMomTime = 0.0; |
116 | d->stepDurationMs = 1000.0 / 60.0; /* TODO: Ask SDL about the display refresh rate. */ | ||
117 | #if defined (iPlatformAppleMobile) | ||
118 | d->stepDurationMs = 1000.0 / (double) displayRefreshRate_iOS(); | ||
119 | #endif | ||
120 | d->momFrictionPerStep = pow(0.985, 120.0 / (1000.0 / d->stepDurationMs)); | ||
114 | } | 121 | } |
115 | return d; | 122 | return d; |
116 | } | 123 | } |
@@ -229,10 +236,16 @@ static void dispatchNotification_Touch_(const iTouch *d, int code) { | |||
229 | } | 236 | } |
230 | } | 237 | } |
231 | 238 | ||
239 | iLocalDef double accurateTicks_(void) { | ||
240 | const uint64_t freq = SDL_GetPerformanceFrequency(); | ||
241 | const uint64_t count = SDL_GetPerformanceCounter(); | ||
242 | return 1000.0 * (double) count / (double) freq; | ||
243 | } | ||
244 | |||
232 | static void update_TouchState_(void *ptr) { | 245 | static void update_TouchState_(void *ptr) { |
233 | iTouchState *d = ptr; | 246 | iTouchState *d = ptr; |
234 | const uint32_t nowTime = SDL_GetTicks(); | ||
235 | /* Check for long presses to simulate right clicks. */ | 247 | /* Check for long presses to simulate right clicks. */ |
248 | const uint32_t nowTime = SDL_GetTicks(); | ||
236 | iForEach(Array, i, d->touches) { | 249 | iForEach(Array, i, d->touches) { |
237 | iTouch *touch = i.value; | 250 | iTouch *touch = i.value; |
238 | if (touch->pinchId || touch->isTouchDrag) { | 251 | if (touch->pinchId || touch->isTouchDrag) { |
@@ -275,14 +288,17 @@ static void update_TouchState_(void *ptr) { | |||
275 | } | 288 | } |
276 | /* Update/cancel momentum scrolling. */ { | 289 | /* Update/cancel momentum scrolling. */ { |
277 | const float minSpeed = 15.0f; | 290 | const float minSpeed = 15.0f; |
278 | const float momFriction = 0.985f; /* per step */ | 291 | if (d->lastMomTime < 0.001) { |
279 | const float stepDurationMs = 1000.0f / 120.0f; | 292 | d->lastMomTime = accurateTicks_(); |
280 | double momAvailMs = nowTime - d->lastMomTime; | 293 | } |
281 | int numSteps = (int) (momAvailMs / stepDurationMs); | 294 | const double momAvailMs = accurateTicks_() - d->lastMomTime; |
282 | d->lastMomTime += numSteps * stepDurationMs; | 295 | /* Display refresh is vsynced and we'll be here at most once per frame. |
296 | However, we may come here TOO early, which would cause a hiccup in the scrolling, | ||
297 | so always do at least one step. */ | ||
298 | int numSteps = iMax(1, momAvailMs / d->stepDurationMs); | ||
299 | d->lastMomTime += numSteps * d->stepDurationMs; | ||
283 | numSteps = iMin(numSteps, 10); /* don't spend too much time here */ | 300 | numSteps = iMin(numSteps, 10); /* don't spend too much time here */ |
284 | // printf("mom steps:%d\n", numSteps); | 301 | // printf("mom steps:%d\n", numSteps); |
285 | // iWindow *window = get_Window(); | ||
286 | iForEach(Array, m, d->moms) { | 302 | iForEach(Array, m, d->moms) { |
287 | if (numSteps == 0) break; | 303 | if (numSteps == 0) break; |
288 | iMomentum *mom = m.value; | 304 | iMomentum *mom = m.value; |
@@ -291,8 +307,8 @@ static void update_TouchState_(void *ptr) { | |||
291 | continue; | 307 | continue; |
292 | } | 308 | } |
293 | for (int step = 0; step < numSteps; step++) { | 309 | for (int step = 0; step < numSteps; step++) { |
294 | mulvf_F3(&mom->velocity, momFriction); | 310 | mulvf_F3(&mom->velocity, d->momFrictionPerStep); |
295 | addv_F3(&mom->accum, mulf_F3(mom->velocity, stepDurationMs / 1000.0f)); | 311 | addv_F3(&mom->accum, mulf_F3(mom->velocity, d->stepDurationMs / 1000.0f)); |
296 | } | 312 | } |
297 | const iInt2 pixels = initF3_I2(mom->accum); | 313 | const iInt2 pixels = initF3_I2(mom->accum); |
298 | if (pixels.x || pixels.y) { | 314 | if (pixels.x || pixels.y) { |
@@ -664,7 +680,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
664 | .velocity = velocity | 680 | .velocity = velocity |
665 | }; | 681 | }; |
666 | if (isEmpty_Array(d->moms)) { | 682 | if (isEmpty_Array(d->moms)) { |
667 | d->lastMomTime = nowTime; | 683 | d->lastMomTime = accurateTicks_(); |
668 | } | 684 | } |
669 | pushBack_Array(d->moms, &mom); | 685 | pushBack_Array(d->moms, &mom); |
670 | //dispatchMotion_Touch_(touch->startPos, 0); | 686 | //dispatchMotion_Touch_(touch->startPos, 0); |