ios-hooks/AppRunMan/server/XSIosTouch.m
2025-09-05 18:48:22 +08:00

504 lines
16 KiB
Objective-C

//
// XSIosTouch.m
// nochange
//
// Created by mac on 2024/10/15.
//
#import <Foundation/Foundation.h>
#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