diff --git a/Config.xcconfig b/Config.xcconfig index b306d24..ae3746c 100644 --- a/Config.xcconfig +++ b/Config.xcconfig @@ -6,7 +6,7 @@ // VERSION = 1.0 -BUILD = 3 +BUILD = 27 // Configuration settings file format documentation can be found at: // https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project diff --git a/ShhShell.xcodeproj/project.pbxproj b/ShhShell.xcodeproj/project.pbxproj index 7bccab1..6465c15 100644 --- a/ShhShell.xcodeproj/project.pbxproj +++ b/ShhShell.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ A9083E402DF2226F0042906E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A9083E3F2DF2225A0042906E /* libz.tbd */; }; - A92317282E07111E00ECE1E6 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = A92317272E07111E00ECE1E6 /* SwiftTerm */; }; A923172A2E07113100ECE1E6 /* TerminalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92317292E07113100ECE1E6 /* TerminalController.swift */; }; A923172D2E07138000ECE1E6 /* SSHTerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A923172C2E07138000ECE1E6 /* SSHTerminalView.swift */; }; A923172F2E08851200ECE1E6 /* ShellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A923172E2E08851200ECE1E6 /* ShellView.swift */; }; @@ -32,6 +31,7 @@ A985545F2E056EDD009051BD /* KeychainLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A985545E2E056EDD009051BD /* KeychainLayer.swift */; }; A98554612E058433009051BD /* HostsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98554602E058433009051BD /* HostsManager.swift */; }; A98554632E0587DF009051BD /* HostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98554622E0587DF009051BD /* HostsView.swift */; }; + A9A587202E0BF220006B31E6 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = A9A5871F2E0BF220006B31E6 /* SwiftTerm */; }; A9B15A9A2E0ABA0400F66E02 /* DialogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B15A992E0ABA0400F66E02 /* DialogView.swift */; }; A9C4140C2E096DB7005E3047 /* SSHError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C4140B2E096DB7005E3047 /* SSHError.swift */; }; A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */; }; @@ -110,7 +110,7 @@ buildActionMask = 2147483647; files = ( A95FAA542DF4B62900DE2F5A /* LibSSH.xcframework in Frameworks */, - A92317282E07111E00ECE1E6 /* SwiftTerm in Frameworks */, + A9A587202E0BF220006B31E6 /* SwiftTerm in Frameworks */, A93143BE2DF4D0B300FCD5DB /* libpthread.tbd in Frameworks */, A9083E402DF2226F0042906E /* libz.tbd in Frameworks */, A95FAA562DF4B62A00DE2F5A /* openssl.xcframework in Frameworks */, @@ -294,7 +294,7 @@ ); name = ShhShell; packageProductDependencies = ( - A92317272E07111E00ECE1E6 /* SwiftTerm */, + A9A5871F2E0BF220006B31E6 /* SwiftTerm */, ); productName = ShhShell; productReference = A925389A2DEE06DC007E0A18 /* ShhShell.app */; @@ -373,7 +373,7 @@ mainGroup = A92538912DEE06DC007E0A18; minimizedProjectReferenceProxies = 1; packageReferences = ( - A92317262E07111E00ECE1E6 /* XCRemoteSwiftPackageReference "SwiftTerm" */, + A9A5871E2E0BF220006B31E6 /* XCRemoteSwiftPackageReference "SwiftTerm" */, ); preferredProjectObjectVersion = 77; productRefGroup = A925389B2DEE06DC007E0A18 /* Products */; @@ -624,7 +624,7 @@ SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -660,7 +660,7 @@ SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -779,20 +779,20 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - A92317262E07111E00ECE1E6 /* XCRemoteSwiftPackageReference "SwiftTerm" */ = { + A9A5871E2E0BF220006B31E6 /* XCRemoteSwiftPackageReference "SwiftTerm" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/migueldeicaza/SwiftTerm"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.2.5; + branch = main; + kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - A92317272E07111E00ECE1E6 /* SwiftTerm */ = { + A9A5871F2E0BF220006B31E6 /* SwiftTerm */ = { isa = XCSwiftPackageProductDependency; - package = A92317262E07111E00ECE1E6 /* XCRemoteSwiftPackageReference "SwiftTerm" */; + package = A9A5871E2E0BF220006B31E6 /* XCRemoteSwiftPackageReference "SwiftTerm" */; productName = SwiftTerm; }; /* End XCSwiftPackageProductDependency section */ diff --git a/ShhShell.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ShhShell.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 19775ed..a41539a 100644 --- a/ShhShell.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ShhShell.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/migueldeicaza/SwiftTerm", "state" : { - "revision" : "e2b431dbf73f775fb4807a33e4572ffd3dc6933a", - "version" : "1.2.5" + "branch" : "main", + "revision" : "f8e6e08b5a8c8eb0b18b9c250f36121e55c68f49" } } ], diff --git a/ShhShell/SSH/SSHHandler.swift b/ShhShell/SSH/SSHHandler.swift index b19f320..2149cd0 100644 --- a/ShhShell/SSH/SSHHandler.swift +++ b/ShhShell/SSH/SSHHandler.swift @@ -17,11 +17,12 @@ class SSHHandler: @unchecked Sendable, ObservableObject { // @Published var hostsManager = HostsManager() + @Published var title: String = "" @Published var connected: Bool = false @Published var authorized: Bool = false @Published var testSuceeded: Bool? = nil - @Published var bell: UUID? + @Published var bell: UUID? = nil @Published var host: Host @@ -76,6 +77,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject { } } openShell() + setTitle("\(host.username)@\(host.address)") ssh_channel_request_env(channel, "TERM", "xterm-256color") ssh_channel_request_env(channel, "LANG", "en_US.UTF-8") ssh_channel_request_env(channel, "LC_ALL", "en_US.UTF-8") @@ -132,6 +134,17 @@ class SSHHandler: @unchecked Sendable, ObservableObject { } + func ring() { + withAnimation { bell = UUID() } + DispatchQueue.main.asyncAfter(deadline: .now()+1) { + withAnimation { self.bell = nil } + } + } + + func setTitle(_ newTitle: String) { + self.title = newTitle + } + func testExec() { if ssh_is_connected(session) == 0 { withAnimation { testSuceeded = false } @@ -208,6 +221,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject { return } + //MARK: auth func authWithPubkey(pub pubInp: Data, priv privInp: Data, pass: String) throws(KeyError) { guard session != nil else { withAnimation { authorized = false } @@ -309,6 +323,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject { print(recievedMethod) } + //MARK: shell func openShell() { var status: CInt @@ -346,7 +361,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject { return nil } - var buffer: [CChar] = Array(repeating: 0, count: 4096) + var buffer: [CChar] = Array(repeating: 0, count: 16_384) let nbytes = ssh_channel_read_nonblocking(channel, &buffer, UInt32(buffer.count), 0) guard nbytes > 0 else { return nil } @@ -354,7 +369,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject { let data = Data(bytes: buffer, count: Int(nbytes)) if let string = String(data: data, encoding: .utf8) { #if DEBUG - print(String(data: Data(bytes: buffer, count: Int(nbytes)), encoding: .utf8)!) +// print(String(data: Data(bytes: buffer, count: Int(nbytes)), encoding: .utf8)!) #endif return string } diff --git a/ShhShell/Views/ConnectionView.swift b/ShhShell/Views/ConnectionView.swift index 6150076..94560f3 100644 --- a/ShhShell/Views/ConnectionView.swift +++ b/ShhShell/Views/ConnectionView.swift @@ -17,6 +17,7 @@ struct ConnectionView: View { @State var pubkeyStr: String = "" @State var privkeyStr: String = "" + @State var showTerminal: Bool = false @State var privPickerPresented: Bool = false @State var pubPickerPresented: Bool = false @@ -128,12 +129,12 @@ struct ConnectionView: View { } } - NavigationLink() { - ShellView(handler: handler) + Button() { + showTerminal.toggle() } label: { - Label("Open Terminal", systemImage: "apple.terminal") + Label("Show Terminal", systemImage: "apple.terminal") } - .disabled(!(handler.connected && handler.authorized)) + .disabled(!handler.connected || !handler.authorized) Button() { if handler.authorized && handler.connected { @@ -169,6 +170,7 @@ struct ConnectionView: View { ToolbarItem() { Button() { handler.go() + showTerminal = handler.connected && handler.authorized } label: { Label( handler.connected ? "Disconnect" : "Connect", @@ -178,6 +180,9 @@ struct ConnectionView: View { } } } + .fullScreenCover(isPresented: $showTerminal) { + ShellView(handler: handler) + } .onDisappear { guard hostsManager.getHostMatching(handler.host) == handler.host else { hostsManager.updateHost(handler.host) diff --git a/ShhShell/Views/HostsView.swift b/ShhShell/Views/HostsView.swift index 480ac83..154d49b 100644 --- a/ShhShell/Views/HostsView.swift +++ b/ShhShell/Views/HostsView.swift @@ -26,15 +26,15 @@ struct HostsView: View { } //proves that u can connect to multiple at the same time -// NavigationLink() { -// ForEach(hostsManager.savedHosts) { host in -// let miniHandler = SSHHandler(host: host) -// TerminalController(handler: miniHandler) -// .onAppear { miniHandler.go() } -// } -// } label: { -// Label("multiview", systemImage: "square.split.2x2") -// } + NavigationLink() { + ForEach(hostsManager.savedHosts) { host in + let miniHandler = SSHHandler(host: host) + TerminalController(handler: miniHandler) + .onAppear { miniHandler.go() } + } + } label: { + Label("multiview", systemImage: "square.split.2x2") + } ForEach(hostsManager.savedHosts) { host in NavigationLink() { diff --git a/ShhShell/Views/Terminal/SSHTerminalView.swift b/ShhShell/Views/Terminal/SSHTerminalView.swift index 6d67043..a12a2d1 100644 --- a/ShhShell/Views/Terminal/SSHTerminalView.swift +++ b/ShhShell/Views/Terminal/SSHTerminalView.swift @@ -35,7 +35,7 @@ final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalVie await self.feed(text: read) } } else { - try? await Task.sleep(nanoseconds: 1_000_000) //1ms + try? await Task.sleep(nanoseconds: 10_000_000) //10ms } } } @@ -54,7 +54,7 @@ final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalVie await self.feed(text: read) } } else { - Task{ try? await Task.sleep(nanoseconds: 1_000_000) } + Task{ try? await Task.sleep(nanoseconds: 10_000_000) } } } } @@ -69,7 +69,9 @@ final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalVie } nonisolated public func setTerminalTitle(source: TerminalView, title: String) { - print("set title to \(title)") + Task { + await MainActor.run { handler?.setTitle(title) } + } } public func sizeChanged(source: TerminalView, newCols: Int, newRows: Int) { diff --git a/ShhShell/Views/Terminal/ShellView.swift b/ShhShell/Views/Terminal/ShellView.swift index 1c48d3b..e701161 100644 --- a/ShhShell/Views/Terminal/ShellView.swift +++ b/ShhShell/Views/Terminal/ShellView.swift @@ -16,6 +16,11 @@ struct ShellView: View { var body: some View { NavigationStack { ZStack { + if handler.bell != nil { + Text("🔔") + .font(.title) + .transition(.scale) + } if !handler.connected { DialogView(handler: handler, showDialog: !handler.connected) } @@ -27,18 +32,27 @@ struct ShellView: View { .toolbar { ToolbarItem { Button() { - handler.go() - if !handler.connected { - dismiss() + Task { + await handler.disconnect() + if !handler.connected { dismiss() } } } label: { - Label( - handler.connected ? "Disconnect" : "Connect", - systemImage: handler.connected ? "xmark.app.fill" : "power" - ) + Label("Disconnect", systemImage: "xmark.app.fill") + } + } + ToolbarItem(placement: .cancellationAction) { + Button() { + dismiss() + } label: { + Label("Close", systemImage: "arrow.down.right.and.arrow.up.left") } } } + .onChange(of: handler.connected) { _ in + if !handler.connected { dismiss() } + } + .navigationTitle(handler.title) + .navigationBarTitleDisplayMode(.inline) } } }