// // XSIosTouch.m // nochange // // Created by mac on 2024/10/15. // #import #import "XSIosTouch.h" #import "XSPhoneConfig.h" #import "XSHelper.h" // static var-------- // touch event sender id static unsigned long long int _XSSenderID = 0x0; static IOHIDEventSystemClientRef _XSIoHIDEventSystemForSenderID = NULL; // static var end-------- /* Get the sender id and unregister itself. */ static void XSSetSenderIdCallback(void* target, void* refcon, IOHIDServiceRef service, IOHIDEventRef event) { NSLog(@"XSSetSenderIdCallback 1"); if (IOHIDEventGetType(event) == kIOHIDEventTypeDigitizer){ NSLog(@"XSSetSenderIdCallback 2"); if (_XSSenderID == 0x0) { NSLog(@"XSSetSenderIdCallback 3"); _XSSenderID = IOHIDEventGetSenderID(event); NSInteger currentTime = [[NSDate date] timeIntervalSince1970]; NSInteger timeSinceReboot = [NSProcessInfo processInfo].systemUptime; NSInteger rebootTime = currentTime - timeSinceReboot; XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; [config SetLastReboot:rebootTime]; if (_XSSenderID > 0) { NSLog(@"XSSetSenderIdCallback 4"); [config SetSenderId:_XSSenderID]; } NSLog(@"com.zjx.springboard: sender id is: %qX", _XSSenderID); } } } /** Start the callback for setting sender id */ static void XSStartSetSenderIDCallBack(void) { _XSIoHIDEventSystemForSenderID = IOHIDEventSystemClientCreate(kCFAllocatorDefault); IOHIDEventSystemClientScheduleWithRunLoop(_XSIoHIDEventSystemForSenderID, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOHIDEventSystemClientRegisterEventCallback(_XSIoHIDEventSystemForSenderID, (IOHIDEventSystemClientEventCallback)XSSetSenderIdCallback, NULL, NULL); } void XSInitGetSenderId(void) { @try { NSInteger currentTime = [[NSDate date] timeIntervalSince1970]; NSInteger timeSinceReboot = [NSProcessInfo processInfo].systemUptime; NSInteger thisRebootTime = currentTime - timeSinceReboot; XSPhoneConfig *config = [XSPhoneConfig sharedInstance]; NSInteger lastRebootTime = config.LastReboot; if (labs(lastRebootTime - thisRebootTime) <= 3) { if (config.SenderId > 0) { _XSSenderID = config.SenderId; return; } } if (!_XSIoHIDEventSystemForSenderID) { XSStartSetSenderIDCallBack(); } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSInteger retryCount = 0; while (retryCount < 10) { [NSThread sleepForTimeInterval:2.0f]; if (_XSIoHIDEventSystemForSenderID && _XSSenderID != 0x0) { IOHIDEventSystemClientUnregisterEventCallback(_XSIoHIDEventSystemForSenderID); IOHIDEventSystemClientUnscheduleWithRunLoop(_XSIoHIDEventSystemForSenderID, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); break; } retryCount++; } if (retryCount >= 10) { NSLog(@"Failed to get sender ID after maximum retries"); } }); } @catch (NSException *exception) { NSLog(@"Error initializing sender ID: %@", exception); } } @interface XSIosTouch() { @private CGFloat device_screen_width; CGFloat device_screen_height; IOHIDEventSystemClientRef ioSystemClient; dispatch_queue_t touchQueue; dispatch_semaphore_t eventSemaphore; NSTimeInterval lastEventTime; BOOL isProcessingEvent; } @end @implementation XSIosTouch +(instancetype)sharedInstance { static XSIosTouch* _sharedInstance = nil; static dispatch_once_t oncePredicate; dispatch_once (&oncePredicate, ^{ _sharedInstance = [[XSIosTouch alloc] init]; }); return _sharedInstance; } -(instancetype)init { if (self = [super init]) { @try { CGFloat screen_scale = [[UIScreen mainScreen] scale]; self->device_screen_width = [UIScreen mainScreen].bounds.size.width * screen_scale; self->device_screen_height = [UIScreen mainScreen].bounds.size.height * screen_scale; self->touchQueue = dispatch_queue_create("com.xsiostouch.queue", DISPATCH_QUEUE_SERIAL); self->ioSystemClient = IOHIDEventSystemClientCreate(kCFAllocatorDefault); self->eventSemaphore = dispatch_semaphore_create(1); self->lastEventTime = 0; self->isProcessingEvent = NO; return self; } @catch (NSException *exception) { NSLog(@"Error initializing XSIosTouch: %@", exception); return nil; } } return nil; } - (void)dealloc { if (self->ioSystemClient) { IOHIDEventSystemClientUnregisterEventCallback(self->ioSystemClient); CFRelease(self->ioSystemClient); self->ioSystemClient = NULL; } if (_XSIoHIDEventSystemForSenderID) { IOHIDEventSystemClientUnregisterEventCallback(_XSIoHIDEventSystemForSenderID); CFRelease(_XSIoHIDEventSystemForSenderID); _XSIoHIDEventSystemForSenderID = NULL; } } - (void)cleanupResources { @synchronized(self) { if (self->ioSystemClient) { IOHIDEventSystemClientUnregisterEventCallback(self->ioSystemClient); CFRelease(self->ioSystemClient); self->ioSystemClient = NULL; } } } - (BOOL)checkSystemStatus { // 检查系统状态,防止过度负载 if (self->isProcessingEvent) { return NO; } NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970]; if (currentTime - self->lastEventTime < 0.016) { return NO; } return YES; } - (void)resetEventSystem { @synchronized(self) { if (self->ioSystemClient) { IOHIDEventSystemClientUnregisterEventCallback(self->ioSystemClient); CFRelease(self->ioSystemClient); self->ioSystemClient = IOHIDEventSystemClientCreate(kCFAllocatorDefault); } self->isProcessingEvent = NO; } } - (void)handleEventSystemFailure { static int failureCount = 0; @synchronized(self) { failureCount++; if (failureCount >= 3) { [self resetEventSystem]; failureCount = 0; } } } // device screen size - (AbsoluteTime) getAbsoluteTime { uint64_t machAbsoluteTime = mach_absolute_time(); AbsoluteTime timeStamp; timeStamp.hi = (UInt32)(machAbsoluteTime >> 32); timeStamp.lo = (UInt32)(machAbsoluteTime); return timeStamp; } - (IOHIDEventRef)generateChildEvent:(int)index type:(int)type x:(float)x y:(float)y { @try { if (x < 0 || y < 0 || index < 0) { NSLog(@"Invalid parameters for child event"); return NULL; } AbsoluteTime timeStamp = [self getAbsoluteTime]; uint32_t isTouch = (type == TOUCH_UP) ? 0 : 1; IOHIDDigitizerEventMask eventMask = 0; // 设置事件掩码 if (type != TOUCH_UP && type != TOUCH_DOWN) { eventMask |= kIOHIDDigitizerEventPosition; } if (type == TOUCH_UP || type == TOUCH_DOWN) { eventMask |= (kIOHIDDigitizerEventTouch | kIOHIDDigitizerEventRange); } // 坐标归一化 double dx = (self->device_screen_width > 0) ? (x / self->device_screen_width) : 0; double dy = (self->device_screen_height > 0) ? (y / self->device_screen_height) : 0; // 确保坐标在有效范围内 dx = fmin(fmax(dx, 0.0), 1.0); dy = fmin(fmax(dy, 0.0), 1.0); float x_rounded = XSRoundToDecimal(dx, 4); float y_rounded = XSRoundToDecimal(dy, 4); IOHIDEventRef child = IOHIDEventCreateDigitizerFingerEvent( kCFAllocatorDefault, timeStamp, index, 3, eventMask, x_rounded, y_rounded, 0.0f, 0.0f, 0.0f, isTouch, isTouch, 0 ); if (!child) { NSLog(@"Failed to create child event"); return NULL; } IOHIDEventSetFloatValue(child, kIOHIDEventFieldDigitizerMajorRadius, 0.04f); IOHIDEventSetFloatValue(child, kIOHIDEventFieldDigitizerMinorRadius, 0.04f); if (!child) { [self handleEventSystemFailure]; return NULL; } return child; } @catch (NSException *exception) { NSLog(@"Error generating child event: %@", exception); return NULL; } } /* Get the child event of touching down. index: index of the finger x: coordinate x of the screen (before conversion) y: coordinate y of the screen (before conversion) */ - (IOHIDEventRef) generateChildEventTouchDown:(int) index x:(float)x y:(float) y { return [self generateChildEvent:index type:TOUCH_DOWN x:x y:y]; } /* Get the child event of touching move. index: index of the finger x: coordinate x of the screen (before conversion) y: coordinate y of the screen (before conversion) */ - (IOHIDEventRef) generateChildEventTouchMove:(int) index x:(float)x y:(float) y { return [self generateChildEvent:index type:TOUCH_MOVE x:x y:y]; } /* Get the child event of touching up. index: index of the finger x: coordinate x of the screen (before conversion) y: coordinate y of the screen (before conversion) */ - (IOHIDEventRef) generateChildEventTouchUp:(int) index x:(float)x y:(float) y { return [self generateChildEvent:index type:TOUCH_UP x:x y:y]; } - (IOHIDEventRef) generateParentEvent { // generate a parent event AbsoluteTime timeStamp = [self getAbsoluteTime]; //IOHIDEventRef parent = IOHIDEventCreateDigitizerEvent(kCFAllocatorDefault, timeStamp, 3, 99, 1, 4, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 0); IOHIDEventRef parent = IOHIDEventCreateDigitizerEvent(kCFAllocatorDefault, //allocator timeStamp, // timeStamp kIOHIDDigitizerTransducerTypeHand, //IOHIDDigitizerTransducerType 1, // uint32_t index 0, // uint32_t identity kIOHIDDigitizerEventTouch, // uint32_t eventMask 0, // uint32_t buttonMask 0.0, // IOHIDFloat x 0.0, // IOHIDFloat y 0.0, // IOHIDFloat z 0.0, // IOHIDFloat tipPressure 0.0, //IOHIDFloat barrelPressure false, // boolean range true, // boolean touch 0// IOOptionBits options ); IOHIDEventSetIntegerValue(parent , 0xb0019, 1); //set flags of parent event flags: 0x20001 -> 0xa0001 IOHIDEventSetIntegerValue(parent , 0x4, 1); //set flags of parent event flags: 0xa0001 -> 0xa0011 //kIOHIDEventFieldDigitizerIsDisplayIntegrated //IOHIDEventSetIntegerValue(parent, kIOHIDEventFieldDigitizerIrregularity, 1); return parent; } - (UIWindow*) getKeyWindow { for (UIWindow *window in [UIApplication sharedApplication].windows) { if (window.isKeyWindow) { return window; } } return NULL; } /** Post the parent event */ - (void)postIOHIDEvent:(IOHIDEventRef)_parent { if (!_parent) return; // 控制事件发送频率 NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970]; if (currentTime - self->lastEventTime < 0.016) { // 限制最高约60fps usleep(((0.016 - (currentTime - self->lastEventTime)) * 1000000)); } dispatch_semaphore_wait(self->eventSemaphore, DISPATCH_TIME_FOREVER); @try { if (!self->ioSystemClient) { self->ioSystemClient = IOHIDEventSystemClientCreate(kCFAllocatorDefault); } if (_XSSenderID > 0) { IOHIDEventSetSenderID(_parent, _XSSenderID); } if (self->ioSystemClient) { IOHIDEventSystemClientDispatchEvent(self->ioSystemClient, _parent); } self->lastEventTime = [[NSDate date] timeIntervalSince1970]; } @catch (NSException *exception) { NSLog(@"Error posting IOHIDEvent: %@", exception); } @finally { dispatch_semaphore_signal(self->eventSemaphore); } } - (void) Down:(int) index x:(float)x y:(float) y { @autoreleasepool { IOHIDEventRef _parent = [self generateParentEvent]; IOHIDEventRef event = [self generateChildEventTouchDown:index x:x y:y]; IOHIDEventAppendEvent(_parent, event); [self End: _parent]; } } - (void) Move:(int) index x:(float)x y:(float) y { @autoreleasepool { IOHIDEventRef _parent = [self generateParentEvent]; IOHIDEventRef event = [self generateChildEventTouchMove:index x:x y:y]; IOHIDEventAppendEvent(_parent, event); [self End:_parent]; } } - (void) Up:(int) index x:(float)x y:(float) y { @autoreleasepool { IOHIDEventRef _parent = [self generateParentEvent]; IOHIDEventRef event = [self generateChildEventTouchUp:index x:x y:y]; IOHIDEventAppendEvent(_parent, event); [self End: _parent]; } } - (void)Tap:(int)index x:(float)x y:(float)y { if (index <= 0 || x < 0 || y < 0) { NSLog(@"Invalid tap parameters"); return; } if (self->isProcessingEvent) { NSLog(@"Already processing event, skipping tap"); return; } __weak typeof(self) weakSelf = self; dispatch_async(self->touchQueue, ^{ __strong typeof(weakSelf) strongSelf = weakSelf; @try { strongSelf->isProcessingEvent = YES; // 添加延迟以防止事件过于密集 NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970]; if (currentTime - strongSelf->lastEventTime < 0.05) { usleep(50000); // 50ms最小间隔 } [strongSelf Down:index x:x y:y]; usleep(50000); // 增加按下和抬起之间的延迟 [strongSelf Up:index x:x y:y]; strongSelf->lastEventTime = [[NSDate date] timeIntervalSince1970]; } @catch (NSException *exception) { NSLog(@"Error performing tap: %@", exception); } @finally { strongSelf->isProcessingEvent = NO; } }); } - (void)End:(IOHIDEventRef)_parent { if (!_parent) { NSLog(@"Invalid parent event"); return; } @try { IOHIDEventSetIntegerValue(_parent, kIOHIDEventFieldDigitizerTiltX, kIOHIDDigitizerTransducerTypeHand); IOHIDEventSetIntegerValue(_parent, kIOHIDEventFieldDigitizerTiltY, 1); IOHIDEventSetIntegerValue(_parent, kIOHIDEventFieldDigitizerAltitude, 1); [self postIOHIDEvent:_parent]; CFRelease(_parent); } @catch (NSException *exception) { NSLog(@"Error ending event: %@", exception); if (_parent) { CFRelease(_parent); } } } - (void) Keyboard:(uint16_t)usage down:(Boolean)down { static IOHIDEventSystemClientRef client = nil; if (client == nil) { client = IOHIDEventSystemClientCreate(kCFAllocatorDefault); } AbsoluteTime timeStamp = [self getAbsoluteTime]; IOHIDEventRef event = IOHIDEventCreateKeyboardEvent( kCFAllocatorDefault, timeStamp, kHIDPage_KeyboardOrKeypad, usage, down, 0 ); IOHIDEventSetSenderID(event, _XSSenderID); IOHIDEventSystemClientDispatchEvent(client, event); CFRelease(event); //CFRelease(client); } - (void) KeyDown:(uint16_t)usage { [self Keyboard:usage down:true]; } - (void) KeyUp:(uint16_t)usage { [self Keyboard:usage down:false]; } @end