serve-sim 0.1.49 → 0.1.50-beta.145.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/Sources/SimCameraInjector/SimCamFakes.h +1 -0
- package/Sources/SimCameraInjector/SimCamFakes.m +15 -0
- package/Sources/SimCameraInjector/SimCamSwizzles.m +17 -13
- package/Sources/SimDuoHID/main.m +53 -1
- package/dist/middleware.js +46 -46
- package/dist/serve-sim.js +70 -70
- package/dist/simcam/libSimCameraInjector.dylib +0 -0
- package/dist/simduo/serve-sim-duo-hid +0 -0
- package/package.json +1 -1
- package/src/device-session.ts +2 -1
- package/src/duo-state.ts +53 -9
- package/src/native.ts +6 -1
package/README.md
CHANGED
|
@@ -54,7 +54,9 @@ serve-sim fold 100 -d <udid>
|
|
|
54
54
|
|
|
55
55
|
Commands wait for the guest to acknowledge dispatch. Invalid angles and failed
|
|
56
56
|
commands do not change capture. The bundled `simduo/serve-sim-duo-hid` executable
|
|
57
|
-
runs inside the simulator and exits with the device session.
|
|
57
|
+
runs inside the simulator and exits with the device session. Its `watch-angle` mode
|
|
58
|
+
reads the hinge angle inside the simulator from CoreMotion, so it doesn't depend on
|
|
59
|
+
`devicectl`. That is how the preview learns the current angle and follows Device Hub.
|
|
58
60
|
|
|
59
61
|
Duo uses one interactive RealityKit preview of the installed Xcode model, with
|
|
60
62
|
live screen textures and touches mapped to each bent screen half. Apple's model
|
|
@@ -69,6 +69,7 @@ void SimCamLogSwallowedRuntimeError(NSString *via, id object, NSDictionary *user
|
|
|
69
69
|
|
|
70
70
|
AVCaptureDeviceFormat *SimCamSharedFakeFormat(void);
|
|
71
71
|
AVCaptureDevice *SimCamFakeDeviceForPosition(AVCaptureDevicePosition p);
|
|
72
|
+
NSArray<AVCaptureDevice *> *SimCamFakeDevices(void);
|
|
72
73
|
AVCaptureDeviceInput *SimCamFakeInputForPosition(AVCaptureDevicePosition p);
|
|
73
74
|
NSArray<AVCaptureInputPort *> *SimCamFakePortsForInput(AVCaptureInput *input);
|
|
74
75
|
|
|
@@ -143,6 +143,14 @@ void SimCamLogSwallowedRuntimeError(NSString *via, id object, NSDictionary *user
|
|
|
143
143
|
- (BOOL)isVideoBinned { return NO; }
|
|
144
144
|
- (BOOL)isVideoHDRSupported { return NO; }
|
|
145
145
|
- (BOOL)isVideoStabilizationModeSupported:(AVCaptureVideoStabilizationMode)m { return NO; }
|
|
146
|
+
// AVFoundation's description reads private state this instance doesn't have.
|
|
147
|
+
- (NSString *)description {
|
|
148
|
+
CMFormatDescriptionRef format = self.formatDescription;
|
|
149
|
+
CMVideoDimensions size = CMVideoFormatDescriptionGetDimensions(format);
|
|
150
|
+
FourCharCode subtype = CMFormatDescriptionGetMediaSubType(format);
|
|
151
|
+
return [NSString stringWithFormat:@"<SimCamFakeFormat %dx%d %c%c%c%c>", size.width, size.height,
|
|
152
|
+
(char)(subtype >> 24), (char)(subtype >> 16), (char)(subtype >> 8), (char)subtype];
|
|
153
|
+
}
|
|
146
154
|
- (CGFloat)videoMaxZoomFactor { return 16.0; }
|
|
147
155
|
- (CGFloat)videoZoomFactorUpscaleThreshold { return 1.0; }
|
|
148
156
|
- (AVCaptureAutoFocusSystem)autoFocusSystem { return AVCaptureAutoFocusSystemNone; }
|
|
@@ -293,6 +301,13 @@ AVCaptureDevice *SimCamFakeDeviceForPosition(AVCaptureDevicePosition p) {
|
|
|
293
301
|
return p == AVCaptureDevicePositionBack ? back : front;
|
|
294
302
|
}
|
|
295
303
|
|
|
304
|
+
NSArray<AVCaptureDevice *> *SimCamFakeDevices(void) {
|
|
305
|
+
return @[
|
|
306
|
+
SimCamFakeDeviceForPosition(AVCaptureDevicePositionFront),
|
|
307
|
+
SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack),
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
|
|
296
311
|
#pragma mark - SimCamFakeConnection
|
|
297
312
|
|
|
298
313
|
@interface AVCaptureConnection (SimCamPrivate)
|
|
@@ -129,22 +129,24 @@ BOOL SwizzleInstanceMethod(Class cls, SEL orig, SEL swiz) {
|
|
|
129
129
|
}
|
|
130
130
|
return [self simcam_defaultDeviceWithMediaType:m];
|
|
131
131
|
}
|
|
132
|
+
+ (AVCaptureDevice *)simcam_deviceWithUniqueID:(NSString *)uniqueID {
|
|
133
|
+
for (AVCaptureDevice *fake in SimCamFakeDevices()) {
|
|
134
|
+
if ([fake.uniqueID isEqualToString:uniqueID]) {
|
|
135
|
+
simcam_log(@"deviceWithUniqueID: %@ → fake", uniqueID);
|
|
136
|
+
return fake;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return [self simcam_deviceWithUniqueID:uniqueID];
|
|
140
|
+
}
|
|
132
141
|
+ (NSArray<AVCaptureDevice *> *)simcam_devicesWithMediaType:(AVMediaType)m {
|
|
133
142
|
if ([m isEqualToString:AVMediaTypeVideo]) {
|
|
134
|
-
return
|
|
135
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionFront),
|
|
136
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack),
|
|
137
|
-
];
|
|
143
|
+
return SimCamFakeDevices();
|
|
138
144
|
}
|
|
139
145
|
return [self simcam_devicesWithMediaType:m];
|
|
140
146
|
}
|
|
141
147
|
+ (NSArray<AVCaptureDevice *> *)simcam_devices {
|
|
142
148
|
NSArray *real = [self simcam_devices];
|
|
143
|
-
|
|
144
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionFront),
|
|
145
|
-
SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack),
|
|
146
|
-
];
|
|
147
|
-
return [fakes arrayByAddingObjectsFromArray:real ?: @[]];
|
|
149
|
+
return [SimCamFakeDevices() arrayByAddingObjectsFromArray:real ?: @[]];
|
|
148
150
|
}
|
|
149
151
|
@end
|
|
150
152
|
|
|
@@ -160,10 +162,9 @@ BOOL SwizzleInstanceMethod(Class cls, SEL orig, SEL swiz) {
|
|
|
160
162
|
[self simcam_discoverySessionWithDeviceTypes:types mediaType:m position:p];
|
|
161
163
|
if ([m isEqualToString:AVMediaTypeVideo] || m == nil) {
|
|
162
164
|
NSMutableArray *list = [NSMutableArray new];
|
|
163
|
-
|
|
164
|
-
[list addObject:
|
|
165
|
-
|
|
166
|
-
[list addObject:SimCamFakeDeviceForPosition(AVCaptureDevicePositionBack)];
|
|
165
|
+
for (AVCaptureDevice *fake in SimCamFakeDevices()) {
|
|
166
|
+
if (p == AVCaptureDevicePositionUnspecified || p == fake.position) [list addObject:fake];
|
|
167
|
+
}
|
|
167
168
|
@try {
|
|
168
169
|
[real setValue:list forKey:@"devices"];
|
|
169
170
|
} @catch (__unused id e) {
|
|
@@ -1221,6 +1222,9 @@ void SimCamInstallSwizzles(void) {
|
|
|
1221
1222
|
@selector(devicesWithMediaType:),
|
|
1222
1223
|
@selector(simcam_devicesWithMediaType:));
|
|
1223
1224
|
SwizzleClassMethod(dev, @selector(devices), @selector(simcam_devices));
|
|
1225
|
+
SwizzleClassMethod(dev,
|
|
1226
|
+
@selector(deviceWithUniqueID:),
|
|
1227
|
+
@selector(simcam_deviceWithUniqueID:));
|
|
1224
1228
|
|
|
1225
1229
|
Class disc = [AVCaptureDeviceDiscoverySession class];
|
|
1226
1230
|
SwizzleClassMethod(disc,
|
package/Sources/SimDuoHID/main.m
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Guest HID transport for hinge angles, physical orientation, and hardware buttons.
|
|
2
2
|
// Spawned inside iOS Simulator; one command/reply per line until EOF.
|
|
3
|
+
// `watch-angle` instead streams the hinge angle the guest reports (see watchAngle).
|
|
3
4
|
#import <Foundation/Foundation.h>
|
|
4
5
|
#import <dlfcn.h>
|
|
5
6
|
#import <mach/mach_time.h>
|
|
@@ -8,6 +9,55 @@
|
|
|
8
9
|
#import <math.h>
|
|
9
10
|
#import <unistd.h>
|
|
10
11
|
|
|
12
|
+
// CoreMotion's private hinge-angle API, the source UIKit's UIHinge reads.
|
|
13
|
+
@interface CMAngle : NSObject
|
|
14
|
+
- (float)angleDegrees;
|
|
15
|
+
@end
|
|
16
|
+
@interface CMAngleManager : NSObject
|
|
17
|
+
+ (BOOL)isAvailable;
|
|
18
|
+
- (void)startAngleUpdatesToQueue:(NSOperationQueue *)queue handler:(void (^)(CMAngle *angle))handler;
|
|
19
|
+
- (void)stopAngleUpdates;
|
|
20
|
+
@end
|
|
21
|
+
|
|
22
|
+
// Prints `angle <degrees>` for the first sample and for every later change of at
|
|
23
|
+
// least 0.5°, until the parent closes stdin. Read-only: it never creates the HID
|
|
24
|
+
// services, so it can run next to the command transport. Reading in the guest
|
|
25
|
+
// keeps the hinge independent of CoreDevice: `devicectl device motion` exists in
|
|
26
|
+
// devicectl 651.13.4 (Xcode 27.1 beta, 27A9269) but not in 651.13.5 (Xcode 27.1
|
|
27
|
+
// 27A9275) or later (seen through 651.32, Xcode 27.2 27B52), and CoreDevice is
|
|
28
|
+
// shared by every installed Xcode.
|
|
29
|
+
static int watchAngle(void) {
|
|
30
|
+
dlopen("/System/Library/Frameworks/CoreMotion.framework/CoreMotion", RTLD_NOW);
|
|
31
|
+
Class managerClass = NSClassFromString(@"CMAngleManager");
|
|
32
|
+
if (!managerClass || ![managerClass respondsToSelector:@selector(isAvailable)] || ![managerClass isAvailable]) {
|
|
33
|
+
fprintf(stderr, "Hinge angle unavailable: CMAngleManager reports no hinge\n");
|
|
34
|
+
return 2;
|
|
35
|
+
}
|
|
36
|
+
CMAngleManager *manager = [managerClass new];
|
|
37
|
+
if (![manager respondsToSelector:@selector(startAngleUpdatesToQueue:handler:)]) {
|
|
38
|
+
fprintf(stderr, "Hinge angle unavailable: CMAngleManager has no angle updates\n");
|
|
39
|
+
return 2;
|
|
40
|
+
}
|
|
41
|
+
NSOperationQueue *queue = [NSOperationQueue new];
|
|
42
|
+
queue.maxConcurrentOperationCount = 1;
|
|
43
|
+
__block double last = NAN;
|
|
44
|
+
[manager startAngleUpdatesToQueue:queue handler:^(CMAngle *sample) {
|
|
45
|
+
double degrees = [sample angleDegrees];
|
|
46
|
+
if (!isfinite(degrees) || degrees < 0 || degrees > 180) return;
|
|
47
|
+
if (isfinite(last) && fabs(degrees - last) < 0.5) return;
|
|
48
|
+
last = degrees;
|
|
49
|
+
if (degrees == 0) degrees = 0; // never print -0.0
|
|
50
|
+
printf("angle %.1f\n", degrees);
|
|
51
|
+
fflush(stdout);
|
|
52
|
+
}];
|
|
53
|
+
// Updates arrive on `queue`, not the main run loop, so the main thread only
|
|
54
|
+
// waits for the parent to close stdin.
|
|
55
|
+
char buffer[64];
|
|
56
|
+
while (read(STDIN_FILENO, buffer, sizeof buffer) > 0) {}
|
|
57
|
+
[manager stopAngleUpdates];
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
11
61
|
@interface DuoServiceDelegate : NSObject
|
|
12
62
|
@property NSDictionary *properties;
|
|
13
63
|
@end
|
|
@@ -87,8 +137,10 @@ static BOOL sendEvent(id service, CFTypeRef event) {
|
|
|
87
137
|
CFRelease(event);
|
|
88
138
|
return ok;
|
|
89
139
|
}
|
|
90
|
-
int main(
|
|
140
|
+
int main(int argc, char **argv) {
|
|
91
141
|
@autoreleasepool {
|
|
142
|
+
if (argc == 2 && strcmp(argv[1], "watch-angle") == 0) return watchAngle();
|
|
143
|
+
if (argc != 1) { fprintf(stderr, "usage: serve-sim-duo-hid [watch-angle]\n"); return 64; }
|
|
92
144
|
dlopen("/System/Library/PrivateFrameworks/HID.framework/HID", RTLD_NOW);
|
|
93
145
|
void *io = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW);
|
|
94
146
|
CFTypeRef (*vendor)(CFAllocatorRef, uint64_t, uint32_t, uint32_t, uint32_t, const void *, CFIndex, uint32_t) = dlsym(io, "IOHIDEventCreateVendorDefinedEvent");
|