//
// Term.swift
// CmdLine
//
// Created by Jacky Jack on 25/02/2023.
//
import Foundation
import Darwin
class Term {
let esc = "\u{001B}"
var ifd:Int32
var ofd:Int32
var orig_i:termios
var orig_o:termios
var raw_i:termios
var raw_o:termios
init() {
self.ifd = Darwin.STDIN_FILENO
self.ofd = Darwin.STDOUT_FILENO
self.orig_i = termios()
self.orig_o = termios()
self.raw_i = termios()
self.raw_o = termios()
//let p_i<> = UnsafeMutablePointer(self.orig_i)
if (-1 == tcgetattr(self.ifd, UnsafeMutablePointer(&self.orig_i))) {
print("tcgetattr error")
return
}
self.raw_i = self.orig_i
if ( -1 == tcgetattr(self.ofd, UnsafeMutablePointer(&self.orig_o))) {
print("tcgetattr error")
return
}
self.raw_o = self.orig_o
if (isatty(STDIN_FILENO) == 0) {
print("isatty failed")
return
}
}
func setSpeed(speed: speed_t) {
var ret:Int32=0
let raw_out = UnsafeMutablePointer(&self.raw_o)
ret = cfsetospeed(raw_out, speed)
tcsetattr(self.ofd, TCSANOW, raw_out)
let raw_in = UnsafeMutablePointer(&self.raw_o)
ret = cfsetispeed(raw_in, speed)
tcsetattr(self.ifd, TCSANOW, raw_in)
}
func getMaxCol() -> Int {
let orig_c = getC()
//go to right marging and get position
if (write(ofd, "\u{001B}[999C", 6) != 6) {
return -1
}
//get the position
let cur_c = getC()
if (cur_c == -1) {
return -1
}
//restore previouse position
let restore_position = "\u{001B}[\(cur_c-orig_c)D"
write(ofd, restore_position, restore_position.count)
return cur_c
}
func getMaxRow() -> Int {
let orig_r = getR()
//go to right marging and get position
if (write(ofd, "\u{001B}[999B", 6) != 6) {
return -1
}
//get the position
let cur_r = getR()
if (cur_r == -1) {
return -1
}
//restore previouse position
let restore_position = "\u{001B}[\(cur_r-orig_r)A"
write(ofd, restore_position, restore_position.count)
return cur_r
}
func getR() -> Int {
var buf = [UInt8](repeating: 0, count: 32)
if (write(ofd, "\u{001B}[6n", 4) != 4) {
print("Cant push escape codes for column")
}
var i=0;
while (i<buf.capacity-1) {
if (read(ifd, &buf[i], 1) == -1) {
break
}
//if eq R
if (buf[i] == 0x52) {
break
}
i+=1
}
//maybe not most optimal way
while (buf.count-1 > i) {
buf.removeLast()
}
if (buf[0] != 0x1b) && (buf[1] != 0x5b) {
return -1
}
var bufs:String = ""
for i in 0..<buf.count {
bufs += String(Character(UnicodeScalar(buf[i])))
}
let sindex = bufs.index(bufs.startIndex, offsetBy: 2)
let split = bufs.suffix(from:sindex).components(separatedBy: ";")
let s1 = split[0] // row
let s2 = split[1] //column
return Int(s1)!
}
func getC() -> Int {
var buf = [UInt8](repeating: 0, count: 32)
if (write(ofd, "\u{001B}[6n", 4) != 4) {
//print("Cant push escape codes for column")
}
var i=0;
while (i<buf.capacity-1) {
if (read(ifd, &buf[i], 1) == -1) {
break
}
//if eq R
if (buf[i] == 0x52) {
break
}
i+=1
}
//maybe not most optimal way
var j=0
while (buf.count > i) {
buf.removeLast()
j+=1
}
if (buf[0] != 0x1b) && (buf[1] != 0x5b) {
return -1
}
var bufs:String = ""
for i in 0..<buf.count {
bufs += String(Character(UnicodeScalar(buf[i])))
}
let sindex = bufs.index(bufs.startIndex, offsetBy: 2)
let split = bufs.suffix(from:sindex).components(separatedBy: ";")
let s1 = split[0] // row
let s2 = split[1] //column
return Int(s2)!
}
func setC(_ pos_c: Int) {
let cur_r = getR()
let escape_string = "\u{001B}[\(cur_r);\(pos_c)H"
write(ofd, escape_string, escape_string.count)
}
func setR(_ pos_r: Int) {
let cur_c = getC()
let escape_string = "\u{001B}[\(pos_r);\(cur_c)H"
write(ofd, escape_string, escape_string.count)
}
func setCR(_ pos_c:Int, _ pos_r: Int) {
let escape_string = "\u{001B}[\(pos_c);\(pos_r)H"
write(ofd, escape_string, escape_string.count)
}
func clearScreen() {
//let escape = String(Unicode.Scalar(0x1b)!)
let s = "\u{001B}[H\u{001B}[2J"
//print(s.count)
let ret = write(self.ofd, s, 7);
//let ret = write(self.ofd, String("\u{001b}[2J"),4);
//print("\(esc)[2J".utf8)
}
func setRawMode() {
if ( tcgetattr(self.ifd, UnsafeMutablePointer(&self.orig_i)) == -1) {
print("Cannot get terminal attribures")
return
}
raw_i = orig_i
var c_iflag:Int32 = Int32(raw_i.c_iflag)
c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)
raw_i.c_iflag = UInt(c_iflag)
var c_oflag:Int32 = Int32(raw_i.c_oflag)
c_oflag &= ~(OPOST)
raw_i.c_oflag = UInt(c_oflag)
var c_cflag:Int32 = Int32(raw_i.c_cflag)
c_cflag |= (CS8)
raw_i.c_cflag = UInt(c_cflag)
var c_lflag:Int32 = Int32(raw_i.c_lflag)
c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG)
raw_i.c_lflag = UInt(c_lflag)
//https://stackoverflow.com/questions/31465943/using-termios-in-swift
//raw_i.c_cc[VTIME] = 1
//raw_i.c_cc[VMIN] = 0;
/*withUnsafeMutablePointer(to: &raw_i.c_cc) { (tuplePtr) -> Void in
tuplePtr.withMemoryRebound(to: cc_t.self, capacity: MemoryLayout.size(ofValue: raw_i.c_cc)) {
$0[Int(VMIN)] = 1
//$0[Int(VTIME)] = 0
}
}*/
//print("\(raw_i)")
//print("VMIN=\(VMIN)")
raw_i.c_cc.16 = 1
//print("VTIME=\(VTIME)")
raw_i.c_cc.17 = 0
let p:UnsafePointer<termios> = UnsafePointer(&self.raw_i)
if (tcsetattr(self.ifd, TCSAFLUSH, UnsafePointer(p)) == -1) {
print("Cannot set new terminal input attribures")
}
}
func setConfigMode(c_iflag:Int32, c_oflag:Int32, c_cflag:Int32, c_lflag:Int32, vmin: UInt8, vtime: UInt8) {
if ( tcgetattr(self.ifd, UnsafeMutablePointer(&self.orig_i)) == -1) {
print("Cannot get terminal attribures")
return
}
raw_i.c_iflag = UInt(c_iflag)
raw_i.c_oflag = UInt(c_oflag)
raw_i.c_cflag = UInt(c_cflag)
raw_i.c_lflag = UInt(c_lflag)
raw_i.c_cc.16 = vmin
raw_i.c_cc.17 = vtime
let p:UnsafePointer<termios> = UnsafePointer(&self.raw_i)
if (tcsetattr(self.ifd, TCSAFLUSH, UnsafePointer(p)) == -1) {
print("Cannot set new terminal input attribures")
}
}
func modeRows() {
}
func modeColumns() {
}
func setOrigMode() {
if (tcsetattr(self.ifd, TCSAFLUSH, UnsafePointer(&self.orig_i)) == -1) {
print("Cannot set new terminal input attribures")
}
}
func print(_ s: String) {
write(self.ofd, s, s.count)
}
}