Expose Apple Pencil input to testing system
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Sep 2016 23:32:08 +0000 (23:32 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 7 Sep 2016 23:32:08 +0000 (23:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161670
<rdar://problem/28183327>

Reviewed by Simon Fraser.

Tools:

Add API to UIScriptController that allows a test to emulate
stylus input, such as from the Apple Pencil.

The code is only implemented for WebKitTestRunner.

There are also four new tests that ensure an Apple Pencil
operates similarly to a regular touch. The main difference
is that you can't have multiple touches when using an Apple Pencil.

* DumpRenderTree/ios/UIScriptControllerIOS.mm: New methods. Empty implementations.
(WTR::UIScriptController::stylusDownAtPoint):
(WTR::UIScriptController::stylusMoveToPoint):
(WTR::UIScriptController::stylusUpAtPoint):
(WTR::UIScriptController::stylusTapAtPoint):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl: New methods.
* TestRunnerShared/UIScriptContext/UIScriptController.cpp: Empty implementations.
(WTR::UIScriptController::stylusDownAtPoint):
(WTR::UIScriptController::stylusMoveToPoint):
(WTR::UIScriptController::stylusUpAtPoint):
(WTR::UIScriptController::stylusTapAtPoint):
* TestRunnerShared/UIScriptContext/UIScriptController.h:

* WebKitTestRunner/ios/HIDEventGenerator.h:
* WebKitTestRunner/ios/HIDEventGenerator.mm: New StylusEvent types. Add some properties
to SyntheticEventDigitizerInfo.
(-[HIDEventGenerator _createIOHIDEventType:]): Add support for StylusEvent types.
(-[HIDEventGenerator touchDownAtPoints:touchCount:]): Clear any old stylus info.
(-[HIDEventGenerator stylusDownAtPoint:azimuthAngle:altitudeAngle:pressure:]):
(-[HIDEventGenerator stylusMoveToPoint:azimuthAngle:altitudeAngle:pressure:]):
(-[HIDEventGenerator stylusUpAtPoint:]):
(-[HIDEventGenerator stylusDownAtPoint:azimuthAngle:altitudeAngle:pressure:completionBlock:]):
(-[HIDEventGenerator stylusMoveToPoint:azimuthAngle:altitudeAngle:pressure:completionBlock:]):
(-[HIDEventGenerator stylusUpAtPoint:completionBlock:]):
(-[HIDEventGenerator stylusTapAtPoint:azimuthAngle:altitudeAngle:pressure:completionBlock:]):
* WebKitTestRunner/ios/IOKitSPI.h: Add new include, and new SPI.
* WebKitTestRunner/ios/UIScriptControllerIOS.mm: Calls into the HIDEventGenerator for the new API.
(WTR::UIScriptController::stylusDownAtPoint):
(WTR::UIScriptController::stylusMoveToPoint):
(WTR::UIScriptController::stylusUpAtPoint):
(WTR::UIScriptController::stylusTapAtPoint):

LayoutTests:

Four new tests that make sure Apple Pencil's register the correct
touch* style events.

* fast/events/touch/ios/pencil-down-gives-touchstart-expected.txt: Added.
* fast/events/touch/ios/pencil-down-gives-touchstart.html: Added.
* fast/events/touch/ios/pencil-move-gives-touchmove-expected.txt: Added.
* fast/events/touch/ios/pencil-move-gives-touchmove.html: Added.
* fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend-expected.txt: Added.
* fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend.html: Added.
* fast/events/touch/ios/pencil-up-gives-touchend-expected.txt: Added.
* fast/events/touch/ios/pencil-up-gives-touchend.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@205572 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/touch/ios/pencil-down-gives-touchstart-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/pencil-down-gives-touchstart.html [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/pencil-move-gives-touchmove-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/pencil-move-gives-touchmove.html [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend.html [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/pencil-up-gives-touchend-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/pencil-up-gives-touchend.html [new file with mode: 0644]
Tools/ChangeLog
Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/ios/HIDEventGenerator.h
Tools/WebKitTestRunner/ios/HIDEventGenerator.mm
Tools/WebKitTestRunner/ios/IOKitSPI.h
Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm

index 115a48e..34ca127 100644 (file)
@@ -1,3 +1,23 @@
+2016-09-06  Dean Jackson  <dino@apple.com>
+
+        Expose Apple Pencil input to testing system
+        https://bugs.webkit.org/show_bug.cgi?id=161670
+        <rdar://problem/28183327>
+
+        Reviewed by Simon Fraser.
+
+        Four new tests that make sure Apple Pencil's register the correct
+        touch* style events.
+
+        * fast/events/touch/ios/pencil-down-gives-touchstart-expected.txt: Added.
+        * fast/events/touch/ios/pencil-down-gives-touchstart.html: Added.
+        * fast/events/touch/ios/pencil-move-gives-touchmove-expected.txt: Added.
+        * fast/events/touch/ios/pencil-move-gives-touchmove.html: Added.
+        * fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend-expected.txt: Added.
+        * fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend.html: Added.
+        * fast/events/touch/ios/pencil-up-gives-touchend-expected.txt: Added.
+        * fast/events/touch/ios/pencil-up-gives-touchend.html: Added.
+
 2016-09-07  Andy VanWagoner  <thetalecrafter@gmail.com>
 
         [INTL] some valid language tags cause errors in Intl constructors
diff --git a/LayoutTests/fast/events/touch/ios/pencil-down-gives-touchstart-expected.txt b/LayoutTests/fast/events/touch/ios/pencil-down-gives-touchstart-expected.txt
new file mode 100644 (file)
index 0000000..0164b96
--- /dev/null
@@ -0,0 +1,11 @@
+Test that an Apple Pencil is registered as a touchstart.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+    touchstart fired.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/events/touch/ios/pencil-down-gives-touchstart.html b/LayoutTests/fast/events/touch/ios/pencil-down-gives-touchstart.html
new file mode 100644 (file)
index 0000000..7aef74c
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <script src="../../../../resources/js-test-pre.js"></script>
+    <style>
+        body {
+            margin: none;
+        }
+    </style>
+    <meta name="viewport" content="initial-scale=1">
+</head>
+<body>
+    <p id="description"></p>
+    <div id="console">
+    </div>
+    <script>
+        description("Test that an Apple Pencil is registered as a touchstart.");
+        window.jsTestIsAsync = true;
+
+        function getUIScript(x, y)
+        {
+            return `
+            (function() {
+                uiController.stylusDownAtPoint(${x}, ${y}, 0, 0, 0, function () {});
+                uiController.stylusUpAtPoint(${x}, ${y}, function () {});
+            })();`
+        }
+
+        function runTest()
+        {
+            window.addEventListener("touchstart", (event) => {
+                debug("touchstart fired.");
+                finishJSTest();
+            });
+
+            if (window.testRunner)
+                testRunner.runUIScript(getUIScript(50, 200), function(result) { });
+        }
+
+        window.addEventListener("load", runTest, false);
+    </script>
+    <script src="../../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/touch/ios/pencil-move-gives-touchmove-expected.txt b/LayoutTests/fast/events/touch/ios/pencil-move-gives-touchmove-expected.txt
new file mode 100644 (file)
index 0000000..89527c8
--- /dev/null
@@ -0,0 +1,13 @@
+Test that Apple Pencil moves are captured.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+    touchstart fired.
+touchmove fired.
+touchend fired.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/events/touch/ios/pencil-move-gives-touchmove.html b/LayoutTests/fast/events/touch/ios/pencil-move-gives-touchmove.html
new file mode 100644 (file)
index 0000000..dfbf8fa
--- /dev/null
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <script src="../../../../resources/js-test-pre.js"></script>
+    <style>
+        body {
+            margin: none;
+        }
+    </style>
+    <meta name="viewport" content="initial-scale=1">
+</head>
+<body>
+    <p id="description"></p>
+    <div id="console">
+    </div>
+    <script>
+        description("Test that Apple Pencil moves are captured.");
+        window.jsTestIsAsync = true;
+
+        function getUIScript(x, y)
+        {
+            return `
+            (function() {
+                uiController.stylusDownAtPoint(${x}, ${y}, 0, 0, 0, function () {});
+                uiController.stylusMoveToPoint(${x + 10}, ${y + 10}, 0, 0, 0, function () {});
+                uiController.stylusUpAtPoint(${x + 10}, ${y + 10}, function () {});
+            })();`
+        }
+
+        function runTest()
+        {
+            window.addEventListener("touchstart", (event) => {
+                debug("touchstart fired.");
+            });
+
+            window.addEventListener("touchmove", (event) => {
+                debug("touchmove fired.");
+            });
+
+            window.addEventListener("touchend", (event) => {
+                debug("touchend fired.");
+                finishJSTest();
+            });
+
+            if (window.testRunner)
+                testRunner.runUIScript(getUIScript(50, 200), function(result) { });
+        }
+
+        window.addEventListener("load", runTest, false);
+    </script>
+    <script src="../../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend-expected.txt b/LayoutTests/fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend-expected.txt
new file mode 100644 (file)
index 0000000..a03918e
--- /dev/null
@@ -0,0 +1,12 @@
+Test that an Apple Pencil tap is registered.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+    touchstart fired.
+touchend fired.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend.html b/LayoutTests/fast/events/touch/ios/pencil-tap-gives-touchstart-and-touchend.html
new file mode 100644 (file)
index 0000000..e37c623
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <script src="../../../../resources/js-test-pre.js"></script>
+    <style>
+        body {
+            margin: none;
+        }
+    </style>
+    <meta name="viewport" content="initial-scale=1">
+</head>
+<body>
+    <p id="description"></p>
+    <div id="console">
+    </div>
+    <script>
+        description("Test that an Apple Pencil tap is registered.");
+        window.jsTestIsAsync = true;
+
+        function getUIScript(x, y)
+        {
+            return `
+            (function() {
+                uiController.stylusTapAtPoint(${x}, ${y}, 0, 0, 0, function () {});
+            })();`
+        }
+
+        function runTest()
+        {
+            window.addEventListener("touchstart", (event) => {
+                debug("touchstart fired.");
+            });
+
+            window.addEventListener("touchend", (event) => {
+                debug("touchend fired.");
+                finishJSTest();
+            });
+
+            if (window.testRunner)
+                testRunner.runUIScript(getUIScript(50, 200), function(result) { });
+        }
+
+        window.addEventListener("load", runTest, false);
+    </script>
+    <script src="../../../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/touch/ios/pencil-up-gives-touchend-expected.txt b/LayoutTests/fast/events/touch/ios/pencil-up-gives-touchend-expected.txt
new file mode 100644 (file)
index 0000000..a72de57
--- /dev/null
@@ -0,0 +1,11 @@
+Test that an Apple Pencil lift is registered as a touchend.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+    touchend fired.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/events/touch/ios/pencil-up-gives-touchend.html b/LayoutTests/fast/events/touch/ios/pencil-up-gives-touchend.html
new file mode 100644 (file)
index 0000000..2f92669
--- /dev/null
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <script src="../../../../resources/js-test-pre.js"></script>
+    <style>
+        body {
+            margin: none;
+        }
+    </style>
+    <meta name="viewport" content="initial-scale=1">
+</head>
+<body>
+    <p id="description"></p>
+    <div id="console">
+    </div>
+    <script>
+        description("Test that an Apple Pencil lift is registered as a touchend.");
+        window.jsTestIsAsync = true;
+
+        function getUIScript(x, y)
+        {
+            return `
+            (function() {
+                uiController.stylusDownAtPoint(${x}, ${y}, 0, 0, 0, function() {});
+                uiController.stylusUpAtPoint(${x}, ${y}, function() {
+                    uiController.uiScriptComplete("Dispatched Stylus Event");
+                });
+            })();`
+        }
+
+        function runTest()
+        {
+            window.addEventListener("touchend", (event) => {
+                debug("touchend fired.");
+                finishJSTest();
+            });
+
+            if (window.testRunner)
+                testRunner.runUIScript(getUIScript(50, 200), function(result) { });
+        }
+
+        window.addEventListener("load", runTest, false);
+    </script>
+    <script src="../../../../resources/js-test-post.js"></script>
+</body>
+</html>
index 784f6df..78f07ce 100644 (file)
@@ -1,3 +1,52 @@
+2016-09-06  Dean Jackson  <dino@apple.com>
+
+        Expose Apple Pencil input to testing system
+        https://bugs.webkit.org/show_bug.cgi?id=161670
+        <rdar://problem/28183327>
+
+        Reviewed by Simon Fraser.
+
+        Add API to UIScriptController that allows a test to emulate
+        stylus input, such as from the Apple Pencil.
+
+        The code is only implemented for WebKitTestRunner.
+
+        There are also four new tests that ensure an Apple Pencil
+        operates similarly to a regular touch. The main difference
+        is that you can't have multiple touches when using an Apple Pencil.
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm: New methods. Empty implementations.
+        (WTR::UIScriptController::stylusDownAtPoint):
+        (WTR::UIScriptController::stylusMoveToPoint):
+        (WTR::UIScriptController::stylusUpAtPoint):
+        (WTR::UIScriptController::stylusTapAtPoint):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl: New methods.
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp: Empty implementations.
+        (WTR::UIScriptController::stylusDownAtPoint):
+        (WTR::UIScriptController::stylusMoveToPoint):
+        (WTR::UIScriptController::stylusUpAtPoint):
+        (WTR::UIScriptController::stylusTapAtPoint):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+
+        * WebKitTestRunner/ios/HIDEventGenerator.h:
+        * WebKitTestRunner/ios/HIDEventGenerator.mm: New StylusEvent types. Add some properties
+        to SyntheticEventDigitizerInfo.
+        (-[HIDEventGenerator _createIOHIDEventType:]): Add support for StylusEvent types.
+        (-[HIDEventGenerator touchDownAtPoints:touchCount:]): Clear any old stylus info.
+        (-[HIDEventGenerator stylusDownAtPoint:azimuthAngle:altitudeAngle:pressure:]):
+        (-[HIDEventGenerator stylusMoveToPoint:azimuthAngle:altitudeAngle:pressure:]):
+        (-[HIDEventGenerator stylusUpAtPoint:]):
+        (-[HIDEventGenerator stylusDownAtPoint:azimuthAngle:altitudeAngle:pressure:completionBlock:]):
+        (-[HIDEventGenerator stylusMoveToPoint:azimuthAngle:altitudeAngle:pressure:completionBlock:]):
+        (-[HIDEventGenerator stylusUpAtPoint:completionBlock:]):
+        (-[HIDEventGenerator stylusTapAtPoint:azimuthAngle:altitudeAngle:pressure:completionBlock:]):
+        * WebKitTestRunner/ios/IOKitSPI.h: Add new include, and new SPI.
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm: Calls into the HIDEventGenerator for the new API.
+        (WTR::UIScriptController::stylusDownAtPoint):
+        (WTR::UIScriptController::stylusMoveToPoint):
+        (WTR::UIScriptController::stylusUpAtPoint):
+        (WTR::UIScriptController::stylusTapAtPoint):
+
 2016-09-07  Daniel Bates  <dabates@apple.com>
 
         Update WebKitSystemInterface
index cba5238..50cce44 100644 (file)
@@ -88,6 +88,22 @@ void UIScriptController::dragFromPointToPoint(long startX, long startY, long end
 {
 }
 
+void UIScriptController::stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+}
+
+void UIScriptController::stylusMoveToPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+}
+
+void UIScriptController::stylusUpAtPoint(long x, long y, JSValueRef callback)
+{
+}
+
+void UIScriptController::stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+}
+
 void UIScriptController::typeCharacterUsingHardwareKeyboard(JSStringRef character, JSValueRef callback)
 {
 }
index 13df6e8..1c6623f 100644 (file)
@@ -38,6 +38,11 @@ interface UIScriptController {
     void doubleTapAtPoint(long x, long y, object callback);
     void dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, object callback);
 
+    void stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, object callback);
+    void stylusMoveToPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, object callback);
+    void stylusUpAtPoint(long x, long y, object callback);
+    void stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, object callback);
+
     void typeCharacterUsingHardwareKeyboard(DOMString character, object callback);
     void keyDownUsingHardwareKeyboard(DOMString character, object callback);
     void keyUpUsingHardwareKeyboard(DOMString character, object callback);
