Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Touch (Part 2 - Multitouch) #283

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
44bc834
initial setup
janek Sep 5, 2018
a8d0a35
changed timestamp to Double in Kotlin, removed unused func in GLRenderer
janek Sep 5, 2018
96ca5e5
cosmetics - removed some comments, removed a typo
janek Sep 5, 2018
3c026ec
removed some whitespaces
janek Sep 5, 2018
d6b8453
retuned hand-made velocity factor from 0.75 to 0.4
janek Sep 7, 2018
c3f67eb
Merge branch 'master' into improve-touch
rikner Oct 16, 2018
962bcca
pass timestamp as Long instead of Double, use @_cdecl for onNativeTouch
rikner Oct 16, 2018
47e5c58
move TouchValues data class and MotionEvent extension declaration to …
rikner Oct 18, 2018
24c4364
update SDL (onNativeTouch removed)
rikner Oct 18, 2018
d7bd6c6
Merge branch 'master' into improve-touch
rikner Oct 18, 2018
95633dc
cleanup SDLOnTouchListener: rename p to pressure, rename time to time…
rikner Oct 18, 2018
be81e50
Merge branch 'master' into improve-touch
rikner Oct 26, 2018
23b73b3
return to SDL master (not removing onNativeTouch)
rikner Oct 26, 2018
3a84c6b
rename onNativeTouch to onNativeTouchUIKit to prevent conflict with S…
rikner Oct 26, 2018
4a056fc
Merge branch 'master' into improve-touch
rikner Oct 31, 2018
bb665c5
check for os(Android) instead of !os(macOS)
michaelknoch Oct 31, 2018
0373454
lint swift files
rikner Oct 31, 2018
d7bbf59
Merge branch 'improve-touch' of github.com:flowkey/UIKit-SDL into imp…
rikner Oct 31, 2018
13ef559
refactor UIEvent.description using map()
rikner Oct 31, 2018
9a847e9
update to SDL version ignoring .DS_Store
rikner Nov 7, 2018
c104af8
Merge branch 'master' into improve-touch-timestamps
rikner Mar 4, 2019
c2bd742
tune UIScrollView deceleration timing (as seen in b6c6d5a)
rikner Mar 4, 2019
28b4b71
Fix multitouch
ephemer Nov 5, 2018
5bb40a3
remove UIEvent+fromSDL_Event.swift from xcode project (Android only),…
rikner Mar 4, 2019
a7719cb
Merge branch 'master' into improve-touch-part2
rikner Feb 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/UIApplication+handleSDLEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ extension UIApplication {
while SDL_PollEvent(&e) == 1 {

#if os(Android)
if let uievent = UIEvent.from(e) {
sendEvent(uievent)
if let uiEvent = UIEvent.from(e) {
sendEvent(uiEvent)
break
}
#endif
Expand Down
127 changes: 127 additions & 0 deletions Sources/UIEvent+fromSDL_Event.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//
// UIEvent+fromSDL_Event.swift
// UIKit
//
// Created by Geordie Jay on 05.11.18.
// Copyright © 2018 flowkey. All rights reserved.
//

import JNI
import SDL

extension UIEvent {
static func from(_ event: SDL_Event) -> UIEvent? {
switch SDL_EventType(event.type) {
case SDL_FINGERDOWN:
let newTouch = UITouch(
touchId: Int(event.tfinger.fingerId),
at: CGPoint(
x: CGFloat(event.tfinger.x),
y: CGFloat(event.tfinger.y)
),
timestamp: event.timestampInSeconds
)

guard let existingTouchEvent = UIEvent.activeEvents.first(where: { $0.allTouches != nil }) else {
return UIEvent(touch: newTouch)
}

assert(!existingTouchEvent.allTouches!.contains(where: { $0.touchId == event.tfinger.fingerId }),
"The active event already has a touch with this fingerId, this should be impossible")

existingTouchEvent.allTouches!.insert(newTouch)
return existingTouchEvent

case SDL_FINGERMOTION:
if
let existingTouchEvent = UIEvent.activeEvents.first(where: { $0.allTouches != nil }),
let matchingTouch = existingTouchEvent.allTouches?.first(where: { $0.touchId == event.tfinger.fingerId })
{
matchingTouch.timestamp = event.timestampInSeconds
matchingTouch.phase = .moved
matchingTouch.updateAbsoluteLocation(.from(event.tfinger))
return existingTouchEvent
} else {
assertionFailure("Got a motion event for a touch we know nothing about")
return nil
}

case SDL_FINGERUP:
if
let firstExistingEvent = UIEvent.activeEvents.first(where: { $0.allTouches != nil }),
let matchingTouch = firstExistingEvent.allTouches?.first(where: { $0.touchId == event.tfinger.fingerId })
{
matchingTouch.timestamp = event.timestampInSeconds
matchingTouch.phase = .ended
return firstExistingEvent
} else {
assertionFailure("Got a finger up event for a touch we know nothing about")
return nil
}

default:
return nil
}
}
}

extension UIEvent: CustomStringConvertible {
public var description: String {
let baseText = "UIEvent:"
guard let allTouches = self.allTouches else {
return baseText + " no touches"
}

return baseText + "\n" + allTouches.map { $0.description }.joined(separator: ",\n")
}
}

extension UITouch: CustomStringConvertible {
public var description: String {
return "TouchID: \(self.touchId), timestamp: \(self.timestamp)"
}
}

@_cdecl("Java_org_libsdl_app_SDLActivity_onNativeTouchUIKit")
public func onNativeTouch(
env: UnsafeMutablePointer<JNIEnv>,
view: JavaObject,
touchDeviceID: JavaInt,
pointerFingerID: JavaInt,
action: JavaInt,
x: JavaFloat,
y: JavaFloat,
pressure: JavaFloat,
timestamp: JavaLong
) {
guard let eventType = SDL_EventType.from(androidAction: action) else { return }

var event = SDL_Event(tfinger:
SDL_TouchFingerEvent(
type: eventType.rawValue,
timestamp: UInt32(timestamp), // the Android system provides timestamps in ms
touchId: Int64(touchDeviceID), // seems to remain constant but actual value depends on phone/tablet
fingerId: Int64(pointerFingerID),
x: x / Float(UIScreen.main.scale),
y: y / Float(UIScreen.main.scale),
dx: 0, // Not used in UIKit cross platform:
dy: 0, // Instead, we do these calculations in our UIGestureRecognizers etc.
pressure: pressure
)
)

// Add the event to SDL's event stack
// Don't use SDL_PushEvent because that overrides `event.timestamp` with its own:
SDL_PeepEvents(&event, 1, SDL_ADDEVENT, 0, 0)
}

extension SDL_EventType {
public static func from(androidAction: JavaInt) -> SDL_EventType? {
switch androidAction {
case 0, 5: return SDL_FINGERDOWN
case 1, 6: return SDL_FINGERUP
case 2: return SDL_FINGERMOTION
default: return nil
}
}
}
49 changes: 26 additions & 23 deletions Sources/UIWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,37 @@ public class UIWindow: UIView {
}

public func sendEvent(_ event: UIEvent) {
guard
let allTouches = event.allTouches,
let currentTouch = allTouches.first,
let hitView = currentTouch.view ?? hitTest(currentTouch.location(in: nil), with: nil)
else { return }
guard let allTouches = event.allTouches else { return }
UIEvent.activeEvents.insert(event)

switch currentTouch.phase {
case .began:
UIEvent.activeEvents.insert(event)
currentTouch.view = hitView
currentTouch.gestureRecognizers = hitView.getRecognizerHierachy()
for touch in allTouches {
guard let hitView = touch.view ?? hitTest(touch.location(in: nil), with: nil) else { return }

currentTouch.runTouchActionOnRecognizerHierachy { $0.touchesBegan(allTouches, with: event) }
hitView.touchesBegan(allTouches, with: event)
switch touch.phase {
case .began:
touch.view = hitView
touch.gestureRecognizers = hitView.getRecognizerHierachy()

case .moved:
currentTouch.runTouchActionOnRecognizerHierachy { $0.touchesMoved(allTouches, with: event) }
if !currentTouch.hasBeenCancelledByAGestureRecognizer {
hitView.touchesMoved(allTouches, with: event)
}
touch.runTouchActionOnRecognizerHierachy { $0.touchesBegan([touch], with: event) }
hitView.touchesBegan([touch], with: event)

case .ended:
currentTouch.runTouchActionOnRecognizerHierachy { $0.touchesEnded(allTouches, with: event) }
if !currentTouch.hasBeenCancelledByAGestureRecognizer {
hitView.touchesEnded(allTouches, with: event)
}
case .moved:
touch.runTouchActionOnRecognizerHierachy { $0.touchesMoved([touch], with: event) }
if !touch.hasBeenCancelledByAGestureRecognizer {
hitView.touchesMoved([touch], with: event)
}

UIEvent.activeEvents.remove(event)
case .ended:
touch.runTouchActionOnRecognizerHierachy { $0.touchesEnded([touch], with: event) }
if !touch.hasBeenCancelledByAGestureRecognizer {
hitView.touchesEnded([touch], with: event)
}

event.allTouches?.remove(touch)
if event.allTouches?.isEmpty == true {
UIEvent.activeEvents.remove(event)
}
}
}
}
}
Expand Down
11 changes: 0 additions & 11 deletions UIKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@
5C1F06AE1F3210F200EFF087 /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIViewController.swift; path = Sources/UIViewController.swift; sourceTree = SOURCE_ROOT; };
5C2714A11F2A3FC50026BBA9 /* SDL+JNIExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SDL+JNIExtensions.swift"; sourceTree = "<group>"; };
5C27E7AE1ECB2F6500F5020D /* UIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = UIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5C27E7B21ECB2F6500F5020D /* Mac-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Mac-Info.plist"; path = "../Sources/Mac-Info.plist"; sourceTree = "<group>"; };
5C27E7B71ECB2F6500F5020D /* UIKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5C27E7BE1ECB2F6500F5020D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5C27E7D21ECB2F9100F5020D /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -572,7 +571,6 @@
5C27E7B01ECB2F6500F5020D /* UIKit */,
5C27E7BB1ECB2F6500F5020D /* UIKitTests */,
83C18F391F0FB486008FEA07 /* Resources */,
5C27E7E61ECB2FE000F5020D /* zSupporting Files */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -700,15 +698,6 @@
name = Frameworks;
sourceTree = "<group>";
};
5C27E7E61ECB2FE000F5020D /* zSupporting Files */ = {
isa = PBXGroup;
children = (
5C27E7B21ECB2F6500F5020D /* Mac-Info.plist */,
);
name = "zSupporting Files";
path = UIKit;
sourceTree = "<group>";
};
5C27E7F51ECB301900F5020D /* SDL */ = {
isa = PBXGroup;
children = (
Expand Down