bell ringing backend done now need to actually trigger it

added ring() to ring the bell
terminal is now a fullscreencover
can be dismissed and reopened or disconnected
 - need to make it keep the scrollback
increased sleep between reads
added support for title
added setTitle function, defaults to username@server
increased the buffer to 16384 bytes
This commit is contained in:
neon443
2025-06-25 11:12:10 +01:00
parent bf4a273a46
commit f71bd0ddf2
8 changed files with 76 additions and 40 deletions

View File

@@ -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

View File

@@ -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 */

View File

@@ -6,8 +6,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/migueldeicaza/SwiftTerm",
"state" : {
"revision" : "e2b431dbf73f775fb4807a33e4572ffd3dc6933a",
"version" : "1.2.5"
"branch" : "main",
"revision" : "f8e6e08b5a8c8eb0b18b9c250f36121e55c68f49"
}
}
],

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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() {

View File

@@ -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) {

View File

@@ -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)
}
}
}