summaryrefslogtreecommitdiff
path: root/src/ui/touch.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-09 16:39:39 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-10 06:29:31 +0300
commit6d36a826ce502737bd1b6ff2b9fe47c79338e58e (patch)
treee4f2938afb74b73cf675507aa6a9ff12e3b9704c /src/ui/touch.c
parentc09d4a5593c4d2ce6814dfd96b5da3368ec9b198 (diff)
Touch: Multitouch pinch events
Diffstat (limited to 'src/ui/touch.c')
-rw-r--r--src/ui/touch.c226
1 files changed, 165 insertions, 61 deletions
diff --git a/src/ui/touch.c b/src/ui/touch.c
index 88746bed..68c493dc 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -32,9 +32,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32# include "../ios.h" 32# include "../ios.h"
33#endif 33#endif
34 34
35iDeclareType(Momentum)
36iDeclareType(Pinch)
35iDeclareType(Touch) 37iDeclareType(Touch)
36iDeclareType(TouchState) 38iDeclareType(TouchState)
37iDeclareType(Momentum)
38 39
39#define numHistory_Touch_ 5 40#define numHistory_Touch_ 5
40#define lastIndex_Touch_ (numHistory_Touch_ - 1) 41#define lastIndex_Touch_ (numHistory_Touch_ - 1)
@@ -59,6 +60,7 @@ struct Impl_Touch {
59 iBool isTapBegun; 60 iBool isTapBegun;
60 iBool isTouchDrag; 61 iBool isTouchDrag;
61 iBool isTapAndHold; 62 iBool isTapAndHold;
63 int pinchId;
62 enum iTouchEdge edge; 64 enum iTouchEdge edge;
63 uint32_t startTime; 65 uint32_t startTime;
64 iFloat3 startPos; 66 iFloat3 startPos;
@@ -85,8 +87,15 @@ struct Impl_Momentum {
85 iFloat3 accum; 87 iFloat3 accum;
86}; 88};
87 89
90struct Impl_Pinch {
91 int id;
92 SDL_FingerID touchIds[2];
93 iWidget *affinity;
94};
95
88struct Impl_TouchState { 96struct Impl_TouchState {
89 iArray *touches; 97 iArray *touches;
98 iArray *pinches;
90 iArray *moms; 99 iArray *moms;
91 double lastMomTime; 100 double lastMomTime;
92}; 101};
@@ -95,8 +104,9 @@ static iTouchState *touchState_(void) {
95 static iTouchState state_; 104 static iTouchState state_;
96 iTouchState *d = &state_; 105 iTouchState *d = &state_;
97 if (!d->touches) { 106 if (!d->touches) {
98 d->touches = new_Array(sizeof(iTouch)); 107 d->touches = new_Array(sizeof(iTouch));
99 d->moms = new_Array(sizeof(iMomentum)); 108 d->pinches = new_Array(sizeof(iPinch));
109 d->moms = new_Array(sizeof(iMomentum));
100 d->lastMomTime = SDL_GetTicks(); 110 d->lastMomTime = SDL_GetTicks();
101 } 111 }
102 return d; 112 return d;
@@ -127,6 +137,16 @@ static iBool isStationary_Touch_(const iTouch *d) {
127 return isStationaryDistance_Touch_(d, tapRadiusPt_); 137 return isStationaryDistance_Touch_(d, tapRadiusPt_);
128} 138}
129 139
140static void clearWidgetMomentum_TouchState_(iTouchState *d, iWidget *widget) {
141 if (!widget) return;
142 iForEach(Array, m, d->moms) {
143 iMomentum *mom = m.value;
144 if (mom->affinity == widget) {
145 remove_ArrayIterator(&m);
146 }
147 }
148}
149
130static void dispatchMotion_Touch_(iFloat3 pos, int buttonState) { 150static void dispatchMotion_Touch_(iFloat3 pos, int buttonState) {
131 dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &(SDL_MouseMotionEvent){ 151 dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &(SDL_MouseMotionEvent){
132 .type = SDL_MOUSEMOTION, 152 .type = SDL_MOUSEMOTION,
@@ -163,14 +183,30 @@ static iBool dispatchClick_Touch_(const iTouch *d, int button) {
163 return wasUsed; 183 return wasUsed;
164} 184}
165 185
166static void clearWidgetMomentum_TouchState_(iTouchState *d, iWidget *widget) { 186static void dispatchButtonDown_Touch_(iFloat3 pos) {
167 if (!widget) return; 187 dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &(SDL_MouseButtonEvent){
168 iForEach(Array, m, d->moms) { 188 .type = SDL_MOUSEBUTTONDOWN,
169 iMomentum *mom = m.value; 189 .timestamp = SDL_GetTicks(),
170 if (mom->affinity == widget) { 190 .clicks = 1,
171 remove_ArrayIterator(&m); 191 .state = SDL_PRESSED,
172 } 192 .which = SDL_TOUCH_MOUSEID,
173 } 193 .button = SDL_BUTTON_LEFT,
194 .x = x_F3(pos),
195 .y = y_F3(pos)
196 });
197}
198
199static void dispatchButtonUp_Touch_(iFloat3 pos) {
200 dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &(SDL_MouseButtonEvent){
201 .type = SDL_MOUSEBUTTONUP,
202 .timestamp = SDL_GetTicks(),
203 .clicks = 1,
204 .state = SDL_RELEASED,
205 .which = SDL_TOUCH_MOUSEID,
206 .button = SDL_BUTTON_LEFT,
207 .x = x_F3(pos),
208 .y = y_F3(pos)
209 });
174} 210}
175 211
176static void update_TouchState_(void *ptr) { 212static void update_TouchState_(void *ptr) {
@@ -179,6 +215,9 @@ static void update_TouchState_(void *ptr) {
179 /* Check for long presses to simulate right clicks. */ 215 /* Check for long presses to simulate right clicks. */
180 iForEach(Array, i, d->touches) { 216 iForEach(Array, i, d->touches) {
181 iTouch *touch = i.value; 217 iTouch *touch = i.value;
218 if (touch->pinchId) {
219 continue;
220 }
182 /* Holding a touch will reset previous momentum for this widget. */ 221 /* Holding a touch will reset previous momentum for this widget. */
183 if (isStationary_Touch_(touch)) { 222 if (isStationary_Touch_(touch)) {
184 const int elapsed = nowTime - touch->startTime; 223 const int elapsed = nowTime - touch->startTime;
@@ -211,8 +250,8 @@ static void update_TouchState_(void *ptr) {
211 int numSteps = (int) (momAvailMs / stepDurationMs); 250 int numSteps = (int) (momAvailMs / stepDurationMs);
212 d->lastMomTime += numSteps * stepDurationMs; 251 d->lastMomTime += numSteps * stepDurationMs;
213 numSteps = iMin(numSteps, 10); /* don't spend too much time here */ 252 numSteps = iMin(numSteps, 10); /* don't spend too much time here */
214// printf("mom steps:%d\n", numSteps); 253 // printf("mom steps:%d\n", numSteps);
215 iWindow *window = get_Window(); 254// iWindow *window = get_Window();
216 iForEach(Array, m, d->moms) { 255 iForEach(Array, m, d->moms) {
217 if (numSteps == 0) break; 256 if (numSteps == 0) break;
218 iMomentum *mom = m.value; 257 iMomentum *mom = m.value;
@@ -229,12 +268,12 @@ static void update_TouchState_(void *ptr) {
229 subv_F3(&mom->accum, initI2_F3(pixels)); 268 subv_F3(&mom->accum, initI2_F3(pixels));
230 dispatchMotion_Touch_(mom->pos, 0); 269 dispatchMotion_Touch_(mom->pos, 0);
231 dispatchEvent_Widget(mom->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){ 270 dispatchEvent_Widget(mom->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){
232 .type = SDL_MOUSEWHEEL, 271 .type = SDL_MOUSEWHEEL,
233 .timestamp = nowTime, 272 .timestamp = nowTime,
234 .x = pixels.x, 273 .x = pixels.x,
235 .y = pixels.y, 274 .y = pixels.y,
236 .direction = perPixel_MouseWheelFlag 275 .direction = perPixel_MouseWheelFlag
237 }); 276 });
238 } 277 }
239 if (length_F3(mom->velocity) < minSpeed) { 278 if (length_F3(mom->velocity) < minSpeed) {
240 setHover_Widget(NULL); 279 setHover_Widget(NULL);
@@ -248,48 +287,6 @@ static void update_TouchState_(void *ptr) {
248 } 287 }
249} 288}
250 289
251void widgetDestroyed_Touch(iWidget *widget) {
252 iTouchState *d = touchState_();
253 iForEach(Array, i, d->touches) {
254 iTouch *touch = i.value;
255 if (touch->affinity == widget) {
256 remove_ArrayIterator(&i);
257 }
258 }
259 iForEach(Array, m, d->moms) {
260 iMomentum *mom = m.value;
261 if (mom->affinity == widget) {
262 remove_ArrayIterator(&m);
263 }
264 }
265}
266
267static void dispatchButtonDown_Touch_(iFloat3 pos) {
268 dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &(SDL_MouseButtonEvent){
269 .type = SDL_MOUSEBUTTONDOWN,
270 .timestamp = SDL_GetTicks(),
271 .clicks = 1,
272 .state = SDL_PRESSED,
273 .which = SDL_TOUCH_MOUSEID,
274 .button = SDL_BUTTON_LEFT,
275 .x = x_F3(pos),
276 .y = y_F3(pos)
277 });
278}
279
280static void dispatchButtonUp_Touch_(iFloat3 pos) {
281 dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &(SDL_MouseButtonEvent){
282 .type = SDL_MOUSEBUTTONUP,
283 .timestamp = SDL_GetTicks(),
284 .clicks = 1,
285 .state = SDL_RELEASED,
286 .which = SDL_TOUCH_MOUSEID,
287 .button = SDL_BUTTON_LEFT,
288 .x = x_F3(pos),
289 .y = y_F3(pos)
290 });
291}
292
293static iWidget *findOverflowScrollable_Widget_(iWidget *d) { 290static iWidget *findOverflowScrollable_Widget_(iWidget *d) {
294 const iInt2 rootSize = rootSize_Window(get_Window()); 291 const iInt2 rootSize = rootSize_Window(get_Window());
295 for (iWidget *w = d; w; w = parent_Widget(w)) { 292 for (iWidget *w = d; w; w = parent_Widget(w)) {
@@ -312,6 +309,81 @@ static iWidget *findSlidePanel_Widget_(iWidget *d) {
312 return NULL; 309 return NULL;
313} 310}
314 311
312static void checkNewPinch_TouchState_(iTouchState *d, iTouch *newTouch) {
313 iWidget *affinity = newTouch->affinity;
314 if (!affinity) {
315 return;
316 }
317 iForEach(Array, i, d->touches) {
318 iTouch *other = i.value;
319 if (other->id == newTouch->id || other->pinchId || other->affinity != affinity) {
320 continue;
321 }
322 /* A second finger on the same widget. */
323 iPinch pinch = { .affinity = affinity, .id = SDL_GetTicks() };
324 pinch.touchIds[0] = newTouch->id;
325 pinch.touchIds[1] = other->id;
326 newTouch->pinchId = other->pinchId = pinch.id;
327 clearWidgetMomentum_TouchState_(d, affinity);
328 /* Remember current positions to determine pinch amount. */
329 newTouch->startPos = newTouch->pos[0];
330 other->startPos = other->pos[0];
331 pushBack_Array(d->pinches, &pinch);
332 /*printf("[Touch] pinch %d starts with fingers %lld and %lld\n", pinch.id,
333 newTouch->id, other->id);*/
334 postCommandf_App("pinch.began ptr:%p", affinity);
335 break;
336 }
337}
338
339static iPinch *findPinch_TouchState_(iTouchState *d, int pinchId) {
340 iForEach(Array, i, d->pinches) {
341 iPinch *pinch = i.value;
342 if (pinch->id == pinchId) {
343 return pinch;
344 }
345 }
346 return NULL;
347}
348
349static void pinchMotion_TouchState_(iTouchState *d, int pinchId) {
350 const iPinch *pinch = findPinch_TouchState_(d, pinchId);
351 iAssert(pinch != NULL);
352 if (!pinch) return;
353 const iTouch *touch[2] = {
354 find_TouchState_(d, pinch->touchIds[0]),
355 find_TouchState_(d, pinch->touchIds[1])
356 };
357 iAssert(pinch->affinity == touch[0]->affinity);
358 iAssert(pinch->affinity == touch[1]->affinity);
359 const float startDist = length_F3(sub_F3(touch[1]->startPos, touch[0]->startPos));
360 if (startDist < gap_UI) {
361 return;
362 }
363 const float dist = length_F3(sub_F3(touch[1]->pos[0], touch[0]->pos[0]));
364// printf("[Touch] pinch %d motion: relative %f\n", pinchId, dist / startDist);
365 postCommandf_App("pinch.moved arg:%f ptr:%p", dist / startDist, pinch->affinity);
366}
367
368static void endPinch_TouchState_(iTouchState *d, int pinchId) {
369 //printf("[Touch] pinch %d ends\n", pinchId);
370 iForEach(Array, i, d->pinches) {
371 iPinch *pinch = i.value;
372 if (pinch->id == pinchId) {
373 postCommandf_App("pinch.ended ptr:%p", pinch->affinity);
374 /* Cancel both touches. */
375 iForEach(Array, j, d->touches) {
376 iTouch *touch = j.value;
377 if (touch->id == pinch->touchIds[0] || touch->id == pinch->touchIds[1]) {
378 remove_ArrayIterator(&j);
379 }
380 }
381 remove_ArrayIterator(&i);
382 break;
383 }
384 }
385}
386
315iBool processEvent_Touch(const SDL_Event *ev) { 387iBool processEvent_Touch(const SDL_Event *ev) {
316 /* We only handle finger events here. */ 388 /* We only handle finger events here. */
317 if (ev->type != SDL_FINGERDOWN && ev->type != SDL_FINGERMOTION && ev->type != SDL_FINGERUP) { 389 if (ev->type != SDL_FINGERDOWN && ev->type != SDL_FINGERMOTION && ev->type != SDL_FINGERUP) {
@@ -367,6 +439,8 @@ iBool processEvent_Touch(const SDL_Event *ev) {
367 if (flags_Widget(aff) & hover_WidgetFlag && ~flags_Widget(aff) & touchDrag_WidgetFlag) { 439 if (flags_Widget(aff) & hover_WidgetFlag && ~flags_Widget(aff) & touchDrag_WidgetFlag) {
368 setHover_Widget(aff); 440 setHover_Widget(aff);
369 } 441 }
442 /* This may begin a pinch. */
443 checkNewPinch_TouchState_(d, back_Array(d->touches));
370 addTicker_App(update_TouchState_, d); 444 addTicker_App(update_TouchState_, d);
371 } 445 }
372 else if (ev->type == SDL_FINGERMOTION) { 446 else if (ev->type == SDL_FINGERMOTION) {
@@ -388,6 +462,10 @@ iBool processEvent_Touch(const SDL_Event *ev) {
388 } 462 }
389 /* Update touch position. */ 463 /* Update touch position. */
390 pushPos_Touch_(touch, pos, nowTime); 464 pushPos_Touch_(touch, pos, nowTime);
465 if (touch->pinchId) {
466 pinchMotion_TouchState_(d, touch->pinchId);
467 return iTrue;
468 }
391 if (!touch->isTouchDrag && !isStationary_Touch_(touch) && 469 if (!touch->isTouchDrag && !isStationary_Touch_(touch) &&
392 flags_Widget(touch->affinity) & touchDrag_WidgetFlag) { 470 flags_Widget(touch->affinity) & touchDrag_WidgetFlag) {
393 touch->hasMoved = iTrue; 471 touch->hasMoved = iTrue;
@@ -496,6 +574,10 @@ iBool processEvent_Touch(const SDL_Event *ev) {
496 if (touch->id != fing->fingerId) { 574 if (touch->id != fing->fingerId) {
497 continue; 575 continue;
498 } 576 }
577 if (touch->pinchId) {
578 endPinch_TouchState_(d, touch->pinchId);
579 break;
580 }
499 if (touch->edgeDragging) { 581 if (touch->edgeDragging) {
500 setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse); 582 setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse);
501 } 583 }
@@ -574,6 +656,28 @@ iBool processEvent_Touch(const SDL_Event *ev) {
574 return iTrue; 656 return iTrue;
575} 657}
576 658
659void widgetDestroyed_Touch(iWidget *widget) {
660 iTouchState *d = touchState_();
661 iForEach(Array, i, d->touches) {
662 iTouch *touch = i.value;
663 if (touch->affinity == widget) {
664 remove_ArrayIterator(&i);
665 }
666 }
667 iForEach(Array, p, d->pinches) {
668 iPinch *pinch = i.value;
669 if (pinch->affinity == widget) {
670 remove_ArrayIterator(&p);
671 }
672 }
673 iForEach(Array, m, d->moms) {
674 iMomentum *mom = m.value;
675 if (mom->affinity == widget) {
676 remove_ArrayIterator(&m);
677 }
678 }
679}
680
577size_t numFingers_Touch(void) { 681size_t numFingers_Touch(void) {
578 return size_Array(touchState_()->touches); 682 return size_Array(touchState_()->touches);
579} 683}