Mac OS X Launcher:

* In general bugfixes
  * Introduced event manager for better control flow
  * Splitted RouterStatusView to own file
  * Added shell script to setup and produce dmg file
This commit is contained in:
meeh
2018-09-22 22:13:40 +00:00
parent 829eb665e9
commit 3b38f5a161
16 changed files with 547 additions and 123 deletions

View File

@ -43,6 +43,9 @@
BFBDCB0021505BEE0014EB07 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFBDCAFF21505BED0014EB07 /* AppKit.framework */; };
BFBDCB02215060190014EB07 /* DetectJava.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBDCB01215060190014EB07 /* DetectJava.swift */; };
BFBDCB04215060970014EB07 /* StatusBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBDCB03215060970014EB07 /* StatusBarController.swift */; };
BFDD81DA2156B3E30014EB07 /* RouterManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDD81D92156B3E30014EB07 /* RouterManager.swift */; };
BFE16BF82156C61E0014EB07 /* RouterStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE16BF72156C61E0014EB07 /* RouterStatusView.swift */; };
BFE16BFA2156DAED0014EB07 /* EventManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE16BF92156DAED0014EB07 /* EventManager.swift */; };
BFE1CBAD2151908F0014EB07 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFE1CBAC2151908F0014EB07 /* CoreFoundation.framework */; };
BFF4581C213C48EA0014EB07 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF4581B213C48EA0014EB07 /* EventMonitor.swift */; };
/* End PBXBuildFile section */
@ -94,6 +97,10 @@
BFBDCAFF21505BED0014EB07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
BFBDCB01215060190014EB07 /* DetectJava.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectJava.swift; sourceTree = "<group>"; };
BFBDCB03215060970014EB07 /* StatusBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarController.swift; sourceTree = "<group>"; };
BFDD81D92156B3E30014EB07 /* RouterManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterManager.swift; sourceTree = "<group>"; };
BFE16BF72156C61E0014EB07 /* RouterStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterStatusView.swift; sourceTree = "<group>"; };
BFE16BF92156DAED0014EB07 /* EventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventManager.swift; sourceTree = "<group>"; };
BFE16BFB2156E94E0014EB07 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ../../../Sparkle/build/Release/Sparkle.framework; sourceTree = "<group>"; };
BFE1CBAC2151908F0014EB07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
BFF45818213C428E0014EB07 /* I2PLauncher-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "I2PLauncher-Bridging-Header.h"; sourceTree = "<group>"; };
BFF4581B213C48EA0014EB07 /* EventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMonitor.swift; sourceTree = "<group>"; };
@ -134,6 +141,7 @@
BF07789C21506D2B0014EB07 /* PopoverViewController.swift */,
BFBDCB03215060970014EB07 /* StatusBarController.swift */,
BF531514215105B40014EB07 /* LogViewController.swift */,
BFE16BF72156C61E0014EB07 /* RouterStatusView.swift */,
);
path = userinterface;
sourceTree = "<group>";
@ -191,6 +199,7 @@
BF5061922113C6ED0014EB07 /* Frameworks */ = {
isa = PBXGroup;
children = (
BFE16BFB2156E94E0014EB07 /* Sparkle.framework */,
BFE1CBAC2151908F0014EB07 /* CoreFoundation.framework */,
BF865416215182820014EB07 /* Foundation.framework */,
BF865414215180F60014EB07 /* libswiftDarwin.tbd */,
@ -209,6 +218,7 @@
BF5315062150C55B0014EB07 /* RouterRunner.swift */,
BF5315082150C6760014EB07 /* RouterDeployer.swift */,
BF53150A2150C6E80014EB07 /* I2PSubprocess.swift */,
BFDD81D92156B3E30014EB07 /* RouterManager.swift */,
);
path = routermgmt;
sourceTree = "<group>";
@ -221,6 +231,7 @@
BFBDCAF52150428D0014EB07 /* StringExtensions.swift */,
BFBDCAF7215047FE0014EB07 /* ArrayExtensions.swift */,
BF53150C2150CE310014EB07 /* DateTimeUtils.swift */,
BFE16BF92156DAED0014EB07 /* EventManager.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -334,8 +345,10 @@
BFBDCB04215060970014EB07 /* StatusBarController.swift in Sources */,
BFBDCAF8215047FE0014EB07 /* ArrayExtensions.swift in Sources */,
BF5315072150C55B0014EB07 /* RouterRunner.swift in Sources */,
BFE16BFA2156DAED0014EB07 /* EventManager.swift in Sources */,
BFBDCAF12150420C0014EB07 /* ExecutionResult.swift in Sources */,
BF5315092150C6760014EB07 /* RouterDeployer.swift in Sources */,
BFE16BF82156C61E0014EB07 /* RouterStatusView.swift in Sources */,
BFBDCAEF215041E30014EB07 /* Error.swift in Sources */,
BF1EFA41215141110014EB07 /* RouterTask.mm in Sources */,
BF7506CB21509CFD0014EB07 /* RouterProcessStatus.swift in Sources */,
@ -350,6 +363,7 @@
BF531515215105B40014EB07 /* LogViewController.swift in Sources */,
BF5315132150EB510014EB07 /* RouterProcessStatus+ObjectiveC.swift in Sources */,
BFBDCAFE2150567D0014EB07 /* SwiftMainDelegate.swift in Sources */,
BFDD81DA2156B3E30014EB07 /* RouterManager.swift in Sources */,
BF53150B2150C6E80014EB07 /* I2PSubprocess.swift in Sources */,
BFF4581C213C48EA0014EB07 /* EventMonitor.swift in Sources */,
BF1EFA3A215140E60014EB07 /* SBridge.mm in Sources */,

View File

@ -11,10 +11,9 @@ import Cocoa
@objc class SwiftMainDelegate : NSObject {
//let statusItem = NSStatusBar.system().statusItem(withLength: NSSquareStatusItemLength )
let statusBarController = StatusBarController()
let sharedRouterMgmr = RouterManager.shared()
static let javaDetector = DetectJava()
static let objCBridge = SBridge()
override init() {
super.init()
@ -37,10 +36,7 @@ import Cocoa
} else {
RouterProcessStatus.isRouterRunning = false
print("I2P Router seems to NOT be running")
}
} // End of init()
@objc func findInstalledI2PVersion() {
@ -63,13 +59,12 @@ import Cocoa
let sub:Subprocess = Subprocess.init(executablePath: "/bin/sh", arguments: cmdArgs)
let results:ExecutionResult = sub.execute(captureOutput: true)!
if (results.didCaptureOutput) {
print("captured output")
let i2pVersion = results.outputLines.first?.replace(target: "I2P Core version: ", withString: "")
NSLog("I2P version detected: %@",i2pVersion ?? "Unknown")
RouterProcessStatus.routerVersion = i2pVersion
RouterManager.shared().eventManager.trigger(eventName: "router_version", information: i2pVersion)
} else {
print("did NOT captured output")
print("Warning: Version Detection did NOT captured output")
}
}
@ -78,21 +73,11 @@ import Cocoa
var i2pPath = NSHomeDirectory()
i2pPath += "/Library/I2P"
let fileManager = FileManager()
var ok = ObjCBool(true)
let doesI2PDirExists = fileManager.fileExists(atPath: i2pPath, isDirectory: &ok)
if (!doesI2PDirExists) {
// Deploy
}
//let i2pJarPath = i2pPath + "/lib/i2p.jar"
findInstalledI2PVersion()
}
@objc static func openLink(url: String) {
objCBridge.openUrl(url)
SBridge.sharedInstance().openUrl(url)
}
@objc func applicationWillTerminate() {

View File

@ -0,0 +1,83 @@
//
// EventManager.swift
// I2PLauncher
//
// Created by Mikal Villa on 22/09/2018.
// Copyright © 2018 The I2P Project. All rights reserved.
//
import Foundation
class EventManager {
var listeners = Dictionary<String, NSMutableArray>();
// Create a new event listener, not expecting information from the trigger
// @param eventName: Matching trigger eventNames will cause this listener to fire
// @param action: The function/lambda you want executed when the event triggers
func listenTo(eventName:String, action: @escaping (()->())) {
let newListener = EventListenerAction(callback: action)
addListener(eventName: eventName, newEventListener: newListener)
}
// Create a new event listener, expecting information from the trigger
// @param eventName: Matching trigger eventNames will cause this listener to fire
// @param action: The function/lambda you want executed when the event triggers
func listenTo(eventName:String, action: @escaping ((Any?)->())) {
let newListener = EventListenerAction(callback: action)
addListener(eventName: eventName, newEventListener: newListener)
}
internal func addListener(eventName:String, newEventListener:EventListenerAction) {
if let listenerArray = self.listeners[eventName] {
listenerArray.add(newEventListener)
} else {
self.listeners[eventName] = [newEventListener] as NSMutableArray
}
}
// Removes all listeners by default, or specific listeners through paramters
// @param eventName: If an event name is passed, only listeners for that event will be removed
func removeListeners(eventNameToRemoveOrNil:String?) {
if let eventNameToRemove = eventNameToRemoveOrNil {
if let actionArray = self.listeners[eventNameToRemove] {
actionArray.removeAllObjects()
}
} else {
self.listeners.removeAll(keepingCapacity: false)
}
}
// Triggers an event
// @param eventName: Matching listener eventNames will fire when this is called
// @param information: pass values to your listeners
func trigger(eventName:String, information:Any? = nil) {
if let actionObjects = self.listeners[eventName] {
for actionObject in actionObjects {
if let actionToPerform = actionObject as? EventListenerAction {
if let methodToCall = actionToPerform.actionExpectsInfo {
methodToCall(information)
}
else if let methodToCall = actionToPerform.action {
methodToCall()
}
}
}
}
}
}
// Class to hold actions to live in NSMutableArray
class EventListenerAction {
let action:(() -> ())?
let actionExpectsInfo:((Any?) -> ())?
init(callback: @escaping (() -> ()) ) {
self.action = callback
self.actionExpectsInfo = nil
}
init(callback: @escaping ((Any?) -> ()) ) {
self.actionExpectsInfo = callback
self.action = nil
}
}

View File

@ -0,0 +1,99 @@
//
// RouterManager.swift
// I2PLauncher
//
// Created by Mikal Villa on 22/09/2018.
// Copyright © 2018 The I2P Project. All rights reserved.
//
import Foundation
class RouterManager : NSObject {
// MARK: - Properties
static let packedVersion : String = "0.9.36"
let eventManager = EventManager()
var logViewStorage: NSTextStorage?
private static func handleRouterStart(information:Any?) {
NSLog("event! - handle router start")
RouterProcessStatus.routerStartedAt = Date()
RouterProcessStatus.isRouterChildProcess = true
RouterProcessStatus.isRouterRunning = true
}
private static func handleRouterStop(information:Any?) {
NSLog("event! - handle router stop")
RouterProcessStatus.routerStartedAt = nil
RouterProcessStatus.isRouterChildProcess = false
RouterProcessStatus.isRouterRunning = false
}
private static func handleRouterPid(information:Any?) {
Swift.print("event! - handle router pid: %s", information ?? "")
}
private static func handleRouterVersion(information:Any?) {
Swift.print("event! - handle router version: %s", information ?? "")
let currentVersion : String = information as! String
if (packedVersion.compare(currentVersion, options: .numeric) == .orderedDescending) {
Swift.print("event! - router version: Packed version is newer, gonna re-deploy")
} else {
Swift.print("event! - router version: No update needed")
RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: "all ok")
}
}
private static var sharedRouterManager: RouterManager = {
let inst = DetectJava()
let routerManager = RouterManager(detectJavaInstance: inst)
// Configuration
// ...
routerManager.updateState()
routerManager.eventManager.listenTo(eventName: "router_start", action: handleRouterStart)
routerManager.eventManager.listenTo(eventName: "router_stop", action: handleRouterStop)
routerManager.eventManager.listenTo(eventName: "router_pid", action: handleRouterPid)
routerManager.eventManager.listenTo(eventName: "router_version", action: handleRouterVersion)
return routerManager
}()
// MARK: -
let detectJava: DetectJava
private var routerInstance: I2PRouterTask?{
//Called after the change
didSet{
print("RouterManager.routerInstance did change to ", self.routerInstance ?? "null")
if (self.routerInstance != nil) {
RouterProcessStatus.isRouterRunning = (self.routerInstance?.isRouterRunning)!
}
}
};
// Initialization
private init(detectJavaInstance: DetectJava) {
self.detectJava = detectJavaInstance
}
// MARK: - Accessors
class func shared() -> RouterManager {
return sharedRouterManager
}
func setRouterTask(router: I2PRouterTask) {
self.routerInstance = router
}
func getRouterTask() -> I2PRouterTask? {
return self.routerInstance
}
func updateState() {
self.routerInstance = SBridge.sharedInstance()?.currentRouterInstance
}
}

View File

@ -9,10 +9,15 @@
import Foundation
extension RouterProcessStatus {
static func createNewRouterProcess(i2pPath: String, javaBinPath: String) {
let bridge = SBridge()
let timeWhenStarted = Date()
RouterProcessStatus.routerStartedAt = timeWhenStarted
bridge.startupI2PRouter(i2pPath, javaBinPath: javaBinPath)
SBridge.sharedInstance().startupI2PRouter(i2pPath, javaBinPath: javaBinPath)
RouterManager.shared().updateState()
}
static func shutdownRouterChildProcess() {
RouterManager.shared().getRouterTask()?.requestShutdown()
RouterManager.shared().updateState()
}
}

View File

@ -32,6 +32,10 @@ import AppKit
@objc func getJavaHome() -> String {
return RouterProcessStatus.knownJavaBinPath!
}
@objc func triggerEvent(en: String, details: String? = nil) {
RouterManager.shared().eventManager.trigger(eventName: en, information: details)
}
}
extension RouterProcessStatus {

View File

@ -14,11 +14,43 @@ class LogViewerViewController : NSTabViewItem {
@IBOutlet var scrollView: NSScrollView?
@IBOutlet var textFieldView: NSTextView?
private var outputPipe : Pipe?
override init(identifier: Any?) {
super.init(identifier: identifier)
self.captureStandardOutputAndRouteToTextView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.captureStandardOutputAndRouteToTextView()
}
func captureStandardOutputAndRouteToTextView() {
outputPipe = RouterManager.shared().getRouterTask()?.processPipe
outputPipe?.fileHandleForReading.waitForDataInBackgroundAndNotify()
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: outputPipe?.fileHandleForReading , queue: nil) {
notification in
let output = self.outputPipe?.fileHandleForReading.availableData
let outputString = String(data: output!, encoding: String.Encoding.utf8) ?? ""
DispatchQueue.main.async(execute: {
let previousOutput = self.textFieldView?.string ?? ""
let nextOutput = previousOutput + "\n" + outputString
self.textFieldView?.string = nextOutput
let range = NSRange(location:nextOutput.characters.count,length:0)
self.textFieldView?.scrollRangeToVisible(range)
})
self.outputPipe?.fileHandleForReading.waitForDataInBackgroundAndNotify()
}
}
}
class LogViewController {
}

