summaryrefslogblamecommitdiffstats
path: root/LibTerm/Term.swift
blob: 9ca17dc23570f01d0a92136ac46d4b97a0508ab5 (plain) (tree)























































































































































































































































































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