Last active
December 13, 2019 21:42
-
-
Save erica/a93c9b7c3f5ca2245f24f8c5e08d318f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Darwin | |
/// Raw mode handling for character-by-character input in term | |
/// | |
/// Via [this stack overflow post][post] | |
/// | |
/// [post]: https://stackoverflow.com/questions/49748507/listening-to-stdin-in-swift | |
public enum RawMode { | |
/// Error states for raw mode entry | |
public enum State: Error { | |
case eof | |
} | |
/// Enable raw mode for user input | |
/// | |
/// Via [this example][example] | |
/// | |
/// [example]: https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html | |
public static func enableRawMode(fileHandle: FileHandle) -> termios { | |
let pointer = UnsafeMutablePointer<termios>.allocate(capacity: 1); defer { pointer.deallocate() } | |
var raw = pointer.pointee | |
tcgetattr(fileHandle.fileDescriptor, &raw) | |
let original = raw | |
raw.c_lflag &= ~(UInt(ECHO | ICANON)) | |
tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &raw) | |
return original | |
} | |
/// Disable raw mode for user input | |
/// | |
/// Via [this example][example] | |
/// | |
/// [example]: https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html | |
public static func disableRawMode(fileHandle: FileHandle) { | |
guard var term = term else { return } | |
tcsetattr(fileHandle.fileDescriptor, TCSAFLUSH, &term); | |
self.term = nil | |
} | |
/// The current termios | |
private static var term: termios? = nil | |
/// Fetch a byte at a time. | |
/// | |
/// Will establish a new termios instance if one is not already available. | |
/// | |
/// - Returns: A single `UInt8` if available, otherwise `nil` | |
/// - Throws: `State.eof` on receiving ^D | |
public static func getByte() throws -> UInt8? { | |
if nil == term { | |
term = enableRawMode(fileHandle: FileHandle.standardInput) | |
} | |
var byte: UInt8 = 0 | |
guard read(FileHandle.standardInput.fileDescriptor, &byte, 1) == 1 else { return nil } | |
if byte == 0x04 { throw State.eof } | |
return byte | |
} | |
} | |
do { | |
// Note: scalar.utf8 is only available in 10.15 | |
while let byte = try RawMode.getByte() { | |
let scalar = UnicodeScalar(byte) | |
switch scalar.isASCII { | |
case true: | |
print(Character(UnicodeScalar(byte)), ":", byte) | |
case false: | |
print(scalar.escaped(asASCII: true), ":", byte) | |
} | |
} | |
} catch RawMode.State.eof { | |
print("\n" + "eof") // ^D | |
RawMode.disableRawMode(fileHandle: FileHandle.standardInput) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment