diff --git a/homebrew-services.xcodeproj/project.pbxproj b/homebrew-services.xcodeproj/project.pbxproj index 0563c03..4d8e959 100644 --- a/homebrew-services.xcodeproj/project.pbxproj +++ b/homebrew-services.xcodeproj/project.pbxproj @@ -8,19 +8,19 @@ /* Begin PBXBuildFile section */ 027D4DE11F3B1FF500602020 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027D4DE01F3B1FF500602020 /* AppDelegate.swift */; }; - 027D4DE31F3B1FF500602020 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027D4DE21F3B1FF500602020 /* ViewController.swift */; }; 027D4DE51F3B1FF500602020 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 027D4DE41F3B1FF500602020 /* Assets.xcassets */; }; 027D4DE81F3B1FF500602020 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 027D4DE61F3B1FF500602020 /* Main.storyboard */; }; + 027D4DF11F3B290600602020 /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027D4DF01F3B290600602020 /* Service.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 027D4DDD1F3B1FF500602020 /* homebrew-services.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "homebrew-services.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 027D4DE01F3B1FF500602020 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 027D4DE21F3B1FF500602020 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 027D4DE41F3B1FF500602020 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 027D4DE71F3B1FF500602020 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 027D4DE91F3B1FF500602020 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 027D4DEA1F3B1FF500602020 /* homebrew_services.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = homebrew_services.entitlements; sourceTree = ""; }; + 027D4DF01F3B290600602020 /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,7 +54,7 @@ isa = PBXGroup; children = ( 027D4DE01F3B1FF500602020 /* AppDelegate.swift */, - 027D4DE21F3B1FF500602020 /* ViewController.swift */, + 027D4DF01F3B290600602020 /* Service.swift */, 027D4DE41F3B1FF500602020 /* Assets.xcassets */, 027D4DE61F3B1FF500602020 /* Main.storyboard */, 027D4DE91F3B1FF500602020 /* Info.plist */, @@ -133,8 +133,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 027D4DE31F3B1FF500602020 /* ViewController.swift in Sources */, 027D4DE11F3B1FF500602020 /* AppDelegate.swift in Sources */, + 027D4DF11F3B290600602020 /* Service.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/homebrew-services/AppDelegate.swift b/homebrew-services/AppDelegate.swift index 16c136f..02aa82f 100644 --- a/homebrew-services/AppDelegate.swift +++ b/homebrew-services/AppDelegate.swift @@ -11,16 +11,122 @@ import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { + let statusBarItem = NSStatusBar.system.statusItem(withLength: -2) - + let services = Services.sharedInstance + + private var notifyToken: Any? + func applicationDidFinishLaunching(_ aNotification: Notification) { - // Insert code here to initialize your application + statusBarItem.menu = NSMenu() + + statusBarItem.button?.image = #imageLiteral(resourceName: "StatusBarButtonIcon") + + notifyToken = NotificationCenter.default + .addObserver(forName: NSNotification.Name(rawValue: "NSMenuDidBeginTrackingNotification"), + object: nil, + queue: nil) {[unowned self] _ in self.fresh()} + + fresh() } - func applicationWillTerminate(_ aNotification: Notification) { - // Insert code here to tear down your application + deinit { + NotificationCenter.default.removeObserver(notifyToken!) } +} + +extension AppDelegate { + @objc func fresh() { + + DispatchQueue.global(qos: .userInteractive).async { + [unowned self] in + + var dict = self.services.list() + + DispatchQueue.main.sync { + [unowned self] in + let menu = self.statusBarItem.menu! + + for item in menu.items { + if let s = dict[item.title] { + if state(s) != item.state { + item.state = state(s) + } + dict.removeValue(forKey: item.title) + } else { + menu.removeItem(item) + } + } + + for(serviceName, state) in dict { + let item = NSMenuItem(title: serviceName, + action: #selector(AppDelegate.toggole), + keyEquivalent: "String") + + if state { + item.state = .onState + } + menu.addItem(item) + } + } // main queue + } // global queue + + } + + + @objc func toggole(_ sender: NSMenuItem!) { + debugPrint("[toggole]: \(sender.title) - \(sender.state)") + sender.action = nil + DispatchQueue.global(qos: .userInteractive).async { + [unowned self] in + + var actionName = "" + var r = false + + switch sender.state { + case .offState: + actionName = "Start" + r = self.services.start(sender.title) + + case .onState: + actionName = "Stop" + r = self.services.stop(sender.title) + + default: + break + } + + + let post = (r ? succPost : failPost)(actionName, sender.title) + + DispatchQueue.main.async { + NSUserNotificationCenter.default.deliver(post) + } + + self.fresh() + + DispatchQueue.main.sync { + sender.action = #selector(AppDelegate.toggole) + } + } + } } +fileprivate func succPost(action: String, name: String) -> NSUserNotification { + let post = NSUserNotification() + post.title = "\(action) \(name) Succeeded" + return post; +} + +fileprivate func failPost(action: String, name: String) -> NSUserNotification { + let post = NSUserNotification() + post.title = "\(action) \(name) Failed" + post.soundName = "Funk" + return post; +} + +fileprivate func state(_ s: Bool) -> NSControl.StateValue { + return s ? .onState : .offState +} diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-1.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-1.png new file mode 100644 index 0000000..1a55751 Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-1.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-2.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-2.png new file mode 100644 index 0000000..1a55751 Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-2.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-3.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-3.png new file mode 100644 index 0000000..af49032 Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-3.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-4.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-4.png new file mode 100644 index 0000000..af49032 Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-4.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-5.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-5.png new file mode 100644 index 0000000..905167d Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-5.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-6.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-6.png new file mode 100644 index 0000000..686b88b Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-6.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-7.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-7.png new file mode 100644 index 0000000..88bd8a1 Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-7.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-8.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-8.png new file mode 100644 index 0000000..88bd8a1 Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-8.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-9.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-9.png new file mode 100644 index 0000000..cfe51ab Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService-9.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService.png b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService.png new file mode 100644 index 0000000..f7eb0d1 Binary files /dev/null and b/homebrew-services/Assets.xcassets/AppIcon.appiconset/BeerService.png differ diff --git a/homebrew-services/Assets.xcassets/AppIcon.appiconset/Contents.json b/homebrew-services/Assets.xcassets/AppIcon.appiconset/Contents.json index 2db2b1c..55455ed 100644 --- a/homebrew-services/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/homebrew-services/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,53 +1,63 @@ { "images" : [ { - "idiom" : "mac", "size" : "16x16", + "idiom" : "mac", + "filename" : "BeerService-9.png", "scale" : "1x" }, { - "idiom" : "mac", "size" : "16x16", + "idiom" : "mac", + "filename" : "BeerService-8.png", "scale" : "2x" }, { - "idiom" : "mac", "size" : "32x32", + "idiom" : "mac", + "filename" : "BeerService-7.png", "scale" : "1x" }, { - "idiom" : "mac", "size" : "32x32", + "idiom" : "mac", + "filename" : "BeerService-6.png", "scale" : "2x" }, { - "idiom" : "mac", "size" : "128x128", + "idiom" : "mac", + "filename" : "BeerService-5.png", "scale" : "1x" }, { - "idiom" : "mac", "size" : "128x128", + "idiom" : "mac", + "filename" : "BeerService-4.png", "scale" : "2x" }, { - "idiom" : "mac", "size" : "256x256", + "idiom" : "mac", + "filename" : "BeerService-3.png", "scale" : "1x" }, { - "idiom" : "mac", "size" : "256x256", + "idiom" : "mac", + "filename" : "BeerService-2.png", "scale" : "2x" }, { - "idiom" : "mac", "size" : "512x512", + "idiom" : "mac", + "filename" : "BeerService-1.png", "scale" : "1x" }, { - "idiom" : "mac", "size" : "512x512", + "idiom" : "mac", + "filename" : "BeerService.png", "scale" : "2x" } ], diff --git a/homebrew-services/Assets.xcassets/Contents.json b/homebrew-services/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/homebrew-services/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/Contents.json b/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/Contents.json new file mode 100644 index 0000000..16d3122 --- /dev/null +++ b/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "baricon-1.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "filename" : "baricon.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/baricon-1.png b/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/baricon-1.png new file mode 100644 index 0000000..a8701da Binary files /dev/null and b/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/baricon-1.png differ diff --git a/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/baricon.png b/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/baricon.png new file mode 100644 index 0000000..2e1a73e Binary files /dev/null and b/homebrew-services/Assets.xcassets/StatusBarButtonIcon.imageset/baricon.png differ diff --git a/homebrew-services/Base.lproj/Main.storyboard b/homebrew-services/Base.lproj/Main.storyboard index 07dbb4d..ace6dab 100644 --- a/homebrew-services/Base.lproj/Main.storyboard +++ b/homebrew-services/Base.lproj/Main.storyboard @@ -1,7 +1,8 @@ - - + + - + + @@ -673,45 +674,11 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/homebrew-services/Info.plist b/homebrew-services/Info.plist index f819ece..fcd1b35 100644 --- a/homebrew-services/Info.plist +++ b/homebrew-services/Info.plist @@ -2,6 +2,8 @@ + LSUIElement + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/homebrew-services/Service.swift b/homebrew-services/Service.swift new file mode 100644 index 0000000..a467dd7 --- /dev/null +++ b/homebrew-services/Service.swift @@ -0,0 +1,88 @@ +// +// Service.swift +// homebrew-services +// +// Created by Lustres on 8/9/17. +// Copyright © 2017 Lustres. All rights reserved. +// + +import Foundation + +class Services { + + static let sharedInstance = Services() + + let brew_path = "/usr/local/bin/brew" + + let start_cmd = ["/usr/local/bin/brew", "services", "start"] + + let stop_cmd = ["/usr/local/bin/brew", "services", "stop"] + + let list_cmd = ["/usr/local/bin/brew", "services", "list"] + + func start(_ name: String) -> Bool { + return (start_cmd + [name]).run() != nil + } + + func stop(_ name: String) -> Bool { + return (stop_cmd + [name]).run() != nil + } + + func list() -> [String: Bool] { + var services = [String: Bool]() + + guard let table = list_cmd.run()?.splitAfterTrim(splitBy: .newlines) else { + return services + } + + for i in 1.. [String] { + return self.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: splitBy) + } +} + +fileprivate extension Array where Element == String { + func run() -> String? { + + let task = Process() + + let outPipe = Pipe() + let errPipe = Pipe() + + task.standardOutput = outPipe + task.standardError = errPipe + + task.launchPath = "/bin/bash" + task.arguments = self + + let outFileHandler = outPipe.fileHandleForReading + let errFileHandler = errPipe.fileHandleForReading + + task.launch() + task.waitUntilExit() + + let outData = outFileHandler.readDataToEndOfFile() + let output = NSString(data: outData, encoding: String.Encoding.utf8.rawValue)! + + let errData = errFileHandler.readDataToEndOfFile() + let errput = NSString(data: errData, encoding: String.Encoding.utf8.rawValue)! + + debugPrint("[shell]: \(output)") + if errput != "" { + debugPrint("[error]: \(errput)") + } + + return task.terminationStatus == 0 ? output as String: nil + } +} diff --git a/homebrew-services/ViewController.swift b/homebrew-services/ViewController.swift deleted file mode 100644 index 4c703c4..0000000 --- a/homebrew-services/ViewController.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// ViewController.swift -// homebrew-services -// -// Created by Lustres on 8/9/17. -// Copyright © 2017 Lustres. All rights reserved. -// - -import Cocoa - -class ViewController: NSViewController { - - override func viewDidLoad() { - super.viewDidLoad() - - // Do any additional setup after loading the view. - } - - override var representedObject: Any? { - didSet { - // Update the view, if already loaded. - } - } - - -} - diff --git a/homebrew-services/homebrew_services.entitlements b/homebrew-services/homebrew_services.entitlements index f2ef3ae..311b32b 100644 --- a/homebrew-services/homebrew_services.entitlements +++ b/homebrew-services/homebrew_services.entitlements @@ -2,9 +2,9 @@ - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-only - + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only +