Miguel de Icaza 5 жил өмнө
parent
commit
2ecbd5d7ca

+ 42 - 1
README.md

@@ -1,7 +1,48 @@
-Validated and up to date:
+
+SwiftTerm
+=========
+
+SwiftTerm is a VT100/Xterm terminal emulator for Swift, the engine is
+intended to be agnostic from potential front-ends and backends (for example
+an AppKit front-end, a UIKit front-end, a TermKit front-end and so on;
+and backends to directly spawn a child shell, or connect it to a remote
+session).
+
+This is a work-in-progress, and a port of
+[XtermSharp](https://github.com/migueldeicaza/XtermSharp), which was
+itself based on xterm.js.
+
+The terminal itself does not deal with connecting the data to to a process
+or a remote server.   Data is sent to the terminal by passing a byte array
+with data to the "Feed" method.
+
+Convenience classes exist to spawn a subprocess and connecting the
+terminal to a local process, and allow some customization of the
+environment variables to pass to the child.
+
+Status
+======
+
+Validated and up to date with XtermSharp:
 
 * Buffer
+* BufferSet
+* CharData
 * TerminalOptions
 * BufferLine
+* CircularList
+* EscapeSequenceParser
+* EscapeSequences
+* Colors
+
+Pending:
+
+* Terminal
+* InputHandler
+* Pty
+* Reflows*
+* SelectionService
+* RuneExt
+
 
 Against version: 57cf109188551c5d5e7fa7d2158448b4e8d2be64 from Feb 27, 2020

+ 4 - 0
SwiftTerm.xcodeproj/project.pbxproj

@@ -20,6 +20,7 @@
 		49BD1A7F2242083D005A2252 /* CharData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BD1A7E2242083D005A2252 /* CharData.swift */; };
 		49EA37BC240B697300BC8A85 /* TerminalOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EA37BB240B697300BC8A85 /* TerminalOptions.swift */; };
 		49EA37BE240B71B800BC8A85 /* Reflow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EA37BD240B71B800BC8A85 /* Reflow.swift */; };
+		49EA37C0240C11CC00BC8A85 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EA37BF240C11CC00BC8A85 /* Colors.swift */; };
 		49F5BFD0224B14DC00E1C62F /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F5BFCF224B14DC00E1C62F /* Buffer.swift */; };
 		49F5BFD2224B150000E1C62F /* BufferLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F5BFD1224B150000E1C62F /* BufferLine.swift */; };
 		49F5BFD4224C5D3500E1C62F /* Terminal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F5BFD3224C5D3500E1C62F /* Terminal.swift */; };
@@ -53,6 +54,7 @@
 		49BD1A7E2242083D005A2252 /* CharData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharData.swift; sourceTree = "<group>"; };
 		49EA37BB240B697300BC8A85 /* TerminalOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalOptions.swift; sourceTree = "<group>"; };
 		49EA37BD240B71B800BC8A85 /* Reflow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reflow.swift; sourceTree = "<group>"; };
+		49EA37BF240C11CC00BC8A85 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
 		49F5BFCF224B14DC00E1C62F /* Buffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = "<group>"; };
 		49F5BFD1224B150000E1C62F /* BufferLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BufferLine.swift; sourceTree = "<group>"; };
 		49F5BFD3224C5D3500E1C62F /* Terminal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Terminal.swift; sourceTree = "<group>"; };
@@ -130,6 +132,7 @@
 				4900D7DD22506F5F0046E774 /* EscapeSequences.swift */,
 				49EA37BB240B697300BC8A85 /* TerminalOptions.swift */,
 				49EA37BD240B71B800BC8A85 /* Reflow.swift */,
+				49EA37BF240C11CC00BC8A85 /* Colors.swift */,
 			);
 			path = SwiftTerm;
 			sourceTree = "<group>";
@@ -245,6 +248,7 @@
 				499D02252249C409002BF7E7 /* CircularList.swift in Sources */,
 				49F5BFD2224B150000E1C62F /* BufferLine.swift in Sources */,
 				49F5BFD0224B14DC00E1C62F /* Buffer.swift in Sources */,
+				49EA37C0240C11CC00BC8A85 /* Colors.swift in Sources */,
 				49EA37BC240B697300BC8A85 /* TerminalOptions.swift in Sources */,
 				4900D7DE22506F5F0046E774 /* EscapeSequences.swift in Sources */,
 				49BD1A5E224207B5005A2252 /* AppDelegate.swift in Sources */,

+ 7 - 5
SwiftTerm/SwiftTerm/BufferSet.swift

@@ -24,10 +24,12 @@ class BufferSet {
         // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
         Alt = Buffer (terminal, hasScrollback: false)
         Active = Normal
-        SetupTabStops ()
+        setupTabStops ()
     }
     
