Ported back to 2.x.
[veency.git] / Tweak.mm
1 /* Veency - VNC Remote Access Server for iPhoneOS
2 * Copyright (C) 2008 Jay Freeman (saurik)
3 */
4
5 /*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
17 * distribution.
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include <substrate.h>
39
40 #include <rfb/rfb.h>
41 #include <rfb/keysym.h>
42
43 #include <mach/mach_port.h>
44
45 #import <QuartzCore/CAWindowServer.h>
46 #import <QuartzCore/CAWindowServerDisplay.h>
47
48 #import <CoreGraphics/CGGeometry.h>
49 #import <GraphicsServices/GraphicsServices.h>
50 #import <Foundation/Foundation.h>
51 #import <IOMobileFramebuffer/IOMobileFramebuffer.h>
52 #import <IOKit/IOKitLib.h>
53 #import <UIKit/UIKit.h>
54
55 #import <SpringBoard/SBAlertItemsController.h>
56 #import <SpringBoard/SBDismissOnlyAlertItem.h>
57 #import <SpringBoard/SBStatusBarController.h>
58
59 #define IOMobileFramebuffer "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/IOMobileFramebuffer"
60
61 static const size_t Width = 320;
62 static const size_t Height = 480;
63 static const size_t BytesPerPixel = 4;
64 static const size_t BitsPerSample = 8;
65
66 static const size_t Stride = Width * BytesPerPixel;
67 static const size_t Size32 = Width * Height;
68 static const size_t Size8 = Size32 * BytesPerPixel;
69
70 static pthread_t thread_;
71 static rfbScreenInfoPtr screen_;
72 static bool running_;
73 static int buttons_;
74 static int x_, y_;
75
76 static unsigned clients_;
77
78 static Class $VNCAlertItem;
79 static Class $SBAlertItemsController;
80 static Class $SBStatusBarController;
81
82 static rfbNewClientAction action_ = RFB_CLIENT_ON_HOLD;
83 static NSCondition *condition_;
84
85 static rfbClientPtr client_;
86
87 static void VNCAccept() {
88 action_ = RFB_CLIENT_ACCEPT;
89 ++clients_;
90 [[$SBStatusBarController sharedStatusBarController] addStatusBarItem:@"Veency"];
91 }
92
93 void VNCAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
94 [condition_ lock];
95
96 switch (button) {
97 case 1:
98 VNCAccept();
99 break;
100
101 case 2:
102 action_ = RFB_CLIENT_REFUSE;
103 break;
104 }
105
106 [condition_ signal];
107 [condition_ unlock];
108 [self dismiss];
109 }
110
111 void VNCAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
112 UIModalView *sheet([self alertSheet]);
113 [sheet setDelegate:self];
114 [sheet setTitle:@"Remote Access Request"];
115 [sheet setBodyText:[NSString stringWithFormat:@"Accept connection from\n%s?\n\nVeency VNC Server\nby Jay Freeman (saurik)\nsaurik@saurik.com\nhttp://www.saurik.com/", client_->host]];
116 [sheet addButtonWithTitle:@"Accept"];
117 [sheet addButtonWithTitle:@"Reject"];
118 }
119
120 void VNCAlertItem$performUnlockAction(id self, SEL sel) {
121 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
122 }
123
124 @interface VNCBridge : NSObject {
125 }
126
127 + (void) askForConnection;
128 + (void) removeStatusBarItem;
129
130 @end
131
132 @implementation VNCBridge
133
134 + (void) askForConnection {
135 if (false) {
136 [condition_ lock];
137 VNCAccept();
138 [condition_ signal];
139 [condition_ unlock];
140 } else {
141 id item = [[[$VNCAlertItem alloc] init] autorelease];
142 [[$SBAlertItemsController sharedInstance] activateAlertItem:item];
143 }
144 }
145
146 + (void) removeStatusBarItem {
147 [[$SBStatusBarController sharedStatusBarController] removeStatusBarItem:@"Veency"];
148 }
149
150 @end
151
152 static mach_port_t (*GSTakePurpleSystemEventPort)(void);
153 static bool PurpleAllocated;
154 static bool Two_;
155
156 static void FixRecord(GSEventRecord *record) {
157 if (Two_)
158 memmove(&record->windowContextId, &record->windowContextId + 1, sizeof(*record) - (reinterpret_cast<uint8_t *>(&record->windowContextId + 1) - reinterpret_cast<uint8_t *>(record)) + record->size);
159 }
160
161 static void VNCPointer(int buttons, int x, int y, rfbClientPtr client) {
162 x_ = x; y_ = y;
163 int diff = buttons_ ^ buttons;
164 bool twas((buttons_ & 0x1) != 0);
165 bool tis((buttons & 0x1) != 0);
166 buttons_ = buttons;
167
168 rfbDefaultPtrAddEvent(buttons, x, y, client);
169
170 mach_port_t purple(0);
171
172 if ((diff & 0x10) != 0) {
173 struct GSEventRecord record;
174
175 memset(&record, 0, sizeof(record));
176
177 record.type = (buttons & 0x4) != 0 ?
178 GSEventTypeHeadsetButtonDown :
179 GSEventTypeHeadsetButtonUp;
180
181 record.timestamp = GSCurrentEventTimestamp();
182
183 FixRecord(&record);
184 GSSendSystemEvent(&record);
185 }
186
187 if ((diff & 0x04) != 0) {
188 struct GSEventRecord record;
189
190 memset(&record, 0, sizeof(record));
191
192 record.type = (buttons & 0x4) != 0 ?
193 GSEventTypeMenuButtonDown :
194 GSEventTypeMenuButtonUp;
195
196 record.timestamp = GSCurrentEventTimestamp();
197
198 FixRecord(&record);
199 GSSendSystemEvent(&record);
200 }
201
202 if ((diff & 0x02) != 0) {
203 struct GSEventRecord record;
204
205 memset(&record, 0, sizeof(record));
206
207 record.type = (buttons & 0x2) != 0 ?
208 GSEventTypeLockButtonDown :
209 GSEventTypeLockButtonUp;
210
211 record.timestamp = GSCurrentEventTimestamp();
212
213 FixRecord(&record);
214 GSSendSystemEvent(&record);
215 }
216
217 if (twas != tis || tis) {
218 struct {
219 struct GSEventRecord record;
220 struct {
221 struct GSEventRecordInfo info;
222 struct GSPathInfo path;
223 } data;
224 } event;
225
226 memset(&event, 0, sizeof(event));
227
228 event.record.type = GSEventTypeMouse;
229 event.record.locationInWindow.x = x;
230 event.record.locationInWindow.y = y;
231 event.record.timestamp = GSCurrentEventTimestamp();
232 event.record.size = sizeof(event.data);
233
234 event.data.info.handInfo.type = twas == tis ?
235 GSMouseEventTypeDragged :
236 tis ?
237 GSMouseEventTypeDown :
238 GSMouseEventTypeUp;
239
240 event.data.info.handInfo.x34 = 0x1;
241 event.data.info.handInfo.x38 = tis ? 0x1 : 0x0;
242
243 event.data.info.pathPositions = 1;
244
245 event.data.path.x00 = 0x01;
246 event.data.path.x01 = 0x02;
247 event.data.path.x02 = tis ? 0x03 : 0x00;
248 event.data.path.position = event.record.locationInWindow;
249
250 mach_port_t port(0);
251
252 if (CAWindowServer *server = [CAWindowServer serverIfRunning]) {
253 NSArray *displays([server displays]);
254 if (displays != nil && [displays count] != 0)
255 if (CAWindowServerDisplay *display = [displays objectAtIndex:0])
256 port = [display clientPortAtPosition:event.record.locationInWindow];
257 }
258
259 if (port == 0) {
260 if (purple == 0)
261 purple = (*GSTakePurpleSystemEventPort)();
262 port = purple;
263 }
264
265 FixRecord(&event.record);
266 GSSendEvent(&event.record, port);
267 }
268
269 if (purple != 0 && PurpleAllocated)
270 mach_port_deallocate(mach_task_self(), purple);
271 }
272
273 static void VNCKeyboard(rfbBool down, rfbKeySym key, rfbClientPtr client) {
274 if (!down)
275 return;
276
277 switch (key) {
278 case XK_Return: key = '\r'; break;
279 case XK_BackSpace: key = 0x7f; break;
280 }
281
282 if (key > 0xfff)
283 return;
284
285 GSEventRef event(_GSCreateSyntheticKeyEvent(key, YES, YES));
286 GSEventRecord *record(_GSEventGetGSEventRecord(event));
287 record->type = GSEventTypeKeyDown;
288
289 mach_port_t port(0);
290
291 if (CAWindowServer *server = [CAWindowServer serverIfRunning]) {
292 NSArray *displays([server displays]);
293 if (displays != nil && [displays count] != 0)
294 if (CAWindowServerDisplay *display = [displays objectAtIndex:0])
295 port = [display clientPortAtPosition:CGPointMake(x_, y_)];
296 }
297
298 mach_port_t purple(0);
299
300 if (port == 0) {
301 if (purple == 0)
302 purple = (*GSTakePurpleSystemEventPort)();
303 port = purple;
304 }
305
306 if (port != 0)
307 GSSendEvent(record, port);
308
309 if (purple != 0 && PurpleAllocated)
310 mach_port_deallocate(mach_task_self(), purple);
311
312 CFRelease(event);
313 }
314
315 static void VNCDisconnect(rfbClientPtr client) {
316 if (--clients_ == 0)
317 [VNCBridge performSelectorOnMainThread:@selector(removeStatusBarItem) withObject:nil waitUntilDone:NO];
318 }
319
320 static rfbNewClientAction VNCClient(rfbClientPtr client) {
321 [condition_ lock];
322 client_ = client;
323 [VNCBridge performSelectorOnMainThread:@selector(askForConnection) withObject:nil waitUntilDone:NO];
324 while (action_ == RFB_CLIENT_ON_HOLD)
325 [condition_ wait];
326 rfbNewClientAction action(action_);
327 action_ = RFB_CLIENT_ON_HOLD;
328 [condition_ unlock];
329 if (action == RFB_CLIENT_ACCEPT)
330 client->clientGoneHook = &VNCDisconnect;
331 return action;
332 }
333
334 static rfbPixel black_[320][480];
335
336 static void *VNCServer(IOMobileFramebufferRef fb) {
337 CGRect rect(CGRectMake(0, 0, Width, Height));
338
339 /*CoreSurfaceBufferRef surface(NULL);
340 kern_return_t value(IOMobileFramebufferGetLayerDefaultSurface(fb, 0, &surface));
341 if (value != 0)
342 return NULL;*/
343
344 condition_ = [[NSCondition alloc] init];
345
346 int argc(1);
347 char *arg0(strdup("VNCServer"));
348 char *argv[] = {arg0, NULL};
349
350 screen_ = rfbGetScreen(&argc, argv, Width, Height, BitsPerSample, 3, BytesPerPixel);
351 screen_->desktopName = "iPhone";
352 screen_->alwaysShared = TRUE;
353 screen_->handleEventsEagerly = TRUE;
354 screen_->deferUpdateTime = 5;
355
356 screen_->serverFormat.redShift = BitsPerSample * 2;
357 screen_->serverFormat.greenShift = BitsPerSample * 1;
358 screen_->serverFormat.blueShift = BitsPerSample * 0;
359
360 /*io_service_t service(IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOCoreSurfaceRoot")));
361 CFMutableDictionaryRef properties(NULL);
362 IORegistryEntryCreateCFProperties(service, &properties, kCFAllocatorDefault, kNilOptions);
363
364 CoreSurfaceBufferLock(surface, kCoreSurfaceLockTypeGimmeVRAM);
365 screen_->frameBuffer = reinterpret_cast<char *>(CoreSurfaceBufferGetBaseAddress(surface));
366 CoreSurfaceBufferUnlock(surface);
367 CFRelease(surface);*/
368
369 screen_->frameBuffer = reinterpret_cast<char *>(black_);
370
371 screen_->kbdAddEvent = &VNCKeyboard;
372 screen_->ptrAddEvent = &VNCPointer;
373
374 screen_->newClientHook = &VNCClient;
375
376 /*char data[0], mask[0];
377 rfbCursorPtr cursor = rfbMakeXCursor(0, 0, data, mask);
378 rfbSetCursor(screen_, cursor);*/
379
380 rfbInitServer(screen_);
381 running_ = true;
382
383 rfbRunEventLoop(screen_, -1, true);
384 NSLog(@"rfbRunEventLoop().");
385 return NULL;
386
387 running_ = false;
388 rfbScreenCleanup(screen_);
389
390 free(arg0);
391 return NULL;
392 }
393
394 MSHook(kern_return_t, IOMobileFramebufferSwapSetLayer,
395 IOMobileFramebufferRef fb,
396 int layer,
397 CoreSurfaceBufferRef buffer,
398 CGRect bounds,
399 CGRect frame,
400 int flags
401 ) {
402 /*if (
403 bounds.origin.x != 0 || bounds.origin.y != 0 || bounds.size.width != 320 || bounds.size.height != 480 ||
404 frame.origin.x != 0 || frame.origin.y != 0 || frame.size.width != 320 || frame.size.height != 480
405 ) NSLog(@"VNC:%f,%f:%f,%f:%f,%f:%f,%f",
406 bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height,
407 frame.origin.x, frame.origin.y, frame.size.width, frame.size.height
408 );*/
409
410 if (running_) {
411 if (buffer == NULL)
412 screen_->frameBuffer = reinterpret_cast<char *>(black_);
413 else {
414 CoreSurfaceBufferLock(buffer, 2);
415 rfbPixel (*data)[480] = reinterpret_cast<rfbPixel (*)[480]>(CoreSurfaceBufferGetBaseAddress(buffer));
416 screen_->frameBuffer = const_cast<char *>(reinterpret_cast<volatile char *>(data));
417 CoreSurfaceBufferUnlock(buffer);
418 }
419 }
420
421 kern_return_t value(_IOMobileFramebufferSwapSetLayer(fb, layer, buffer, bounds, frame, flags));
422
423 if (thread_ == NULL)
424 pthread_create(&thread_, NULL, &VNCServer, fb);
425 else if (running_)
426 rfbMarkRectAsModified(screen_, 0, 0, Width, Height);
427
428 return value;
429 }
430
431 extern "C" void TweakInitialize() {
432 GSTakePurpleSystemEventPort = reinterpret_cast<mach_port_t (*)()>(dlsym(RTLD_DEFAULT, "GSGetPurpleSystemEventPort"));
433 if (GSTakePurpleSystemEventPort == NULL) {
434 GSTakePurpleSystemEventPort = reinterpret_cast<mach_port_t (*)()>(dlsym(RTLD_DEFAULT, "GSCopyPurpleSystemEventPort"));
435 PurpleAllocated = true;
436 }
437
438 Two_ = dlsym(RTLD_DEFAULT, "GSEventGetWindowContextId") == NULL;
439
440 MSHookFunction(&IOMobileFramebufferSwapSetLayer, &$IOMobileFramebufferSwapSetLayer, &_IOMobileFramebufferSwapSetLayer);
441
442 $SBAlertItemsController = objc_getClass("SBAlertItemsController");
443 $SBStatusBarController = objc_getClass("SBStatusBarController");
444
445 $VNCAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "VNCAlertItem", 0);
446 class_addMethod($VNCAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &VNCAlertItem$alertSheet$buttonClicked$, "v@:@i");
447 class_addMethod($VNCAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &VNCAlertItem$configure$requirePasscodeForActions$, "v@:cc");
448 class_addMethod($VNCAlertItem, @selector(performUnlockAction), (IMP) VNCAlertItem$performUnlockAction, "v@:");
449 objc_registerClassPair($VNCAlertItem);
450 }