Add dual capture mode support and aspect ratio adjustments
- Introduced CaptureMode enum for selecting between CARenderServer and IOMobileFramebuffer. - Updated FrameUpdater and ScreenDumpVNC to handle new capture mode logic. - Implemented aspect ratio calculations and adjustments in ScreenDumpVNC. - Added toggleCaptureMode method to switch between capture modes via keyboard input. - Enhanced initialization of FrameUpdater to accommodate new parameters.
This commit is contained in:
@@ -2,9 +2,18 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <rfb/rfb.h>
|
||||
#import "IOMobileFramebuffer.h"
|
||||
#import "ScreenDumpVNC.h"
|
||||
|
||||
@interface FrameUpdater : NSObject
|
||||
-(instancetype)initWithSurfaceInfo:(IOSurfaceRef)screenSurface rfbScreenInfo:(rfbScreenInfoPtr)rfbScreenInfo accelerator:(IOSurfaceAcceleratorRef)accelerator staticBuffer:(IOSurfaceRef)staticBuffer width:(size_t)width height:(size_t)height;
|
||||
-(instancetype)initWithSurfaceInfo:(IOSurfaceRef)screenSurface
|
||||
rfbScreenInfo:(rfbScreenInfoPtr)rfbScreenInfo
|
||||
accelerator:(IOSurfaceAcceleratorRef)accelerator
|
||||
staticBuffer:(IOSurfaceRef)staticBuffer
|
||||
width:(size_t)width
|
||||
height:(size_t)height
|
||||
useCADisplayLink:(BOOL)useCADisplayLink
|
||||
renderServerSurface:(IOSurfaceRef)renderServerSurface
|
||||
captureModeBlock:(CaptureMode(^)(void))captureModeBlock;
|
||||
- (void)startFrameLoop;
|
||||
- (void)stopFrameLoop;
|
||||
@end
|
||||
|
||||
@@ -16,9 +16,21 @@
|
||||
size_t _width;
|
||||
size_t _height;
|
||||
BOOL _useCADisplayLink;
|
||||
|
||||
// Dual capture mode support
|
||||
IOSurfaceRef _renderServerSurface;
|
||||
CaptureMode(^_captureModeBlock)(void);
|
||||
}
|
||||
|
||||
-(instancetype)initWithSurfaceInfo:(IOSurfaceRef)screenSurface rfbScreenInfo:(rfbScreenInfoPtr)rfbScreenInfo accelerator:(IOSurfaceAcceleratorRef)accelerator staticBuffer:(IOSurfaceRef)staticBuffer width:(size_t)width height:(size_t)height useCADisplayLink:(BOOL)useCADisplayLink {
|
||||
-(instancetype)initWithSurfaceInfo:(IOSurfaceRef)screenSurface
|
||||
rfbScreenInfo:(rfbScreenInfoPtr)rfbScreenInfo
|
||||
accelerator:(IOSurfaceAcceleratorRef)accelerator
|
||||
staticBuffer:(IOSurfaceRef)staticBuffer
|
||||
width:(size_t)width
|
||||
height:(size_t)height
|
||||
useCADisplayLink:(BOOL)useCADisplayLink
|
||||
renderServerSurface:(IOSurfaceRef)renderServerSurface
|
||||
captureModeBlock:(CaptureMode(^)(void))captureModeBlock {
|
||||
if ((self = [super init])) {
|
||||
_q = [[NSOperationQueue alloc] init];
|
||||
_updatingFrames = NO;
|
||||
@@ -32,6 +44,9 @@
|
||||
_width = width;
|
||||
_height = height;
|
||||
_useCADisplayLink = useCADisplayLink;
|
||||
|
||||
_renderServerSurface = renderServerSurface;
|
||||
_captureModeBlock = [captureModeBlock copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -55,7 +70,18 @@
|
||||
if (updateFrame) {
|
||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_q addOperationWithBlock: ^{
|
||||
// Get current capture mode
|
||||
CaptureMode mode = _captureModeBlock ? _captureModeBlock() : CaptureModeCARenderServer;
|
||||
|
||||
if (mode == CaptureModeIOMobileFramebuffer) {
|
||||
// Legacy mode: Use IOMobileFramebuffer surface transfer (shows passwords)
|
||||
IOSurfaceAcceleratorTransferSurface(_accelerator, _screenSurface, _staticBuffer, NULL, NULL, NULL, NULL);
|
||||
} else {
|
||||
// Default mode: Use CARenderServer (faster, no secure fields)
|
||||
CARenderServerRenderDisplay(0, CFSTR("LCD"), _renderServerSurface, 0, 0);
|
||||
IOSurfaceAcceleratorTransferSurface(_accelerator, _renderServerSurface, _staticBuffer, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
rfbMarkRectAsModified(_rfbScreenInfo, 0, 0, _width, _height);
|
||||
}];
|
||||
// });
|
||||
|
||||
@@ -35,3 +35,12 @@ extern IOMobileFramebufferReturn IOMobileFramebufferCopyLayerDisplayedSurface(IO
|
||||
extern IOMobileFramebufferReturn IOMobileFramebufferOpen(IOMobileFramebufferService service, mach_port_t owningTask, unsigned int type, IOMobileFramebufferRef *pointer);
|
||||
extern IOMobileFramebufferReturn IOMobileFramebufferGetMainDisplay(IOMobileFramebufferRef *pointer);
|
||||
extern mach_port_t mach_task_self();
|
||||
|
||||
// CARenderServer for screen capture (faster, no secure fields)
|
||||
extern kern_return_t CARenderServerRenderDisplay(
|
||||
uint32_t displayId,
|
||||
CFStringRef displayName,
|
||||
IOSurfaceRef surface,
|
||||
uint32_t x,
|
||||
uint32_t y
|
||||
);
|
||||
@@ -10,7 +10,7 @@ TOOL_NAME = screendumpd
|
||||
$(TOOL_NAME)_FILES = $(wildcard *.m)
|
||||
$(TOOL_NAME)_FRAMEWORKS := IOSurface IOKit
|
||||
$(TOOL_NAME)_PRIVATE_FRAMEWORKS := IOMobileFramebuffer IOSurface
|
||||
$(TOOL_NAME)_OBJCFLAGS += -I./vncbuild/include -Iinclude
|
||||
$(TOOL_NAME)_OBJCFLAGS += -I./vncbuild/include -Iinclude -fobjc-arc
|
||||
$(TOOL_NAME)_LDFLAGS += -Wl,-segalign,4000 -L./vncbuild/lib -lvncserver -lpng -llzo2 -ljpeg -lssl -lcrypto -lz
|
||||
$(TOOL_NAME)_CFLAGS = -w
|
||||
$(TOOL_NAME)_CODESIGN_FLAGS = "-Sen.plist"
|
||||
|
||||
@@ -3,10 +3,17 @@
|
||||
|
||||
#define kVNCServerName "ScreenDumpVNC"
|
||||
|
||||
typedef enum {
|
||||
CaptureModeCARenderServer = 0, // Default: faster, no secure fields
|
||||
CaptureModeIOMobileFramebuffer = 1 // Legacy: shows passwords
|
||||
} CaptureMode;
|
||||
|
||||
@interface ScreenDumpVNC : NSObject
|
||||
+(void)load;
|
||||
+(instancetype)sharedInstance;
|
||||
-(rfbBool)handleVNCAuthorization:(rfbClientPtr)client data:(const char *)data size:(int)size;
|
||||
-(size_t)width;
|
||||
-(size_t)height;
|
||||
-(void)toggleCaptureMode;
|
||||
-(CaptureMode)currentCaptureMode;
|
||||
@end
|
||||
@@ -14,6 +14,11 @@
|
||||
rfbScreenInfoPtr _rfbScreenInfo;
|
||||
bool _vncIsRunning;
|
||||
|
||||
// Aspect ratio tracking
|
||||
double _displayAspectRatio; // width/height of native display
|
||||
size_t _nativeWidth; // IOSurface native width
|
||||
size_t _nativeHeight; // IOSurface native height
|
||||
|
||||
// sent to FrameUpdater
|
||||
IOSurfaceRef _screenSurface;
|
||||
size_t _sizeImage;
|
||||
@@ -23,6 +28,11 @@
|
||||
size_t _height;
|
||||
|
||||
FrameUpdater *_frameUpdater;
|
||||
|
||||
// Dual capture mode support
|
||||
CaptureMode _captureMode;
|
||||
NSTimer *_legacyModeTimer;
|
||||
IOSurfaceRef _renderServerSurface;
|
||||
}
|
||||
|
||||
+(void)load {
|
||||
@@ -67,7 +77,7 @@
|
||||
|
||||
_rfbScreenInfo->authPasswdData = nil;
|
||||
if (_password && _password.length) {
|
||||
_rfbScreenInfo->authPasswdData = (void *)_password;
|
||||
_rfbScreenInfo->authPasswdData = (__bridge void *)_password;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +98,83 @@
|
||||
rfbShutdownServer(_rfbScreenInfo, YES);
|
||||
}
|
||||
|
||||
-(void)_calculateDisplayAspectRatio {
|
||||
_nativeWidth = IOSurfaceGetWidth(_screenSurface);
|
||||
_nativeHeight = IOSurfaceGetHeight(_screenSurface);
|
||||
|
||||
// Validate to prevent division by zero
|
||||
if (_nativeHeight == 0 || _nativeWidth == 0) {
|
||||
NSLog(@"[ScreenDumpVNC] ERROR: Invalid native dimensions, defaulting to 16:9");
|
||||
_displayAspectRatio = 16.0 / 9.0;
|
||||
if (_nativeHeight == 0) _nativeHeight = 1;
|
||||
if (_nativeWidth == 0) _nativeWidth = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
_displayAspectRatio = (double)_nativeWidth / (double)_nativeHeight;
|
||||
NSLog(@"[ScreenDumpVNC] Native display: %zux%zu, aspect ratio: %.6f",
|
||||
_nativeWidth, _nativeHeight, _displayAspectRatio);
|
||||
}
|
||||
|
||||
-(void)_adjustResolutionForAspectRatio {
|
||||
// Only adjust if custom preferences are set
|
||||
if (_prefsWidth == 0 || _prefsHeight == 0) {
|
||||
NSLog(@"[ScreenDumpVNC] No custom dimensions set, using native resolution");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate custom preferences
|
||||
if (_prefsWidth <= 0 || _prefsHeight <= 0) {
|
||||
NSLog(@"[ScreenDumpVNC] ERROR: Invalid custom dimensions (%dx%d), ignoring",
|
||||
_prefsWidth, _prefsHeight);
|
||||
_prefsWidth = 0;
|
||||
_prefsHeight = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate ideal dimensions maintaining aspect ratio
|
||||
double idealHeight = (double)_prefsWidth / _displayAspectRatio;
|
||||
double idealWidth = (double)_prefsHeight * _displayAspectRatio;
|
||||
|
||||
size_t adjustedWidth, adjustedHeight;
|
||||
|
||||
// Choose option that maximizes pixels while staying within BOTH constraints
|
||||
if (idealHeight <= (double)_prefsHeight) {
|
||||
// Use full width, adjust height down
|
||||
adjustedWidth = _prefsWidth;
|
||||
adjustedHeight = (size_t)floor(idealHeight);
|
||||
} else {
|
||||
// Use full height, adjust width down
|
||||
adjustedWidth = (size_t)floor(idealWidth);
|
||||
adjustedHeight = _prefsHeight;
|
||||
}
|
||||
|
||||
// Round width down to nearest multiple of 4 (VNC alignment requirement)
|
||||
adjustedWidth = (adjustedWidth / 4) * 4;
|
||||
|
||||
// Recalculate height based on aligned width to maintain exact aspect ratio
|
||||
if (adjustedWidth > 0) {
|
||||
adjustedHeight = (size_t)floor((double)adjustedWidth / _displayAspectRatio);
|
||||
}
|
||||
|
||||
// Round height down to nearest multiple of 4 for consistency
|
||||
adjustedHeight = (adjustedHeight / 4) * 4;
|
||||
|
||||
// Ensure minimum dimensions (4x4 to satisfy alignment requirements)
|
||||
if (adjustedWidth < 4) adjustedWidth = 4;
|
||||
if (adjustedHeight < 4) adjustedHeight = 4;
|
||||
|
||||
// Log adjustment if changed
|
||||
if (adjustedWidth != _prefsWidth || adjustedHeight != _prefsHeight) {
|
||||
NSLog(@"[ScreenDumpVNC] Adjusted resolution from %dx%d to %zux%zu to maintain %.6f aspect ratio",
|
||||
_prefsWidth, _prefsHeight, adjustedWidth, adjustedHeight, _displayAspectRatio);
|
||||
}
|
||||
|
||||
// Update preferences with corrected values
|
||||
_prefsWidth = (int)adjustedWidth;
|
||||
_prefsHeight = (int)adjustedHeight;
|
||||
}
|
||||
|
||||
-(void)setupScreenInfo {
|
||||
size_t bytesPerPixel;
|
||||
size_t bitsPerSample;
|
||||
@@ -101,8 +188,15 @@
|
||||
|
||||
if (_screenSurface == NULL) IOMobileFramebufferCopyLayerDisplayedSurface(framebufferConnection, 0, &_screenSurface);
|
||||
|
||||
_width = _prefsWidth == 0 ? IOSurfaceGetWidth(_screenSurface) : _prefsWidth;
|
||||
_height = _prefsHeight == 0 ? IOSurfaceGetHeight(_screenSurface) : _prefsHeight;
|
||||
// Calculate display aspect ratio from native IOSurface dimensions
|
||||
[self _calculateDisplayAspectRatio];
|
||||
|
||||
// Adjust custom preferences to maintain aspect ratio
|
||||
[self _adjustResolutionForAspectRatio];
|
||||
|
||||
// Set final dimensions (preferences are now corrected if custom, or will use native)
|
||||
_width = _prefsWidth == 0 ? _nativeWidth : _prefsWidth;
|
||||
_height = _prefsHeight == 0 ? _nativeHeight : _prefsHeight;
|
||||
|
||||
_sizeImage = IOSurfaceGetAllocSize(_screenSurface);
|
||||
// TODO: do these change at all? this might have been done for perf reasons
|
||||
@@ -122,6 +216,21 @@
|
||||
[NSNumber numberWithInt:'BGRA'], kIOSurfacePixelFormat,
|
||||
[NSNumber numberWithInt:(_width*_height*bytesPerPixel)], kIOSurfaceAllocSize,
|
||||
nil]);
|
||||
|
||||
// Create surface for CARenderServer capture (default mode)
|
||||
_renderServerSurface = IOSurfaceCreate((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"PurpleEDRAM", kIOSurfaceMemoryRegion,
|
||||
[NSNumber numberWithInt:bytesPerPixel*_width], kIOSurfaceBytesPerRow,
|
||||
[NSNumber numberWithInt:bytesPerPixel], kIOSurfaceBytesPerElement,
|
||||
[NSNumber numberWithInt:_width], kIOSurfaceWidth,
|
||||
[NSNumber numberWithInt:_height], kIOSurfaceHeight,
|
||||
[NSNumber numberWithInt:'BGRA'], kIOSurfacePixelFormat,
|
||||
[NSNumber numberWithInt:(_width*_height*bytesPerPixel)], kIOSurfaceAllocSize,
|
||||
nil]);
|
||||
|
||||
// Initialize capture mode to CARenderServer (default)
|
||||
_captureMode = CaptureModeCARenderServer;
|
||||
_legacyModeTimer = nil;
|
||||
}
|
||||
|
||||
int argc = 1;
|
||||
@@ -144,7 +253,20 @@
|
||||
|
||||
NSDictionary* defaults = getPrefsForAppId(@"com.mousen.screendump");
|
||||
bool useCADisplayLink = [[defaults objectForKey:@"displaysync"]?:@NO boolValue];
|
||||
_frameUpdater = [[FrameUpdater alloc] initWithSurfaceInfo:_screenSurface rfbScreenInfo:_rfbScreenInfo accelerator:_accelerator staticBuffer:_staticBuffer width:_width height:_height useCADisplayLink:useCADisplayLink];
|
||||
|
||||
__weak ScreenDumpVNC *weakSelf = self;
|
||||
_frameUpdater = [[FrameUpdater alloc] initWithSurfaceInfo:_screenSurface
|
||||
rfbScreenInfo:_rfbScreenInfo
|
||||
accelerator:_accelerator
|
||||
staticBuffer:_staticBuffer
|
||||
width:_width
|
||||
height:_height
|
||||
useCADisplayLink:useCADisplayLink
|
||||
renderServerSurface:_renderServerSurface
|
||||
captureModeBlock:^CaptureMode(void) {
|
||||
ScreenDumpVNC *strongSelf = weakSelf;
|
||||
return strongSelf ? strongSelf->_captureMode : CaptureModeCARenderServer;
|
||||
}];
|
||||
}
|
||||
|
||||
-(rfbBool)handleVNCAuthorization:(rfbClientPtr)client data:(const char *)data size:(int)size {
|
||||
@@ -172,4 +294,74 @@
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
-(void)toggleCaptureMode {
|
||||
if (_captureMode == CaptureModeCARenderServer) {
|
||||
// Switch to legacy mode
|
||||
_captureMode = CaptureModeIOMobileFramebuffer;
|
||||
NSLog(@"[ScreenDumpVNC] Switched to LEGACY capture mode (shows secure fields)");
|
||||
|
||||
// Cancel existing timer if any
|
||||
if (_legacyModeTimer && [_legacyModeTimer isValid]) {
|
||||
[_legacyModeTimer invalidate];
|
||||
_legacyModeTimer = nil;
|
||||
}
|
||||
|
||||
// Create new 10-second timer to auto-disable
|
||||
__weak ScreenDumpVNC *weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
ScreenDumpVNC *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
strongSelf->_legacyModeTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
|
||||
target:strongSelf
|
||||
selector:@selector(_disableLegacyMode)
|
||||
userInfo:nil
|
||||
repeats:NO];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Already in legacy mode, switch back to default immediately
|
||||
_captureMode = CaptureModeCARenderServer;
|
||||
NSLog(@"[ScreenDumpVNC] Switched to DEFAULT capture mode");
|
||||
|
||||
// Cancel timer
|
||||
if (_legacyModeTimer && [_legacyModeTimer isValid]) {
|
||||
[_legacyModeTimer invalidate];
|
||||
}
|
||||
_legacyModeTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
-(void)_disableLegacyMode {
|
||||
if (_captureMode != CaptureModeIOMobileFramebuffer) {
|
||||
return; // Already in default mode
|
||||
}
|
||||
|
||||
_captureMode = CaptureModeCARenderServer;
|
||||
NSLog(@"[ScreenDumpVNC] Auto-disabled legacy mode, returned to DEFAULT");
|
||||
|
||||
// Clean up timer
|
||||
if (_legacyModeTimer && [_legacyModeTimer isValid]) {
|
||||
[_legacyModeTimer invalidate];
|
||||
}
|
||||
_legacyModeTimer = nil;
|
||||
}
|
||||
|
||||
-(CaptureMode)currentCaptureMode {
|
||||
return _captureMode;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
// Clean up timer
|
||||
if (_legacyModeTimer && [_legacyModeTimer isValid]) {
|
||||
[_legacyModeTimer invalidate];
|
||||
_legacyModeTimer = nil;
|
||||
}
|
||||
|
||||
// Clean up render server surface
|
||||
if (_renderServerSurface) {
|
||||
CFRelease(_renderServerSurface);
|
||||
_renderServerSurface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -4,7 +4,7 @@ NSDictionary* getPrefsForAppId(NSString *appID) {
|
||||
NSDictionary* defaults = nil;
|
||||
CFArrayRef keyList = CFPreferencesCopyKeyList((CFStringRef)appID, CFSTR("mobile"), kCFPreferencesAnyHost);
|
||||
if (keyList) {
|
||||
defaults = (NSDictionary *)CFPreferencesCopyMultiple(keyList, (CFStringRef)appID, CFSTR("mobile"), kCFPreferencesAnyHost) ? : @{};
|
||||
defaults = CFBridgingRelease(CFPreferencesCopyMultiple(keyList, (CFStringRef)appID, CFSTR("mobile"), kCFPreferencesAnyHost)) ? : @{};
|
||||
CFRelease(keyList);
|
||||
}
|
||||
return defaults;
|
||||
|
||||
@@ -187,6 +187,14 @@ void VNCPointer(int buttons, int x, int y, rfbClientPtr client) {
|
||||
}
|
||||
|
||||
void handleVNCKeyboard(rfbBool down, rfbKeySym key, rfbClientPtr client) {
|
||||
// Check for backslash key press to toggle capture mode
|
||||
if (down && (key == XK_backslash || key == XK_bar)) {
|
||||
ScreenDumpVNC *sharedInstance = [ScreenDumpVNC sharedInstance];
|
||||
[sharedInstance toggleCaptureMode];
|
||||
// Consume the key - don't send to device
|
||||
return;
|
||||
}
|
||||
|
||||
VNCKeyboard(down, key, client);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user