Skip to content

Commit

Permalink
[app] Start implementing ssh:// url scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
kirb committed Mar 24, 2022
1 parent 3fd8039 commit 46e0efd
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 36 deletions.
69 changes: 56 additions & 13 deletions App/Controllers/TerminalSceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,44 +19,87 @@ class TerminalSceneDelegate: UIResponder, UIWindowSceneDelegate, IdentifiableSce

var window: UIWindow?

private var rootViewController: RootViewController! {
(window?.rootViewController as? UINavigationController)?.viewControllers.first as? RootViewController
}

override init() {
super.init()

NotificationCenter.default.addObserver(self, selector: #selector(self.preferencesUpdated), name: Preferences.didChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(preferencesUpdated), name: Preferences.didChangeNotification, object: nil)
}

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else {
guard let scene = scene as? UIWindowScene else {
return
}

window = UIWindow(windowScene: windowScene)
window = UIWindow(windowScene: scene)
window!.tintColor = .tint
window!.rootViewController = UINavigationController(rootViewController: RootViewController())
window!.makeKeyAndVisible()

scene.title = .localize("TERMINAL", comment: "Generic title displayed before the terminal sets a proper title.")

#if targetEnvironment(macCatalyst)
windowScene.titlebar?.separatorStyle = .none
scene.titlebar?.separatorStyle = .none
#endif

preferencesUpdated()
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for context in URLContexts {
let url = context.url
switch url.scheme {
case "ssh":
createWindow(asTab: true, openingURL: url)

default: break
}
}
}

// MARK: - Window management

func createWindow(asTab: Bool) {
let options = UIScene.ActivationRequestOptions()
#if targetEnvironment(macCatalyst)
if asTab {
func createWindow(asTab: Bool, openingURL url: URL? = nil) {
// Handle SSH URL
var sshPayload: String?
if let url = url,
let host = url.host,
url.scheme == "ssh" {
sshPayload = host
if let user = url.user {
sshPayload = "\(user)@\(host)"
}
let port = url.port ?? 22
if port != 22 {
sshPayload = "\(sshPayload!) -p \(port)"
}
}

if UIApplication.shared.supportsMultipleScenes {
let options = UIScene.ActivationRequestOptions()
#if targetEnvironment(macCatalyst)
if asTab {
options.requestingScene = window!.windowScene
}
options.collectionJoinBehavior = asTab ? .preferred : .disallowed
#else
options.requestingScene = window!.windowScene
#endif

let activity = NSUserActivity(activityType: Self.activityType)
activity.userInfo = [:]
activity.userInfo!["sshPayload"] = sshPayload

UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: options, errorHandler: nil)
} else {
if let sshPayload = sshPayload {
rootViewController.initialCommand = "ssh \(sshPayload)"
}
rootViewController.addTerminal()
}
options.collectionJoinBehavior = asTab ? .preferred : .disallowed
#else
options.requestingScene = window!.windowScene
#endif
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: nil, options: options, errorHandler: nil)
}

@objc func removeWindow() {
Expand Down
13 changes: 13 additions & 0 deletions App/Supporting Files/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>ssh</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ssh</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSApplicationCategoryType</key>
Expand Down
12 changes: 8 additions & 4 deletions App/View Controllers/RootViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ class RootViewController: UIViewController {

static let settingsViewDoneNotification = Notification.Name(rawValue: "RootViewControllerSettingsViewDoneNotification")

var initialCommand: String?

private var terminals: [UIViewController] = []
private var selectedTabIndex = Int(0)
private var selectedTabIndex = 0

private var tabToolbar: TabToolbarViewController?

Expand Down Expand Up @@ -62,7 +64,7 @@ class RootViewController: UIViewController {
modifierFlags: .command))
#endif

let digits = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
let digits = [Array(1...9) + [0]].map { "\($0)" }
for digit in digits {
addKeyCommand(UIKeyCommand(action: #selector(self.selectTabFromKeyCommand),
input: digit,
Expand Down Expand Up @@ -133,16 +135,18 @@ class RootViewController: UIViewController {

func addTerminal() {
let index = min(selectedTabIndex + 1, terminals.count)
addTerminal(at: index)
addTerminal(at: index, initialCommand: initialCommand)
selectTerminal(at: index)
initialCommand = nil
}

private func addTerminal(at index: Int, axis: NSLayoutConstraint.Axis? = nil) {
private func addTerminal(at index: Int, axis: NSLayoutConstraint.Axis? = nil, initialCommand: String? = nil) {
let splitViewController = TerminalSplitViewController()
splitViewController.view.autoresizingMask = [ .flexibleWidth, .flexibleHeight ]
splitViewController.view.frame = view.bounds

let newTerminal = TerminalSessionViewController()
newTerminal.initialCommand = initialCommand

addChild(splitViewController)
splitViewController.willMove(toParent: self)
Expand Down
26 changes: 8 additions & 18 deletions App/View Controllers/TerminalSessionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class TerminalSessionViewController: UIViewController, TerminalSplitViewControll
fatalError("Couldn’t initialise bell sound")
}()

var initialCommand: String?

var isSplitViewResizing = false {
didSet { updateIsSplitViewResizing() }
}
Expand Down Expand Up @@ -107,24 +109,6 @@ class TerminalSessionViewController: UIViewController, TerminalSplitViewControll
modifierFlags: [ .command, .alternate ]))
#endif

if #available(iOS 13.4, *) {
// Handled by TerminalKeyInput
} else {
addKeyCommand(UIKeyCommand(input: UIKeyCommand.inputUpArrow, modifierFlags: [], action: #selector(TerminalKeyInput.upKeyPressed)))
addKeyCommand(UIKeyCommand(input: UIKeyCommand.inputDownArrow, modifierFlags: [], action: #selector(TerminalKeyInput.downKeyPressed)))
addKeyCommand(UIKeyCommand(input: UIKeyCommand.inputLeftArrow, modifierFlags: [], action: #selector(TerminalKeyInput.leftKeyPressed)))
addKeyCommand(UIKeyCommand(input: UIKeyCommand.inputRightArrow, modifierFlags: [], action: #selector(TerminalKeyInput.rightKeyPressed)))
addKeyCommand(UIKeyCommand(input: UIKeyCommand.inputEscape, modifierFlags: [], action: #selector(TerminalKeyInput.metaKeyPressed)))

let letters = [
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
"s", "t", "u", "v", "w", "x", "y", "z"
]
for key in letters {
addKeyCommand(UIKeyCommand(input: key, modifierFlags: [ .control ], action: #selector(TerminalKeyInput.ctrlKeyCommandPressed(_:))))
}
}

if UIApplication.shared.supportsMultipleScenes {
NotificationCenter.default.addObserver(self, selector: #selector(self.sceneDidEnterBackground), name: UIWindowScene.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.sceneWillEnterForeground), name: UIWindowScene.willEnterForegroundNotification, object: nil)
Expand All @@ -145,7 +129,13 @@ class TerminalSessionViewController: UIViewController, TerminalSplitViewControll

if let error = failureError {
didReceiveError(error: error)
} else {
if let initialCommand = initialCommand?.data(using: .utf8) {
terminalController.write(initialCommand + EscapeSequences.return)
}
}

initialCommand = nil
}

override func viewWillDisappear(_ animated: Bool) {
Expand Down
2 changes: 1 addition & 1 deletion Common/Controllers/TerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public class TerminalController {
}
if let hostname = hostname {
let user = self.user == NSUserName() ? nil : self.user
let cleanedHostname = hostname.replacingOccurrences(of: "\\.local$", with: "", options: .regularExpression, range: hostname.startIndex..<hostname.endIndex)
let cleanedHostname = hostname.replacingOccurrences(of: #"\.local$"#, with: "", options: .regularExpression, range: hostname.startIndex..<hostname.endIndex)
let hostString: String
if isLocalhost {
hostString = user ?? ""
Expand Down

0 comments on commit 46e0efd

Please sign in to comment.