index 1005cbb..610f97d 100644 (file)
@@ -160,6 +160,22 @@ void UIScriptController::dragFromPointToPoint(long startX, long startY, long end
 {
 }
 
+void UIScriptController::stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+}
+
+void UIScriptController::stylusMoveToPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+}
+
+void UIScriptController::stylusUpAtPoint(long x, long y, JSValueRef callback)
+{
+}
+
+void UIScriptController::stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+}
+
 void UIScriptController::typeCharacterUsingHardwareKeyboard(JSStringRef, JSValueRef)
 {
 }
index 8463ac2..42255da 100644 (file)
@@ -56,7 +56,12 @@ public:
     void singleTapAtPoint(long x, long y, JSValueRef callback);
     void doubleTapAtPoint(long x, long y, JSValueRef callback);
     void dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, JSValueRef callback);
-    
+
+    void stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback);
+    void stylusMoveToPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback);
+    void stylusUpAtPoint(long x, long y, JSValueRef callback);
+    void stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback);
+
     void typeCharacterUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
     void keyDownUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
     void keyUpUsingHardwareKeyboard(JSStringRef character, JSValueRef callback);
index 96bf7f5..2c96fea 100644 (file)
 - (void)touchDown:(CGPoint)location touchCount:(NSUInteger)count completionBlock:(void (^)(void))completionBlock;
 - (void)liftUp:(CGPoint)location touchCount:(NSUInteger)count completionBlock:(void (^)(void))completionBlock;
 