View File

@ -12,9 +12,6 @@ class PopoverViewController: NSViewController {
required init?(coder: NSCoder) {
super.init(coder: coder)
//super.init(nibName: "UserInterfaces", bundle: Bundle.main)!
//let nib = NSNib(nibNamed: "UserInterfaces", bundle: Bundle.main)
}
@ -22,83 +19,6 @@ class PopoverViewController: NSViewController {
super.viewDidLoad()
// Do view setup here.
}
}
@objc class RouterStatusView : NSView {
static var instance: RouterStatusView?
static func getInstance() -> RouterStatusView? {
if (self.instance != Optional.none) {
return RouterStatusView.instance
}
return Optional.none
}
@IBOutlet var routerStatusLabel: NSTextField?
@IBOutlet var routerVersionLabel: NSTextField?
@IBOutlet var routerStartedByLabel: NSTextField?
@IBOutlet var routerUptimeLabel: NSTextField?
@IBOutlet var quickControlView: NSView?
@IBOutlet var routerStartStopButton: NSButton?
@objc func actionBtnStartRouter(_ sender: Any?) {
NSLog("START ROUTER")
(sender as! NSButton).cell?.stringValue = "Stop Router"
let timeWhenStarted = Date()
RouterProcessStatus.routerStartedAt = timeWhenStarted
SwiftMainDelegate.objCBridge.startupI2PRouter(RouterProcessStatus.i2pDirectoryPath, javaBinPath: RouterProcessStatus.knownJavaBinPath!)
}
@objc func actionBtnStopRouter(_ sender: Any?) {
NSLog("STOP ROUTER")
}
@objc func actionBtnRestartRouter(sender: Any?) {}
override func viewWillDraw() {
super.viewWillDraw()
if (RouterStatusView.instance != nil) {
RouterStatusView.instance = self
}
self.setRouterStatusLabelText()
}
func setRouterStatusLabelText() {
if (RouterProcessStatus.isRouterRunning) {
routerStatusLabel?.cell?.stringValue = "Router status: Running"
routerStartStopButton?.action = #selector(self.actionBtnStopRouter(_:))
} else {
routerStatusLabel?.cell?.stringValue = "Router status: Not running"
routerStartStopButton?.action = #selector(self.actionBtnStartRouter(_:))
}
routerStartStopButton?.needsDisplay = true
routerStartStopButton?.target = self
quickControlView?.needsDisplay = true
if let version = RouterProcessStatus.routerVersion {
routerVersionLabel?.cell?.stringValue = "Router version: " + version
} else {
routerVersionLabel?.cell?.stringValue = "Router version: Still unknown"
}
if let routerStartTime = RouterProcessStatus.routerStartedAt {
routerUptimeLabel?.cell?.stringValue = "Router has runned for " + DateTimeUtils.timeAgoSinceDate(date: NSDate(date: routerStartTime), numericDates: false)
}
}
init() {
let c = NSCoder()
super.init(coder: c)!
self.setRouterStatusLabelText()
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
self.setRouterStatusLabelText()
}
}

