From 1f687f0b6252c8d682a116607377d87565a7b0a7 Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Sat, 30 Aug 2025 09:48:54 +0100 Subject: [PATCH] fully working v1 of jelly cursor i plan to redo this cos its an overlay and not actually there, so it kinda breaks the illusion if scrolling --- ShhShell/Terminal/SSHTerminalDelegate.swift | 30 ++++++++++++++++++++- ShhShell/Views/Terminal/ShellView.swift | 13 +++++---- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/ShhShell/Terminal/SSHTerminalDelegate.swift b/ShhShell/Terminal/SSHTerminalDelegate.swift index c4deb31..cf2c9ec 100644 --- a/ShhShell/Terminal/SSHTerminalDelegate.swift +++ b/ShhShell/Terminal/SSHTerminalDelegate.swift @@ -27,6 +27,7 @@ final class SSHTerminalDelegate: TerminalView, Sendable, @preconcurrency Termina restoreScrollback() if let hostsManager { font = UIFont(name: hostsManager.selectedFont, size: hostsManager.fontSize)! + print(computeFontDimensions()) } applySelectedTheme() applyScrollbackLength() @@ -66,6 +67,34 @@ final class SSHTerminalDelegate: TerminalView, Sendable, @preconcurrency Termina super.cursorStyleChanged(source: source, newStyle: newStyle) } + //excerpt from SwiftTerm, modified to get around private properties + func computeFontDimensions () -> CGSize + { + let lineAscent = CTFontGetAscent (font) + let lineDescent = CTFontGetDescent (font) + let lineLeading = CTFontGetLeading (font) + let cellHeight = ceil(lineAscent + lineDescent + lineLeading) +#if os(macOS) + // The following is a more robust way of getting the largest ascii character width, but comes with a performance hit. + // See: https://github.com/migueldeicaza/SwiftTerm/issues/286 + // var sizes = UnsafeMutablePointer.allocate(capacity: 95) + // let ctFont = (font as CTFont) + // var glyphs = (32..<127).map { CTFontGetGlyphWithName(ctFont, String(Unicode.Scalar($0)) as CFString) } + // withUnsafePointer(to: glyphs[0]) { glyphsPtr in + // fontSet.normal.getAdvancements(NSSizeArray(sizes), forCGGlyphs: glyphsPtr, count: 95) + // } + // let cellWidth = (0..<95).reduce(into: 0) { partialResult, idx in + // partialResult = max(partialResult, sizes[idx].width) + // } + let glyph = font.glyph(withName: "W") + let cellWidth = font.advancement(forGlyph: glyph).width +#else + let fontAttributes = [NSAttributedString.Key.font: font] + let cellWidth = "W".size(withAttributes: fontAttributes).width +#endif + return CGSize(width: max (1, cellWidth), height: max (min (cellHeight, 8192), 1)) + } + func startFeedLoop() { Task { guard let handler else { return } @@ -73,7 +102,6 @@ final class SSHTerminalDelegate: TerminalView, Sendable, @preconcurrency Termina if let read = handler.readFromChannel() { await MainActor.run { self.feed(text: read) - print(getTerminal().getCursorLocation()) } } else { try? await Task.sleep(nanoseconds: 10_000_000) //10ms diff --git a/ShhShell/Views/Terminal/ShellView.swift b/ShhShell/Views/Terminal/ShellView.swift index 1ca9628..570a88b 100644 --- a/ShhShell/Views/Terminal/ShellView.swift +++ b/ShhShell/Views/Terminal/ShellView.swift @@ -14,6 +14,7 @@ struct ShellView: View { @ObservedObject var container = TerminalViewContainer.shared @State var jellyLoc: (x: Int, y: Int) = (0, 0) + @State var jellySize: CGSize = CGSize(width: 0, height: 0) @State var jellyShow: Bool = true @Environment(\.dismiss) var dismiss @@ -31,19 +32,21 @@ struct ShellView: View { let timer = Timer(timeInterval: 0.1, repeats: true) { timer in DispatchQueue.main.async { let terminalView = container.sessions[handler.sessionID ?? UUID()]?.terminalView + let delegate = terminalView?.terminalDelegate as? SSHTerminalDelegate terminalView?.getTerminal().hideCursor() - jellyLoc = terminalView?.getTerminal().getCursorLocation() ?? (0,0) - jellyShow = terminalView?.getTerminal().buffer.isCursorInViewPort ?? true + jellyLoc = terminalView?.getTerminal().getCursorLocation() ?? jellyLoc + jellySize = delegate?.computeFontDimensions() ?? jellySize +// jellyShow = terminalView?.getTerminal().buffer.isCursorInViewPort ?? jellyShow } } RunLoop.main.add(timer, forMode: .common) } Rectangle() - .frame(width: 4.3, height: 12) + .frame(width: jellySize.width, height: jellySize.height) .offset( - x: CGFloat(jellyLoc.x)*4.3, - y: CGFloat(jellyLoc.y)*8.66 + x: CGFloat(jellyLoc.x)*jellySize.width, + y: CGFloat(jellyLoc.y)*jellySize.height ) .opacity(jellyShow ? 1 : 0) .animation(.spring, value: jellyLoc.x)