+// Stylus
+- (void)stylusDownAtPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure completionBlock:(void (^)(void))completionBlock;
+- (void)stylusMoveToPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure completionBlock:(void (^)(void))completionBlock;
+- (void)stylusUpAtPoint:(CGPoint)location completionBlock:(void (^)(void))completionBlock;
+- (void)stylusTapAtPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure completionBlock:(void (^)(void))completionBlock;
+
 // Taps
 - (void)tap:(CGPoint)location completionBlock:(void (^)(void))completionBlock;
 - (void)doubleTap:(CGPoint)location completionBlock:(void (^)(void))completionBlock;
index 5cd33c7..84e6a38 100644 (file)
@@ -55,6 +55,9 @@ typedef enum {
     HandEventCanceled,
     HandEventInRange,
     HandEventInRangeLift,
+    StylusEventTouched,
+    StylusEventMoved,
+    StylusEventLifted,
 } HandEventType;
 
 typedef struct {
@@ -63,6 +66,9 @@ typedef struct {
     IOHIDFloat pathMajorRadius;
     IOHIDFloat pathPressure;
     UInt8 pathProximity;
+    BOOL isStylus;
+    IOHIDFloat azimuthAngle;
+    IOHIDFloat altitudeAngle;
 } SyntheticEventDigitizerInfo;
 
 static CFTimeInterval secondsSinceAbsoluteTime(CFAbsoluteTime startTime)
@@ -142,7 +148,7 @@ static void delayBetweenMove(int eventIndex, double elapsed)
 
 - (IOHIDEventRef)_createIOHIDEventType:(HandEventType)eventType
 {
-    BOOL isTouching = (eventType == HandEventTouched || eventType == HandEventMoved || eventType == HandEventChordChanged);
+    BOOL isTouching = (eventType == HandEventTouched || eventType == HandEventMoved || eventType == HandEventChordChanged || eventType == StylusEventTouched || eventType == StylusEventMoved);
 
     IOHIDDigitizerEventMask eventMask = kIOHIDDigitizerEventTouch;
     if (eventType == HandEventMoved) {
@@ -182,7 +188,7 @@ static void delayBetweenMove(int eventIndex, double elapsed)
                 pointInfo->pathPressure = defaultPathPressure;
             if (!pointInfo->pathProximity)
                 pointInfo->pathProximity = kGSEventPathInfoInTouch | kGSEventPathInfoInRange;
-        } else if (eventType == HandEventLifted || eventType == HandEventCanceled) {
+        } else if (eventType == HandEventLifted || eventType == HandEventCanceled || eventType == StylusEventLifted) {
             pointInfo->pathMajorRadius = 0;
             pointInfo->pathPressure = 0;
             pointInfo->pathProximity = 0;
@@ -190,18 +196,51 @@ static void delayBetweenMove(int eventIndex, double elapsed)
 
         CGPoint point = pointInfo->point;
         point = CGPointMake(roundf(point.x), roundf(point.y));
-        RetainPtr<IOHIDEventRef> subEvent = adoptCF(IOHIDEventCreateDigitizerFingerEvent(kCFAllocatorDefault, machTime,
-            pointInfo->identifier,
-            pointInfo->identifier,
-            eventMask,
-            point.x, point.y, 0,
-            pointInfo->pathPressure,
-            0,
-            pointInfo->pathProximity & kGSEventPathInfoInRange,
-            pointInfo->pathProximity & kGSEventPathInfoInTouch,
-            kIOHIDEventOptionNone));
+        RetainPtr<IOHIDEventRef> subEvent;
+        if (pointInfo->isStylus) {
+            if (eventType == StylusEventTouched) {
+                eventMask |= kIOHIDDigitizerEventEstimatedAltitude;
+                eventMask |= kIOHIDDigitizerEventEstimatedAzimuth;
+                eventMask |= kIOHIDDigitizerEventEstimatedPressure;
+            } else if (eventType == StylusEventMoved)
+                eventMask = kIOHIDDigitizerEventPosition;
+
+            subEvent = adoptCF(IOHIDEventCreateDigitizerStylusEventWithPolarOrientation(kCFAllocatorDefault, machTime,
+                pointInfo->identifier,
+                pointInfo->identifier,
+                eventMask,
+                0,
+                point.x, point.y, 0,
+                pointInfo->pathPressure,
+                pointInfo->pathPressure,
+                0,
+                pointInfo->altitudeAngle,
+                pointInfo->azimuthAngle,
+                1,
+                0,
+                isTouching ? kIOHIDTransducerTouch : 0));
+
+            if (eventType == StylusEventTouched)
+                IOHIDEventSetIntegerValue(subEvent.get(), kIOHIDEventFieldDigitizerWillUpdateMask, 0x0400);
+            else if (eventType == StylusEventMoved)
+                IOHIDEventSetIntegerValue(subEvent.get(), kIOHIDEventFieldDigitizerDidUpdateMask, 0x0400);
+
+        } else {
+            subEvent = adoptCF(IOHIDEventCreateDigitizerFingerEvent(kCFAllocatorDefault, machTime,
+                pointInfo->identifier,
+                pointInfo->identifier,
+                eventMask,
+                point.x, point.y, 0,
+                pointInfo->pathPressure,
+                0,
+                pointInfo->pathProximity & kGSEventPathInfoInRange,
+                pointInfo->pathProximity & kGSEventPathInfoInTouch,
+                kIOHIDEventOptionNone));
+        }
+
         IOHIDEventSetFloatValue(subEvent.get(), kIOHIDEventFieldDigitizerMajorRadius, pointInfo->pathMajorRadius);
         IOHIDEventSetFloatValue(subEvent.get(), kIOHIDEventFieldDigitizerMinorRadius, pointInfo->pathMajorRadius);
+
         IOHIDEventAppendEvent(eventRef.get(), subEvent.get(), 0);
     }
 
@@ -284,8 +323,10 @@ static void delayBetweenMove(int eventIndex, double elapsed)
 
     _activePointCount = touchCount;
 
-    for (NSUInteger index = 0; index < touchCount; ++index)
+    for (NSUInteger index = 0; index < touchCount; ++index) {
         _activePoints[index].point = locations[index];
+        _activePoints[index].isStylus = NO;
+    }
 
     RetainPtr<IOHIDEventRef> eventRef = adoptCF([self _createIOHIDEventType:HandEventTouched]);
     [self _sendHIDEvent:eventRef.get()];
@@ -382,6 +423,74 @@ static void delayBetweenMove(int eventIndex, double elapsed)
     [self _sendMarkerHIDEventWithCompletionBlock:completionBlock];
 }
 
+- (void)stylusDownAtPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure
+{
+    _activePointCount = 1;
+    _activePoints[0].point = location;
+    _activePoints[0].isStylus = YES;
+    _activePoints[0].pathPressure = pressure;
+    _activePoints[0].azimuthAngle = azimuthAngle;
+    _activePoints[0].altitudeAngle = altitudeAngle;
+
+    RetainPtr<IOHIDEventRef> eventRef = adoptCF([self _createIOHIDEventType:StylusEventTouched]);
+    [self _sendHIDEvent:eventRef.get()];
+}
+
+- (void)stylusMoveToPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure
+{
+    _activePointCount = 1;
+    _activePoints[0].point = location;
+    _activePoints[0].isStylus = YES;
+    _activePoints[0].pathPressure = pressure;
+    _activePoints[0].azimuthAngle = azimuthAngle;
+    _activePoints[0].altitudeAngle = altitudeAngle;
+
+    RetainPtr<IOHIDEventRef> eventRef = adoptCF([self _createIOHIDEventType:StylusEventMoved]);
+    [self _sendHIDEvent:eventRef.get()];
+}
+
+- (void)stylusUpAtPoint:(CGPoint)location
+{
+    _activePointCount = 1;
+    _activePoints[0].point = location;
+    _activePoints[0].isStylus = YES;
+    _activePoints[0].pathPressure = 0;
+    _activePoints[0].azimuthAngle = 0;
+    _activePoints[0].altitudeAngle = 0;
+
+    RetainPtr<IOHIDEventRef> eventRef = adoptCF([self _createIOHIDEventType:StylusEventLifted]);
+    [self _sendHIDEvent:eventRef.get()];
+}
+
+- (void)stylusDownAtPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure completionBlock:(void (^)(void))completionBlock
+{
+    [self stylusDownAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure];
+    [self _sendMarkerHIDEventWithCompletionBlock:completionBlock];
+}
+
+- (void)stylusMoveToPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure completionBlock:(void (^)(void))completionBlock
+{
+    [self stylusMoveToPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure];
+    [self _sendMarkerHIDEventWithCompletionBlock:completionBlock];
+}
+
+- (void)stylusUpAtPoint:(CGPoint)location completionBlock:(void (^)(void))completionBlock
+{
+    [self stylusUpAtPoint:location];
+    [self _sendMarkerHIDEventWithCompletionBlock:completionBlock];
+}
+
+- (void)stylusTapAtPoint:(CGPoint)location azimuthAngle:(CGFloat)azimuthAngle altitudeAngle:(CGFloat)altitudeAngle pressure:(CGFloat)pressure completionBlock:(void (^)(void))completionBlock
+{
+    struct timespec pressDelay = { 0, static_cast<long>(fingerLiftDelay) };
+
+    [self stylusDownAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure];
+    nanosleep(&pressDelay, 0);
+    [self stylusUpAtPoint:location];
+
+    [self _sendMarkerHIDEventWithCompletionBlock:completionBlock];
+}
+
 - (void)sendTaps:(int)tapCount location:(CGPoint)location withNumberOfTouches:(int)touchCount completionBlock:(void (^)(void))completionBlock
 {
     struct timespec doubleDelay = { 0, static_cast<long>(multiTapInterval) };
index 7a0d6a1..7e652a4 100644 (file)
@@ -33,6 +33,7 @@
 #if USE(APPLE_INTERNAL_SDK)
 
 #import <IOKit/hid/IOHIDEvent.h>
+#import <IOKit/hid/IOHIDEventData.h>
 #import <IOKit/hid/IOHIDEventSystemClient.h>
 #import <IOKit/hid/IOHIDUsageTables.h>
 
@@ -72,6 +73,9 @@ enum {
     kIOHIDDigitizerEventAttribute   = 1<<6,
     kIOHIDDigitizerEventCancel      = 1<<7,
     kIOHIDDigitizerEventStart       = 1<<8,
+    kIOHIDDigitizerEventEstimatedAltitude = 1<<28,
+    kIOHIDDigitizerEventEstimatedAzimuth = 1<<29,
+    kIOHIDDigitizerEventEstimatedPressure = 1<<30,
 };
 typedef uint32_t IOHIDDigitizerEventMask;
 
@@ -100,10 +104,22 @@ enum {
 };
 
 enum {
+    kIOHIDTransducerRange               = 0x00010000,
+    kIOHIDTransducerTouch               = 0x00020000,
+    kIOHIDTransducerInvert              = 0x00040000,
+    kIOHIDTransducerDisplayIntegrated   = 0x00080000
+};
+
+enum {
     kIOHIDDigitizerTransducerTypeHand = 3
 };
 typedef uint32_t IOHIDDigitizerTransducerType;
 
+enum {
+    kIOHIDEventFieldDigitizerWillUpdateMask = 720924,
+    kIOHIDEventFieldDigitizerDidUpdateMask = 720925
+};
+
 IOHIDEventRef IOHIDEventCreateDigitizerEvent(CFAllocatorRef, uint64_t, IOHIDDigitizerTransducerType, uint32_t, uint32_t, IOHIDDigitizerEventMask, uint32_t, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, boolean_t, boolean_t, IOOptionBits);
 
 IOHIDEventRef IOHIDEventCreateDigitizerFingerEvent(CFAllocatorRef, uint64_t, uint32_t, uint32_t, IOHIDDigitizerEventMask, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, boolean_t, boolean_t, IOHIDEventOptionBits);
@@ -114,6 +130,8 @@ IOHIDEventRef IOHIDEventCreateKeyboardEvent(CFAllocatorRef, uint64_t, uint32_t,
 
 IOHIDEventRef IOHIDEventCreateVendorDefinedEvent(CFAllocatorRef, uint64_t, uint32_t, uint32_t, uint32_t, uint8_t*, CFIndex, IOHIDEventOptionBits);
 
+IOHIDEventRef IOHIDEventCreateDigitizerStylusEventWithPolarOrientation(CFAllocatorRef, uint64_t, uint32_t, uint32_t, IOHIDDigitizerEventMask, uint32_t, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, IOHIDFloat, boolean_t, boolean_t, IOHIDEventOptionBits);
+
 IOHIDEventType IOHIDEventGetType(IOHIDEventRef);
 
 void IOHIDEventSetFloatValue(IOHIDEventRef, IOHIDEventField, IOHIDFloat);
index 4f70ae6..bd7ec00 100644 (file)
@@ -126,6 +126,54 @@ void UIScriptController::doubleTapAtPoint(long x, long y, JSValueRef callback)
     }];
 }
 
