Remove timer-based polling, always use CADisplayLink for frame updates
CADisplayLink provides better vsync alignment and consistent 60fps updates. The timer-based 400Hz polling path added unnecessary complexity and CPU overhead without meaningful benefits. - Remove useCADisplayLink parameter and displaysync preference - Simplify FrameUpdater to always use CADisplayLink - Remove preference UI toggle for display sync option - Add CLAUDE.md project documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
86
screendump/CLAUDE.md
Normal file
86
screendump/CLAUDE.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
ScreenDump is a VNC server for jailbroken iOS 15+ devices (rootless/Ellekit). It captures the device screen and streams it via VNC protocol while supporting remote keyboard and touch input.
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the package (requires Theos installed)
|
||||||
|
make package
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
make clean
|
||||||
|
|
||||||
|
# Build and install to device (requires SSH access)
|
||||||
|
make package install THEOS_DEVICE_IP=<device-ip>
|
||||||
|
```
|
||||||
|
|
||||||
|
The build produces `screendumpd` daemon installed to `/usr/libexec`.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
- **ScreenDumpVNC** ([ScreenDumpVNC.m](ScreenDumpVNC.m)) - Main singleton controller that:
|
||||||
|
- Initializes libvncserver with screen dimensions
|
||||||
|
- Manages IOSurface framebuffer acquisition via IOMobileFramebuffer
|
||||||
|
- Creates IOSurfaceAccelerator for frame scaling/transfer
|
||||||
|
- Handles dual capture mode switching
|
||||||
|
|
||||||
|
- **FrameUpdater** ([FrameUpdater.m](FrameUpdater.m)) - Frame capture loop:
|
||||||
|
- Runs on NSOperationQueue with max 1 concurrent operation (serialized)
|
||||||
|
- Uses CADisplayLink for vsync-based updates at 60fps
|
||||||
|
- Transfers frames via IOSurfaceAcceleratorTransferSurface or CARenderServerRenderDisplay
|
||||||
|
|
||||||
|
- **VNC Event Handlers** ([vnc.m](vnc.m)) - Input injection:
|
||||||
|
- Keyboard events via IOHIDEventSystemClient (XK keysym to HID usage mapping)
|
||||||
|
- Touch/pointer via IOHIDDigitizer events (normalized coordinates)
|
||||||
|
- Backslash key (`\`) toggles capture mode
|
||||||
|
|
||||||
|
### Capture Modes
|
||||||
|
|
||||||
|
Two capture modes available, toggled via backslash key during VNC session:
|
||||||
|
|
||||||
|
1. **CARenderServer** (default) - Uses `CARenderServerRenderDisplay()`, faster but hides secure text fields
|
||||||
|
2. **IOMobileFramebuffer** (legacy) - Direct framebuffer access, shows password fields but auto-reverts after 10 seconds
|
||||||
|
|
||||||
|
### Frame Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
IOMobileFramebuffer → screenSurface (native res)
|
||||||
|
↓
|
||||||
|
[CARenderServer or direct transfer]
|
||||||
|
↓
|
||||||
|
renderServerSurface (if scaling needed)
|
||||||
|
↓
|
||||||
|
IOSurfaceAccelerator transfer
|
||||||
|
↓
|
||||||
|
staticBuffer (VNC output res)
|
||||||
|
↓
|
||||||
|
rfbMarkRectAsModified → VNC clients
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
Pre-built libraries in `vncbuild/`:
|
||||||
|
- libvncserver (VNC protocol)
|
||||||
|
- libpng, libjpeg (image encoding)
|
||||||
|
- libssl, libcrypto (VNC authentication)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
User preferences read from `com.mousen.screendump` preference domain:
|
||||||
|
- `enabled` - Enable/disable daemon
|
||||||
|
- `width`, `height` - Custom output resolution (maintains aspect ratio, aligns to 4px)
|
||||||
|
- `password` - VNC authentication password
|
||||||
|
|
||||||
|
## Entitlements
|
||||||
|
|
||||||
|
[en.plist](en.plist) contains required entitlements for:
|
||||||
|
- IOSurface/IOMobileFramebuffer access
|
||||||
|
- HID event dispatch (input injection)
|
||||||
|
- QuartzCore global capture
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
height:(size_t)height
|
height:(size_t)height
|
||||||
nativeWidth:(size_t)nativeWidth
|
nativeWidth:(size_t)nativeWidth
|
||||||
nativeHeight:(size_t)nativeHeight
|
nativeHeight:(size_t)nativeHeight
|
||||||
useCADisplayLink:(BOOL)useCADisplayLink
|
|
||||||
renderServerSurface:(IOSurfaceRef)renderServerSurface
|
renderServerSurface:(IOSurfaceRef)renderServerSurface
|
||||||
captureModeBlock:(CaptureMode(^)(void))captureModeBlock;
|
captureModeBlock:(CaptureMode(^)(void))captureModeBlock;
|
||||||
- (void)startFrameLoop;
|
- (void)startFrameLoop;
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
// Process
|
// Process
|
||||||
NSOperationQueue *_q;
|
NSOperationQueue *_q;
|
||||||
BOOL _updatingFrames;
|
BOOL _updatingFrames;
|
||||||
uint32_t _lastUpdatedSeed;
|
CADisplayLink *_displayLink;
|
||||||
NSTimer* _updateFrameTimer;
|
|
||||||
|
|
||||||
// Shared from ScreenDumpVNC
|
// Shared from ScreenDumpVNC
|
||||||
IOSurfaceRef _screenSurface;
|
IOSurfaceRef _screenSurface;
|
||||||
@@ -17,7 +16,6 @@
|
|||||||
size_t _height;
|
size_t _height;
|
||||||
size_t _nativeWidth;
|
size_t _nativeWidth;
|
||||||
size_t _nativeHeight;
|
size_t _nativeHeight;
|
||||||
BOOL _useCADisplayLink;
|
|
||||||
|
|
||||||
// Dual capture mode support
|
// Dual capture mode support
|
||||||
IOSurfaceRef _renderServerSurface; // Separate surface for CARenderServer (avoids framebuffer locking)
|
IOSurfaceRef _renderServerSurface; // Separate surface for CARenderServer (avoids framebuffer locking)
|
||||||
@@ -32,15 +30,13 @@
|
|||||||
height:(size_t)height
|
height:(size_t)height
|
||||||
nativeWidth:(size_t)nativeWidth
|
nativeWidth:(size_t)nativeWidth
|
||||||
nativeHeight:(size_t)nativeHeight
|
nativeHeight:(size_t)nativeHeight
|
||||||
useCADisplayLink:(BOOL)useCADisplayLink
|
|
||||||
renderServerSurface:(IOSurfaceRef)renderServerSurface
|
renderServerSurface:(IOSurfaceRef)renderServerSurface
|
||||||
captureModeBlock:(CaptureMode(^)(void))captureModeBlock {
|
captureModeBlock:(CaptureMode(^)(void))captureModeBlock {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_q = [[NSOperationQueue alloc] init];
|
_q = [[NSOperationQueue alloc] init];
|
||||||
_q.maxConcurrentOperationCount = 1; // Serialize to prevent frame queue buildup
|
_q.maxConcurrentOperationCount = 1; // Serialize to prevent frame queue buildup
|
||||||
_updatingFrames = NO;
|
_updatingFrames = NO;
|
||||||
_lastUpdatedSeed = 0;
|
_displayLink = nil;
|
||||||
_updateFrameTimer = nil;
|
|
||||||
|
|
||||||
_screenSurface = screenSurface;
|
_screenSurface = screenSurface;
|
||||||
_rfbScreenInfo = rfbScreenInfo;
|
_rfbScreenInfo = rfbScreenInfo;
|
||||||
@@ -50,7 +46,6 @@
|
|||||||
_height = height;
|
_height = height;
|
||||||
_nativeWidth = nativeWidth;
|
_nativeWidth = nativeWidth;
|
||||||
_nativeHeight = nativeHeight;
|
_nativeHeight = nativeHeight;
|
||||||
_useCADisplayLink = useCADisplayLink;
|
|
||||||
|
|
||||||
_renderServerSurface = renderServerSurface;
|
_renderServerSurface = renderServerSurface;
|
||||||
_captureModeBlock = [captureModeBlock copy];
|
_captureModeBlock = [captureModeBlock copy];
|
||||||
@@ -70,18 +65,6 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool updateFrame = true;
|
|
||||||
if (!_useCADisplayLink) {
|
|
||||||
bool updateFrame = false;
|
|
||||||
int32_t currentFrameSeed = IOSurfaceGetSeed(_screenSurface);
|
|
||||||
if (_lastUpdatedSeed != currentFrameSeed && rfbIsActive(_rfbScreenInfo)) {
|
|
||||||
_lastUpdatedSeed = currentFrameSeed;
|
|
||||||
updateFrame = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (updateFrame) {
|
|
||||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[_q addOperationWithBlock: ^{
|
[_q addOperationWithBlock: ^{
|
||||||
// Get current capture mode
|
// Get current capture mode
|
||||||
CaptureMode mode = _captureModeBlock ? _captureModeBlock() : CaptureModeCARenderServer;
|
CaptureMode mode = _captureModeBlock ? _captureModeBlock() : CaptureModeCARenderServer;
|
||||||
@@ -105,16 +88,14 @@
|
|||||||
|
|
||||||
rfbMarkRectAsModified(_rfbScreenInfo, 0, 0, _width, _height);
|
rfbMarkRectAsModified(_rfbScreenInfo, 0, 0, _width, _height);
|
||||||
}];
|
}];
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)stopFrameLoop {
|
-(void)stopFrameLoop {
|
||||||
if (_updateFrameTimer == nil || ![_updateFrameTimer isValid]) return;
|
if (_displayLink == nil) return;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void){
|
dispatch_async(dispatch_get_main_queue(), ^(void){
|
||||||
[_updateFrameTimer invalidate];
|
[_displayLink invalidate];
|
||||||
_updateFrameTimer = nil;
|
_displayLink = nil;
|
||||||
_updatingFrames = NO;
|
_updatingFrames = NO;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -123,17 +104,10 @@
|
|||||||
[self stopFrameLoop];
|
[self stopFrameLoop];
|
||||||
_updatingFrames = YES;
|
_updatingFrames = YES;
|
||||||
|
|
||||||
if (_useCADisplayLink) {
|
|
||||||
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_updateFrame)];
|
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_updateFrame)];
|
||||||
displayLink.preferredFramesPerSecond = 60; // Limit to 60 FPS
|
displayLink.preferredFramesPerSecond = 60; // Limit to 60 FPS
|
||||||
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||||
_updateFrameTimer = (NSTimer *)displayLink;
|
_displayLink = displayLink;
|
||||||
} else {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void){
|
|
||||||
// Reduced from 1/400 (400 Hz) to 1/60 (60 Hz) to reduce overhead
|
|
||||||
_updateFrameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/400.0 target:self selector:@selector(_updateFrame) userInfo:nil repeats:YES];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)dealloc {
|
-(void)dealloc {
|
||||||
|
|||||||
@@ -286,9 +286,6 @@
|
|||||||
|
|
||||||
free(arg0);
|
free(arg0);
|
||||||
|
|
||||||
NSDictionary* defaults = getPrefsForAppId(@"com.mousen.screendump");
|
|
||||||
bool useCADisplayLink = [[defaults objectForKey:@"displaysync"]?:@NO boolValue];
|
|
||||||
|
|
||||||
__weak ScreenDumpVNC *weakSelf = self;
|
__weak ScreenDumpVNC *weakSelf = self;
|
||||||
_frameUpdater = [[FrameUpdater alloc] initWithSurfaceInfo:_screenSurface
|
_frameUpdater = [[FrameUpdater alloc] initWithSurfaceInfo:_screenSurface
|
||||||
rfbScreenInfo:_rfbScreenInfo
|
rfbScreenInfo:_rfbScreenInfo
|
||||||
@@ -298,7 +295,6 @@
|
|||||||
height:_height
|
height:_height
|
||||||
nativeWidth:_nativeWidth
|
nativeWidth:_nativeWidth
|
||||||
nativeHeight:_nativeHeight
|
nativeHeight:_nativeHeight
|
||||||
useCADisplayLink:useCADisplayLink
|
|
||||||
renderServerSurface:_renderServerSurface
|
renderServerSurface:_renderServerSurface
|
||||||
captureModeBlock:^CaptureMode(void) {
|
captureModeBlock:^CaptureMode(void) {
|
||||||
ScreenDumpVNC *strongSelf = weakSelf;
|
ScreenDumpVNC *strongSelf = weakSelf;
|
||||||
|
|||||||
@@ -81,26 +81,6 @@
|
|||||||
<key>isNumeric</key>
|
<key>isNumeric</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>cell</key>
|
|
||||||
<string>PSGroupCell</string>
|
|
||||||
<key>label</key>
|
|
||||||
<string>Tuning</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>PostNotification</key>
|
|
||||||
<string>com.mousen.screendump/restart</string>
|
|
||||||
<key>cell</key>
|
|
||||||
<string>PSSwitchCell</string>
|
|
||||||
<key>default</key>
|
|
||||||
<false/>
|
|
||||||
<key>defaults</key>
|
|
||||||
<string>com.mousen.screendump</string>
|
|
||||||
<key>key</key>
|
|
||||||
<string>displaysync</string>
|
|
||||||
<key>label</key>
|
|
||||||
<string>Update screen using CADisplayLink</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
Reference in New Issue
Block a user