mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-15 20:10:30 +00:00
Return to the target app after attaching for JIT
This commit is contained in:
parent
c5b23e2282
commit
5e0e48d4e6
3 changed files with 81 additions and 8 deletions
|
|
@ -642,6 +642,31 @@ final class JITEnableContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func relaunchApp(_ bundleID: String, logger: LogFunc? = nil) -> Bool {
|
||||||
|
do {
|
||||||
|
let pid = try withConnectedRemoteServer { remoteServer in
|
||||||
|
try withProcessControl(remoteServer: remoteServer) { processControl in
|
||||||
|
var pid: UInt64 = 0
|
||||||
|
let ffiError = bundleID.withCString { bundleID in
|
||||||
|
process_control_launch_app(processControl, bundleID, nil, 0, nil, 0, false, false, &pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ffiError {
|
||||||
|
throw error(from: ffiError, fallback: "Failed to return to app")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitLog("Returned to \(bundleID) (PID \(pid))", logger: logger)
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
emitLog("Failed to return to \(bundleID): \(error.localizedDescription)", logger: logger)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func startSyslogRelay(handler: @escaping SyslogLineHandler, onError: @escaping SyslogErrorHandler) {
|
func startSyslogRelay(handler: @escaping SyslogLineHandler, onError: @escaping SyslogErrorHandler) {
|
||||||
do {
|
do {
|
||||||
try ensureTunnel()
|
try ensureTunnel()
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,17 @@ final class RunJSViewModel: ObservableObject, Identifiable, @unchecked Sendable
|
||||||
var debugProxy: OpaquePointer?
|
var debugProxy: OpaquePointer?
|
||||||
var remoteServer: OpaquePointer?
|
var remoteServer: OpaquePointer?
|
||||||
var semaphore: DispatchSemaphore?
|
var semaphore: DispatchSemaphore?
|
||||||
|
let resumeBundleID: String?
|
||||||
|
|
||||||
init(pid: Int, debugProxy: OpaquePointer?, remoteServer: OpaquePointer?, semaphore: DispatchSemaphore?) {
|
private let resumeLock = NSLock()
|
||||||
|
private var hasResumedApp = false
|
||||||
|
|
||||||
|
init(pid: Int, debugProxy: OpaquePointer?, remoteServer: OpaquePointer?, semaphore: DispatchSemaphore?, resumeBundleID: String? = nil) {
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self.debugProxy = debugProxy
|
self.debugProxy = debugProxy
|
||||||
self.remoteServer = remoteServer
|
self.remoteServer = remoteServer
|
||||||
self.semaphore = semaphore
|
self.semaphore = semaphore
|
||||||
|
self.resumeBundleID = resumeBundleID
|
||||||
}
|
}
|
||||||
|
|
||||||
func runScript(path: URL, scriptName: String? = nil) throws {
|
func runScript(path: URL, scriptName: String? = nil) throws {
|
||||||
|
|
@ -58,7 +63,13 @@ final class RunJSViewModel: ObservableObject, Identifiable, @unchecked Sendable
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleJSContextSendDebugCommand(self.context, commandStr, self.debugProxy) ?? ""
|
let response = handleJSContextSendDebugCommand(self.context, commandStr, self.debugProxy) ?? ""
|
||||||
|
|
||||||
|
if commandStr.hasPrefix("vAttach"), response.hasPrefix("T") {
|
||||||
|
self.resumeTargetApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
let logFunction: @convention(block) (String) -> Void = { logStr in
|
let logFunction: @convention(block) (String) -> Void = { logStr in
|
||||||
|
|
@ -79,7 +90,12 @@ final class RunJSViewModel: ObservableObject, Identifiable, @unchecked Sendable
|
||||||
return ProcessInfo.processInfo.hasTXM
|
return ProcessInfo.processInfo.hasTXM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let resumeAppFunction: @convention(block) () -> String = {
|
||||||
|
return self.resumeTargetApp()
|
||||||
|
}
|
||||||
|
|
||||||
context = JSContext()
|
context = JSContext()
|
||||||
|
context?.setObject(resumeAppFunction, forKeyedSubscript: "resume_app" as NSString)
|
||||||
context?.setObject(hasTXMFunction, forKeyedSubscript: "hasTXM" as NSString)
|
context?.setObject(hasTXMFunction, forKeyedSubscript: "hasTXM" as NSString)
|
||||||
context?.setObject(getPidFunction, forKeyedSubscript: "get_pid" as NSString)
|
context?.setObject(getPidFunction, forKeyedSubscript: "get_pid" as NSString)
|
||||||
context?.setObject(sendCommandFunction, forKeyedSubscript: "send_command" as NSString)
|
context?.setObject(sendCommandFunction, forKeyedSubscript: "send_command" as NSString)
|
||||||
|
|
@ -101,6 +117,35 @@ final class RunJSViewModel: ObservableObject, Identifiable, @unchecked Sendable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
private func resumeTargetApp() -> String {
|
||||||
|
resumeLock.lock()
|
||||||
|
if hasResumedApp {
|
||||||
|
resumeLock.unlock()
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
guard let resumeBundleID else {
|
||||||
|
resumeLock.unlock()
|
||||||
|
return "no app to return to for this session"
|
||||||
|
}
|
||||||
|
hasResumedApp = true
|
||||||
|
resumeLock.unlock()
|
||||||
|
|
||||||
|
appendLog("Returning to \(resumeBundleID)...")
|
||||||
|
let success = JITEnableContext.shared.relaunchApp(resumeBundleID)
|
||||||
|
guard success else {
|
||||||
|
appendLog("Failed to return to \(resumeBundleID) — continuing in the foreground.")
|
||||||
|
return "failed to return to \(resumeBundleID)"
|
||||||
|
}
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func appendLog(_ message: String) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.logs.append(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func captureScreenshot(named preferredName: String?) -> String {
|
private func captureScreenshot(named preferredName: String?) -> String {
|
||||||
if executionInterrupted {
|
if executionInterrupted {
|
||||||
raiseException("Script execution is interrupted by StikDebug.")
|
raiseException("Script execution is interrupted by StikDebug.")
|
||||||
|
|
|
||||||
|
|
@ -259,12 +259,13 @@ struct HomeView: View {
|
||||||
.accessibilityLabel(feedback.message)
|
.accessibilityLabel(feedback.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getJsCallback(_ script: Data, name: String? = nil) -> DebugAppCallback {
|
private func getJsCallback(_ script: Data, name: String? = nil, resumeBundleID: String? = nil) -> DebugAppCallback {
|
||||||
return { pid, debugProxyHandle, remoteServerHandle, semaphore in
|
return { pid, debugProxyHandle, remoteServerHandle, semaphore in
|
||||||
let model = RunJSViewModel(pid: Int(pid),
|
let model = RunJSViewModel(pid: Int(pid),
|
||||||
debugProxy: debugProxyHandle,
|
debugProxy: debugProxyHandle,
|
||||||
remoteServer: remoteServerHandle,
|
remoteServer: remoteServerHandle,
|
||||||
semaphore: semaphore)
|
semaphore: semaphore,
|
||||||
|
resumeBundleID: resumeBundleID)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
scriptRunModel = model
|
scriptRunModel = model
|
||||||
|
|
@ -338,9 +339,11 @@ struct HomeView: View {
|
||||||
scriptData = preferred.data
|
scriptData = preferred.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let resumeBundleID = pid == nil ? nil : bundleID
|
||||||
|
|
||||||
var callback: DebugAppCallback? = nil
|
var callback: DebugAppCallback? = nil
|
||||||
if ProcessInfo.processInfo.hasTXM, let sd = scriptData {
|
if ProcessInfo.processInfo.hasTXM, let sd = scriptData {
|
||||||
callback = getJsCallback(sd, name: scriptName ?? bundleID ?? "Script")
|
callback = getJsCallback(sd, name: scriptName ?? bundleID ?? "Script", resumeBundleID: resumeBundleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastDebugMessage: String?
|
var lastDebugMessage: String?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue