@@ -2015,6 +2015,67 @@ static BOOL DFPressHomeViaHIDClient(id hidClient, NSError **error) {
20152015 return NO ;
20162016}
20172017
2018+ static BOOL DFOpenAppSwitcherViaHIDClient (id hidClient, NSError **error) {
2019+ if (hidClient == nil ) {
2020+ if (error != NULL ) {
2021+ *error = DFMakeError (
2022+ DFPrivateSimulatorErrorCodeTouchDispatchFailed,
2023+ @" SimulatorKit did not provide a headless HID client for App Switcher."
2024+ );
2025+ }
2026+ return NO ;
2027+ }
2028+
2029+ static const DFHomeButtonHIDStrategy strategies[] = {
2030+ { " IndigoHIDMessageForHIDArbitrary page=0x0c usage=0x40 (Menu) target=0x32" , NO , 0 , DFConsumerControlUsagePage, 0x40 , DFIndigoTouchTarget },
2031+ { " IndigoHIDMessageForHIDArbitrary page=0x0c usage=0x65 (Home) target=0x32" , NO , 0 , DFConsumerControlUsagePage, DFHomeConsumerUsage, DFIndigoTouchTarget },
2032+ { " IndigoHIDMessageForButton code=0x191 target=0x2" , YES , DFHomeButtonCode, 0 , 0 , 0x2 },
2033+ { " IndigoHIDMessageForButton code=0x191 target=0x32" , YES , DFHomeButtonCode, 0 , 0 , DFIndigoTouchTarget },
2034+ };
2035+
2036+ NSError *lastError = nil ;
2037+ for (size_t index = 0 ; index < sizeof (strategies) / sizeof (strategies[0 ]); index++) {
2038+ const DFHomeButtonHIDStrategy *strategy = &strategies[index];
2039+ BOOL dispatched = YES ;
2040+ for (NSUInteger pressIndex = 0 ; pressIndex < 2 ; pressIndex += 1 ) {
2041+ NSError *downError = nil ;
2042+ if (!DFSendHomeStrategyEdge (hidClient, strategy, DFButtonDirectionDown, &downError)) {
2043+ DFLog (@" App Switcher strategy rejected (down): %s — %@ " , strategy->label , downError.localizedDescription ?: @" no error" );
2044+ lastError = downError;
2045+ dispatched = NO ;
2046+ break ;
2047+ }
2048+
2049+ [NSThread sleepForTimeInterval: 0.06 ];
2050+
2051+ NSError *upError = nil ;
2052+ if (!DFSendHomeStrategyEdge (hidClient, strategy, DFButtonDirectionUp, &upError)) {
2053+ DFLog (@" App Switcher strategy rejected (up): %s — %@ " , strategy->label , upError.localizedDescription ?: @" no error" );
2054+ lastError = upError;
2055+ dispatched = NO ;
2056+ break ;
2057+ }
2058+
2059+ if (pressIndex == 0 ) {
2060+ [NSThread sleepForTimeInterval: 0.12 ];
2061+ }
2062+ }
2063+
2064+ if (dispatched) {
2065+ DFLog (@" App Switcher dispatched via %s " , strategy->label );
2066+ return YES ;
2067+ }
2068+ }
2069+
2070+ if (error != NULL ) {
2071+ *error = lastError ?: DFMakeError (
2072+ DFPrivateSimulatorErrorCodeTouchDispatchFailed,
2073+ @" SimulatorKit rejected every App Switcher HID strategy."
2074+ );
2075+ }
2076+ return NO ;
2077+ }
2078+
20182079@interface DFPrivateSimulatorDisplayBridge ()
20192080
20202081@property (nonatomic , strong ) NSView *displayView;
@@ -2912,6 +2973,37 @@ - (BOOL)pressHomeButton:(NSError * _Nullable __autoreleasing *)error {
29122973 return success;
29132974}
29142975
2976+ - (BOOL )openAppSwitcher : (NSError * _Nullable __autoreleasing *)error {
2977+ __block BOOL success = NO ;
2978+ __block NSError *dispatchError = nil ;
2979+
2980+ dispatch_block_t work = ^{
2981+ NSError *hidError = nil ;
2982+ if (DFOpenAppSwitcherViaHIDClient (self->_hidClient , &hidError)) {
2983+ success = YES ;
2984+ return ;
2985+ }
2986+
2987+ DFLog (@" HID App Switcher path failed: %@ " , hidError.localizedDescription ?: @" unknown error" );
2988+ dispatchError = hidError ?: DFMakeError (
2989+ DFPrivateSimulatorErrorCodeTouchDispatchFailed,
2990+ @" No App Switcher path succeeded."
2991+ );
2992+ };
2993+
2994+ if (dispatch_get_specific (DFPrivateSimulatorCallbackQueueKey) != NULL ) {
2995+ work ();
2996+ } else {
2997+ dispatch_sync (_callbackQueue, work);
2998+ }
2999+
3000+ if (!success && error != NULL ) {
3001+ *error = dispatchError;
3002+ }
3003+
3004+ return success;
3005+ }
3006+
29153007- (BOOL )rotateByDegrees : (double )deltaDegrees error : (NSError * _Nullable __autoreleasing *)error {
29163008 __block BOOL success = NO ;
29173009 __block NSError *dispatchError = nil ;
0 commit comments