Skip to content

Commit

Permalink
Change Terminal a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
zaneenders committed Jul 4, 2024
1 parent 071acc2 commit a3df175
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 40 deletions.
9 changes: 4 additions & 5 deletions Sources/ChromaShell/ChromaFrame.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@ public struct ChromaFrame {
/// NOTE: we can not test this as the test terminal has no size. I
/// wonder if can detect if testing or just have to work around this.
init(
fill char: Character,
fill char: Character, _ height: Int, _ width: Int,
_ foreground: Color = .default,
_ background: Color = .default
) {
var out = ""
let size = Terminal.size()
for y in 0...size.y {
for _ in 1...size.x {
for y in 0...height {
for _ in 1...width {
out += "\(char)"
}
if y != size.y {
if y != height {
out += "\n"
}
}
Expand Down
17 changes: 4 additions & 13 deletions Sources/ChromaShell/InteractionLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,22 @@ import ScribeCore
/// having a deinit for structs.
struct InteractionLoop: ~Copyable {

private let originalConfig = Terminal.enableRawMode()
private let scribe: Scribe
let terminal: Terminal = Terminal()

init(_ block: some Block) async {
let size = Terminal.size()
self.scribe = await Scribe(
observing: block, width: size.x, height: size.y, draw(_:_:_:))
Terminal.setup()
observing: block, width: terminal.size.x, height: terminal.size.y, draw(_:_:_:))
}

deinit {
Terminal.restore(originalConfig)
Terminal.reset()
}

func start() async {
let standardInput = Process().standardInput
let fileHandleForStandardIn = standardInput as! FileHandle

var size = Terminal.size()
// Draw the first frame
let frame = await scribe.current
draw(frame, size.x, size.y)
draw(frame, terminal.size.x, terminal.size.y)
do {
for try await byte in fileHandleForStandardIn.asyncByteIterator() {
// update on input
Expand All @@ -37,8 +30,7 @@ struct InteractionLoop: ~Copyable {
}

// Update size for next frame
size = Terminal.size()
await scribe.updateSize(width: size.x, height: size.y)
await scribe.updateSize(width: terminal.size.x, height: terminal.size.y)

switch await scribe.mode {
case .input:
Expand Down Expand Up @@ -75,7 +67,6 @@ struct InteractionLoop: ~Copyable {
} catch let error {
// Lets keep all errors internal as we are changing the default behavior of the Terminal
// TODO logging
Terminal.reset()
print(error.localizedDescription)
return
}
Expand Down
53 changes: 31 additions & 22 deletions Sources/ChromaShell/Terminal/Terminal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,31 @@ import Foundation
/// they are pressed. This is definitely a HACK on top of the terminal to test
/// out an idea I have been stuck on as It seemed easier then learning how
/// MacOS and other operating systems send key commands to programs.
public enum Terminal {
static func enableRawMode() -> termios {
public struct Terminal: ~Copyable {
private let prev: termios

public init() {
self.prev = Terminal.enableRawMode()
Terminal.setup()
}

/// Restore the original terminal config
/// Clear the last frame from the screen
deinit {
Terminal.restore(prev)
Terminal.reset()
}

private static func restore(_ originalConfig: termios) {
var term = originalConfig
// restores the original terminal state
tcsetattr(FileHandle.standardInput.fileDescriptor, TCSAFLUSH, &term)
}

private static func enableRawMode() -> termios {
// see https://stackoverflow.com/a/24335355/669586
// init raw: termios variable
var raw: termios = initCStruct()
var raw: termios = Terminal.initCStruct()
// sets raw to a copy of the file handlers attributes
tcgetattr(FileHandle.standardInput.fileDescriptor, &raw)
// saves a copy of the original standard output file descriptor to revert back to
Expand All @@ -39,24 +59,17 @@ public enum Terminal {
return originalConfig
}

static func restore(_ originalConfig: termios) {
var term = originalConfig
// restores the original terminal state
tcsetattr(FileHandle.standardInput.fileDescriptor, TCSAFLUSH, &term)
}

static func initCStruct<S>() -> S {
private static func initCStruct<S>() -> S {
let structPointer = UnsafeMutablePointer<S>.allocate(capacity: 1)
let structMemory = structPointer.pointee
structPointer.deallocate()
return structMemory
}

/// Returns the max dimensions of the current Terminal
public static func size() -> TerminalSize {
public var size: TerminalSize {
// TODO look into the SIGWINCH signal maybe replace this function or
// its call sites.
var w: winsize = initCStruct()
var w: winsize = Terminal.initCStruct()
//???: Is it possible to get a call back or notification of when the window is resized
_ = ioctl(STDOUT_FILENO, UInt(TIOCGWINSZ), &w)
// Check that we have a valid window size
Expand All @@ -69,10 +82,6 @@ public enum Terminal {
}
}

enum TerminalSizeError: Error {
case hight
case width
}
}

public struct TerminalSize: Hashable {
Expand All @@ -93,13 +102,13 @@ extension Terminal {
}

/// Used to write the contents of of the frame to the screen.
static func write(frame strFrame: String) {
public static func write(frame strFrame: String) {
clear()
FileHandle.standardOutput.write(Data(strFrame.utf8))
}

/// clears the screen to setup, reset or write a new frame to the screen.
static func clear() {
private static func clear() {
FileHandle.standardOutput.write(Data(Terminal.clearCode.utf8))
}

Expand All @@ -109,17 +118,17 @@ extension Terminal {
FileHandle.standardOutput.write(Data(Terminal.restCode.utf8))
}

static var restCode: String {
private static var restCode: String {
AnsiEscapeCode.Cursor.show.rawValue
+ AnsiEscapeCode.Cursor.Style.Block.blinking.rawValue
+ AnsiEscapeCode.home.rawValue
}

static var setupCode: String {
private static var setupCode: String {
AnsiEscapeCode.Cursor.hide.rawValue + clearCode
}

static var clearCode: String {
private static var clearCode: String {
AnsiEscapeCode.eraseScreen.rawValue + AnsiEscapeCode.eraseSaved.rawValue
+ AnsiEscapeCode.home.rawValue
+ AnsiEscapeCode.Cursor.Style.Block.blinking.rawValue
Expand Down

0 comments on commit a3df175

Please sign in to comment.