Checkpointing old code.
[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/UIModalView.h>
54 #import <UIKit/UIModalView-Private.h>
55
56 #import <SpringBoard/SBAlertItemsController.h>
57 #import <SpringBoard/SBDismissOnlyAlertItem.h>
58 #import <SpringBoard/SBStatusBarController.h>
59
60 #define IOMobileFramebuffer "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/IOMobileFramebuffer"
61
62 static const size_t Width = 320;
63 static const size_t Height = 480;
64 static const size_t BytesPerPixel = 4;
65 static const size_t BitsPerSample = 8;
66
67 static const size_t Stride = Width * BytesPerPixel;
68 static const size_t Size32 = Width * Height;
69 static const size_t Size8 = Size32 * BytesPerPixel;
70
71 static pthread_t thread_;
72 static rfbScreenInfoPtr screen_;
73 static bool running_;
74 static int buttons_;
75 static int x_, y_;
76
77 static unsigned clients_;
78
79 static Class $VNCAlertItem;
80 static Class $SBAlertItemsController;
81 static Class $SBStatusBarController;
82
83 static rfbNewClientAction action_ = RFB_CLIENT_ON_HOLD;
84 static NSCondition *condition_;
85
86 static rfbClientPtr client_;
87
88 static void VNCAccept() {
89 action_ = RFB_CLIENT_ACCEPT;
90 ++clients_;
91 [[$SBStatusBarController sharedStatusBarController] addStatusBarItem:@"Veency"];
92 }
93
94 void VNCAlertItem$alertSheet$buttonClicked$(id self, SEL sel, id sheet, int button) {
95 [condition_ lock];
96
97 switch (button) {
98 case 1:
99 VNCAccept();
100 break;
101
102 case 2:
103 action_ = RFB_CLIENT_REFUSE;
104 break;
105 }
106
107 [condition_ signal];
108 [condition_ unlock];
109 [self dismiss];
110 }
111
112 void VNCAlertItem$configure$requirePasscodeForActions$(id self, SEL sel, BOOL configure, BOOL require) {
113 UIModalView *sheet([self alertSheet]);
114 [sheet setDelegate:self];
115 [sheet setTitle:@"Remote Access Request"];
116 [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]];
117 [sheet addButtonWithTitle:@"Accept"];
118 [sheet addButtonWithTitle:@"Reject"];
119 }
120
121 void VNCAlertItem$performUnlockAction(id self, SEL sel) {
122 [[$SBAlertItemsController sharedInstance] activateAlertItem:self];
123 }
124
125 @interface VNCBridge : NSObject {
126 }
127
128 + (void) askForConnection;
129 + (void) removeStatusBarItem;
130
131 @end
132
133 @implementation VNCBridge
134
135 + (void) askForConnection {
136 if (false) {
137 [condition_ lock];
138 VNCAccept();
139 [condition_ signal];
140 [condition_ unlock];
141 } else {
142 id item = [[[$VNCAlertItem alloc] init] autorelease];
143 [[$SBAlertItemsController sharedInstance] activateAlertItem:item];
144 }
145 }
146
147 + (void) removeStatusBarItem {
148 [[$SBStatusBarController sharedStatusBarController] removeStatusBarItem:@"Veency"];
149 }
150
151 @end
152
153 static void VNCPointer(int buttons, int x, int y, rfbClientPtr client) {
154 x_ = x; y_ = y;
155 int diff = buttons_ ^ buttons;
156 bool twas((buttons_ & 0x1) != 0);
157 bool tis((buttons & 0x1) != 0);
158 buttons_ = buttons;
159
160 rfbDefaultPtrAddEvent(buttons, x, y, client);
161
162 mach_port_t purple(0);
163
164 if ((diff & 0x10) != 0) {
165 struct GSEventRecord record;
166
167 memset(&record, 0, sizeof(record));
168
169 record.type = (buttons & 0x4) != 0 ?
170 GSEventTypeHeadsetButtonDown :
171 GSEventTypeHeadsetButtonUp;
172
173 record.timestamp = GSCurrentEventTimestamp();
174
175 GSSendSystemEvent(&record);
176 }
177
178 if ((diff & 0x04) != 0) {
179 struct GSEventRecord record;
180
181 memset(&record, 0, sizeof(record));
182
183 record.type = (buttons & 0x4) != 0 ?
184 GSEventTypeMenuButtonDown :
185 GSEventTypeMenuButtonUp;
186
187 record.timestamp = GSCurrentEventTimestamp();
188
189 GSSendSystemEvent(&record);
190 }
191
192 if ((diff & 0x02) != 0) {
193 struct GSEventRecord record;
194
195 memset(&record, 0, sizeof(record));
196
197 record.type = (buttons & 0x2) != 0 ?
198 GSEventTypeLockButtonDown :
199 GSEventTypeLockButtonUp;
200
201 record.timestamp = GSCurrentEventTimestamp();
202
203 GSSendSystemEvent(&record);
204 }
205
206 if (twas != tis || tis) {
207 struct {
208 struct GSEventRecord record;
209 struct {
210 struct GSEventRecordInfo info;
211 struct GSPathInfo path;
212 } data;
213 } event;
214
215 memset(&event, 0, sizeof(event));
216
217 event.record.type = GSEventTypeMouse;
218 event.record.locationInWindow.x = x;
219 event.record.locationInWindow.y = y;
220 event.record.timestamp = GSCurrentEventTimestamp();
221 event.record.size = sizeof(event.data);
222
223 event.data.info.handInfo.type = twas == tis ?
224 GSMouseEventTypeDragged :
225 tis ?
226 GSMouseEventTypeDown :
227 GSMouseEventTypeUp;
228
229 event.data.info.handInfo.x34 = 0x1;
230 event.data.info.handInfo.x38 = tis ? 0x1 : 0x0;
231
232 event.data.info.pathPositions = 1;
233
234 event.data.path.x00 = 0x01;
235 event.data.path.x01 = 0x02;
236 event.data.path.x02 = tis ? 0x03 : 0x00;
237 event.data.path.position = event.record.locationInWindow;
238
239 mach_port_t port(0);
240
241 if (CAWindowServer *server = [CAWindowServer serverIfRunning]) {
242 NSArray *displays([server displays]);
243 if (displays != nil && [displays count] != 0)
244 if (CAWindowServerDisplay *display = [displays objectAtIndex:0])
245 port = [display clientPortAtPosition:event.record.locationInWindow];
246 }
247
248 if (port == 0) {
249 if (purple == 0)
250 purple = GSCopyPurpleSystemEventPort();
251 port = purple;
252 }
253
254 GSSendEvent(&event.record, port);
255 }
256
257 if (purple != 0)
258 mach_port_deallocate(mach_task_self(), purple);
259 }
260
261 static void VNCKeyboard(rfbBool down, rfbKeySym key, rfbClientPtr client) {
262 if (!down)
263 return;
264
265 switch (key) {
266 case XK_Return: key = '\r'; break;
267 case XK_BackSpace: key = 0x7f; break;
268 }
269
270 if (key > 0xfff)
271 return;
272
273 struct {
274 struct GSEventRecord record;
275 struct GSEventKeyInfo data;
276 } event;
277
278 memset(&event, 0, sizeof(event));
279
280 event.record.type = GSEventTypeKeyDown;
281 event.record.timestamp = GSCurrentEventTimestamp();
282 event.record.size = sizeof(event.data);
283
284 event.data.character = key;
285
286 mach_port_t port(0);
287
288 if (CAWindowServer *server = [CAWindowServer serverIfRunning]) {
289 NSArray *displays([server displays]);
290 if (displays != nil && [displays count] != 0)
291 if (CAWindowServerDisplay *display = [displays objectAtIndex:0])
292 port = [display clientPortAtPosition:CGPointMake(x_, y_)];
293 }
294
295 if (port != 0)
296 GSSendEvent(&event.record, port);
297 }
298
299 static void VNCDisconnect(rfbClientPtr client) {
300 if (--clients_ == 0)
301 [VNCBridge performSelectorOnMainThread:@selector(removeStatusBarItem) withObject:nil waitUntilDone:NO];
302 }
303
304 static rfbNewClientAction VNCClient(rfbClientPtr client) {
305 [condition_ lock];
306 client_ = client;
307 [VNCBridge performSelectorOnMainThread:@selector(askForConnection) withObject:nil waitUntilDone:NO];
308 while (action_ == RFB_CLIENT_ON_HOLD)
309 [condition_ wait];
310 rfbNewClientAction action(action_);
311 action_ = RFB_CLIENT_ON_HOLD;
312 [condition_ unlock];
313 if (action == RFB_CLIENT_ACCEPT)
314 client->clientGoneHook = &VNCDisconnect;
315 return action;
316 }
317
318 static rfbPixel black_[320][480];
319
320 static void *VNCServer(IOMobileFramebufferRef fb) {
321 CGRect rect(CGRectMake(0, 0, Width, Height));
322
323 /*CoreSurfaceBufferRef surface(NULL);
324 kern_return_t value(IOMobileFramebufferGetLayerDefaultSurface(fb, 0, &surface));
325 if (value != 0)
326 return NULL;*/
327
328 condition_ = [[NSCondition alloc] init];
329
330 int argc(1);
331 char *arg0(strdup("VNCServer"));
332 char *argv[] = {arg0, NULL};
333
334 screen_ = rfbGetScreen(&argc, argv, Width, Height, BitsPerSample, 3, BytesPerPixel);
335 screen_->desktopName = "iPhone";
336 screen_->alwaysShared = TRUE;
337 screen_->handleEventsEagerly = TRUE;
338 screen_->deferUpdateTime = 5;
339
340 screen_->serverFormat.redShift = BitsPerSample * 2;
341 screen_->serverFormat.greenShift = BitsPerSample * 1;
342 screen_->serverFormat.blueShift = BitsPerSample * 0;
343
344 /*io_service_t service(IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOCoreSurfaceRoot")));
345 CFMutableDictionaryRef properties(NULL);
346 IORegistryEntryCreateCFProperties(service, &properties, kCFAllocatorDefault, kNilOptions);
347
348 CoreSurfaceBufferLock(surface, kCoreSurfaceLockTypeGimmeVRAM);
349 screen_->frameBuffer = reinterpret_cast<char *>(CoreSurfaceBufferGetBaseAddress(surface));
350 CoreSurfaceBufferUnlock(surface);
351 CFRelease(surface);*/
352
353 screen_->frameBuffer = reinterpret_cast<char *>(black_);
354
355 screen_->kbdAddEvent = &VNCKeyboard;
356 screen_->ptrAddEvent = &VNCPointer;
357
358 screen_->newClientHook = &VNCClient;
359
360 /*char data[0], mask[0];
361 rfbCursorPtr cursor = rfbMakeXCursor(0, 0, data, mask);
362 rfbSetCursor(screen_, cursor);*/
363
364 rfbInitServer(screen_);
365 running_ = true;
366
367 rfbRunEventLoop(screen_, -1, true);
368 NSLog(@"rfbRunEventLoop().");
369 return NULL;
370
371 running_ = false;
372 rfbScreenCleanup(screen_);
373
374 free(arg0);
375 return NULL;
376 }
377
378 MSHook(kern_return_t, IOMobileFramebufferSwapSetLayer,
379 IOMobileFramebufferRef fb,
380 int layer,
381 CoreSurfaceBufferRef buffer,
382 CGRect bounds,
383 CGRect frame,
384 int flags
385 ) {
386 /*if (
387 bounds.origin.x != 0 || bounds.origin.y != 0 || bounds.size.width != 320 || bounds.size.height != 480 ||
388 frame.origin.x != 0 || frame.origin.y != 0 || frame.size.width != 320 || frame.size.height != 480
389 ) NSLog(@"VNC:%f,%f:%f,%f:%f,%f:%f,%f",
390 bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height,
391 frame.origin.x, frame.origin.y, frame.size.width, frame.size.height
392 );*/
393
394 if (running_) {
395 if (buffer == NULL)
396 screen_->frameBuffer = reinterpret_cast<char *>(black_);
397 else {
398 CoreSurfaceBufferLock(buffer, 2);
399 rfbPixel (*data)[480] = reinterpret_cast<rfbPixel (*)[480]>(CoreSurfaceBufferGetBaseAddress(buffer));
400 screen_->frameBuffer = const_cast<char *>(reinterpret_cast<volatile char *>(data));
401 CoreSurfaceBufferUnlock(buffer);
402 }
403 }
404
405 kern_return_t value(_IOMobileFramebufferSwapSetLayer(fb, layer, buffer, bounds, frame, flags));
406
407 if (thread_ == NULL)
408 pthread_create(&thread_, NULL, &VNCServer, fb);
409 else if (running_)
410 rfbMarkRectAsModified(screen_, 0, 0, Width, Height);
411
412 return value;
413 }
414
415 extern "C" void TweakInitialize() {
416 MSHookFunction(&IOMobileFramebufferSwapSetLayer, &$IOMobileFramebufferSwapSetLayer, &_IOMobileFramebufferSwapSetLayer);
417
418 $SBAlertItemsController = objc_getClass("SBAlertItemsController");
419 $SBStatusBarController = objc_getClass("SBStatusBarController");
420
421 $VNCAlertItem = objc_allocateClassPair(objc_getClass("SBAlertItem"), "VNCAlertItem", 0);
422 class_addMethod($VNCAlertItem, @selector(alertSheet:buttonClicked:), (IMP) &VNCAlertItem$alertSheet$buttonClicked$, "v@:@i");
423 class_addMethod($VNCAlertItem, @selector(configure:requirePasscodeForActions:), (IMP) &VNCAlertItem$configure$requirePasscodeForActions$, "v@:cc");
424 class_addMethod($VNCAlertItem, @selector(performUnlockAction), (IMP) VNCAlertItem$performUnlockAction, "v@:");
425 objc_registerClassPair($VNCAlertItem);
426 }