e675396e18f4deb35ec82db98619254560b65e2a
[WebKit-https.git] / Tools / TestRunnerShared / EventSerialization / mac / EventSerializerMac.mm
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "EventSerializerMac.h"
28
29 #if PLATFORM(MAC)
30
31 #import "CoreGraphicsSPI.h"
32 #import "IOKitSPI.h"
33 #import <algorithm>
34 #import <mach/mach_time.h>
35 #import <wtf/RetainPtr.h>
36
37 #define MOUSE_EVENT_TYPES \
38     (CGSEventType)kCGEventLeftMouseDown, \
39     (CGSEventType)kCGEventLeftMouseUp, \
40     (CGSEventType)kCGEventRightMouseDown, \
41     (CGSEventType)kCGEventRightMouseUp, \
42     (CGSEventType)kCGEventMouseMoved, \
43     (CGSEventType)kCGEventLeftMouseDragged, \
44     (CGSEventType)kCGEventRightMouseDragged, \
45     (CGSEventType)kCGEventOtherMouseDown, \
46     (CGSEventType)kCGEventOtherMouseUp, \
47     (CGSEventType)kCGEventOtherMouseDragged
48
49 #define KEY_EVENT_TYPES \
50     (CGSEventType)kCGEventKeyDown, \
51     (CGSEventType)kCGEventKeyUp, \
52     (CGSEventType)kCGEventFlagsChanged
53
54 #define GESTURE_EVENT_TYPES \
55     kCGSEventGesture, \
56     kCGSEventFluidTouchGesture, \
57     kCGSEventDockControl
58
59 bool eventIsOfType(CGEventRef event, CGSEventType type)
60 {
61     return (CGSEventType)CGEventGetType(event) == type;
62 }
63
64 bool eventIsOfTypes(CGEventRef) { return false; }
65
66 template<typename ... Types>
67 bool eventIsOfTypes(CGEventRef event, CGSEventType first, Types ... rest)
68 {
69     return eventIsOfType(event, first) || eventIsOfTypes(event, rest...);
70 }
71
72 bool eventIsOfGestureType(CGEventRef event, IOHIDEventType type)
73 {
74     return (IOHIDEventType)CGEventGetIntegerValueField(event, kCGEventGestureHIDType) == type;
75 }
76
77 bool eventIsOfGestureTypes(CGEventRef) { return false; }
78
79 template<typename ... Types>
80 bool eventIsOfGestureTypes(CGEventRef event, IOHIDEventType first, Types ... rest)
81 {
82     if (!eventIsOfTypes(event, GESTURE_EVENT_TYPES))
83         return false;
84     return eventIsOfGestureType(event, first) || eventIsOfGestureTypes(event, rest...);
85 }
86
87 #define FOR_EACH_CGEVENT_INTEGER_FIELD(macro) \
88     macro(true, kCGSEventTypeField) \
89     \
90     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventDeltaAxis1) \
91     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventDeltaAxis2) \
92     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventDeltaAxis3) \
93     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventIsContinuous) \
94     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventMomentumPhase) \
95     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventPointDeltaAxis1) \
96     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventPointDeltaAxis2) \
97     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventPointDeltaAxis3) \
98     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventScrollCount) \
99     macro(eventIsOfTypes(rawEvent, kCGSEventScrollWheel, kCGSEventZoom), kCGScrollWheelEventScrollPhase) \
100     \
101     macro(eventIsOfTypes(rawEvent, MOUSE_EVENT_TYPES), kCGMouseEventButtonNumber) \
102     macro(eventIsOfTypes(rawEvent, MOUSE_EVENT_TYPES), kCGMouseEventClickState) \
103     macro(eventIsOfTypes(rawEvent, MOUSE_EVENT_TYPES), kCGMouseEventDeltaX) \
104     macro(eventIsOfTypes(rawEvent, MOUSE_EVENT_TYPES), kCGMouseEventDeltaY) \
105     macro(eventIsOfTypes(rawEvent, MOUSE_EVENT_TYPES), kCGMouseEventSubtype) \
106     macro(eventIsOfTypes(rawEvent, MOUSE_EVENT_TYPES), kCGMouseEventNumber) \
107     \
108     macro(eventIsOfTypes(rawEvent, KEY_EVENT_TYPES), kCGKeyboardEventAutorepeat) \
109     macro(eventIsOfTypes(rawEvent, KEY_EVENT_TYPES), kCGKeyboardEventKeyboardType) \
110     macro(eventIsOfTypes(rawEvent, KEY_EVENT_TYPES), kCGKeyboardEventKeycode) \
111     \
112     macro(eventIsOfTypes(rawEvent, GESTURE_EVENT_TYPES), kCGEventGestureHIDType) \
113     macro(eventIsOfTypes(rawEvent, GESTURE_EVENT_TYPES), kCGEventGestureBehavior) \
114     macro(eventIsOfTypes(rawEvent, GESTURE_EVENT_TYPES), kCGEventGestureFlavor) \
115     macro(eventIsOfTypes(rawEvent, GESTURE_EVENT_TYPES), kCGEventGestureMask) \
116     macro(eventIsOfTypes(rawEvent, GESTURE_EVENT_TYPES), kCGEventGesturePhase) \
117     macro(eventIsOfTypes(rawEvent, GESTURE_EVENT_TYPES), kCGEventGestureStage) \
118     \
119     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeScroll), kCGEventScrollGestureFlagBits) \
120     \
121     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventSwipeGestureFlagBits) \
122     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipeMask) \
123     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipeMotion) \
124     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipeValue) \
125     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureFlavor) \
126     \
127     macro(eventIsOfGestureTypes(rawEvent, kCGHIDEventTypeGestureStarted, kCGHIDEventTypeGestureEnded), kCGEventGestureStartEndSeriesType)
128
129 #define FOR_EACH_CGEVENT_DOUBLE_FIELD(macro) \
130     macro(eventIsOfTypes(rawEvent, GESTURE_EVENT_TYPES), kCGEventGestureProgress) \
131     \
132     macro(eventIsOfTypes(rawEvent, MOUSE_EVENT_TYPES), kCGMouseEventPressure) \
133     \
134     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeZoom), kCGEventGestureZoomDeltaX) \
135     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeZoom), kCGEventGestureZoomDeltaY) \
136     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeZoom), kCGEventGestureZoomValue) \
137     \
138     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeRotation), kCGEventGestureRotationValue) \
139     \
140     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeScroll), kCGEventGestureScrollX) \
141     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeScroll), kCGEventGestureScrollY) \
142     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeScroll), kCGEventGestureScrollZ) \
143     \
144     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipePositionX) \
145     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipePositionY) \
146     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipeProgress) \
147     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipeVelocityX) \
148     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipeVelocityY) \
149     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeNavigationSwipe), kCGEventGestureSwipeVelocityZ) \
150     \
151     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeForce), kCGEventTransitionProgress) \
152     macro(eventIsOfGestureType(rawEvent, kIOHIDEventTypeForce), kCGEventStagePressure)
153
154 #define LOAD_INTEGER_FIELD_FROM_EVENT(eventTypeFilter, field) \
155 ^ { \
156     if (!(eventTypeFilter)) \
157         return; \
158     int64_t value = CGEventGetIntegerValueField(rawEvent, field); \
159     int64_t plainValue = CGEventGetIntegerValueField(rawPlainEvent, field); \
160     if (value != plainValue) \
161         dict[@#field] = @(value); \
162 }();
163
164 #define LOAD_DOUBLE_FIELD_FROM_EVENT(eventTypeFilter, field) \
165 ^ { \
166     if (!(eventTypeFilter)) \
167         return; \
168     double value = CGEventGetDoubleValueField(rawEvent, field); \
169     if (!isnan(value)) { \
170         double plainValue = CGEventGetDoubleValueField(rawPlainEvent, field); \
171         if (fabs(value - plainValue) >= FLT_EPSILON) \
172             dict[@#field] = @(value); \
173     } \
174 }();
175
176 #define STORE_INTEGER_FIELD_TO_EVENT(eventTypeFilter, field) \
177 ^ { \
178     if (!(eventTypeFilter)) \
179         return; \
180     NSNumber *value = dict[@#field]; \
181     if (value) \
182         CGEventSetIntegerValueField(rawEvent, field, value.unsignedLongLongValue); \
183 }();
184
185 #define STORE_DOUBLE_FIELD_TO_EVENT(eventTypeFilter, field) \
186 ^ { \
187     if (!(eventTypeFilter)) \
188         return; \
189     NSNumber *value = dict[@#field]; \
190     if (value) \
191         CGEventSetDoubleValueField(rawEvent, field, value.doubleValue); \
192 }();
193
194 @implementation EventSerializer
195
196 + (NSDictionary *)dictionaryForEvent:(CGEventRef)rawEvent relativeToTime:(CGEventTimestamp)referenceTimestamp
197 {
198     RetainPtr<CGEventRef> plainEvent = adoptCF(CGEventCreate(NULL));
199     CGEventRef rawPlainEvent = plainEvent.get();
200
201     NSMutableDictionary *dict = [[[NSMutableDictionary alloc] init] autorelease];
202
203     FOR_EACH_CGEVENT_INTEGER_FIELD(LOAD_INTEGER_FIELD_FROM_EVENT);
204     FOR_EACH_CGEVENT_DOUBLE_FIELD(LOAD_DOUBLE_FIELD_FROM_EVENT);
205
206     CGEventTimestamp timestamp = CGEventGetTimestamp(rawEvent);
207     dict[@"relativeTimeMS"] = @(std::max(static_cast<double>(timestamp - referenceTimestamp) / NSEC_PER_MSEC, 0.0));
208
209     CGSEventType eventType = (CGSEventType)CGEventGetIntegerValueField(rawEvent, kCGSEventTypeField);
210     if (eventType == kCGSEventGesture || eventType == kCGSEventFluidTouchGesture || eventType == kCGSEventDockControl) {
211         if (CGEventGetIntegerValueField(rawEvent, kCGEventGestureIsPreflight)) {
212             dict[@"kCGEventGestureIsPreflight"] = @YES;
213             dict[@"kCGEventGesturePreflightProgress"] = @(CGEventGetDoubleValueField(rawEvent, kCGEventGesturePreflightProgress));
214         }
215     }
216
217     dict[@"windowLocation"] = NSStringFromPoint(NSPointFromCGPoint(CGEventGetWindowLocation(rawEvent)));
218
219     auto flags = static_cast<CGEventFlags>(CGEventGetFlags(rawEvent) & ~NX_NONCOALSESCEDMASK);
220     auto plainFlags = static_cast<CGEventFlags>(CGEventGetFlags(rawPlainEvent) & ~NX_NONCOALSESCEDMASK);
221     if (flags != plainFlags)
222         dict[@"flags"] = @(flags);
223
224     return dict;
225 }
226
227 + (RetainPtr<CGEventRef>)createEventForDictionary:(NSDictionary *)dict inWindow:(NSWindow *)window relativeToTime:(CGEventTimestamp)referenceTimestamp
228 {
229     RetainPtr<CGEventRef> event = adoptCF(CGEventCreate(NULL));
230     CGEventRef rawEvent = event.get();
231
232     FOR_EACH_CGEVENT_INTEGER_FIELD(STORE_INTEGER_FIELD_TO_EVENT);
233     FOR_EACH_CGEVENT_DOUBLE_FIELD(STORE_DOUBLE_FIELD_TO_EVENT);
234
235     if (dict[@"relativeTimeMS"])
236         CGEventSetTimestamp(event.get(), referenceTimestamp + static_cast<CGEventTimestamp>(([dict[@"relativeTimeMS"] doubleValue] * NSEC_PER_MSEC)));
237
238     if ([dict[@"kCGEventGestureIsPreflight"] boolValue]) {
239         CGEventSetIntegerValueField(rawEvent, kCGEventGestureIsPreflight, 1);
240         CGEventSetDoubleValueField(rawEvent, kCGEventGesturePreflightProgress, [dict[@"kCGEventGesturePreflightProgress"] doubleValue]);
241     }
242
243     if (dict[@"windowLocation"]) {
244         CGPoint windowLocation = NSPointToCGPoint(NSPointFromString(dict[@"windowLocation"]));
245         CGEventSetWindowLocation(rawEvent, windowLocation);
246
247         NSPoint screenPoint = [window convertRectToScreen:NSMakeRect(windowLocation.x, windowLocation.y, 1, 1)].origin;
248         CGEventSetLocation(rawEvent, CGPointMake(screenPoint.x, NSScreen.screens.firstObject.frame.size.height - screenPoint.y));
249     }
250
251     if (dict[@"flags"])
252         CGEventSetFlags(rawEvent, static_cast<CGEventFlags>([dict[@"flags"] unsignedLongLongValue]));
253
254     CGEventSetIntegerValueField(rawEvent, kCGSEventWindowIDField, window.windowNumber);
255
256     return event;
257 }
258
259 @end
260
261 @implementation EventStreamPlayer
262
263 const float eventDispatchTimerRate = 1. / 120.;
264
265 + (void)playStream:(NSArray<NSDictionary *> *)eventDicts window:(NSWindow *)window completionHandler:(void(^)())completionHandler
266 {
267     RetainPtr<EventStreamPlayer> player = adoptNS([[EventStreamPlayer alloc] init]);
268
269     player->_remainingEventDictionaries = adoptNS([eventDicts mutableCopy]);
270     player->_window = window;
271
272     if (completionHandler)
273         player->_completionHandler = makeBlockPtr(completionHandler);
274
275     player->_startTime = mach_absolute_time();
276
277     [NSTimer scheduledTimerWithTimeInterval:eventDispatchTimerRate target:player.get() selector:@selector(playbackTimerFired:) userInfo:nil repeats:YES];
278 }
279
280 - (void)playbackTimerFired:(NSTimer *)timer
281 {
282     auto removeList = adoptNS([[NSMutableArray alloc] init]);
283     NSEvent *nsEvent = nil;
284     for (id eventDict in _remainingEventDictionaries.get()) {
285         RetainPtr<CGEventRef> event = [EventSerializer createEventForDictionary:eventDict inWindow:_window.get() relativeToTime:_startTime];
286         if (CGEventGetTimestamp(event.get()) < mach_absolute_time()) {
287             nsEvent = [NSEvent eventWithCGEvent:event.get()];
288             [NSApp postEvent:nsEvent atStart:NO];
289             [removeList addObject:eventDict];
290         }
291     }
292     [_remainingEventDictionaries removeObjectsInArray:removeList.get()];
293
294     if ([_remainingEventDictionaries count])
295         return;
296
297 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
298     NSEventType applicationDefinedEventType = NSEventTypeApplicationDefined;
299 #else
300     NSEventType applicationDefinedEventType = NSApplicationDefined;
301 #endif
302     [NSApp postEvent:[NSEvent otherEventWithType:applicationDefinedEventType location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 context:0 subtype:0 data1:42 data2:0] atStart:NO];
303     // Block until we send the last event we posted.
304     while (true) {
305         NSEvent *nextEvent = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:eventDispatchTimerRate] inMode:NSDefaultRunLoopMode dequeue:YES];
306         if (nextEvent.type == applicationDefinedEventType && nextEvent.data1 == 42)
307             break;
308         if (nextEvent)
309             [NSApp sendEvent:nextEvent];
310     }
311
312     if (_completionHandler)
313         _completionHandler();
314
315     [timer invalidate];
316 }
317
318 @end
319
320 #endif