View File

@ -0,0 +1,113 @@
//
// RouterStatusView.swift
// I2PLauncher
//
// Created by Mikal Villa on 22/09/2018.
// Copyright © 2018 The I2P Project. All rights reserved.
//
import Cocoa
@objc class RouterStatusView : NSView {
static var instance: RouterStatusView?
static func getInstance() -> RouterStatusView? {
if (self.instance != Optional.none) {
return RouterStatusView.instance
}
return Optional.none
}
@IBOutlet var routerStatusLabel: NSTextField?
@IBOutlet var routerVersionLabel: NSTextField?
@IBOutlet var routerStartedByLabel: NSTextField?
@IBOutlet var routerUptimeLabel: NSTextField?
@IBOutlet var quickControlView: NSView?
@IBOutlet var routerStartStopButton: NSButton?
@objc func actionBtnStartRouter(_ sender: Any?) {
NSLog("START ROUTER")
if (!(RouterManager.shared().getRouterTask()?.isRunning())!) {
SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath, javaBinPath: RouterProcessStatus.knownJavaBinPath!)
}
RouterManager.shared().updateState()
}
@objc func actionBtnStopRouter(_ sender: Any?) {
NSLog("STOP ROUTER")
if ((RouterManager.shared().getRouterTask()?.isRunning())!) {
NSLog("Found running router")
RouterManager.shared().getRouterTask()?.requestShutdown()
RouterManager.shared().updateState()
}
}
@objc func actionBtnRestartRouter(sender: Any?) {
if ((RouterManager.shared().getRouterTask()?.isRunning())!) {
RouterManager.shared().getRouterTask()?.requestRestart()
} else {
NSLog("Can't restart a non running router, start it however...")
SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath, javaBinPath: RouterProcessStatus.knownJavaBinPath!)
}
RouterManager.shared().updateState()
}
override func viewWillDraw() {
super.viewWillDraw()
if (RouterStatusView.instance != nil) {
RouterStatusView.instance = self
}
self.setRouterStatusLabelText()
}
func setRouterStatusLabelText() {
if (RouterProcessStatus.isRouterRunning) {
routerStatusLabel?.cell?.stringValue = "Router status: Running"
routerStartStopButton?.title = "Stop Router"
routerStartStopButton?.action = #selector(self.actionBtnStopRouter(_:))
} else {
routerStatusLabel?.cell?.stringValue = "Router status: Not running"
routerStartStopButton?.title = "Start Router"
routerStartStopButton?.action = #selector(self.actionBtnStartRouter(_:))
}
routerStartStopButton?.needsDisplay = true
routerStartStopButton?.target = self
quickControlView?.needsDisplay = true
let staticStartedByLabelText = "Router started by launcher?"
if RouterProcessStatus.isRouterChildProcess {
routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+" Yes"
} else {
routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+" No"
}
routerStartedByLabel?.needsDisplay = true
if let version = RouterProcessStatus.routerVersion {
routerVersionLabel?.cell?.stringValue = "Router version: " + version
} else {
routerVersionLabel?.cell?.stringValue = "Router version: Still unknown"
}
if let routerStartTime = RouterProcessStatus.routerStartedAt {
routerUptimeLabel?.cell?.stringValue = "Uptime: Router started " + DateTimeUtils.timeAgoSinceDate(date: NSDate(date: routerStartTime), numericDates: false)
} else {
routerUptimeLabel?.cell?.stringValue = "Uptime: Router isn't running"
}
routerUptimeLabel?.needsDisplay = true
}
init() {
let c = NSCoder()
super.init(coder: c)!
self.setRouterStatusLabelText()
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
self.setRouterStatusLabelText()
}
}

