Skip to content

Commit ce1b855

Browse files
committed
Integate changes from main
2 parents 64a96cc + 9dab8ae commit ce1b855

5 files changed

Lines changed: 194 additions & 62 deletions

File tree

Sources/SwiftTerm/Apple/AppleTerminalView.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -735,9 +735,6 @@ extension TerminalView {
735735
let runFont = runAttributes[.font] as! TTFont
736736
let startColumn = segment.column + (processedGlyphs * segment.columnWidth)
737737
let endColumn = startColumn + (runGlyphsCount * segment.columnWidth)
738-
if row == 0 {
739-
print(run)
740-
}
741738
var backgroundColor: TTColor?
742739
if runAttributes.keys.contains(.selectionBackgroundColor) {
743740
backgroundColor = runAttributes[.selectionBackgroundColor] as? TTColor
@@ -1116,7 +1113,6 @@ extension TerminalView {
11161113
let oldPosition = displayBuffer.yDisp
11171114

11181115
let maxScrollback = displayBuffer.lines.count - displayBuffer.rows
1119-
print ("maxScrollBack: \(maxScrollback)")
11201116
var newScrollPosition = Int (Double (maxScrollback) * toPosition)
11211117

11221118
if newScrollPosition < 0 {
@@ -1125,8 +1121,7 @@ extension TerminalView {
11251121
if newScrollPosition > maxScrollback {
11261122
newScrollPosition = maxScrollback
11271123
}
1128-
print ("newScrollpsitin: \(newScrollPosition)")
1129-
1124+
11301125
if newScrollPosition != oldPosition {
11311126
scrollTo(row: newScrollPosition)
11321127
}
@@ -1232,6 +1227,13 @@ extension TerminalView {
12321227
public func send(data: ArraySlice<UInt8>)
12331228
{
12341229
ensureCaretIsVisible ()
1230+
#if os(iOS) || os(visionOS)
1231+
if TerminalView.textInputDebugEnabled {
1232+
let previewBytes = data.prefix(32).map { String(format: "%02X", $0) }.joined(separator: " ")
1233+
print("UITextInput[\(TerminalView.textInputLogCounter + 1)]: send bytes=\(data.count) [\(previewBytes)]")
1234+
TerminalView.textInputLogCounter += 1
1235+
}
1236+
#endif
12351237
terminalDelegate?.send (source: self, data: data)
12361238
}
12371239

@@ -1240,6 +1242,12 @@ extension TerminalView {
12401242
* - Parameter txt: the string to send to the client
12411243
*/
12421244
public func send (txt: String) {
1245+
#if os(iOS) || os(visionOS)
1246+
if TerminalView.textInputDebugEnabled {
1247+
print("UITextInput[\(TerminalView.textInputLogCounter + 1)]: send txt=\(txt.debugDescription)")
1248+
TerminalView.textInputLogCounter += 1
1249+
}
1250+
#endif
12431251
let array = [UInt8] (txt.utf8)
12441252
send (data: array[...])
12451253
}

Sources/SwiftTerm/Mac/MacLocalTerminalView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,11 @@ open class LocalProcessTerminalView: TerminalView, TerminalViewDelegate, LocalPr
156156
* - Parameter args: an array of strings that is passed as the arguments to the underlying process
157157
* - Parameter environment: an array of environment variables to pass to the child process, if this is null, this picks a good set of defaults from `Terminal.getEnvironmentVariables`.
158158
* - Parameter execName: If provided, this is used as the Unix argv[0] parameter, otherwise, the executable is used as the args [0], this is used when the intent is to set a different process name than the file that backs it.
159+
* - Parameter currentDirectory: If provided, the process will be launched with this as the current working directory.
159160
*/
160-
public func startProcess(executable: String = "/bin/bash", args: [String] = [], environment: [String]? = nil, execName: String? = nil)
161+
public func startProcess(executable: String = "/bin/bash", args: [String] = [], environment: [String]? = nil, execName: String? = nil, currentDirectory: String? = nil)
161162
{
162-
process.startProcess(executable: executable, args: args, environment: environment, execName: execName)
163+
process.startProcess(executable: executable, args: args, environment: environment, execName: execName, currentDirectory: currentDirectory)
163164
}
164165

165166
/**

Sources/SwiftTerm/Mac/MacTerminalView.swift

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -907,19 +907,23 @@ open class TerminalView: NSView, NSTextInputClient, NSUserInterfaceValidations,
907907
let y = min (max (p.y, 0), bounds.height)
908908
return Position (col: Int (x), row: Int (bounds.height-y))
909909
}
910+
let displayBuffer = terminal.displayBuffer
910911
let col = Int (point.x / cellDimension.width)
911912
let row = Int ((frame.height-point.y) / cellDimension.height)
912-
if row < 0 {
913-
return (Position(col: 0, row: 0), toInt (point))
914-
}
915-
return (Position(col: min (max (0, col), terminal.cols-1), row: row), toInt (point))
913+
let colValue = min (max (0, col), terminal.cols-1)
914+
let bufferRow = row + displayBuffer.yDisp
915+
let maxRow = max (0, displayBuffer.lines.count - 1)
916+
let rowValue = min (max (0, bufferRow), maxRow)
917+
return (Position(col: colValue, row: rowValue), toInt (point))
916918
}
917919

918920
private func sharedMouseEvent (with event: NSEvent)
919921
{
922+
let displayBuffer = terminal.displayBuffer
920923
let hit = calculateMouseHit(with: event)
921924
let buttonFlags = encodeMouseEvent(with: event)
922-
terminal.sendEvent(buttonFlags: buttonFlags, x: hit.grid.col, y: hit.grid.row, pixelX: hit.pixels.col, pixelY: hit.pixels.row)
925+
let screenRow = max (0, min (displayBuffer.rows - 1, hit.grid.row - displayBuffer.yDisp))
926+
terminal.sendEvent(buttonFlags: buttonFlags, x: hit.grid.col, y: screenRow, pixelX: hit.pixels.col, pixelY: hit.pixels.row)
923927
}
924928

925929
private var autoScrollDelta = 0
@@ -948,19 +952,19 @@ open class TerminalView: NSView, NSTextInputClient, NSUserInterfaceValidations,
948952
case 1:
949953
if selection.active == true {
950954
if event.modifierFlags.contains(.shift) {
951-
selection.shiftExtend(row: hit.row, col: hit.col)
955+
selection.shiftExtend(bufferPosition: Position(col: hit.col, row: hit.row))
952956
} else {
953957
selection.active = false
954958
}
955959
}
956960
case 2:
957961
let displayBuffer = terminal.displayBuffer
958-
selection.selectWordOrExpression(at: Position(col: hit.col, row: hit.row + displayBuffer.yDisp), in: displayBuffer)
962+
selection.selectWordOrExpression(at: Position(col: hit.col, row: hit.row), in: displayBuffer)
959963

960964
default:
961965
// 3 and higher
962966

963-
selection.select(row: hit.row + terminal.displayBuffer.yDisp)
967+
selection.select(row: hit.row)
964968
}
965969
setNeedsDisplay(bounds)
966970
}
@@ -969,7 +973,7 @@ open class TerminalView: NSView, NSTextInputClient, NSUserInterfaceValidations,
969973
{
970974
let hit = calculateMouseHit(with: event).grid
971975
let displayBuffer = terminal.displayBuffer
972-
let cd = displayBuffer.lines [displayBuffer.yDisp+hit.row][hit.col]
976+
let cd = displayBuffer.lines [hit.row][hit.col]
973977
return cd.getPayload()
974978
}
975979

@@ -997,13 +1001,14 @@ open class TerminalView: NSView, NSTextInputClient, NSUserInterfaceValidations,
9971001
}
9981002

9991003
public override func mouseDragged(with event: NSEvent) {
1004+
let displayBuffer = terminal.displayBuffer
10001005
let mouseHit = calculateMouseHit(with: event)
10011006
let hit = mouseHit.grid
10021007
if allowMouseReporting {
10031008
if terminal.mouseMode.sendMotionEvent() {
10041009
let flags = encodeMouseEvent(with: event)
1005-
1006-
terminal.sendMotion(buttonFlags: flags, x: hit.col, y: hit.row, pixelX: mouseHit.pixels.col, pixelY: mouseHit.pixels.row)
1010+
let screenRow = max (0, min (displayBuffer.rows - 1, hit.row - displayBuffer.yDisp))
1011+
terminal.sendMotion(buttonFlags: flags, x: hit.col, y: screenRow, pixelX: mouseHit.pixels.col, pixelY: mouseHit.pixels.row)
10071012

10081013
return
10091014
}
@@ -1013,17 +1018,19 @@ open class TerminalView: NSView, NSTextInputClient, NSUserInterfaceValidations,
10131018
}
10141019

10151020
if selection.active {
1016-
selection.dragExtend(row: hit.row, col: hit.col)
1021+
selection.dragExtend(bufferPosition: Position(col: hit.col, row: hit.row))
10171022
} else {
1018-
selection.startSelection(row: hit.row, col: hit.col)
1023+
selection.setSoftStart(bufferPosition: Position(col: hit.col, row: hit.row))
1024+
selection.startSelection()
10191025
}
10201026
didSelectionDrag = true
10211027
autoScrollDelta = 0
1028+
let screenRow = hit.row - displayBuffer.yDisp
10221029
if selection.active {
1023-
if hit.row <= 0 {
1024-
autoScrollDelta = calcScrollingVelocity(delta: hit.row * -1) * -1
1025-
} else if hit.row >= terminal.rows {
1026-
autoScrollDelta = calcScrollingVelocity(delta: hit.row - terminal.rows)
1030+
if screenRow <= 0 {
1031+
autoScrollDelta = calcScrollingVelocity(delta: screenRow * -1) * -1
1032+
} else if screenRow >= displayBuffer.rows {
1033+
autoScrollDelta = calcScrollingVelocity(delta: screenRow - displayBuffer.rows)
10271034
}
10281035
}
10291036
setNeedsDisplay(bounds)

Sources/SwiftTerm/iOS/iOSTerminalView.swift

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ internal var log: Logger = Logger(subsystem: "org.tirania.SwiftTerm", category:
4242
* defaults, otherwise, this uses its own set of defaults colors.
4343
*/
4444
open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollViewDelegate, TerminalDelegate {
45+
public static var textInputDebugEnabled: Bool = false
46+
internal static var textInputLogCounter: Int = 0
47+
4548
struct FontSet {
4649
public let normal: UIFont
4750
let bold: UIFont
@@ -1086,14 +1089,20 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
10861089
}
10871090

10881091
public var hasText: Bool {
1089-
return true
1092+
return !textInputStorage.isEmpty
10901093
}
10911094

10921095
/*
1093-
Soft keyboard input. Hardware keyboard input is handled in pressesBegan.
1096+
Soft keyboard input. Hardware keyboard text input is delivered here; special keys are handled in pressesBegan.
10941097
*/
10951098
open func insertText(_ text: String) {
1096-
uitiLog("insertText(\"\(text)\") textInputStorage:\"\(textInputStorage)\"")
1099+
//uitiLog("insertText(\(text.debugDescription)) \(textInputStateDescription())")
1100+
1101+
if tryComposeKoreanFinal(text) {
1102+
return
1103+
}
1104+
1105+
beginTextInputEdit()
10971106

10981107
let rangeToReplace = _markedTextRange ?? _selectedTextRange
10991108
let rangeStartIndex = rangeToReplace.startPosition.offset
@@ -1102,6 +1111,8 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11021111
let insertedPosition = TextPosition(offset: rangeStartIndex + text.count)
11031112
_selectedTextRange = TextRange(from: insertedPosition, to: insertedPosition)
11041113

1114+
endTextInputEdit()
1115+
11051116
if terminalAccessory?.controlModifier ?? false {
11061117
self.send(applyControlToEventCharacters(text))
11071118
terminalAccessory?.controlModifier = false
@@ -1117,15 +1128,78 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11171128
queuePendingDisplay()
11181129
}
11191130

1131+
// this is necessary because something in the iOS IME seems to prevent
1132+
// the sequence "ㅇ", "ㅜ", "ㅇ" from becoming "웅", and instead
1133+
// it becomes "우" followed by "ㅇ"
1134+
private func tryComposeKoreanFinal(_ text: String) -> Bool {
1135+
guard let language = textInputMode?.primaryLanguage, language.hasPrefix("ko") else { return false }
1136+
guard _markedTextRange == nil else { return false }
1137+
guard _selectedTextRange.isEmpty, _selectedTextRange.endPosition.offset == textInputStorage.count else { return false }
1138+
guard text.count == 1, let jamo = text.first else { return false }
1139+
guard let finalIndex = koreanFinalIndex[jamo] else { return false }
1140+
guard let lastChar = textInputStorage.last else { return false }
1141+
guard let composed = composeHangulSyllable(base: lastChar, finalIndex: finalIndex) else { return false }
1142+
1143+
uitiLog("koreanComposeFinal base:\(lastChar) jamo:\(jamo) -> \(composed)")
1144+
1145+
beginTextInputEdit()
1146+
textInputStorage.removeLast()
1147+
textInputStorage.append(composed)
1148+
let newOffset = textInputStorage.count
1149+
_markedTextRange = nil
1150+
_selectedTextRange = TextRange(from: TextPosition(offset: newOffset), to: TextPosition(offset: newOffset))
1151+
endTextInputEdit()
1152+
1153+
send([backspaceSendsControlH ? 8 : 0x7f])
1154+
send(txt: String(composed))
1155+
queuePendingDisplay()
1156+
return true
1157+
}
1158+
1159+
private let koreanFinalIndex: [Character: Int] = [
1160+
"": 1, "": 2, "": 3,
1161+
"": 4, "": 5, "": 6,
1162+
"": 7,
1163+
"": 8, "": 9, "": 10, "": 11, "": 12, "": 13, "": 14, "": 15,
1164+
"": 16,
1165+
"": 17, "": 18,
1166+
"": 19, "": 20,
1167+
"": 21,
1168+
"": 22,
1169+
"": 23,
1170+
"": 24,
1171+
"": 25,
1172+
"": 26,
1173+
"": 27
1174+
]
1175+
1176+
private func composeHangulSyllable(base: Character, finalIndex: Int) -> Character? {
1177+
guard finalIndex > 0 && finalIndex < 28 else { return nil }
1178+
guard let scalar = base.unicodeScalars.first, base.unicodeScalars.count == 1 else { return nil }
1179+
let scalarValue = Int(scalar.value)
1180+
let sBase = 0xAC00
1181+
let sEnd = 0xD7A3
1182+
guard scalarValue >= sBase && scalarValue <= sEnd else { return nil }
1183+
let vCount = 21
1184+
let tCount = 28
1185+
let sIndex = scalarValue - sBase
1186+
let lIndex = sIndex / (vCount * tCount)
1187+
let vIndex = (sIndex % (vCount * tCount)) / tCount
1188+
let tIndex = sIndex % tCount
1189+
guard tIndex == 0 else { return nil }
1190+
let newScalarValue = sBase + (lIndex * vCount + vIndex) * tCount + finalIndex
1191+
guard let newScalar = UnicodeScalar(newScalarValue) else { return nil }
1192+
return Character(newScalar)
1193+
}
1194+
11201195
func ensureCaretIsVisible ()
11211196
{
11221197
let displayBuffer = terminal.displayBuffer
11231198
contentOffset = CGPoint (x: 0, y: CGFloat (displayBuffer.lines.count-displayBuffer.rows)*cellDimension.height)
11241199
}
11251200

11261201
public func deleteBackward() {
1127-
uitiLog("deleteBackward() textInputStorage:\"\(textInputStorage)\" markedTextRange:\"\(_markedTextRange)\" selectedTextRange:\"\(_selectedTextRange)\"")
1128-
inputDelegate?.selectionWillChange(self)
1202+
uitiLog("deleteBackward() \(textInputStateDescription())")
11291203

11301204
// after backward deletion, marked range is always cleared, and length of selected range is always zero
11311205
let rangeToDelete = _markedTextRange ?? _selectedTextRange
@@ -1143,12 +1217,15 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11431217
return
11441218
}
11451219

1220+
beginTextInputEdit()
1221+
11461222
rangeStartIndex -= 1
11471223
textInputStorage.remove(at: textInputStorage.index(textInputStorage.startIndex, offsetBy: rangeStartIndex))
11481224
rangeStartPosition = TextPosition(offset: rangeStartIndex)
11491225

11501226
self.send ([backspaceSendsControlH ? 8 : 0x7f])
11511227
} else {
1228+
beginTextInputEdit()
11521229
// Send as many backspaces that are in the range to delete. When on auto-repeat, after a some time
11531230
// pressing the backspace, it will delete chunks of text at a time.
11541231
let oldText = textInputStorage[rangeToDelete.fullRange(in: textInputStorage)]
@@ -1163,7 +1240,7 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11631240
_markedTextRange = nil
11641241
_selectedTextRange = TextRange(from: rangeStartPosition, to: rangeStartPosition)
11651242

1166-
inputDelegate?.selectionDidChange(self)
1243+
endTextInputEdit()
11671244
}
11681245

11691246
enum SendData {
@@ -1214,9 +1291,15 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
12141291

12151292
public override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
12161293
var didHandleEvent = false
1294+
1295+
if _markedTextRange != nil {
1296+
super.pressesBegan(presses, with: event)
1297+
return
1298+
}
12171299

12181300
for press in presses {
12191301
guard let key = press.key else { continue }
1302+
uitiLog("pressesBegan keyCode:\(key.keyCode) chars:\(key.characters.debugDescription) ignoring:\(key.charactersIgnoringModifiers.debugDescription) modifiers:\(key.modifierFlags)")
12201303

12211304
var data: SendData? = nil
12221305

@@ -1284,19 +1367,13 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
12841367
case .keyboardDeleteForward:
12851368
data = .bytes (EscapeSequences.cmdDelKey)
12861369

1287-
case .keyboardDeleteOrBackspace:
1288-
data = .bytes ([backspaceSendsControlH ? 8 : 0x7f])
1289-
12901370
case .keyboardEscape:
12911371
data = .bytes ([0x1b])
12921372

12931373
case .keyboardInsert:
12941374
print (".keyboardInsert ignored")
12951375
break
12961376

1297-
case .keyboardReturn:
1298-
data = .bytes (returnByteSequence)
1299-
13001377
case .keyboardTab:
13011378
data = .bytes ([9])
13021379

@@ -1334,16 +1411,6 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
13341411
optionAsMetaKey.toggle()
13351412
} else if key.modifierFlags.contains (.alternate) && optionAsMetaKey {
13361413
data = .text("\u{1b}\(key.charactersIgnoringModifiers)")
1337-
} else if !key.modifierFlags.contains (.command){
1338-
if let keyboardLanguage = self.textInputMode?.primaryLanguage {
1339-
// Is the keyboard language one of the multi-input languages? Chinese, Japanese, Korean and Hindi-Transliteration
1340-
// If so, do not process the input yet (we'll do it later in unmarkText())
1341-
if (!keyboardLanguage.hasPrefix("hi") && !keyboardLanguage.hasPrefix("zh") && !keyboardLanguage.hasPrefix("ja") && !keyboardLanguage.hasPrefix("ko")) {
1342-
if key.characters.count > 0 {
1343-
data = .text (key.characters)
1344-
}
1345-
}
1346-
}
13471414
}
13481415
}
13491416
if let sendableData = data {

0 commit comments

Comments
 (0)