]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
macOS: Ability to activate / deactivate a tunnel
authorRoopesh Chander <roop@roopc.net>
Sat, 29 Dec 2018 13:14:29 +0000 (18:44 +0530)
committerRoopesh Chander <roop@roopc.net>
Mon, 14 Jan 2019 09:22:30 +0000 (14:52 +0530)
Signed-off-by: Roopesh Chander <roop@roopc.net>
WireGuard/WireGuard/UI/macOS/AppDelegate.swift
WireGuard/WireGuard/UI/macOS/StatusMenu.swift

index 970e56d21579ab8b3232cfcb0c9b0f1378d4aafc..3a5aa1d7c3d3b5aae0145eda41f77d9cb25d3ec0 100644 (file)
@@ -18,10 +18,29 @@ class AppDelegate: NSObject, NSApplicationDelegate {
             self.statusItem = createStatusBarItem(with: statusMenu)
 
             tunnelsManager.tunnelsListDelegate = statusMenu
+            tunnelsManager.activationDelegate = self
         }
     }
 }
 
+extension AppDelegate: TunnelsManagerActivationDelegate {
+    func tunnelActivationAttemptFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationAttemptError) {
+        ErrorPresenter.showErrorAlert(error: error, from: nil)
+    }
+
+    func tunnelActivationAttemptSucceeded(tunnel: TunnelContainer) {
+        // Nothing to do
+    }
+
+    func tunnelActivationFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationError) {
+        ErrorPresenter.showErrorAlert(error: error, from: nil)
+    }
+
+    func tunnelActivationSucceeded(tunnel: TunnelContainer) {
+        // Nothing to do
+    }
+}
+
 func createStatusBarItem(with statusMenu: StatusMenu) -> NSStatusItem {
     let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
     if let statusBarImage = NSImage(named: "WireGuardMacStatusBarIcon") {
index 440ec984be28c0d3d3c1d3e3b597c8b897313bde..2c9e85af6cd5ca36777affc54655491f319cec2c 100644 (file)
@@ -6,7 +6,10 @@ import Cocoa
 class StatusMenu: NSMenu {
 
     let tunnelsManager: TunnelsManager
+    var tunnelStatusObservers = [AnyObject]()
+
     var firstTunnelMenuItemIndex: Int = 0
+    var numberOfTunnelMenuItems: Int = 0
 
     init(tunnelsManager: TunnelsManager) {
         self.tunnelsManager = tunnelsManager
@@ -27,24 +30,11 @@ class StatusMenu: NSMenu {
         let numberOfTunnels = tunnelsManager.numberOfTunnels()
         for index in 0 ..< tunnelsManager.numberOfTunnels() {
             let tunnel = tunnelsManager.tunnel(at: index)
-            let menuItem = createTunnelMenuItem(for: tunnel)
-            addItem(menuItem)
+            insertTunnelMenuItem(for: tunnel, at: numberOfTunnelMenuItems)
         }
         return numberOfTunnels > 0
     }
 
-    func createTunnelMenuItem(for tunnel: TunnelContainer) -> NSMenuItem {
-        let menuItem = NSMenuItem(title: tunnel.name, action: #selector(tunnelClicked(sender:)), keyEquivalent: "")
-        menuItem.target = self
-        menuItem.representedObject = tunnel
-        return menuItem
-    }
-
-    @objc func tunnelClicked(sender: AnyObject) {
-        guard let tunnel = sender.representedObject as? TunnelContainer else { return }
-        print("Tunnel \(tunnel.name) clicked")
-    }
-
     func addTunnelManagementItems() {
         let manageItem = NSMenuItem(title: tr("macMenuManageTunnels"), action: #selector(manageTunnelsClicked), keyEquivalent: "")
         manageItem.target = self
@@ -54,6 +44,16 @@ class StatusMenu: NSMenu {
         addItem(importItem)
     }
 
+    @objc func tunnelClicked(sender: AnyObject) {
+        guard let tunnelMenuItem = sender as? NSMenuItem else { return }
+        guard let tunnel = tunnelMenuItem.representedObject as? TunnelContainer else { return }
+        if tunnelMenuItem.state == .off {
+            tunnelsManager.startActivation(of: tunnel)
+        } else {
+            tunnelsManager.startDeactivation(of: tunnel)
+        }
+    }
+
     @objc func manageTunnelsClicked() {
         print("Unimplemented")
     }
@@ -70,36 +70,72 @@ class StatusMenu: NSMenu {
     }
 }
 
+extension StatusMenu {
+    func insertTunnelMenuItem(for tunnel: TunnelContainer, at tunnelIndex: Int) {
+        let menuItem = NSMenuItem(title: tunnel.name, action: #selector(tunnelClicked(sender:)), keyEquivalent: "")
+        menuItem.target = self
+        menuItem.representedObject = tunnel
+        updateTunnelMenuItem(menuItem)
+        let statusObservationToken = tunnel.observe(\.status) { _, _ in
+            updateTunnelMenuItem(menuItem)
+        }
+        tunnelStatusObservers.insert(statusObservationToken, at: tunnelIndex)
+        insertItem(menuItem, at: firstTunnelMenuItemIndex + tunnelIndex)
+        if numberOfTunnelMenuItems == 0 {
+            insertItem(NSMenuItem.separator(), at: firstTunnelMenuItemIndex + tunnelIndex + 1)
+        }
+        numberOfTunnelMenuItems += 1
+    }
+
+    func removeTunnelMenuItem(at tunnelIndex: Int) {
+        removeItem(at: firstTunnelMenuItemIndex + tunnelIndex)
+        tunnelStatusObservers.remove(at: tunnelIndex)
+        numberOfTunnelMenuItems -= 1
+        if numberOfTunnelMenuItems == 0 {
+            if let firstItem = item(at: firstTunnelMenuItemIndex), firstItem.isSeparatorItem {
+                removeItem(at: firstTunnelMenuItemIndex)
+            }
+        }
+    }
+
+    func moveTunnelMenuItem(from oldTunnelIndex: Int, to newTunnelIndex: Int) {
+        let oldMenuItem = item(at: firstTunnelMenuItemIndex + oldTunnelIndex)!
+        let oldMenuItemTitle = oldMenuItem.title
+        let oldMenuItemTunnel = oldMenuItem.representedObject
+        removeItem(at: firstTunnelMenuItemIndex + oldTunnelIndex)
+        let menuItem = NSMenuItem(title: oldMenuItemTitle, action: #selector(tunnelClicked(sender:)), keyEquivalent: "")
+        menuItem.target = self
+        menuItem.representedObject = oldMenuItemTunnel
+        insertItem(menuItem, at: firstTunnelMenuItemIndex + newTunnelIndex)
+        let statusObserver = tunnelStatusObservers.remove(at: oldTunnelIndex)
+        tunnelStatusObservers.insert(statusObserver, at: newTunnelIndex)
+    }
+}
+
+private func updateTunnelMenuItem(_ tunnelMenuItem: NSMenuItem) {
+    guard let tunnel = tunnelMenuItem.representedObject as? TunnelContainer else { return }
+    tunnelMenuItem.title = tunnel.name
+    let shouldShowCheckmark = (tunnel.status != .inactive && tunnel.status != .deactivating)
+    tunnelMenuItem.state = shouldShowCheckmark ? .on : .off
+}
+
 extension StatusMenu: TunnelsManagerListDelegate {
     func tunnelAdded(at index: Int) {
         let tunnel = tunnelsManager.tunnel(at: index)
-        let menuItem = createTunnelMenuItem(for: tunnel)
-        if tunnelsManager.numberOfTunnels() == 1 {
-            insertItem(NSMenuItem.separator(), at: firstTunnelMenuItemIndex + index)
-        }
-        insertItem(menuItem, at: firstTunnelMenuItemIndex + index)
+        insertTunnelMenuItem(for: tunnel, at: index)
     }
 
     func tunnelModified(at index: Int) {
-        let tunnel = tunnelsManager.tunnel(at: index)
-        if let menuItem = item(at: firstTunnelMenuItemIndex + index) {
-            menuItem.title = tunnel.name
+        if let tunnelMenuItem = item(at: firstTunnelMenuItemIndex + index) {
+            updateTunnelMenuItem(tunnelMenuItem)
         }
     }
 
     func tunnelMoved(from oldIndex: Int, to newIndex: Int) {
-        let tunnel = tunnelsManager.tunnel(at: oldIndex)
-        let menuItem = createTunnelMenuItem(for: tunnel)
-        removeItem(at: firstTunnelMenuItemIndex + oldIndex)
-        insertItem(menuItem, at: firstTunnelMenuItemIndex + newIndex)
+        moveTunnelMenuItem(from: oldIndex, to: newIndex)
     }
 
     func tunnelRemoved(at index: Int) {
-        removeItem(at: firstTunnelMenuItemIndex + index)
-        if tunnelsManager.numberOfTunnels() == 0 {
-            if let firstItem = item(at: firstTunnelMenuItemIndex), firstItem.isSeparatorItem {
-                removeItem(at: firstTunnelMenuItemIndex)
-            }
-        }
+        removeTunnelMenuItem(at: index)
     }
 }