├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── STLibrary.mm ├── SimulateTouch.h ├── SimulateTouch.mm ├── SimulateTouch.plist ├── control └── main.mm /.gitignore: -------------------------------------------------------------------------------- 1 | *.[ao] 2 | ._* 3 | .DS_Store 4 | *.deb 5 | *.dylib 6 | obj/ 7 | .theos 8 | theos 9 | 2scp.sh 10 | _Tweak.xm 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "private-headers"] 2 | path = private-headers 3 | url = https://github.com/iolate/iOS-Private-Headers.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 iolate (Kim Seung-ho) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include ${THEOS}/makefiles/common.mk 2 | 3 | TWEAK_NAME = SimulateTouch 4 | SimulateTouch_FILES = SimulateTouch.mm 5 | SimulateTouch_PRIVATE_FRAMEWORKS = GraphicsServices IOKit 6 | SimulateTouch_LDFLAGS = -lsubstrate -lrocketbootstrap 7 | 8 | LIBRARY_NAME = libsimulatetouch 9 | libsimulatetouch_FILES = STLibrary.mm 10 | libsimulatetouch_LDFLAGS = -lrocketbootstrap 11 | libsimulatetouch_INSTALL_PATH = /usr/lib/ 12 | libsimulatetouch_FRAMEWORKS = UIKit CoreGraphics 13 | 14 | TOOL_NAME = stouch 15 | stouch_FILES = main.mm 16 | stouch_FRAMEWORKS = UIKit 17 | stouch_INSTALL_PATH = /usr/bin/ 18 | stouch_LDFLAGS = -lsimulatetouch 19 | 20 | include $(THEOS_MAKE_PATH)/tweak.mk 21 | include $(THEOS_MAKE_PATH)/library.mk 22 | include $(THEOS_MAKE_PATH)/tool.mk 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SimulateTouch 2 | ============= 3 | 4 | Simulate touch library for iOS 5 | 6 | You can make fake touch(even multi-touch) and swipe. 7 | 8 | Support iOS6 and 7 9 | 10 | REQUIREMENT: RocketBootstrap by Ryan Petrich 11 | (https://github.com/rpetrich/rocketbootstrap) 12 | 13 | API Info: http://api.iolate.kr/simulatetouch/ 14 | 15 | and refer to main.mm (stouch; command line tool) for how to use. 16 | 17 | # How it works 18 | 19 | There are two components in this project: 20 | 21 | ### SimulateTouch.dylib MobileSubstrate 22 | 23 | This hooks into `com.apple.backboardd` and exposes a Mach messaging port defined as `MACH_PORT_NAME`, current code value is `kr.iolate.simulatetouch`. It listens for Mach messages and uses `IOHIDEvents` to simulate touches/swipes/buttons. 24 | 25 | ### stouch command line tool 26 | 27 | Interprets command line requests into Mach messages and sends it to `MACH_PORT_NAME` Mach port. 28 | 29 | # Dependencies 30 | 31 | ### Theos 32 | 33 | Follow instructions to setup Theos [here](http://iphonedevwiki.net/index.php/Theos/Setup). 34 | 35 | ### rocketboostrap 36 | 37 | * Install the `rocketbootstrap` on the device and copy `rocketbootstrap.h` and ``rocketbootstrap_dynamic.h` from `/usr/include/` to your `$THEOS/usr/include/`. 38 | * Copy `/usr/lib/librocketbootstrap.dylib` from device to your `$THEOS/usr/include`. 39 | 40 | ### bootstrap.h 41 | 42 | Get it from rpetrich's RocketBoostrap [repo](https://github.com/rpetrich/RocketBootstrap). 43 | 44 | ### simulatetouch 45 | 46 | * Install `simulatetouch` on the device and copy `/usr/lib/libsimulatetouch.dylib` from device to your `$THEOS/usr/lib`. -------------------------------------------------------------------------------- /STLibrary.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Name: libSimulateTouch 3 | * Author: iolate 4 | * 5 | */ 6 | 7 | #import 8 | #import 9 | #import 10 | 11 | #define LOOP_TIMES_IN_SECOND 40 12 | //60 13 | #define MACH_PORT_NAME "kr.iolate.simulatetouch" 14 | 15 | typedef enum { 16 | STTouchMove = 0, 17 | STTouchDown, 18 | STTouchUp, 19 | 20 | // For these types, (int)point_x denotes button type 21 | STButtonUp, 22 | STButtonDown 23 | } STTouchType; 24 | 25 | typedef struct { 26 | int type; // STTouchType values 27 | int index; // pathIndex holder in message 28 | float point_x; 29 | float point_y; 30 | } STEvent; 31 | 32 | typedef enum { 33 | UIInterfaceOrientationPortrait = 1,//UIDeviceOrientationPortrait, 34 | UIInterfaceOrientationPortraitUpsideDown = 2,//UIDeviceOrientationPortraitUpsideDown, 35 | UIInterfaceOrientationLandscapeLeft = 4,//UIDeviceOrientationLandscapeRight, 36 | UIInterfaceOrientationLandscapeRight = 3,//UIDeviceOrientationLandscapeLeft 37 | } UIInterfaceOrientation; 38 | 39 | @interface UIScreen 40 | +(id)mainScreen; 41 | -(CGRect)bounds; 42 | @end 43 | 44 | @interface STTouchA : NSObject 45 | { 46 | @public 47 | int type; //터치 종류 0: move/stay| 1: down| 2: up 48 | int pathIndex; 49 | CGPoint startPoint; 50 | CGPoint endPoint; 51 | uint64_t startTime; 52 | float requestedTime; 53 | } 54 | @end 55 | @implementation STTouchA 56 | @end 57 | 58 | static CFMessagePortRef messagePort = NULL; 59 | static NSMutableArray* ATouchEvents = nil; 60 | static BOOL FTLoopIsRunning = FALSE; 61 | 62 | #pragma mark - 63 | 64 | static int send_event(STEvent *event) { 65 | if (messagePort && !CFMessagePortIsValid(messagePort)){ 66 | CFRelease(messagePort); 67 | messagePort = NULL; 68 | } 69 | if (!messagePort) { 70 | messagePort = rocketbootstrap_cfmessageportcreateremote(NULL, CFSTR(MACH_PORT_NAME)); 71 | //messagePort = CFMessagePortCreateRemote(NULL, CFSTR(MACH_PORT_NAME)); 72 | } 73 | if (!messagePort || !CFMessagePortIsValid(messagePort)) { 74 | NSLog(@"ST Error: MessagePort is invalid"); 75 | return 0; //kCFMessagePortIsInvalid; 76 | } 77 | 78 | CFDataRef cfData = CFDataCreate(NULL, (uint8_t*)event, sizeof(*event)); 79 | CFDataRef rData = NULL; 80 | 81 | CFMessagePortSendRequest(messagePort, 1/*type*/, cfData, 1, 1, kCFRunLoopDefaultMode, &rData); 82 | 83 | if (cfData) { 84 | CFRelease(cfData); 85 | } 86 | 87 | int pathIndex; 88 | [(NSData *)rData getBytes:&pathIndex length:sizeof(pathIndex)]; 89 | 90 | if (rData) { 91 | CFRelease(rData); 92 | } 93 | 94 | return pathIndex; 95 | } 96 | 97 | static int simulate_button_event(int index, int button, int state) { 98 | STEvent event; 99 | event.index = index; 100 | 101 | event.type = (int)STButtonUp + state; 102 | event.point_x = button; 103 | event.point_y = 0.0f; 104 | 105 | return send_event(&event); 106 | } 107 | 108 | static int simulate_touch_event(int index, int type, CGPoint point) { 109 | STEvent event; 110 | event.index = index; 111 | 112 | event.type = type; 113 | event.point_x = point.x; 114 | event.point_y = point.y; 115 | 116 | return send_event(&event); 117 | } 118 | 119 | double MachTimeToSecs(uint64_t time) 120 | { 121 | mach_timebase_info_data_t timebase; 122 | mach_timebase_info(&timebase); 123 | return (double)time * (double)timebase.numer / (double)timebase.denom / 1e9; 124 | } 125 | 126 | 127 | static void _simulateTouchLoop() 128 | { 129 | if (FTLoopIsRunning == FALSE) { 130 | return; 131 | } 132 | int touchCount = [ATouchEvents count]; 133 | 134 | if (touchCount == 0) { 135 | FTLoopIsRunning = FALSE; 136 | return; 137 | } 138 | 139 | NSMutableArray* willRemoveObjects = [NSMutableArray array]; 140 | uint64_t curTime = mach_absolute_time(); 141 | 142 | for (int i = 0; i < touchCount; i++) 143 | { 144 | STTouchA* touch = [ATouchEvents objectAtIndex:i]; 145 | 146 | int touchType = touch->type; 147 | //0: move/stay 1: down 2: up 148 | 149 | if (touchType == 1) { 150 | //Already simulate_touch_event is called 151 | touch->type = STTouchMove; 152 | }else { 153 | double dif = MachTimeToSecs(curTime - touch->startTime); 154 | 155 | float req = touch->requestedTime; 156 | if (dif >= 0 && dif < req) { 157 | //Move 158 | 159 | float dx = touch->endPoint.x - touch->startPoint.x; 160 | float dy = touch->endPoint.y - touch->startPoint.y; 161 | 162 | double per = dif / (double)req; 163 | CGPoint point = CGPointMake(touch->startPoint.x + (float)(dx * per), touch->startPoint.y + (float)(dy * per)); 164 | 165 | int r = simulate_touch_event(touch->pathIndex, STTouchMove, point); 166 | if (r == 0) { 167 | NSLog(@"ST Error: touchLoop type:0 index:%d, point:(%d,%d) pathIndex:0", touch->pathIndex, (int)point.x, (int)point.y); 168 | continue; 169 | } 170 | 171 | }else { 172 | //Up 173 | simulate_touch_event(touch->pathIndex, STTouchMove, touch->endPoint); 174 | int r = simulate_touch_event(touch->pathIndex, STTouchUp, touch->endPoint); 175 | if (r == 0) { 176 | NSLog(@"ST Error: touchLoop type:2 index:%d, point:(%d,%d) pathIndex:0", touch->pathIndex, (int)touch->endPoint.x, (int)touch->endPoint.y); 177 | continue; 178 | } 179 | 180 | [willRemoveObjects addObject:touch]; 181 | } 182 | } 183 | } 184 | 185 | for (STTouchA* touch in willRemoveObjects) { 186 | [ATouchEvents removeObject:touch]; 187 | [touch release]; 188 | } 189 | 190 | willRemoveObjects = nil; 191 | 192 | //recursive 193 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / LOOP_TIMES_IN_SECOND); 194 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 195 | _simulateTouchLoop(); 196 | }); 197 | } 198 | 199 | #pragma mark - 200 | 201 | @interface SimulateTouch : NSObject 202 | @end 203 | 204 | @implementation SimulateTouch 205 | 206 | +(CGPoint)STScreenToWindowPoint:(CGPoint)point withOrientation:(UIInterfaceOrientation)orientation { 207 | CGSize screen = [[UIScreen mainScreen] bounds].size; 208 | 209 | if (orientation == UIInterfaceOrientationPortrait) { 210 | return point; 211 | }else if (orientation == UIInterfaceOrientationPortraitUpsideDown) { 212 | return CGPointMake(screen.width - point.x, screen.height - point.y); 213 | }else if (orientation == UIInterfaceOrientationLandscapeLeft) { 214 | //Homebutton is left 215 | return CGPointMake(screen.height - point.y, point.x); 216 | }else if (orientation == UIInterfaceOrientationLandscapeRight) { 217 | return CGPointMake(point.y, screen.width - point.x); 218 | }else return point; 219 | } 220 | 221 | +(CGPoint)STWindowToScreenPoint:(CGPoint)point withOrientation:(UIInterfaceOrientation)orientation { 222 | CGSize screen = [[UIScreen mainScreen] bounds].size; 223 | 224 | if (orientation == UIInterfaceOrientationPortrait) { 225 | return point; 226 | }else if (orientation == UIInterfaceOrientationPortraitUpsideDown) { 227 | return CGPointMake(screen.width - point.x, screen.height - point.y); 228 | }else if (orientation == UIInterfaceOrientationLandscapeLeft) { 229 | //Homebutton is left 230 | return CGPointMake(point.y, screen.height - point.x); 231 | }else if (orientation == UIInterfaceOrientationLandscapeRight) { 232 | return CGPointMake(screen.width - point.y, point.x); 233 | }else return point; 234 | } 235 | 236 | +(int)simulateButton:(int)button state:(int)state 237 | { 238 | int r = simulate_button_event(0, button, state); 239 | 240 | if (r == 0) { 241 | NSLog(@"ST Error: simulateButton:state: button:%d state:%d pathIndex:0", button, state); 242 | return 0; 243 | } 244 | return r; 245 | } 246 | 247 | +(int)simulateTouch:(int)pathIndex atPoint:(CGPoint)point withType:(STTouchType)type 248 | { 249 | int r = simulate_touch_event(pathIndex, type, point); 250 | 251 | if (r == 0) { 252 | NSLog(@"ST Error: simulateTouch:atPoint:withType: index:%d type:%d pathIndex:0", pathIndex, type); 253 | return 0; 254 | } 255 | return r; 256 | } 257 | 258 | +(int)simulateSwipeFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint duration:(float)duration 259 | { 260 | if (ATouchEvents == nil) { 261 | ATouchEvents = [[NSMutableArray alloc] init]; 262 | } 263 | 264 | STTouchA* touch = [[STTouchA alloc] init]; 265 | 266 | touch->type = STTouchMove; 267 | touch->startPoint = fromPoint; 268 | touch->endPoint = toPoint; 269 | touch->requestedTime = duration; 270 | touch->startTime = mach_absolute_time(); 271 | 272 | [ATouchEvents addObject:touch]; 273 | 274 | int r = simulate_touch_event(0, STTouchDown, fromPoint); 275 | if (r == 0) { 276 | NSLog(@"ST Error: simulateSwipeFromPoint:toPoint:duration: pathIndex:0"); 277 | return 0; 278 | } 279 | touch->pathIndex = r; 280 | 281 | if (!FTLoopIsRunning) { 282 | FTLoopIsRunning = TRUE; 283 | _simulateTouchLoop(); 284 | } 285 | 286 | return r; 287 | } 288 | 289 | @end -------------------------------------------------------------------------------- /SimulateTouch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Name: libSimulateTouch (kr.iolate.simulatetouch) 3 | * Author: iolate 4 | * 5 | * http://api.iolate.kr/simulatetouch/ 6 | * 7 | */ 8 | 9 | /* 10 | typedef enum { 11 | UIInterfaceOrientationPortrait = 1,//UIDeviceOrientationPortrait, 12 | UIInterfaceOrientationPortraitUpsideDown = 2,//UIDeviceOrientationPortraitUpsideDown, 13 | UIInterfaceOrientationLandscapeLeft = 4,//UIDeviceOrientationLandscapeRight, 14 | UIInterfaceOrientationLandscapeRight = 3,//UIDeviceOrientationLandscapeLeft 15 | } UIInterfaceOrientation; 16 | */ 17 | 18 | typedef enum { 19 | STTouchMove = 0, 20 | STTouchDown, 21 | STTouchUp, 22 | 23 | // For these types, (int)point_x denotes button type 24 | STButtonUp, 25 | STButtonDown 26 | } STTouchType; 27 | 28 | //Library - libsimulatetouch.dylib 29 | @interface SimulateTouch 30 | 31 | // Screen point: Absolute point (Portrait point) 32 | // Window point: Orientated point 33 | 34 | // Sreen point to window point. Portrait to 'orientation' 35 | +(CGPoint)STScreenToWindowPoint:(CGPoint)point withOrientation:(int)orientation; 36 | 37 | // Window point to screen point. 'orientation' to Portrait. 38 | +(CGPoint)STWindowToScreenPoint:(CGPoint)point withOrientation:(int)orientation; 39 | 40 | 41 | // if pathIndex is 0, SimulateTouch alloc its pathIndex. 42 | // retrun value is pathIndex. if 0, touch was failed. 43 | 44 | // Class methods' point is screen point. 45 | +(int)simulateButton:(int)button state:(int)state; 46 | +(int)simulateTouch:(int)pathIndex atPoint:(CGPoint)point withType:(STTouchType)type; 47 | +(int)simulateSwipeFromPoint:(CGPoint)fromPoint toPoint:(CGPoint)toPoint duration:(float)duration; 48 | @end -------------------------------------------------------------------------------- /SimulateTouch.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Name: libSimulateTouch ( kr.iolate.simulatetouch ) 3 | * Author: iolate 4 | * 5 | */ 6 | 7 | #include 8 | #include 9 | #import 10 | #import 11 | 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | //https://github.com/iolate/iOS-Private-Headers/tree/master/IOKit/hid 18 | #import "private-headers/IOKit/hid/IOHIDEvent7.h" 19 | #import "private-headers/IOKit/hid/IOHIDEventTypes7.h" 20 | #import "private-headers/IOKit/hid/IOHIDEventSystemConnection.h" 21 | 22 | #import 23 | 24 | #pragma mark - Common declaration 25 | 26 | //#define DEBUG 27 | #ifdef DEBUG 28 | # define DLog(...) NSLog(__VA_ARGS__) 29 | #else 30 | # define DLog(...) 31 | #endif 32 | 33 | @interface STTouch : NSObject 34 | { 35 | @public 36 | int type; //터치 종류 0: move/stay 1: down 2: up 3: button up 4: button down 37 | CGPoint point; 38 | } 39 | @end 40 | @implementation STTouch 41 | @end 42 | 43 | static void SendTouchesEvent(mach_port_t port); 44 | 45 | static NSMutableDictionary* STTouches = nil; //Dictionary{index:STTouch} 46 | static unsigned int lastPort = 0; 47 | 48 | static BOOL iOS7 = NO; 49 | 50 | @interface CAWindowServer //in QuartzCore 51 | +(id)serverIfRunning; 52 | -(id)displayWithName:(id)name; 53 | -(NSArray *)displays; //@property(readonly, assign) NSArray* displays; 54 | @end 55 | 56 | @interface CAWindowServerDisplay 57 | 58 | -(unsigned)clientPortAtPosition:(CGPoint)position; 59 | -(unsigned)contextIdAtPosition:(CGPoint)position; 60 | - (unsigned int)clientPortOfContextId:(unsigned int)arg1; 61 | -(CGRect)bounds; 62 | 63 | //iOS7 64 | - (unsigned int)taskPortOfContextId:(unsigned int)arg1; //New! 65 | @end 66 | 67 | @interface BKUserEventTimer 68 | + (id)sharedInstance; 69 | - (void)userEventOccurred; //iOS6 70 | - (void)userEventOccurredOnDisplay:(id)arg1; //iOS7 71 | 72 | -(BOOL)respondsToSelector:(SEL)selector; 73 | @end 74 | 75 | @interface BKHIDSystemInterface 76 | + (id)sharedInstance; 77 | - (void)injectHIDEvent:(IOHIDEventRef)arg1; 78 | @end 79 | 80 | @interface BKAccessibility 81 | //IOHIDEventSystemConnectionRef 82 | + (id)_eventRoutingClientConnectionManager; 83 | @end 84 | 85 | @interface BKHIDClientConnectionManager 86 | - (IOHIDEventSystemConnectionRef)clientForTaskPort:(unsigned int)arg1; 87 | - (IOHIDEventSystemConnectionRef)clientForBundleID:(id)arg1; 88 | @end 89 | 90 | #define Int2String(i) [NSString stringWithFormat:@"%d", i] 91 | 92 | #pragma mark - Implementation 93 | /* 94 | MSHook(IOHIDEventRef, IOHIDEventCreateDigitizerEvent, CFAllocatorRef allocator, AbsoluteTime timeStamp, IOHIDDigitizerTransducerType type, 95 | uint32_t index, uint32_t identity, uint32_t eventMask, uint32_t buttonMask, 96 | IOHIDFloat x, IOHIDFloat y, IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat barrelPressure, 97 | Boolean range, Boolean touch, IOOptionBits options) { 98 | 99 | //NSLog(@"##### Event %d", type); 100 | //NSLog(@"##### Event %d %d %d %d %d (%f, %f, %f) %f %f %d %d %d", type, index, identity, eventMask, buttonMask, x, y, z, tipPressure, barrelPressure, range, touch, (unsigned int)options); 101 | return _IOHIDEventCreateDigitizerEvent(allocator, timeStamp, type, index, identity, eventMask, buttonMask, x, y, z, tipPressure, barrelPressure, range, touch, options); 102 | } 103 | MSHook(IOHIDEventRef, IOHIDEventCreateDigitizerFingerEventWithQuality, CFAllocatorRef allocator, AbsoluteTime timeStamp, 104 | uint32_t index, uint32_t identity, uint32_t eventMask, 105 | IOHIDFloat x, IOHIDFloat y, IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat twist, 106 | IOHIDFloat minorRadius, IOHIDFloat majorRadius, IOHIDFloat quality, IOHIDFloat density, IOHIDFloat irregularity, 107 | Boolean range, Boolean touch, IOOptionBits options) { 108 | 109 | //NSLog(@"##### Quality %d %d %d %f %f", index, identity, eventMask, x, y); 110 | 111 | return _IOHIDEventCreateDigitizerFingerEventWithQuality(allocator, timeStamp, index, identity, eventMask, x, y, z, tipPressure, twist, minorRadius, majorRadius, quality, density, irregularity, range, touch, options); 112 | }*/ 113 | 114 | 115 | static IOHIDEventSystemCallback original_callback; 116 | static void iohid_event_callback (void* target, void* refcon, IOHIDServiceRef service, IOHIDEventRef event) { 117 | if (IOHIDEventGetType(event) == kIOHIDEventTypeDigitizer) { 118 | [STTouches removeAllObjects]; 119 | } 120 | 121 | original_callback(target, refcon, service, event); 122 | } 123 | MSHook(Boolean, IOHIDEventSystemOpen, IOHIDEventSystemRef system, IOHIDEventSystemCallback callback, void* target, void* refcon, void* unused) { 124 | original_callback = callback; 125 | return _IOHIDEventSystemOpen(system, iohid_event_callback, target, refcon, unused); 126 | } 127 | 128 | static int getExtraIndexNumber() 129 | { 130 | int r = arc4random()%14; 131 | r += 1; //except 0 132 | 133 | NSString* pin = Int2String(r); 134 | 135 | if ([[STTouches allKeys] containsObject:pin]) { 136 | return getExtraIndexNumber(); 137 | }else{ 138 | return r; 139 | } 140 | } 141 | 142 | static void SimulateTouchEvent(mach_port_t port, int pathIndex, int type, CGPoint touchPoint) { 143 | if (pathIndex == 0) return; 144 | 145 | STTouch* touch = [STTouches objectForKey:Int2String(pathIndex)] ?: [[STTouch alloc] init]; 146 | 147 | touch->type = type; 148 | touch->point = touchPoint; 149 | 150 | [STTouches setObject:touch forKey:Int2String(pathIndex)]; 151 | 152 | SendTouchesEvent(port); 153 | } 154 | 155 | // ============= from veency http://gitweb.saurik.com/veency.git 156 | extern "C" { 157 | IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef allocator); 158 | void IOHIDEventSystemClientDispatchEvent(IOHIDEventSystemClientRef client, IOHIDEventRef event); 159 | } 160 | static void SendHIDEvent(IOHIDEventRef event) { 161 | static IOHIDEventSystemClientRef client_(NULL); 162 | if (client_ == NULL) 163 | client_ = IOHIDEventSystemClientCreate(kCFAllocatorDefault); 164 | 165 | IOHIDEventSetSenderID(event, 0xDEFACEDBEEFFECE5); 166 | IOHIDEventSystemClientDispatchEvent(client_, event); 167 | CFRelease(event); 168 | } 169 | // ============= 170 | 171 | static void SendTouchesEvent(mach_port_t port) { 172 | 173 | int touchCount = [[STTouches allKeys] count]; 174 | 175 | if (touchCount == 0) { 176 | return; 177 | } 178 | 179 | uint64_t abTime = mach_absolute_time(); 180 | AbsoluteTime timeStamp = *(AbsoluteTime *) &abTime; 181 | 182 | //iOS6 kIOHIDDigitizerTransducerTypeHand == 35 183 | //iOS7 kIOHIDTransducerTypeHand == 3 184 | IOHIDEventRef handEvent = IOHIDEventCreateDigitizerEvent(kCFAllocatorDefault, timeStamp, iOS7 ? kIOHIDTransducerTypeHand : kIOHIDDigitizerTransducerTypeHand, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 185 | 186 | //Got on iOS7. 187 | IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerDisplayIntegrated, 1, -268435456); //-268435456 188 | IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldBuiltIn, 1, -268435456); //-268435456 189 | 190 | //It looks changing each time, but it doens't care. just don't use 0 191 | #define kIOHIDEventDigitizerSenderID 0x000000010000027F 192 | IOHIDEventSetSenderID(handEvent, kIOHIDEventDigitizerSenderID); 193 | // 194 | 195 | int handEventMask = 0; 196 | int handEventTouch = 0; 197 | int touchingCount = 0; //except Up touch 198 | 199 | int i = 0; 200 | for (NSString* pIndex in [STTouches allKeys]) 201 | { 202 | STTouch* touch = [STTouches objectForKey:pIndex]; 203 | int touchType = touch->type; 204 | 205 | DLog(@"### touchType:%d", touchType); 206 | if (touchType < 3) { // Less than STButtonUp - see STLibrary.mm's STTouchType 207 | int eventM = (touchType == 0) ? kIOHIDDigitizerEventPosition : (kIOHIDDigitizerEventRange | kIOHIDDigitizerEventTouch); //Originally, 0, 1 and 2 are used too... 208 | int touch_ = (touchType == 2) ? 0 : 1; 209 | 210 | float x = touch->point.x; 211 | float y = touch->point.y; 212 | 213 | float rX, rY; 214 | 215 | //========================= 216 | 217 | //0~1 point 218 | id display = [[objc_getClass("CAWindowServer") serverIfRunning] displayWithName:@"LCD"]; 219 | CGSize screen = [(CAWindowServerDisplay *)display bounds].size; 220 | 221 | //I don't know why, but iPad Air's screen size is {width:2048,height:1536} 222 | float width = MIN(screen.width, screen.height); 223 | float height = MAX(screen.width, screen.height); 224 | 225 | float factor = 1.0f; 226 | if (width == 640 || width == 1536) factor = 2.0f; 227 | else if (width == 750) factor = 2.0f; // iPhone6, I don't have this so I cannot sure. 228 | else if (width == 1242 || width == 1080) factor = 3.0f; //iPhone6+, I don't have this so I cannot sure. 229 | 230 | 231 | rX = x/width*factor; 232 | rY = y/height*factor; 233 | 234 | //========================= 235 | 236 | IOHIDEventRef fingerEvent = IOHIDEventCreateDigitizerFingerEventWithQuality(kCFAllocatorDefault, timeStamp, 237 | [pIndex intValue], i + 2, eventM, rX, rY, 0, 0, 0, 0, 0, 0, 0, 0, touch_, touch_, 0); 238 | IOHIDEventAppendEvent(handEvent, fingerEvent); 239 | i++; 240 | 241 | handEventTouch |= touch_; 242 | if (touchType == 0) { 243 | handEventMask |= kIOHIDDigitizerEventPosition; //4 244 | }else{ 245 | handEventMask |= (kIOHIDDigitizerEventRange | kIOHIDDigitizerEventTouch | kIOHIDDigitizerEventIdentity); //1 + 2 + 32 = 35 246 | } 247 | 248 | if (touchType == 2) { 249 | handEventMask |= kIOHIDDigitizerEventPosition; 250 | [STTouches removeObjectForKey:pIndex]; 251 | [touch release]; 252 | }else{ 253 | touchingCount++; 254 | } 255 | } else { 256 | // This is a button event, not a touch event. 257 | int state = touchType - 3; // 3 == STButtonUp - see STLibrary.mm's STTouchType 258 | int button = (int)touch->point.x; 259 | DLog(@"### button:%d state:%d", button, state); 260 | 261 | int hidButton = kHIDUsage_Csmr_Power; 262 | if (button == 1) hidButton = kHIDUsage_Csmr_Menu; 263 | 264 | IOHIDEventRef buttonEvent = IOHIDEventCreateKeyboardEvent(kCFAllocatorDefault, 265 | timeStamp, 266 | kHIDPage_Consumer, 267 | hidButton, state, 0); 268 | SendHIDEvent(buttonEvent); 269 | } 270 | } 271 | 272 | 273 | //Got on iOS7. 274 | IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerEventMask, handEventMask, -268435456); 275 | IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerRange, handEventTouch, -268435456); 276 | IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerTouch, handEventTouch, -268435456); 277 | //IOHIDEventSetIntegerValueWithOptions(handEvent, kIOHIDEventFieldDigitizerIndex, (1<<22) + (int)pow(2.0, (double)(touchingCount+1)) - 2, -268435456); 278 | 279 | BKUserEventTimer* etimer = [NSClassFromString(@"BKUserEventTimer") sharedInstance]; 280 | 281 | if ([etimer respondsToSelector:@selector(userEventOccurred)]) { 282 | [etimer userEventOccurred]; 283 | }else if ([etimer respondsToSelector:@selector(userEventOccurredOnDisplay:)]) { 284 | [etimer userEventOccurredOnDisplay:nil]; 285 | } 286 | 287 | if (iOS7) { 288 | SendHIDEvent(handEvent); 289 | }else { 290 | original_callback(NULL, NULL, NULL, handEvent); 291 | } 292 | } 293 | 294 | #pragma mark - Communicate with Library 295 | 296 | typedef struct { 297 | int type; // STTouchType values 298 | int index; // pathIndex holder in message 299 | float point_x; 300 | float point_y; 301 | } STEvent; 302 | #define POINT(a) CGPointMake(a->point_x, a->point_y) 303 | 304 | static CFDataRef messageCallBack(CFMessagePortRef local, SInt32 msgid, CFDataRef cfData, void *info) 305 | { 306 | DLog(@"### ST: Receive Message Id: %d", (int)msgid); 307 | if (msgid == 1) { 308 | if (CFDataGetLength(cfData) == sizeof(STEvent)) { 309 | STEvent* touch = (STEvent *)[(NSData *)cfData bytes]; 310 | if (touch != NULL) { 311 | 312 | unsigned int port = 0; 313 | if (iOS7) { 314 | id display = [[objc_getClass("CAWindowServer") serverIfRunning] displayWithName:@"LCD"]; 315 | unsigned int contextId = [display contextIdAtPosition:POINT(touch)]; 316 | port = [display taskPortOfContextId:contextId]; 317 | 318 | if (lastPort && lastPort != port) { 319 | [STTouches removeAllObjects]; 320 | } 321 | lastPort = port; 322 | } 323 | 324 | int pathIndex = touch->index; 325 | DLog(@"### ST: Received Path Index: %d", pathIndex); 326 | if (pathIndex == 0) { 327 | pathIndex = getExtraIndexNumber(); 328 | } 329 | 330 | SimulateTouchEvent(port, pathIndex, touch->type, POINT(touch)); 331 | 332 | return (CFDataRef)[[NSData alloc] initWithBytes:&pathIndex length:sizeof(pathIndex)]; 333 | }else{ 334 | DLog(@"### ST: Received STEvent is nil"); 335 | return NULL; 336 | } 337 | } 338 | DLog(@"### ST: Received data is not STEvent. event size: %lu received size: %lu", sizeof(STEvent), CFDataGetLength(cfData)); 339 | } else { 340 | NSLog(@"### ST: Unknown message type: %d", (int)msgid); //%x 341 | } 342 | 343 | return NULL; 344 | } 345 | 346 | #pragma mark - MSInitialize 347 | 348 | #define MACH_PORT_NAME "kr.iolate.simulatetouch" 349 | 350 | #ifdef __cplusplus 351 | extern "C" { 352 | #endif 353 | //Cydia Substrate 354 | typedef const void *MSImageRef; 355 | 356 | MSImageRef MSGetImageByName(const char *file); 357 | void *MSFindSymbol(MSImageRef image, const char *name); 358 | #ifdef __cplusplus 359 | } 360 | #endif 361 | 362 | MSInitialize { 363 | STTouches = [[NSMutableDictionary alloc] init]; 364 | 365 | if (objc_getClass("BKHIDSystemInterface")) { 366 | iOS7 = YES; 367 | }else{ 368 | //iOS6 369 | MSHookFunction(IOHIDEventSystemOpen, MSHake(IOHIDEventSystemOpen)); 370 | iOS7 = NO; 371 | } 372 | //MSHookFunction(&IOHIDEventCreateDigitizerEvent, MSHake(IOHIDEventCreateDigitizerEvent)); 373 | //MSHookFunction(&IOHIDEventCreateDigitizerFingerEventWithQuality, MSHake(IOHIDEventCreateDigitizerFingerEventWithQuality)); 374 | 375 | CFMessagePortRef local = CFMessagePortCreateLocal(NULL, CFSTR(MACH_PORT_NAME), messageCallBack, NULL, NULL); 376 | if (rocketbootstrap_cfmessageportexposelocal(local) != 0) { 377 | NSLog(@"### ST: RocketBootstrap failed"); 378 | return; 379 | } 380 | 381 | CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(NULL, local, 0); 382 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); 383 | 384 | NSLog(@"### ST: Mach port initialized successfully."); 385 | } 386 | -------------------------------------------------------------------------------- /SimulateTouch.plist: -------------------------------------------------------------------------------- 1 | { 2 | Filter = { 3 | Bundles = ( 4 | "com.apple.backboardd", 5 | ); 6 | }; 7 | } -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: kr.iolate.simulatetouch 2 | Name: libSimulateTouch 3 | Depends: mobilesubstrate, firmware (>= 6.0), com.rpetrich.rocketbootstrap 4 | Version: 0.7 5 | Architecture: iphoneos-arm 6 | Description: Simulate fake touch and swipe. Support multi-touch. 7 | Maintainer: iolate 8 | Author: iolate 9 | Section: System 10 | -------------------------------------------------------------------------------- /main.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Name: libSimulateTouch 3 | * Author: iolate 4 | * 5 | */ 6 | 7 | #import 8 | #import "SimulateTouch.h" 9 | 10 | #define PRINT_USAGE printf("[Usage]\n 1. Touch:\n %s touch x y [orientation]\n\n 2. Swipe:\n %s swipe fromX fromY toX toY [duration(0.3)] [orientation]\n\n 3. Button: \n %s button Type State\n\n[Example]\n # %s touch 50 100\n # %s swipe 50 100 100 200 0.5\n # %s button 0 1\n # %s button 1 0\n\n[Orientation]\n Portrait:1 UpsideDown:2 Right:3 Left:4\n\n[Button]\n Power:0 Home:1\n\n[State]\n Up/Raise:0 Down/Press:1\n\n", argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0]); 11 | 12 | int main(int argc, char **argv, char **envp) { 13 | if (argc == 1) { 14 | PRINT_USAGE; 15 | return 0; 16 | } 17 | 18 | if (!strcmp(argv[1], "touch")) { 19 | if (argc != 4 && argc != 5) { 20 | PRINT_USAGE; 21 | return 0; 22 | } 23 | 24 | if (argc == 4) { 25 | int x = atoi(argv[2]); 26 | int y = atoi(argv[3]); 27 | 28 | int r = [SimulateTouch simulateTouch:0 atPoint:CGPointMake(x, y) withType:STTouchDown]; 29 | [SimulateTouch simulateTouch:r atPoint:CGPointMake(x, y) withType:STTouchUp]; 30 | }else if (argc == 5) { 31 | int px = atoi(argv[2]); 32 | int py = atoi(argv[3]); 33 | CGPoint p = CGPointMake(px, py); 34 | 35 | CGPoint rp = [SimulateTouch STWindowToScreenPoint:p withOrientation:atoi(argv[4])]; 36 | int r = [SimulateTouch simulateTouch:0 atPoint:rp withType:STTouchDown]; 37 | [SimulateTouch simulateTouch:r atPoint:rp withType:STTouchUp]; 38 | } 39 | 40 | }else if (!strcmp(argv[1], "swipe")) { 41 | if (argc < 6 || argc > 8) { 42 | PRINT_USAGE; 43 | return 0; 44 | } 45 | 46 | float duration = 0.3f; 47 | if (argc == 6) { 48 | CGPoint fromPoint = CGPointMake(atoi(argv[2]), atoi(argv[3])); 49 | CGPoint toPoint = CGPointMake(atoi(argv[4]), atoi(argv[5])); 50 | 51 | [SimulateTouch simulateSwipeFromPoint:fromPoint toPoint:toPoint duration:duration]; 52 | }else if (argc == 7) { 53 | CGPoint fromPoint = CGPointMake(atoi(argv[2]), atoi(argv[3])); 54 | CGPoint toPoint = CGPointMake(atoi(argv[4]), atoi(argv[5])); 55 | duration = atof(argv[6]); 56 | [SimulateTouch simulateSwipeFromPoint:fromPoint toPoint:toPoint duration:duration]; 57 | }else if (argc == 8) { 58 | CGPoint pfromPoint = CGPointMake(atoi(argv[2]), atoi(argv[3])); 59 | CGPoint ptoPoint = CGPointMake(atoi(argv[4]), atoi(argv[5])); 60 | 61 | CGPoint fromPoint = [SimulateTouch STWindowToScreenPoint:pfromPoint withOrientation:atoi(argv[7])]; 62 | CGPoint toPoint = [SimulateTouch STWindowToScreenPoint:ptoPoint withOrientation:atoi(argv[7])]; 63 | 64 | duration = atof(argv[6]); 65 | [SimulateTouch simulateSwipeFromPoint:fromPoint toPoint:toPoint duration:duration]; 66 | } 67 | 68 | CFRunLoopRunInMode(kCFRunLoopDefaultMode , duration+0.1f, NO); 69 | }else if (!strcmp(argv[1], "button")) { 70 | if (argc != 4) { 71 | PRINT_USAGE; 72 | return 0; 73 | } 74 | 75 | int button = atoi(argv[2]); 76 | int state = atoi(argv[3]); 77 | 78 | [SimulateTouch simulateButton:button state:state]; 79 | }else{ 80 | PRINT_USAGE; 81 | return 0; 82 | } 83 | 84 | return 0; 85 | } --------------------------------------------------------------------------------