-    public func ActivateNormalBuffer ()
+    public var isAlternateBuffer: Bool { Active === Normal }
+
+    public func activateNormalBuffer ()
     {
         if (Active === Normal) {
             return
@@ -43,7 +45,7 @@ class BufferSet {
         Active = Normal
     }
     
-    public func ActivateAltBuffer (fillAttr : Int32?)
+    public func activateAltBuffer (fillAttr : Int32?)
     {
         if (Active === Alt) {
             return
@@ -58,13 +60,13 @@ class BufferSet {
         Active = Alt
     }
     
-    func Resize (newColumns : Int, newRows : Int )
+    public func resize (newColumns : Int, newRows : Int )
     {
         Normal.resize (newCols: newColumns, newRows: newRows)
         Alt.resize (newCols: newColumns, newRows: newRows)
     }
     
-    func SetupTabStops (index : Int = -1)
+    public func setupTabStops (index : Int = -1)
     {
         Normal.setupTabStops(index: index)
         Alt.setupTabStops(index: index)

+ 2 - 2
SwiftTerm/SwiftTerm/CircularList.swift

@@ -44,7 +44,7 @@ class CircularList<T> {
         }
     }
 
-    init (maxLength: Int)
+    public init (maxLength: Int)
     {
         array = Array.init(repeating: nil, count: Int(maxLength))
         self.maxLength = maxLength
@@ -151,7 +151,7 @@ class CircularList<T> {
         }
     }
     
-    var IsFull: Bool {
+    var isFull: Bool {
         get {
             return length == maxLength
         }

+ 66 - 0
SwiftTerm/SwiftTerm/Colors.swift

@@ -0,0 +1,66 @@
+//
+//  Colors.swift
+//  SwiftTerm
+//
+//  Created by Miguel de Icaza on 3/1/20.
+//  Copyright © 2020 Miguel de Icaza. All rights reserved.
+//
+
+import Foundation
+
+public class Color {
+    public var red, green, blue: UInt8
+    static var defaultAnsiColors: [Color] = setupDefaultAnsiColors ()
+    static var defaultForeground = Color (red: 0xff, green: 0xff, blue: 0xff)
+    static var defaultBackground = Color (red: 0, green: 0, blue: 0)
+    
+    static func setupDefaultAnsiColors () -> [Color]
+    {
+        var colors: [Color] = [
+            // dark colors
+            Color (red: 0x2e, green: 0x34, blue: 0x36),
+            Color (red: 0xcc, green: 0x00, blue: 0x00),
+            Color (red: 0x4e, green: 0x9a, blue: 0x06),
+            Color (red: 0xc4, green: 0xa0, blue: 0x00),
+            Color (red: 0x34, green: 0x65, blue: 0xa4),
+            Color (red: 0x75, green: 0x50, blue: 0x7b),
+            Color (red: 0x06, green: 0x98, blue: 0x9a),
+            Color (red: 0xd3, green: 0xd7, blue: 0xcf),
+            
+            // bright colors
+            Color (red: 0x55, green: 0x57, blue: 0x53),
+            Color (red: 0xef, green: 0x29, blue: 0x29),
+            Color (red: 0x8a, green: 0xe2, blue: 0x34),
+            Color (red: 0xfc, green: 0xe9, blue: 0x4f),
+            Color (red: 0x72, green: 0x9f, blue: 0xcf),
+            Color (red: 0xad, green: 0x7f, blue: 0xa8),
+            Color (red: 0x34, green: 0xe2, blue: 0xe2),
+            Color (red: 0xee, green: 0xee, blue: 0xec)
+        ]
+        // Fill in the remaining 240 ANSI colors.
+        let v = [ 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff ];
+        
+        // Generate colors (16-231)
+        for i in 0..<216 {
+            let r = UInt8 (v [(i / 36) % 6])
+            let g = UInt8 (v [(i / 6) % 6])
+            let b = UInt8 (v [i % 6])
+
+            colors.append(Color (red: r, green: g, blue: b));
+        }
+
+        // Generate greys (232-255)
+        for i in 0..<24 {
+            let c = UInt8 (8 + i * 10)
+            colors.append (Color (red: c, green: c, blue: c));
+        }
+        return colors
+    }
+    
+    public init(red: UInt8, green: UInt8, blue: UInt8)
+    {
+        self.red = red
+        self.green = green
+        self.blue = blue
+    }
+}

+ 42 - 41
SwiftTerm/SwiftTerm/EscapeSequenceParser.swift

@@ -28,15 +28,15 @@ enum ParserState : UInt8 {
 typealias cstring = [UInt8]
 
 class ParsingState {
-    var position : Int
-    var code : UInt8
-    var currentState : ParserState
-    var print : Int
-    var dcs : Int
-    var osc : cstring
-    var collect : cstring
-    var parameters : [Int32]
-    var abort : Bool
+    var position: Int
+    var code: UInt8
+    var currentState: ParserState
+    var print: Int
+    var dcs: Int
+    var osc: cstring
+    var collect: cstring
+    var parameters: [Int32]
+    var abort: Bool
     
     init ()
     {
@@ -69,30 +69,31 @@ enum ParserAction : UInt8 {
     case DcsPut
     case DcsUnhook
 }
+
 class TransitionTable {
     // data is packed like this:
     // currentState << 8 | characterCode  -->  action << 4 | nextState
-    var table : [UInt8]
+    var table: [UInt8]
     
-    init (len : Int)
+    init (len: Int)
     {
         table = Array.init (repeating: 0, count: len)
     }
     
-    func Add (code : UInt8, state : ParserState, action : ParserAction, next : ParserState)
+    func Add (code: UInt8, state: ParserState, action: ParserAction, next: ParserState)
     {
         let v = (UInt8 (action.rawValue) << 4) | state.rawValue
         table [Int (UInt8(state.rawValue << 8) | code)] = v
     }
     
-    func Add (codes : [UInt8], state : ParserState, action : ParserAction, next : ParserState)
+    func Add (codes: [UInt8], state: ParserState, action: ParserAction, next: ParserState)
     {
         for c in codes {
             Add (code: c, state: state, action: action, next: next)
         }
     }
     
-    subscript (idx : Int) -> UInt8 {
+    subscript (idx: Int) -> UInt8 {
         get {
             return table [idx]
         }
@@ -100,14 +101,14 @@ class TransitionTable {
 }
 
 protocol  DcsHandler {
-    func hook (collect : cstring, parameters : [Int],  flag : UInt8)
+    func hook (collect: cstring, parameters: [Int],  flag: UInt8)
     func put (data : ArraySlice<UInt8>)
     func unhook ()
 }
 
 class EscapeSequenceParser {
     
-    static func r (low : UInt8, high : UInt8) -> [UInt8]
+    static func r (low: UInt8, high: UInt8) -> [UInt8]
     {
         let c = high-low
         var ret = [UInt8]()
@@ -132,7 +133,7 @@ class EscapeSequenceParser {
     
     static let NonAsciiPrintable : UInt8 = 0xa0
     
-    static func BuildVt500TransitionTable () -> TransitionTable
+    static func buildVt500TransitionTable () -> TransitionTable
     {
         let table = TransitionTable(len: 4095)
         let states = rinclusive(low: .Ground, high: .DcsPassthrough)
@@ -264,28 +265,28 @@ class EscapeSequenceParser {
     typealias ExecuteHandler = () -> ()
     
     // Handlers
-    var csiHandlers : [UInt8:CsiHandler] = [:]
-    var oscHandlers : [Int:OscHandler] = [:]
-    var executeHandlers : [UInt8:ExecuteHandler] = [:]
-    var escHandlers : [cstring:EscHandler] = [:]
-    var dcsHandlers : [cstring:DcsHandler] = [:]
-    var activeDcsHandler : DcsHandler? = nil
-    var errorHandler : (ParsingState) -> ParsingState = { (state : ParsingState) -> ParsingState in return state; }
+    var csiHandlers: [UInt8:CsiHandler] = [:]
+    var oscHandlers: [Int:OscHandler] = [:]
+    var executeHandlers: [UInt8:ExecuteHandler] = [:]
+    var escHandlers: [cstring:EscHandler] = [:]
+    var dcsHandlers: [cstring:DcsHandler] = [:]
+    var activeDcsHandler: DcsHandler? = nil
+    var errorHandler: (ParsingState) -> ParsingState = { (state : ParsingState) -> ParsingState in return state; }
     
-    var initialState : ParserState = .Ground
-    var currentState : ParserState = .Ground
+    var initialState: ParserState = .Ground
+    var currentState: ParserState = .Ground
     
     // buffers over several calls
-    var _osc : cstring
-    var _pars : [Int]
-    var _collect : cstring
-    var printHandler : PrintHandler = { (slice : ArraySlice<UInt8>) -> () in
+    var _osc: cstring
+    var _pars: [Int]
+    var _collect: cstring
+    var printHandler: PrintHandler = { (slice : ArraySlice<UInt8>) -> () in
     }
-    var table : TransitionTable
+    var table: TransitionTable
     
     init ()
     {
-        table = EscapeSequenceParser.BuildVt500TransitionTable()
+        table = EscapeSequenceParser.buildVt500TransitionTable()
         _osc = []
         _pars = [0]
         _collect = []
@@ -293,9 +294,9 @@ class EscapeSequenceParser {
         SetEscHandler([92], callback: EscHandlerFallback)
     }
     
-    func EscHandlerFallback (collect : cstring, flag : UInt8) {}
+    func EscHandlerFallback (collect: cstring, flag: UInt8) {}
     
-    func SetEscHandler (_ flag : cstring, callback : @escaping EscHandler)
+    func SetEscHandler (_ flag: cstring, callback: @escaping EscHandler)
     {
         escHandlers [flag] = callback
     }
@@ -303,11 +304,11 @@ class EscapeSequenceParser {
     var executeHandlerFallback : ExecuteHandler = { () -> () in
     }
     
-    var csiHandlerFallback : CsiHandlerFallback = { (pars : [Int], collect : cstring, code : UInt8) -> () in
+    var csiHandlerFallback : CsiHandlerFallback = { (pars: [Int], collect: cstring, code: UInt8) -> () in
         print ("Cannot handle ESC-\(code)")
     }
     
-    func Reset ()
+    func reset ()
     {
         currentState = initialState
         _osc = []
@@ -316,7 +317,7 @@ class EscapeSequenceParser {
         activeDcsHandler = nil
     }
 
-    func Parse (data : [UInt8], end : Int)
+    func Parse (data: [UInt8], end: Int)
     {
         var code : UInt8 = 0
         var transition : UInt8 = 0
@@ -371,7 +372,7 @@ class EscapeSequenceParser {
                 if ~print != 0 {
                     printHandler (data [print..<i])
                     print = -1
-                } else {
+                } else if ~dcs != 0 {
                     dcsHandler!.put (data: data [dcs..<i])
                     dcs = -1
                 }
@@ -494,10 +495,10 @@ class EscapeSequenceParser {
                     let semiColonAscii = 59 // ';'
                     
                     if let idx = osc.firstIndex (of: UInt8(semiColonAscii)){
-                        oscCode = ParseInt (osc [0..<idx])
+                        oscCode = parseInt (osc [0..<idx])
                         content = osc [(idx+1)...]
                     } else {
-                        oscCode = ParseInt (osc[0...])
+                        oscCode = parseInt (osc[0...])
                         content = []
                     }
                     if let handler = oscHandlers [oscCode] {
@@ -534,7 +535,7 @@ class EscapeSequenceParser {
         self.currentState = currentState
     }
     
-    func ParseInt (_ str: ArraySlice<UInt8>) -> Int
+    func parseInt (_ str: ArraySlice<UInt8>) -> Int
     {
         var result = 0
         for x in str {

+ 40 - 0
SwiftTerm/SwiftTerm/EscapeSequences.swift

@@ -25,3 +25,43 @@ struct ControlCodes  {
     static let SP  : UInt8 = 0x20
     static let DEL : UInt8 = 0x7f
 }
+
+/**
+* C1 control codes
+* See = https://en.wikipedia.org/wiki/C0_and_C1_control_codes
+*/
+struct C1 {
+    static let PAD : UInt8 = 0x80
+    static let HOP : UInt8 = 0x81
+    static let BPH : UInt8 = 0x82
+    static let NBH : UInt8 = 0x83
+    static let IND : UInt8 = 0x84
+    static let NEL : UInt8 = 0x85
+    static let SSA : UInt8 = 0x86
+    static let ESA : UInt8 = 0x87
+    static let HTS : UInt8 = 0x88
+    static let HTJ : UInt8 = 0x89
+    static let VTS : UInt8 = 0x8a
+    static let PLD : UInt8 = 0x8b
+    static let PLU : UInt8 = 0x8c
+    static let RI : UInt8 = 0x8d
+    static let SS2 : UInt8 = 0x8e
+    static let SS3 : UInt8 = 0x8f
+    static let DCS : UInt8 = 0x90
+    static let PU1 : UInt8 = 0x91
+    static let PU2 : UInt8 = 0x92
+    static let STS : UInt8 = 0x93
+    static let CCH : UInt8 = 0x94
+    static let MW : UInt8 = 0x95
+    static let SPA : UInt8 = 0x96
+    static let EPA : UInt8 = 0x97
+    static let SOS : UInt8 = 0x98
+    static let SGCI : UInt8 = 0x99
+    static let SCI : UInt8 = 0x9a
+    static let CSI : UInt8 = 0x9b
+    static let ST : UInt8 = 0x9c
+    static let OSC : UInt8 = 0x9d
+    static let PM : UInt8 = 0x9e
+    static let APC : UInt8 = 0x9f
+
+}