504 lines
16 KiB
Objective-C
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
|