|
32 | 32 | typedef uint32_t IndigoHIDEdge; |
33 | 33 |
|
34 | 34 | typedef IndigoHIDMessage *(*DFIndigoHIDMessageForMouseNSEventFn)(CGPoint *location, CGPoint *windowLocation, uint32_t target, NSEventType type, NSSize displaySize, IndigoHIDEdge edge); |
| 35 | +typedef IndigoHIDMessage *(*DFIndigoHIDMessageForMouseNSEvent9Fn)(CGPoint *location, CGPoint *windowLocation, uint32_t target, uint32_t eventType, uint32_t direction, double unused1, double unused2, double widthPoints, double heightPoints); |
35 | 36 | typedef IndigoHIDMessage *(*DFIndigoHIDMessageForKeyboardArbitraryFn)(int keyCode, int op); |
36 | 37 | typedef IndigoHIDMessage *(*DFIndigoHIDMessageForKeyboardNSEventFn)(NSEvent *event); |
37 | 38 | typedef IndigoHIDMessage *(*DFIndigoHIDMessageForButtonFn)(uint32_t buttonCode, uint32_t operation, uint32_t target); |
38 | 39 | typedef IndigoHIDMessage *(*DFIndigoHIDMessageForHIDArbitraryFn)(uint32_t target, uint32_t page, uint32_t usage, uint32_t operation); |
| 40 | +typedef IndigoHIDMessage *(*DFIndigoHIDServiceMessageFn)(void); |
39 | 41 |
|
40 | 42 | #pragma pack(push, 4) |
41 | 43 | typedef struct { |
|
91 | 93 | static const uint32_t DFIndigoTouchTarget = 0x32; |
92 | 94 | static const uint8_t DFIndigoEventTypeTouch = 0x02; |
93 | 95 | static const uint32_t DFIndigoTouchEventKind = 0x0b; |
| 96 | +static const uint32_t DFIndigoMouseEventDown = 1; |
| 97 | +static const uint32_t DFIndigoMouseEventUp = 2; |
| 98 | +static const uint32_t DFIndigoMouseEventDragged = 6; |
| 99 | +static const uint32_t DFIndigoMouseDirectionDown = 1; |
| 100 | +static const uint32_t DFIndigoMouseDirectionMove = 0; |
| 101 | +static const uint32_t DFIndigoMouseDirectionUp = 2; |
94 | 102 | static const int DFKeyboardDirectionDown = 1; |
95 | 103 | static const int DFKeyboardDirectionUp = 2; |
96 | 104 | static const uint32_t DFButtonDirectionDown = 1; |
@@ -187,6 +195,36 @@ static BOOL DFVerboseTouchLoggingEnabled(void) { |
187 | 195 | return enabled; |
188 | 196 | } |
189 | 197 |
|
| 198 | +static BOOL DFShouldUseIndigoMouse9Path(void) { |
| 199 | + static BOOL enabled = NO; |
| 200 | + static dispatch_once_t onceToken; |
| 201 | + dispatch_once(&onceToken, ^{ |
| 202 | + NSString *override = NSProcessInfo.processInfo.environment[@"SIMDECK_INDIGO_MOUSE_9ARG"]; |
| 203 | + if (override.length > 0) { |
| 204 | + enabled = [override isEqualToString:@"1"] || |
| 205 | + [override caseInsensitiveCompare:@"true"] == NSOrderedSame || |
| 206 | + [override caseInsensitiveCompare:@"yes"] == NSOrderedSame; |
| 207 | + return; |
| 208 | + } |
| 209 | + |
| 210 | + FILE *pipe = popen("/usr/bin/xcodebuild -version 2>/dev/null", "r"); |
| 211 | + if (pipe == NULL) { |
| 212 | + return; |
| 213 | + } |
| 214 | + char buffer[256] = {0}; |
| 215 | + if (fgets(buffer, sizeof(buffer), pipe) != NULL) { |
| 216 | + NSString *line = [[NSString stringWithUTF8String:buffer] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; |
| 217 | + NSArray<NSString *> *parts = [line componentsSeparatedByString:@" "]; |
| 218 | + if (parts.count >= 2 && [parts[0] isEqualToString:@"Xcode"]) { |
| 219 | + NSInteger major = [[parts[1] componentsSeparatedByString:@"."].firstObject integerValue]; |
| 220 | + enabled = major >= 26; |
| 221 | + } |
| 222 | + } |
| 223 | + pclose(pipe); |
| 224 | + }); |
| 225 | + return enabled; |
| 226 | +} |
| 227 | + |
190 | 228 | #pragma mark - SimulatorKit Swift symbol resolver |
191 | 229 | // |
192 | 230 | // We call into SimulatorKit's private Swift API by dlsym'ing mangled symbol |
@@ -1002,6 +1040,109 @@ static BOOL DFSendHIDMessage(id hidClient, IndigoHIDMessage *message, BOOL freeW |
1002 | 1040 | return YES; |
1003 | 1041 | } |
1004 | 1042 |
|
| 1043 | +static uint32_t DFIndigoMouseEventTypeForPhase(DFPrivateSimulatorTouchPhase phase) { |
| 1044 | + switch (phase) { |
| 1045 | + case DFPrivateSimulatorTouchPhaseBegan: |
| 1046 | + return DFIndigoMouseEventDown; |
| 1047 | + case DFPrivateSimulatorTouchPhaseMoved: |
| 1048 | + return DFIndigoMouseEventDragged; |
| 1049 | + case DFPrivateSimulatorTouchPhaseEnded: |
| 1050 | + case DFPrivateSimulatorTouchPhaseCancelled: |
| 1051 | + return DFIndigoMouseEventUp; |
| 1052 | + } |
| 1053 | +} |
| 1054 | + |
| 1055 | +static uint32_t DFIndigoMouseDirectionForPhase(DFPrivateSimulatorTouchPhase phase) { |
| 1056 | + switch (phase) { |
| 1057 | + case DFPrivateSimulatorTouchPhaseBegan: |
| 1058 | + return DFIndigoMouseDirectionDown; |
| 1059 | + case DFPrivateSimulatorTouchPhaseMoved: |
| 1060 | + return DFIndigoMouseDirectionMove; |
| 1061 | + case DFPrivateSimulatorTouchPhaseEnded: |
| 1062 | + case DFPrivateSimulatorTouchPhaseCancelled: |
| 1063 | + return DFIndigoMouseDirectionUp; |
| 1064 | + } |
| 1065 | +} |
| 1066 | + |
| 1067 | +static IndigoHIDMessage *DFCreateIndigoTouchMessage9(CGPoint normalizedPoint, NSSize displaySize, DFPrivateSimulatorTouchPhase phase) { |
| 1068 | + if (!DFShouldUseIndigoMouse9Path()) { |
| 1069 | + return NULL; |
| 1070 | + } |
| 1071 | + DFIndigoHIDMessageForMouseNSEvent9Fn mouseMessage = (DFIndigoHIDMessageForMouseNSEvent9Fn)dlsym(RTLD_DEFAULT, "IndigoHIDMessageForMouseNSEvent"); |
| 1072 | + if (mouseMessage == NULL) { |
| 1073 | + return NULL; |
| 1074 | + } |
| 1075 | + |
| 1076 | + CGPoint ratioPoint = CGPointMake( |
| 1077 | + fmax(0.0, fmin(1.0, normalizedPoint.x)), |
| 1078 | + fmax(0.0, fmin(1.0, normalizedPoint.y)) |
| 1079 | + ); |
| 1080 | + return mouseMessage(&ratioPoint, |
| 1081 | + NULL, |
| 1082 | + DFIndigoTouchTarget, |
| 1083 | + DFIndigoMouseEventTypeForPhase(phase), |
| 1084 | + DFIndigoMouseDirectionForPhase(phase), |
| 1085 | + 1.0, |
| 1086 | + 1.0, |
| 1087 | + displaySize.width, |
| 1088 | + displaySize.height); |
| 1089 | +} |
| 1090 | + |
| 1091 | +static IndigoHIDMessage *DFCreateIndigoMultiTouchMessage9(CGPoint normalizedPoint1, CGPoint normalizedPoint2, NSSize displaySize, DFPrivateSimulatorTouchPhase phase) { |
| 1092 | + if (!DFShouldUseIndigoMouse9Path()) { |
| 1093 | + return NULL; |
| 1094 | + } |
| 1095 | + DFIndigoHIDMessageForMouseNSEvent9Fn mouseMessage = (DFIndigoHIDMessageForMouseNSEvent9Fn)dlsym(RTLD_DEFAULT, "IndigoHIDMessageForMouseNSEvent"); |
| 1096 | + if (mouseMessage == NULL) { |
| 1097 | + return NULL; |
| 1098 | + } |
| 1099 | + |
| 1100 | + CGPoint ratioPoint = CGPointMake( |
| 1101 | + fmax(0.0, fmin(1.0, normalizedPoint1.x)), |
| 1102 | + fmax(0.0, fmin(1.0, normalizedPoint1.y)) |
| 1103 | + ); |
| 1104 | + CGPoint secondRatioPoint = CGPointMake( |
| 1105 | + fmax(0.0, fmin(1.0, normalizedPoint2.x)), |
| 1106 | + fmax(0.0, fmin(1.0, normalizedPoint2.y)) |
| 1107 | + ); |
| 1108 | + return mouseMessage(&ratioPoint, |
| 1109 | + &secondRatioPoint, |
| 1110 | + DFIndigoTouchTarget, |
| 1111 | + DFIndigoMouseEventTypeForPhase(phase), |
| 1112 | + DFIndigoMouseDirectionForPhase(phase), |
| 1113 | + 1.0, |
| 1114 | + 1.0, |
| 1115 | + displaySize.width, |
| 1116 | + displaySize.height); |
| 1117 | +} |
| 1118 | + |
| 1119 | +static void DFWarmIndigoHIDServices(id hidClient) { |
| 1120 | + if (hidClient == nil) { |
| 1121 | + return; |
| 1122 | + } |
| 1123 | + |
| 1124 | + DFIndigoHIDServiceMessageFn createPointer = (DFIndigoHIDServiceMessageFn)dlsym(RTLD_DEFAULT, "IndigoHIDMessageToCreatePointerService"); |
| 1125 | + DFIndigoHIDServiceMessageFn createMouse = (DFIndigoHIDServiceMessageFn)dlsym(RTLD_DEFAULT, "IndigoHIDMessageToCreateMouseService"); |
| 1126 | + NSError *error = nil; |
| 1127 | + if (createPointer != NULL) { |
| 1128 | + IndigoHIDMessage *message = createPointer(); |
| 1129 | + if (message != NULL) { |
| 1130 | + (void)DFSendHIDMessage(hidClient, message, YES, &error); |
| 1131 | + usleep(20 * 1000); |
| 1132 | + } |
| 1133 | + } |
| 1134 | + if (createMouse != NULL) { |
| 1135 | + IndigoHIDMessage *message = createMouse(); |
| 1136 | + if (message != NULL) { |
| 1137 | + (void)DFSendHIDMessage(hidClient, message, YES, &error); |
| 1138 | + usleep(20 * 1000); |
| 1139 | + } |
| 1140 | + } |
| 1141 | + if (error != nil) { |
| 1142 | + DFLog(@"Indigo HID service warm-up reported: %@", error.localizedDescription ?: @"unknown error"); |
| 1143 | + } |
| 1144 | +} |
| 1145 | + |
1005 | 1146 | static DFIndigoMessage *DFCreateIndigoTouchMessage(CGPoint normalizedPoint, NSSize displaySize, BOOL touchDown, NSError **error) { |
1006 | 1147 | DFIndigoHIDMessageForMouseNSEventFn mouseMessage = (DFIndigoHIDMessageForMouseNSEventFn)dlsym(RTLD_DEFAULT, "IndigoHIDMessageForMouseNSEvent"); |
1007 | 1148 | if (mouseMessage == NULL) { |
@@ -2378,6 +2519,7 @@ - (nullable instancetype)initWithUDID:(NSString *)udid |
2378 | 2519 |
|
2379 | 2520 | if (_hidClient != nil) { |
2380 | 2521 | DFLog(@"Created private SimulatorKit HID client for %@", udid); |
| 2522 | + DFWarmIndigoHIDServices(_hidClient); |
2381 | 2523 | } else { |
2382 | 2524 | DFLog(@"Failed to create private SimulatorKit HID client for %@: %@", udid, hidClientError.localizedDescription ?: @"unknown error"); |
2383 | 2525 | } |
@@ -3311,14 +3453,18 @@ - (BOOL)sendTouchAtNormalizedX:(double)normalizedX |
3311 | 3453 | phaseLabel = phase == DFPrivateSimulatorTouchPhaseEnded ? @"ended" : @"cancelled"; |
3312 | 3454 | break; |
3313 | 3455 | } |
3314 | | - BOOL touchDown = phase == DFPrivateSimulatorTouchPhaseBegan || phase == DFPrivateSimulatorTouchPhaseMoved; |
3315 | | - DFIndigoMessage *message = DFCreateIndigoTouchMessage(CGPointMake(clampedX, clampedY), displaySize, touchDown, &dispatchError); |
| 3456 | + IndigoHIDMessage *message = DFCreateIndigoTouchMessage9(CGPointMake(clampedX, clampedY), displaySize, phase); |
| 3457 | + BOOL freeWhenDone = YES; |
3316 | 3458 | if (message == NULL) { |
3317 | | - return; |
| 3459 | + BOOL touchDown = phase == DFPrivateSimulatorTouchPhaseBegan || phase == DFPrivateSimulatorTouchPhaseMoved; |
| 3460 | + message = (IndigoHIDMessage *)DFCreateIndigoTouchMessage(CGPointMake(clampedX, clampedY), displaySize, touchDown, &dispatchError); |
| 3461 | + if (message == NULL) { |
| 3462 | + return; |
| 3463 | + } |
3318 | 3464 | } |
3319 | 3465 |
|
3320 | 3466 | NSError *messageError = nil; |
3321 | | - if (!DFSendHIDMessage(self->_hidClient, (IndigoHIDMessage *)message, YES, &messageError)) { |
| 3467 | + if (!DFSendHIDMessage(self->_hidClient, message, freeWhenDone, &messageError)) { |
3322 | 3468 | dispatchError = messageError ?: DFMakeError( |
3323 | 3469 | DFPrivateSimulatorErrorCodeTouchDispatchFailed, |
3324 | 3470 | @"SimulatorKit rejected the Indigo HID touch packet." |
@@ -3394,14 +3540,25 @@ - (BOOL)sendMultiTouchAtNormalizedX1:(double)normalizedX1 |
3394 | 3540 | break; |
3395 | 3541 | } |
3396 | 3542 |
|
3397 | | - BOOL touchDown = phase == DFPrivateSimulatorTouchPhaseBegan || phase == DFPrivateSimulatorTouchPhaseMoved; |
3398 | | - DFIndigoMessage *message = DFCreateIndigoMultiTouchMessage(CGPointMake(x1, y1), CGPointMake(x2, y2), displaySize, touchDown, &dispatchError); |
| 3543 | + IndigoHIDMessage *message = NULL; |
| 3544 | + const NSUInteger maxAttempts = phase == DFPrivateSimulatorTouchPhaseMoved ? 12 : 3; |
| 3545 | + for (NSUInteger attempt = 0; attempt < maxAttempts; attempt++) { |
| 3546 | + message = DFCreateIndigoMultiTouchMessage9(CGPointMake(x1, y1), CGPointMake(x2, y2), displaySize, phase); |
| 3547 | + if (message != NULL) { |
| 3548 | + break; |
| 3549 | + } |
| 3550 | + usleep(5 * 1000); |
| 3551 | + } |
3399 | 3552 | if (message == NULL) { |
3400 | | - return; |
| 3553 | + BOOL touchDown = phase == DFPrivateSimulatorTouchPhaseBegan || phase == DFPrivateSimulatorTouchPhaseMoved; |
| 3554 | + message = (IndigoHIDMessage *)DFCreateIndigoMultiTouchMessage(CGPointMake(x1, y1), CGPointMake(x2, y2), displaySize, touchDown, &dispatchError); |
| 3555 | + if (message == NULL) { |
| 3556 | + return; |
| 3557 | + } |
3401 | 3558 | } |
3402 | 3559 |
|
3403 | 3560 | NSError *messageError = nil; |
3404 | | - if (!DFSendHIDMessage(self->_hidClient, (IndigoHIDMessage *)message, YES, &messageError)) { |
| 3561 | + if (!DFSendHIDMessage(self->_hidClient, message, YES, &messageError)) { |
3405 | 3562 | dispatchError = messageError ?: DFMakeError( |
3406 | 3563 | DFPrivateSimulatorErrorCodeTouchDispatchFailed, |
3407 | 3564 | @"SimulatorKit rejected the Indigo HID multi-touch packet." |
|
0 commit comments