From df2953cc7f5d84670d88dbd9a54554ed79196962 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 2 Jun 2026 16:59:51 +0100 Subject: [PATCH] Add SSH-triggered Eve screenshots --- README.md | 6 +++ Sources/EVEViewController.mm | 63 ++++++++++++++++++++++++++++++ scripts/capture-eve-screenshot.ps1 | 31 +++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 scripts/capture-eve-screenshot.ps1 diff --git a/README.md b/README.md index 7264f91..82234d6 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,12 @@ Launch `EveCanvas` from SpringBoard, or over SSH: uiopen --bundleid org.gamecult.evecanvas ``` +Capture the running native renderer over SSH: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\capture-eve-screenshot.ps1 +``` + If OpenGL ES context creation fails on-device, the app stays alive with the UIKit overlay instead of aborting launch. The render surface can then be fixed without losing the basic app deployment path. diff --git a/Sources/EVEViewController.mm b/Sources/EVEViewController.mm index 043ef78..92d90cf 100644 --- a/Sources/EVEViewController.mm +++ b/Sources/EVEViewController.mm @@ -321,6 +321,69 @@ static NSData *EVEMediaObservation(NSString *observationId, [self.glView renderAtTime:link.timestamp]; [self updateOverlay]; + [self serviceScreenshotRequestIfNeeded]; +} + +- (void)serviceScreenshotRequestIfNeeded { + NSString *directory = @"/var/mobile/Library/EveCanvas"; + NSString *requestPath = [directory stringByAppendingPathComponent:@"capture-request"]; + NSFileManager *fileManager = NSFileManager.defaultManager; + if (![fileManager fileExistsAtPath:requestPath]) { + return; + } + + [fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:nil]; + [fileManager removeItemAtPath:requestPath error:nil]; + + CGSize size = self.view.bounds.size; + if (size.width <= 0.0 || size.height <= 0.0) { + [@"invalid-view-size" writeToFile:[directory stringByAppendingPathComponent:@"capture-status.txt"] + atomically:YES + encoding:NSUTF8StringEncoding + error:nil]; + return; + } + + UIGraphicsBeginImageContextWithOptions(size, YES, UIScreen.mainScreen.scale); + BOOL rendered = [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:YES]; + if (!rendered) { + CGContextRef context = UIGraphicsGetCurrentContext(); + if (context) { + [self.view.layer renderInContext:context]; + rendered = YES; + } + } + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + if (!rendered || !image) { + [@"render-failed" writeToFile:[directory stringByAppendingPathComponent:@"capture-status.txt"] + atomically:YES + encoding:NSUTF8StringEncoding + error:nil]; + return; + } + + NSData *png = UIImagePNGRepresentation(image); + if (!png) { + [@"png-encoding-failed" writeToFile:[directory stringByAppendingPathComponent:@"capture-status.txt"] + atomically:YES + encoding:NSUTF8StringEncoding + error:nil]; + return; + } + + NSString *latestPath = [directory stringByAppendingPathComponent:@"latest-screenshot.png"]; + if ([png writeToFile:latestPath atomically:YES]) { + [@"ok" writeToFile:[directory stringByAppendingPathComponent:@"capture-status.txt"] + atomically:YES + encoding:NSUTF8StringEncoding + error:nil]; + } else { + [@"write-failed" writeToFile:[directory stringByAppendingPathComponent:@"capture-status.txt"] + atomically:YES + encoding:NSUTF8StringEncoding + error:nil]; + } } - (void)updateOverlay { diff --git a/scripts/capture-eve-screenshot.ps1 b/scripts/capture-eve-screenshot.ps1 new file mode 100644 index 0000000..43b9c40 --- /dev/null +++ b/scripts/capture-eve-screenshot.ps1 @@ -0,0 +1,31 @@ +param( + [string] $Target = "eve", + [string] $OutputDirectory = "artifacts\screenshots" +) + +$ErrorActionPreference = "Stop" + +$projectRoot = Split-Path -Parent $PSScriptRoot +$outputRoot = Join-Path $projectRoot $OutputDirectory +New-Item -ItemType Directory -Force -Path $outputRoot | Out-Null + +$stamp = Get-Date -Format "yyyyMMdd-HHmmss" +$localPath = Join-Path $outputRoot "eve-$stamp.png" +$remoteDir = "/var/mobile/Library/EveCanvas" +$remotePath = "$remoteDir/latest-screenshot.png" +$requestPath = "$remoteDir/capture-request" + +ssh $Target "mkdir -p '$remoteDir' && chown mobile:mobile '$remoteDir' && rm -f '$remotePath' '$requestPath' && touch '$requestPath' && chown mobile:mobile '$requestPath'" + +$deadline = (Get-Date).AddSeconds(10) +do { + Start-Sleep -Milliseconds 250 + ssh $Target "test -s '$remotePath'" + if ($LASTEXITCODE -eq 0) { + scp "${Target}:$remotePath" $localPath + Write-Host $localPath + exit 0 + } +} while ((Get-Date) -lt $deadline) + +throw "Timed out waiting for EveCanvas screenshot at ${Target}:$remotePath"