mirror of
https://github.com/utmapp/UTM.git
synced 2026-10-12 02:24:01 +00:00
Merge pull request #7901 from utmapp/feature/swiftterm-2
terminal: update SwiftTerm and adopt its new renderer and find bar
This commit is contained in:
@@ -33,6 +33,9 @@ struct VMCommands: Commands {
|
||||
#endif
|
||||
SidebarCommands()
|
||||
ToolbarCommands()
|
||||
#if os(macOS)
|
||||
TextEditingCommands()
|
||||
#endif
|
||||
CommandGroup(replacing: .help) {
|
||||
Button(action: { NotificationCenter.default.post(name: NSNotification.ShowReleaseNotes, object: nil) }, label: {
|
||||
Text("What's New")
|
||||
|
||||
@@ -24,8 +24,8 @@ import SwiftUI
|
||||
willSet {
|
||||
vmSerialPort.delegate = nil
|
||||
newValue.delegate = self
|
||||
terminalView.getTerminal().resetToInitialState()
|
||||
terminalView.getTerminal().softReset()
|
||||
terminalView.resetToInitialState()
|
||||
terminalView.softReset()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,22 @@ import SwiftUI
|
||||
setupKeyboardMonitor()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
#if !os(visionOS) // SwiftTerm's Metal renderer draws at the wrong scale on visionOS
|
||||
do {
|
||||
try terminalView.setUseMetal(true)
|
||||
} catch {
|
||||
logger.debug("Metal terminal renderer unavailable, using CoreGraphics: \(error)")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
override func enterLive() {
|
||||
super.enterLive()
|
||||
DispatchQueue.main.async {
|
||||
let terminalSize = CGSize(width: self.terminalView.getTerminal().cols, height: self.terminalView.getTerminal().rows)
|
||||
let dimensions = self.terminalView.terminalDimensions
|
||||
let terminalSize = CGSize(width: dimensions.cols, height: dimensions.rows)
|
||||
self.delegate.displayViewSize = terminalSize
|
||||
}
|
||||
}
|
||||
@@ -72,6 +84,22 @@ import SwiftUI
|
||||
super.hideKeyboard()
|
||||
_ = terminalView.resignFirstResponder()
|
||||
}
|
||||
|
||||
func closeTerminal() {
|
||||
if let terminalView = terminalView {
|
||||
Self.closeWhenIdle(terminalView)
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases the terminal's renderer, retrying while GPU work is still in flight
|
||||
private static func closeWhenIdle(_ terminalView: TerminalView) {
|
||||
guard !terminalView.updateUiClosed() else {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
closeWhenIdle(terminalView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Layout terminal
|
||||
@@ -127,7 +155,7 @@ extension VMDisplayTerminalViewController {
|
||||
terminalView.nativeForegroundColor = UIColor(textColor)
|
||||
terminalView.nativeBackgroundColor = UIColor(backgroundColor)
|
||||
}
|
||||
terminalView.getTerminal().setCursorStyle(style.hasCursorBlink ? .blinkBlock : .steadyBlock)
|
||||
terminalView.setCursorStyle(style.hasCursorBlink ? .blinkBlock : .steadyBlock)
|
||||
terminalView.optionAsMetaKey = boolForSetting("OptionAsMetaKey")
|
||||
}
|
||||
}
|
||||
@@ -142,6 +170,10 @@ extension VMDisplayTerminalViewController: TerminalViewDelegate {
|
||||
}
|
||||
|
||||
func requestOpenLink(source: TerminalView, link: String, params: [String : String]) {
|
||||
guard let url = URL(string: link), let scheme = url.scheme?.lowercased(), ["http", "https"].contains(scheme) else {
|
||||
return
|
||||
}
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
|
||||
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
|
||||
@@ -156,9 +188,6 @@ extension VMDisplayTerminalViewController: TerminalViewDelegate {
|
||||
delegate?.displayDidAssertUserInteraction()
|
||||
}
|
||||
|
||||
func bell(source: TerminalView) {
|
||||
}
|
||||
|
||||
func rangeChanged(source: TerminalView, startY: Int, endY: Int) {
|
||||
}
|
||||
|
||||
@@ -180,10 +209,7 @@ extension VMDisplayTerminalViewController: CSPortDelegate {
|
||||
|
||||
func port(_ port: CSPort, didRecieveData data: Data) {
|
||||
if let terminalView = terminalView {
|
||||
let arr = [UInt8](data)[...]
|
||||
DispatchQueue.main.async {
|
||||
terminalView.feed(byteArray: arr)
|
||||
}
|
||||
terminalView.feed(byteArray: [UInt8](data)[...])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,12 @@ struct VMDisplayHostedView: UIViewControllerRepresentable {
|
||||
}
|
||||
}
|
||||
|
||||
static func dismantleUIViewController(_ uiViewController: VMDisplayViewController, coordinator: Coordinator) {
|
||||
if let vc = uiViewController as? VMDisplayTerminalViewController {
|
||||
vc.closeTerminal()
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(with: vm, device: device, state: $state)
|
||||
}
|
||||
|
||||
@@ -63,6 +63,12 @@ class VMDisplayAppleTerminalWindowController: VMDisplayAppleWindowController, VM
|
||||
terminalView!.terminalDelegate = self
|
||||
terminalView.allowMouseReporting = false
|
||||
super.windowDidLoad()
|
||||
enableMetalRenderer(for: terminalView)
|
||||
}
|
||||
|
||||
override func windowWillClose(_ notification: Notification) {
|
||||
closeTerminal(terminalView)
|
||||
super.windowWillClose(notification)
|
||||
}
|
||||
|
||||
override func updateWindowFrame() {
|
||||
@@ -113,6 +119,10 @@ extension VMDisplayAppleTerminalWindowController: TerminalViewDelegate, UTMSeria
|
||||
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
|
||||
}
|
||||
|
||||
func requestOpenLink(source: TerminalView, link: String, params: [String : String]) {
|
||||
openLink(link)
|
||||
}
|
||||
|
||||
func send(source: TerminalView, data: ArraySlice<UInt8>) {
|
||||
serialPort.write(data: Data(data))
|
||||
}
|
||||
@@ -122,10 +132,7 @@ extension VMDisplayAppleTerminalWindowController: TerminalViewDelegate, UTMSeria
|
||||
|
||||
func serialPort(_ serialPort: UTMSerialPort, didRecieveData data: Data) {
|
||||
if let terminalView = terminalView {
|
||||
let arr = [UInt8](data)[...]
|
||||
DispatchQueue.main.async {
|
||||
terminalView.feed(byteArray: arr)
|
||||
}
|
||||
terminalView.feed(byteArray: [UInt8](data)[...])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,10 +46,16 @@ class VMDisplayQemuTerminalWindowController: VMDisplayQemuWindowController, VMDi
|
||||
terminalView.autoresizingMask = [.width, .height]
|
||||
terminalView.allowMouseReporting = false
|
||||
displayView.addSubview(terminalView)
|
||||
enableMetalRenderer(for: terminalView)
|
||||
vmSerialPort?.delegate = self // can be nil for primary window
|
||||
super.windowDidLoad()
|
||||
}
|
||||
|
||||
override func windowWillClose(_ notification: Notification) {
|
||||
closeTerminal(terminalView)
|
||||
super.windowWillClose(notification)
|
||||
}
|
||||
|
||||
override func enterLive() {
|
||||
super.enterLive()
|
||||
isSizeChangeIgnored = true
|
||||
@@ -113,6 +119,10 @@ extension VMDisplayQemuTerminalWindowController: TerminalViewDelegate {
|
||||
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
|
||||
}
|
||||
|
||||
func requestOpenLink(source: TerminalView, link: String, params: [String : String]) {
|
||||
openLink(link)
|
||||
}
|
||||
|
||||
func send(source: TerminalView, data: ArraySlice<UInt8>) {
|
||||
if let vmSerialPort = vmSerialPort {
|
||||
vmSerialPort.write(Data(data))
|
||||
@@ -154,10 +164,7 @@ extension VMDisplayQemuTerminalWindowController: CSPortDelegate {
|
||||
|
||||
func port(_ port: CSPort, didRecieveData data: Data) {
|
||||
if let terminalView = terminalView {
|
||||
let arr = [UInt8](data)[...]
|
||||
DispatchQueue.main.async {
|
||||
terminalView.feed(byteArray: arr)
|
||||
}
|
||||
terminalView.feed(byteArray: [UInt8](data)[...])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,16 @@ import SwiftUI
|
||||
|
||||
private let kVMDefaultResizeCmd = "stty cols $COLS rows $ROWS\\n"
|
||||
|
||||
/// Releases the terminal's renderer, retrying while GPU work is still in flight
|
||||
@MainActor private func closeWhenIdle(_ terminalView: TerminalView) {
|
||||
guard !terminalView.updateUiClosed() else {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
closeWhenIdle(terminalView)
|
||||
}
|
||||
}
|
||||
|
||||
protocol VMDisplayTerminal {
|
||||
var vm: (any UTMVirtualMachine)! { get }
|
||||
var isOptionAsMetaKey: Bool { get }
|
||||
@@ -27,6 +37,9 @@ protocol VMDisplayTerminal {
|
||||
func resizeCommand(for terminal: TerminalView, using config: UTMConfigurationTerminal) -> String
|
||||
func sizeChanged(id: Int, newCols: Int, newRows: Int)
|
||||
func sendString(_ string: String)
|
||||
@MainActor func enableMetalRenderer(for terminalView: TerminalView)
|
||||
@MainActor func closeTerminal(_ terminalView: TerminalView)
|
||||
@MainActor func openLink(_ link: String)
|
||||
}
|
||||
|
||||
extension VMDisplayTerminal {
|
||||
@@ -50,8 +63,10 @@ extension VMDisplayTerminal {
|
||||
terminalView.nativeForegroundColor = NSColor(textColor)
|
||||
terminalView.nativeBackgroundColor = NSColor(backgroundColor)
|
||||
}
|
||||
terminalView.getTerminal().resize(cols: windowConfig.columns, rows: windowConfig.rows)
|
||||
terminalView.getTerminal().setCursorStyle(config.hasCursorBlink ? .blinkBlock : .steadyBlock)
|
||||
if terminalView.terminalDimensions != TerminalDimensions(cols: windowConfig.columns, rows: windowConfig.rows) {
|
||||
terminalView.resize(cols: windowConfig.columns, rows: windowConfig.rows)
|
||||
}
|
||||
terminalView.setCursorStyle(config.hasCursorBlink ? .blinkBlock : .steadyBlock)
|
||||
let size = window.frameRect(forContentRect: terminalView.getOptimalFrameSize()).size
|
||||
let frame = CGRect(origin: window.frame.origin, size: size)
|
||||
window.setFrame(frame, display: false, animate: true)
|
||||
@@ -59,8 +74,8 @@ extension VMDisplayTerminal {
|
||||
}
|
||||
|
||||
func resizeCommand(for terminalView: TerminalView, using config: UTMConfigurationTerminal) -> String {
|
||||
let cols = terminalView.getTerminal().cols
|
||||
let rows = terminalView.getTerminal().rows
|
||||
let cols = terminalView.terminalDimensions.cols
|
||||
let rows = terminalView.terminalDimensions.rows
|
||||
let template = config.resizeCommand ?? kVMDefaultResizeCmd
|
||||
let cmd = template
|
||||
.replacingOccurrences(of: "$COLS", with: String(cols))
|
||||
@@ -69,6 +84,26 @@ extension VMDisplayTerminal {
|
||||
return cmd
|
||||
}
|
||||
|
||||
@MainActor func enableMetalRenderer(for terminalView: TerminalView) {
|
||||
do {
|
||||
try terminalView.setUseMetal(true)
|
||||
} catch {
|
||||
logger.debug("Metal terminal renderer unavailable, using CoreGraphics: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor func closeTerminal(_ terminalView: TerminalView) {
|
||||
closeWhenIdle(terminalView)
|
||||
}
|
||||
|
||||
/// Only web links are opened so that guest output cannot open host files or apps
|
||||
@MainActor func openLink(_ link: String) {
|
||||
guard let url = URL(string: link), let scheme = url.scheme?.lowercased(), ["http", "https"].contains(scheme) else {
|
||||
return
|
||||
}
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
|
||||
func sizeChanged(id: Int, newCols: Int, newRows: Int) {
|
||||
Task { @MainActor in
|
||||
let windowConfig = UTMRegistryEntry.Terminal(columns: newCols, rows: newRows)
|
||||
|
||||
@@ -5601,9 +5601,9 @@
|
||||
};
|
||||
848F71E4277A2466006A0240 /* XCRemoteSwiftPackageReference "SwiftTerm" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/migueldeicaza/SwiftTerm.git";
|
||||
repositoryURL = "https://github.com/osy/SwiftTerm.git";
|
||||
requirement = {
|
||||
branch = main;
|
||||
branch = "fix/visionos-build";
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
@@ -5737,9 +5737,9 @@
|
||||
};
|
||||
CEF7F58F2AEEDCC400E34952 /* XCRemoteSwiftPackageReference "SwiftTerm" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/migueldeicaza/SwiftTerm.git";
|
||||
repositoryURL = "https://github.com/osy/SwiftTerm.git";
|
||||
requirement = {
|
||||
branch = main;
|
||||
branch = "fix/visionos-build";
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -28,6 +28,15 @@
|
||||
"revision" : "c359a08accfb49662a17cdfc5e333c7b4e5c2c56"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "h",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/rarestype/h",
|
||||
"state" : {
|
||||
"revision" : "aa3626829160917d4378330617971977cbd78f5b",
|
||||
"version" : "1.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "inappsettingskit",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -73,6 +82,15 @@
|
||||
"version" : "1.5.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-png",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tayloraswift/swift-png",
|
||||
"state" : {
|
||||
"revision" : "8a0bcd4df5e4b307c804937776a56dd6ecdf6396",
|
||||
"version" : "4.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftconnect",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -103,10 +121,10 @@
|
||||
{
|
||||
"identity" : "swiftterm",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/migueldeicaza/SwiftTerm.git",
|
||||
"location" : "https://github.com/osy/SwiftTerm.git",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "ea0f681b25c8385b4a5a48d435e61d11392216e0"
|
||||
"branch" : "fix/visionos-build",
|
||||
"revision" : "f2f5349b2a1c629ec6c87286d814147136e44eca"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+24
-3
@@ -55,7 +55,7 @@ while [ "x$1" != "x" ]; do
|
||||
done
|
||||
|
||||
case $SDK in
|
||||
macos )
|
||||
macosx )
|
||||
SCHEME="macOS"
|
||||
;;
|
||||
* )
|
||||
@@ -65,12 +65,33 @@ macos )
|
||||
;;
|
||||
esac
|
||||
|
||||
ARCH_ARGS=$(echo $ARCH | xargs printf -- "-arch %s ")
|
||||
# -sdk would also apply to host tools such as package build plugins, so select
|
||||
# the platform with a destination instead
|
||||
case $SDK in
|
||||
iphoneos )
|
||||
PLATFORM="iOS"
|
||||
;;
|
||||
iphonesimulator )
|
||||
PLATFORM="iOS Simulator"
|
||||
;;
|
||||
xros )
|
||||
PLATFORM="visionOS"
|
||||
;;
|
||||
xrsimulator )
|
||||
PLATFORM="visionOS Simulator"
|
||||
;;
|
||||
macosx )
|
||||
PLATFORM="macOS"
|
||||
;;
|
||||
* )
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
if [ ! -z "$TEAM_IDENTIFIER" ]; then
|
||||
TEAM_IDENTIFIER_PREFIX="TeamIdentifierPrefix=${TEAM_IDENTIFIER}."
|
||||
fi
|
||||
|
||||
xcodebuild archive -archivePath "$OUTPUT" -scheme "$SCHEME" -sdk "$SDK" $ARCH_ARGS -configuration Release CODE_SIGNING_ALLOWED=NO $TEAM_IDENTIFIER_PREFIX
|
||||
xcodebuild archive -archivePath "$OUTPUT" -scheme "$SCHEME" -destination "generic/platform=$PLATFORM" ARCHS="$ARCH" -configuration Release -skipPackagePluginValidation CODE_SIGNING_ALLOWED=NO $TEAM_IDENTIFIER_PREFIX
|
||||
BUILT_PATH=$(find $OUTPUT.xcarchive -name '*.app' -type d | head -1)
|
||||
# Only retain the target architecture to address < iOS 15 crash & save disk space
|
||||
if [ "$SDK" == "iphoneos" ]; then
|
||||
|
||||
Reference in New Issue
Block a user