View File

@ -22,7 +22,6 @@ import Cocoa
@objc func constructMenu() -> NSMenu {
let menu = NSMenu()
//let sb = SwiftMainDelegate.objCBridge
menu.addItem(NSMenuItem(title: "Open I2P Console", action: #selector(self.handleOpenConsole(_:)), keyEquivalent: "O"))
menu.addItem(NSMenuItem.separator())
@ -33,17 +32,12 @@ import Cocoa
override init() {
super.init()//(xib: "UserInterface", bundle: nil)
super.init()
popover.contentViewController = PopoverViewController.freshController()
if let button = statusItem.button {
button.image = NSImage(named:"StatusBarButtonImage")
//button.title = "I2P"
button.toolTip = "I2P Launch Manager"
//button.isVisible = true
//button.action = #selector(self.statusBarButtonClicked)
//button.sendAction(on: [.leftMouseUp, .rightMouseUp])
//button.doubleAction = #selector(self.systemBarIconDoubleClick)
button.target = self
button.action = #selector(self.statusBarButtonClicked(sender:))
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
@ -97,6 +91,7 @@ import Cocoa
if popover.isShown {
closePopover(sender: sender)
} else {
RouterManager.shared().updateState()
showPopover(sender: sender)
}
}

View File

@ -39,11 +39,16 @@ const std::vector<std::string> defaultFlagsForExtractorJob {
@class I2PRouterTask;
@interface I2PRouterTask : NSObject
@property (strong) NSTask* routerTask;
// TODO: Not in use, remove?
/*
@property (strong) NSUserDefaults *userPreferences;
@property (strong) NSFileHandle *readLogHandle;
@property (strong) NSMutableData *totalLogData;
@property (strong) NSPipe *processPipe;
@property (strong) NSFileHandle *input;
*/
@property (strong) NSPipe *processPipe;
@property (atomic) BOOL isRouterRunning;
@property (atomic) BOOL userRequestedRestart;
- (instancetype) initWithOptions : (RTaskOptions*) options;

View File

@ -31,7 +31,7 @@
{
self.userRequestedRestart = NO;
self.isRouterRunning = NO;
self.input = [NSFileHandle fileHandleWithStandardInput];
//self.input = [NSFileHandle fileHandleWithStandardInput];
self.routerTask = [NSTask new];
self.processPipe = [NSPipe new];
[self.routerTask setLaunchPath:options.binPath];
@ -44,13 +44,41 @@
[self.routerTask setStandardOutput:self.processPipe];
[self.routerTask setStandardError:self.processPipe];
/*
NSFileHandle *stdoutFileHandle = [self.processPipe fileHandleForReading];
dup2([[self.processPipe fileHandleForWriting] fileDescriptor], fileno(stdout));
auto source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [stdoutFileHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_event_handler(source, ^{
void* data = malloc(4096);
ssize_t readResult = 0;
do
{
errno = 0;
readResult = read([stdoutFileHandle fileDescriptor], data, 4096);
} while (readResult == -1 && errno == EINTR);
if (readResult > 0)
{
//AppKit UI should only be updated from the main thread
dispatch_async(dispatch_get_main_queue(),^{
NSString* stdOutString = [[NSString alloc] initWithBytesNoCopy:data length:readResult encoding:NSUTF8StringEncoding freeWhenDone:YES];
NSAttributedString* stdOutAttributedString = [[NSAttributedString alloc] initWithString:stdOutString];
NSLog(@"Router stdout: %@", stdOutString);
//auto logForwarder = new LogForwarder();
//[logForwarder appendLogViewWithLogLine:stdOutAttributedString];
});
}
else{free(data);}
});
dispatch_resume(source);
*/
/*
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(routerStdoutData:)
name:NSFileHandleDataAvailableNotification
object:stdoutFileHandle];
[stdoutFileHandle waitForDataInBackgroundAndNotify];
*/
[self.routerTask setTerminationHandler:^(NSTask* task) {
// Cleanup
@ -58,6 +86,8 @@
auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
[swiftRouterStatus setRouterStatus: false];
[swiftRouterStatus setRouterRanByUs: false];
[swiftRouterStatus triggerEventWithEn:@"router_stop" details:@"normal shutdown"];
[[SBridge sharedInstance] setCurrentRouterInstance:nil];
sendUserNotification(APP_IDSTR, @"I2P Router has stopped");
}];
return self;
@ -82,8 +112,9 @@
- (int) execute
{
@try {
auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
[swiftRouterStatus triggerEventWithEn:@"router_start" details:@"normal start"];
[self.routerTask launch];
watchPid([self.routerTask processIdentifier]);
self.isRouterRunning = YES;
return 1;
}
@ -91,8 +122,11 @@
{
NSLog(@"Expection occurred %@", [e reason]);
auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
self.isRouterRunning = NO;
[swiftRouterStatus setRouterStatus: false];
[swiftRouterStatus setRouterRanByUs: false];
[swiftRouterStatus triggerEventWithEn:@"router_stop" details:@"error shutdown"];
[[SBridge sharedInstance] setCurrentRouterInstance:nil];
sendUserNotification(@"An error occured, can't start the I2P Router", [e reason]);
return 0;
}

View File

@ -9,6 +9,8 @@
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "RouterTask.h"
#ifdef __cplusplus
#include <memory>
#include <future>
@ -54,7 +56,9 @@ inline std::string buildClassPathForObjC(std::string basePath)
#endif
@interface SBridge : NSObject
@property (nonatomic, assign) I2PRouterTask* currentRouterInstance;
- (NSString*) buildClassPath:(NSString*)i2pPath;
- (void) startupI2PRouter:(NSString*)i2pRootPath javaBinPath:(NSString*)javaBinPath;
- (void) openUrl:(NSString*)url;
+ (instancetype)sharedInstance; // this makes it a singleton
@end

View File

@ -33,9 +33,16 @@ std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments,
options.arguments = arguments;
options.i2pBaseDir = i2pBaseDir;
auto instance = [[I2PRouterTask alloc] initWithOptions: options];
[[SBridge sharedInstance] setCurrentRouterInstance:instance];
[instance execute];
sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
auto pid = [instance getPID];
NSLog(@"Got pid: %d", pid);
auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
[swiftRouterStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
return std::async(std::launch::async, [&pid]{
return pid;
});
@ -45,6 +52,13 @@ std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments,
auto errStr = [NSString stringWithFormat:@"Expection occurred %@",[e reason]];
NSLog(@"%@", errStr);
sendUserNotification(APP_IDSTR, errStr);
[[SBridge sharedInstance] setCurrentRouterInstance:nil];
auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
[swiftRouterStatus setRouterStatus: false];
[swiftRouterStatus setRouterRanByUs: false];
[swiftRouterStatus triggerEventWithEn:@"router_exception" details:errStr];
return std::async(std::launch::async, [&]{
return 0;
});
@ -55,6 +69,16 @@ std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments,
@implementation SBridge
// this makes it a singleton
+ (instancetype)sharedInstance {
static SBridge *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SBridge alloc] init];
});
return sharedInstance;
}
- (void) openUrl:(NSString*)url
{
@ -125,6 +149,7 @@ std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments,
sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg]);
[routerStatus setRouterStatus: false];
[routerStatus setRouterRanByUs: false];
[routerStatus triggerEventWithEn:@"router_exception" details:[NSString stringWithFormat:@"Error: %@", errMsg]];
}
}
@end

View File

@ -204,7 +204,6 @@ using namespace subprocess;
NSBundle *launcherBundle = [NSBundle mainBundle];
auto sBridge = [[SBridge alloc] init];
// Helper object to hold statefull path information
self.metaInfo = [[ExtractMetaInfo alloc] init];
@ -237,7 +236,7 @@ using namespace subprocess;
NSLog(@"Time to detect I2P version in install directory");
[self.swiftRuntime findInstalledI2PVersion];
if (shouldAutoStartRouter) {
[sBridge startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
[[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
[routerStatus setRouterRanByUs: true];
}
}];
@ -248,7 +247,7 @@ using namespace subprocess;
[self.swiftRuntime findInstalledI2PVersion];
if (shouldAutoStartRouter) {
[sBridge startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
[[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase javaBinPath:self.metaInfo.javaBinary];
[routerStatus setRouterRanByUs: true];
}
}

View File

@ -0,0 +1,107 @@
#!/bin/bash
APP_NAME="I2PLauncher"
VERSION="0.9.36"
DMG_BACKGROUND_IMG="Background.png"
APP_EXE="${APP_NAME}.app/Contents/MacOS/${APP_NAME}"
VOL_NAME="${APP_NAME} ${VERSION}"
DMG_TMP="${VOL_NAME}-temp.dmg"
DMG_FINAL="${VOL_NAME}.dmg"
STAGING_DIR="/tmp/mkdmg$$"
# Check the background image DPI and convert it if it isn't 72x72
_BACKGROUND_IMAGE_DPI_H=`sips -g dpiHeight ${DMG_BACKGROUND_IMG} | grep -Eo '[0-9]+\.[0-9]+'`
_BACKGROUND_IMAGE_DPI_W=`sips -g dpiWidth ${DMG_BACKGROUND_IMG} | grep -Eo '[0-9]+\.[0-9]+'`
if [ $(echo " $_BACKGROUND_IMAGE_DPI_H != 72.0 " | bc) -eq 1 -o $(echo " $_BACKGROUND_IMAGE_DPI_W != 72.0 " | bc) -eq 1 ]; then
echo "WARNING: The background image's DPI is not 72. This will result in distorted backgrounds on Mac OS X 10.7+."
echo " I will convert it to 72 DPI for you."
_DMG_BACKGROUND_TMP="${DMG_BACKGROUND_IMG%.*}"_dpifix."${DMG_BACKGROUND_IMG##*.}"
sips -s dpiWidth 72 -s dpiHeight 72 ${DMG_BACKGROUND_IMG} --out ${_DMG_BACKGROUND_TMP}
DMG_BACKGROUND_IMG="${_DMG_BACKGROUND_TMP}"
fi
# clear out any old data
rm -rf "${STAGING_DIR}" "${DMG_TMP}" "${DMG_FINAL}"
# copy over the stuff we want in the final disk image to our staging dir
mkdir -p "${STAGING_DIR}"
cp -rpf "${APP_NAME}.app" "${STAGING_DIR}"
# ... cp anything else you want in the DMG - documentation, etc.
# figure out how big our DMG needs to be
# assumes our contents are at least 1M!
SIZE=`du -sh "${STAGING_DIR}" | sed 's/\([0-9\.]*\)M\(.*\)/\1/'`
SIZE=`echo "${SIZE} + 1.0" | bc | awk '{print int($1+0.5)}'`
if [ $? -ne 0 ]; then
echo "Error: Cannot compute size of staging dir"
exit
fi
# create the temp DMG file
hdiutil create -srcfolder "${STAGING_DIR}" -volname "${VOL_NAME}" -fs HFS+ \
-fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M "${DMG_TMP}"
echo "Created DMG: ${DMG_TMP}"
# mount it and save the device
DEVICE=$(hdiutil attach -readwrite -noverify "${DMG_TMP}" | \
egrep '^/dev/' | sed 1q | awk '{print $1}')
sleep 2
# add a link to the Applications dir
echo "Add link to /Applications"
pushd /Volumes/"${VOL_NAME}"
ln -s /Applications
popd
# add a background image
mkdir /Volumes/"${VOL_NAME}"/.background
cp "${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/
# tell the Finder to resize the window, set the background,
# change the icon size, place the icons in the right position, etc.
echo '
tell application "Finder"
tell disk "'${VOL_NAME}'"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set the bounds of container window to {400, 100, 920, 440}
set viewOptions to the icon view options of container window
set arrangement of viewOptions to not arranged
set icon size of viewOptions to 72
set background picture of viewOptions to file ".background:'${DMG_BACKGROUND_IMG}'"
set position of item "'${APP_NAME}'.app" of container window to {160, 205}
set position of item "Applications" of container window to {360, 205}
close
open
update without registering applications
delay 2
end tell
end tell
' | osascript
sync
# unmount it
hdiutil detach "${DEVICE}"
# now make the final image a compressed disk image
echo "Creating compressed image"
hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FINAL}"
# clean up
rm -rf "${DMG_TMP}"
rm -rf "${STAGING_DIR}"
echo 'Done.'
exit