2016-05-14 20:54:49 +00:00
|
|
|
//
|
|
|
|
// AppDelegate.swift
|
|
|
|
// ZeroTier One
|
|
|
|
//
|
|
|
|
// Created by Grant Limberg on 5/14/16.
|
|
|
|
// Copyright © 2016 ZeroTier, Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
|
|
|
|
@NSApplicationMain
|
|
|
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
|
|
|
|
|
|
|
@IBOutlet weak var window: NSWindow!
|
|
|
|
|
|
|
|
|
2016-05-14 22:42:45 +00:00
|
|
|
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2.0)
|
|
|
|
|
|
|
|
let networkListPopover = NSPopover()
|
|
|
|
let joinNetworkPopover = NSPopover()
|
2016-06-24 03:04:57 +00:00
|
|
|
let preferencesPopover = NSPopover()
|
2016-06-24 03:22:03 +00:00
|
|
|
let aboutPopover = NSPopover()
|
2016-05-14 22:42:45 +00:00
|
|
|
|
|
|
|
var transientMonitor: AnyObject? = nil
|
|
|
|
|
2016-06-17 03:53:55 +00:00
|
|
|
let monitor = NetworkMonitor()
|
2016-05-14 22:42:45 +00:00
|
|
|
|
2016-06-22 02:44:59 +00:00
|
|
|
var networks = [Network]()
|
|
|
|
|
2016-07-01 04:24:45 +00:00
|
|
|
var status: NodeStatus? = nil
|
|
|
|
|
|
|
|
var pasteboard = NSPasteboard.generalPasteboard()
|
|
|
|
|
2016-06-17 03:53:55 +00:00
|
|
|
func applicationDidFinishLaunching(aNotification: NSNotification) {
|
2016-07-01 04:24:45 +00:00
|
|
|
pasteboard.declareTypes([NSPasteboardTypeString], owner: nil)
|
2016-06-28 02:04:55 +00:00
|
|
|
|
|
|
|
let defaults = NSUserDefaults.standardUserDefaults()
|
|
|
|
let defaultsDict = ["firstRun": true]
|
|
|
|
defaults.registerDefaults(defaultsDict)
|
|
|
|
|
2016-07-27 03:04:59 +00:00
|
|
|
let firstRun = defaults.boolForKey("firstRun")
|
2016-06-28 02:04:55 +00:00
|
|
|
|
2016-07-27 03:04:59 +00:00
|
|
|
if firstRun {
|
2016-06-28 02:04:55 +00:00
|
|
|
defaults.setBool(false, forKey: "firstRun")
|
|
|
|
defaults.synchronize()
|
|
|
|
|
|
|
|
let loginController = LaunchAtLoginController()
|
|
|
|
|
|
|
|
let bundle = NSBundle.mainBundle()
|
|
|
|
let bundleURL = bundle.bundleURL
|
|
|
|
|
|
|
|
loginController.setLaunchAtLogin(true, forURL: bundleURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-17 03:53:55 +00:00
|
|
|
let nc = NSNotificationCenter.defaultCenter()
|
|
|
|
nc.addObserver(self, selector: #selector(onNetworkListUpdated(_:)), name: networkUpdateKey, object: nil)
|
2016-07-01 04:24:45 +00:00
|
|
|
nc.addObserver(self, selector: #selector(onNodeStatusUpdated(_:)), name: statusUpdateKey, object: nil)
|
2016-05-14 22:42:45 +00:00
|
|
|
|
|
|
|
statusItem.image = NSImage(named: "MenuBarIconMac")
|
|
|
|
|
2016-06-22 02:44:59 +00:00
|
|
|
buildMenu()
|
2016-05-14 22:42:45 +00:00
|
|
|
|
|
|
|
joinNetworkPopover.contentViewController = JoinNetworkViewController(
|
|
|
|
nibName: "JoinNetworkViewController", bundle: nil)
|
|
|
|
joinNetworkPopover.behavior = .Transient
|
|
|
|
|
2016-06-28 00:54:54 +00:00
|
|
|
let showNetworksView = ShowNetworksViewController(
|
2016-05-14 22:42:45 +00:00
|
|
|
nibName: "ShowNetworksViewController", bundle: nil)
|
2016-06-28 00:54:54 +00:00
|
|
|
showNetworksView?.netMonitor = monitor
|
|
|
|
|
|
|
|
networkListPopover.contentViewController = showNetworksView
|
2016-05-14 22:42:45 +00:00
|
|
|
networkListPopover.behavior = .Transient
|
2016-05-27 22:07:20 +00:00
|
|
|
|
2016-06-24 03:04:57 +00:00
|
|
|
preferencesPopover.contentViewController = PreferencesViewController(
|
|
|
|
nibName: "PreferencesViewController", bundle: nil)
|
|
|
|
preferencesPopover.behavior = .Transient
|
|
|
|
|
2016-06-24 03:22:03 +00:00
|
|
|
aboutPopover.contentViewController = AboutViewController(
|
|
|
|
nibName: "AboutViewController", bundle: nil)
|
|
|
|
aboutPopover.behavior = .Transient
|
2016-07-27 03:04:59 +00:00
|
|
|
|
|
|
|
if firstRun {
|
|
|
|
NSOperationQueue.mainQueue().addOperationWithBlock() {
|
|
|
|
self.showAbout()
|
|
|
|
}
|
|
|
|
}
|
2016-05-14 20:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func applicationWillTerminate(aNotification: NSNotification) {
|
|
|
|
// Insert code here to tear down your application
|
2016-06-17 03:53:55 +00:00
|
|
|
let nc = NSNotificationCenter.defaultCenter()
|
|
|
|
nc.removeObserver(self)
|
2016-05-14 20:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-14 22:42:45 +00:00
|
|
|
func showNetworks() {
|
|
|
|
if let button = statusItem.button {
|
|
|
|
networkListPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY)
|
|
|
|
|
|
|
|
if transientMonitor == nil {
|
|
|
|
transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask(
|
|
|
|
[.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in
|
|
|
|
|
|
|
|
NSEvent.removeMonitor(self.transientMonitor!)
|
|
|
|
self.transientMonitor = nil
|
|
|
|
self.networkListPopover.close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func joinNetwork() {
|
|
|
|
if let button = statusItem.button {
|
|
|
|
joinNetworkPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY)
|
|
|
|
|
|
|
|
if transientMonitor == nil {
|
|
|
|
transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask(
|
|
|
|
[.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in
|
|
|
|
|
|
|
|
NSEvent.removeMonitor(self.transientMonitor!)
|
|
|
|
self.transientMonitor = nil
|
|
|
|
self.joinNetworkPopover.close()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-24 03:04:57 +00:00
|
|
|
func showPreferences() {
|
|
|
|
if let button = statusItem.button {
|
|
|
|
preferencesPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY)
|
|
|
|
|
|
|
|
if transientMonitor == nil {
|
|
|
|
transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask(
|
|
|
|
[.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in
|
|
|
|
|
|
|
|
NSEvent.removeMonitor(self.transientMonitor!)
|
|
|
|
self.transientMonitor = nil
|
|
|
|
self.preferencesPopover.close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-24 03:22:03 +00:00
|
|
|
func showAbout() {
|
|
|
|
if let button = statusItem.button {
|
|
|
|
aboutPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY)
|
|
|
|
|
|
|
|
if transientMonitor == nil {
|
|
|
|
transientMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask(
|
|
|
|
[.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> Void in
|
|
|
|
|
|
|
|
NSEvent.removeMonitor(self.transientMonitor!)
|
|
|
|
self.transientMonitor = nil
|
|
|
|
self.aboutPopover.close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-14 22:42:45 +00:00
|
|
|
func quit() {
|
|
|
|
NSApp.performSelector(#selector(NSApp.terminate(_:)), withObject: nil, afterDelay: 0.0)
|
|
|
|
}
|
2016-06-17 03:53:55 +00:00
|
|
|
|
|
|
|
func onNetworkListUpdated(note: NSNotification) {
|
|
|
|
let netList = note.userInfo!["networks"] as! [Network]
|
|
|
|
(networkListPopover.contentViewController as! ShowNetworksViewController).setNetworks(netList)
|
2016-06-22 02:44:59 +00:00
|
|
|
|
|
|
|
self.networks = netList
|
|
|
|
|
|
|
|
buildMenu()
|
|
|
|
}
|
|
|
|
|
2016-07-01 04:24:45 +00:00
|
|
|
func onNodeStatusUpdated(note: NSNotification) {
|
|
|
|
let status = note.userInfo!["status"] as! NodeStatus
|
|
|
|
self.status = status
|
|
|
|
|
|
|
|
buildMenu()
|
|
|
|
}
|
|
|
|
|
2016-06-22 02:44:59 +00:00
|
|
|
func buildMenu() {
|
|
|
|
let menu = NSMenu()
|
|
|
|
|
2016-07-01 04:24:45 +00:00
|
|
|
if let s = self.status {
|
|
|
|
menu.addItem(NSMenuItem(title: "Node ID: \(s.address)", action: #selector(AppDelegate.copyNodeID), keyEquivalent: ""))
|
|
|
|
menu.addItem(NSMenuItem.separatorItem())
|
|
|
|
}
|
|
|
|
|
2016-06-24 22:24:29 +00:00
|
|
|
menu.addItem(NSMenuItem(title: "Network Details...", action: #selector(AppDelegate.showNetworks), keyEquivalent: "n"))
|
|
|
|
menu.addItem(NSMenuItem(title: "Join Network...", action: #selector(AppDelegate.joinNetwork), keyEquivalent: "j"))
|
2016-06-22 02:44:59 +00:00
|
|
|
menu.addItem(NSMenuItem.separatorItem())
|
|
|
|
|
|
|
|
if networks.count > 0 {
|
|
|
|
for net in networks {
|
|
|
|
let id = String(net.nwid, radix: 16)
|
2016-06-22 03:02:18 +00:00
|
|
|
var networkName = ""
|
|
|
|
if net.name.isEmpty {
|
|
|
|
networkName = "\(id)"
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
networkName = "\(id) (\(net.name))"
|
|
|
|
}
|
2016-06-22 02:44:59 +00:00
|
|
|
|
2016-06-27 01:18:59 +00:00
|
|
|
if net.allowDefault && net.connected {
|
|
|
|
networkName += " [default]"
|
|
|
|
}
|
2016-06-22 02:44:59 +00:00
|
|
|
let item = NSMenuItem(title: networkName, action: #selector(AppDelegate.toggleNetwork(_:)), keyEquivalent: "")
|
|
|
|
|
|
|
|
if net.connected {
|
|
|
|
item.state = NSOnState
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
item.state = NSOffState
|
|
|
|
}
|
|
|
|
|
|
|
|
item.representedObject = net
|
|
|
|
|
|
|
|
menu.addItem(item)
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.addItem(NSMenuItem.separatorItem())
|
|
|
|
}
|
|
|
|
|
2016-06-24 03:22:03 +00:00
|
|
|
menu.addItem(NSMenuItem(title: "About ZeroTier One...", action: #selector(AppDelegate.showAbout), keyEquivalent: ""))
|
2016-06-24 03:04:57 +00:00
|
|
|
menu.addItem(NSMenuItem(title: "Preferences...", action: #selector(AppDelegate.showPreferences), keyEquivalent: ","))
|
|
|
|
|
|
|
|
menu.addItem(NSMenuItem.separatorItem())
|
|
|
|
|
2016-06-22 02:44:59 +00:00
|
|
|
menu.addItem(NSMenuItem(title: "Quit ZeroTier One", action: #selector(AppDelegate.quit), keyEquivalent: "q"))
|
|
|
|
|
|
|
|
statusItem.menu = menu
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func toggleNetwork(sender: NSMenuItem) {
|
|
|
|
NSLog("\(sender.title)")
|
|
|
|
|
|
|
|
let network = sender.representedObject as! Network
|
|
|
|
|
|
|
|
let id = String(network.nwid, radix: 16)
|
|
|
|
|
|
|
|
if network.connected {
|
|
|
|
ServiceCom.leaveNetwork(id)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ServiceCom.joinNetwork(id)
|
|
|
|
}
|
2016-06-17 03:53:55 +00:00
|
|
|
}
|
2016-07-01 04:24:45 +00:00
|
|
|
|
|
|
|
func copyNodeID() {
|
|
|
|
if let s = self.status {
|
|
|
|
pasteboard.setString(s.address, forType: NSPasteboardTypeString)
|
|
|
|
}
|
|
|
|
}
|
2016-05-14 20:54:49 +00:00
|
|
|
}
|
|
|
|
|