+void UIScriptController::stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+
+    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
+    [[HIDEventGenerator sharedHIDEventGenerator] stylusDownAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
+        if (!m_context)
+            return;
+        m_context->asyncTaskComplete(callbackID);
+    }];
+}
+
+void UIScriptController::stylusMoveToPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+
+    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
+    [[HIDEventGenerator sharedHIDEventGenerator] stylusMoveToPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
+        if (!m_context)
+            return;
+        m_context->asyncTaskComplete(callbackID);
+    }];
+}
+
+void UIScriptController::stylusUpAtPoint(long x, long y, JSValueRef callback)
+{
+    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+
+    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
+    [[HIDEventGenerator sharedHIDEventGenerator] stylusUpAtPoint:location completionBlock:^{
+        if (!m_context)
+            return;
+        m_context->asyncTaskComplete(callbackID);
+    }];
+}
+
+void UIScriptController::stylusTapAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, JSValueRef callback)
+{
+    unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+
+    auto location = globalToContentCoordinates(TestController::singleton().mainWebView()->platformView(), x, y);
+    [[HIDEventGenerator sharedHIDEventGenerator] stylusTapAtPoint:location azimuthAngle:azimuthAngle altitudeAngle:altitudeAngle pressure:pressure completionBlock:^{
+        if (!m_context)
+            return;
+        m_context->asyncTaskComplete(callbackID);
+    }];
+}
+
 void UIScriptController::dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, JSValueRef callback)
 {
     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);