diff --git a/LomoAgent.xcodeproj/project.pbxproj b/LomoAgent.xcodeproj/project.pbxproj index b0bf879..d8a95fe 100644 --- a/LomoAgent.xcodeproj/project.pbxproj +++ b/LomoAgent.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3A2FE40022E2B339000022B0 /* QRImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2FE3FF22E2B339000022B0 /* QRImage.swift */; }; 3A35C5D92240B5F30030FF1B /* avconv in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3A35C5D62240B5EA0030FF1B /* avconv */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 3A35C5DA2240B5F30030FF1B /* lomod in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3A35C5D52240B5E90030FF1B /* lomod */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 3A35C5DC2240B6040030FF1B /* avconv in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3A35C5CF2240B5A90030FF1B /* avconv */; }; @@ -48,6 +49,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3A2FE3FF22E2B339000022B0 /* QRImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRImage.swift; sourceTree = ""; }; + 3A2FE40122E2BC34000022B0 /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; 3A35C5CE2240B5A90030FF1B /* lomod */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lomod; path = dependencies/lomod/Contents/Frameworks/lomod; sourceTree = ""; }; 3A35C5CF2240B5A90030FF1B /* avconv */ = {isa = PBXFileReference; lastKnownFileType = folder; name = avconv; path = dependencies/lomod/Contents/Frameworks/avconv; sourceTree = ""; }; 3A35C5D52240B5E90030FF1B /* lomod */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = lomod; path = dependencies/lomod/Contents/MacOS/lomod; sourceTree = ""; }; @@ -131,11 +134,13 @@ isa = PBXGroup; children = ( 3A3896FB222F77FA004A2001 /* AppDelegate.swift */, + 3A2FE3FF22E2B339000022B0 /* QRImage.swift */, 3A389709222F8168004A2001 /* StatusMenuController.swift */, 3A3896FD222F7801004A2001 /* Assets.xcassets */, 3A3896FF222F7801004A2001 /* MainMenu.xib */, 3A38970D22321A03004A2001 /* PreferencesWindow.swift */, 3A35C5E322435B710030FF1B /* PreferencesWindow.xib */, + 3A2FE40122E2BC34000022B0 /* Bridging-Header.h */, 3A35C5E8224365340030FF1B /* AboutWindow.swift */, 3AC3E845229F9B60004EA1D7 /* HyperlinkTextField.swift */, 3A35C5EE22436CBA0030FF1B /* AboutWindow.xib */, @@ -241,6 +246,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3A2FE40022E2B339000022B0 /* QRImage.swift in Sources */, 3A38970F22321A03004A2001 /* PreferencesWindow.swift in Sources */, 3A38970A222F8168004A2001 /* StatusMenuController.swift in Sources */, 3A3896FC222F77FA004A2001 /* AppDelegate.swift in Sources */, diff --git a/LomoAgent/Base.lproj/PreferencesWindow.xib b/LomoAgent/Base.lproj/PreferencesWindow.xib index 53a033e..15f9efc 100644 --- a/LomoAgent/Base.lproj/PreferencesWindow.xib +++ b/LomoAgent/Base.lproj/PreferencesWindow.xib @@ -10,6 +10,7 @@ + @@ -20,14 +21,14 @@ - + - + - + @@ -36,7 +37,7 @@ - + @@ -45,7 +46,7 @@ - + @@ -64,22 +65,10 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + diff --git a/LomoAgent/Bridging-Header.h b/LomoAgent/Bridging-Header.h new file mode 100644 index 0000000..a758860 --- /dev/null +++ b/LomoAgent/Bridging-Header.h @@ -0,0 +1,14 @@ +// +// Bridging-Header.h +// LomoAgent +// +// Created by Jiantao Fu on 7/19/19. +// Copyright © 2019 lomoware. All rights reserved. +// + +#ifndef Bridging_Header_h +#define Bridging_Header_h + +#include + +#endif /* Bridging_Header_h */ diff --git a/LomoAgent/Info.plist b/LomoAgent/Info.plist index 07c12aa..4051706 100644 --- a/LomoAgent/Info.plist +++ b/LomoAgent/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - ff6d861 + c06f176 CFBundleVersion - 2019_06_04.09_09_24.0.ff6d861 + 2019_07_19.21_11_50.0.c06f176 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement diff --git a/LomoAgent/PreferencesWindow.swift b/LomoAgent/PreferencesWindow.swift index 2cb49e8..04af0a1 100644 --- a/LomoAgent/PreferencesWindow.swift +++ b/LomoAgent/PreferencesWindow.swift @@ -14,11 +14,43 @@ let PREF_HOME_DIR = "PrefHomeDir" let PREF_PORT = "PrefPort" extension Notification.Name { - static let NotifyHomeDirChanged = NSNotification.Name("NotifyHomeDirChanged") + static let NotifySettingsChanged = NSNotification.Name("NotifySettingsChanged") static let NotifyExit = NSNotification.Name("NotifyExit") static let NotifyStart = NSNotification.Name("NotifyStart") } +func getIFAddresses() -> [String] { + var addresses = [String]() + + // Get list of all interfaces on the local machine: + var ifaddr : UnsafeMutablePointer? + guard getifaddrs(&ifaddr) == 0 else { return [] } + guard let firstAddr = ifaddr else { return [] } + + // For each interface ... + for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) { + let flags = Int32(ptr.pointee.ifa_flags) + let addr = ptr.pointee.ifa_addr.pointee + + // Check for running IPv4. Skip the loopback interface and IPv6 interfaces(AF_INET6). + if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) { + if addr.sa_family == UInt8(AF_INET) { + + // Convert interface address to a human readable string: + var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + if (getnameinfo(ptr.pointee.ifa_addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count), + nil, socklen_t(0), NI_NUMERICHOST) == 0) { + let address = String(cString: hostname) + addresses.append(address) + } + } + } + } + + freeifaddrs(ifaddr) + return addresses +} + class PreferencesWindow: NSWindowController, NSWindowDelegate { @IBOutlet weak var homeDirTextField: NSTextFieldCell! @@ -29,14 +61,26 @@ class PreferencesWindow: NSWindowController, NSWindowDelegate { @IBOutlet weak var openButton: NSButton! + @IBOutlet weak var imageQRCode: NSImageView! + @IBAction func onDebugModeClick(_ sender: Any) { - UserDefaults.standard.set(debugModeCheckBox.state == .on, forKey: PREF_DEBUG_MODE) + let oldState = UserDefaults.standard.bool(forKey: PREF_DEBUG_MODE) + let newState = (debugModeCheckBox.state == .on) + if newState != oldState { + UserDefaults.standard.set(newState, forKey: PREF_DEBUG_MODE) + NotificationCenter.default.post(name: .NotifySettingsChanged, object: self) + } } @IBAction func onPortChange(_ sender: Any) { + let oldPort = UserDefaults.standard.string(forKey: PREF_PORT) let port = portTextField.stringValue - os_log("Save port: %{public}s", log: .ui, port) - UserDefaults.standard.set(port, forKey: PREF_PORT) + if oldPort != port { + os_log("Save port: %{public}s", log: .ui, port) + UserDefaults.standard.set(port, forKey: PREF_PORT) + generateQRCode() + NotificationCenter.default.post(name: .NotifySettingsChanged, object: self) + } } @IBAction func onOpenPath(_ sender: Any) { @@ -58,10 +102,14 @@ class PreferencesWindow: NSWindowController, NSWindowDelegate { if let result = dialog.url { homeDirTextField.stringValue = result.path openButton.isEnabled = true - UserDefaults.standard.set(result.path, forKey: PREF_HOME_DIR) - os_log("Save home dir: %{public}s", log: .ui, homeDirTextField.stringValue) - NotificationCenter.default.post(name: .NotifyHomeDirChanged, object: self) + let oldHomeDir = UserDefaults.standard.string(forKey: PREF_HOME_DIR) + if oldHomeDir != homeDirTextField.stringValue { + UserDefaults.standard.set(homeDirTextField.stringValue, forKey: PREF_HOME_DIR) + os_log("Save home dir: %{public}s", log: .ui, homeDirTextField.stringValue) + NotificationCenter.default.post(name: .NotifySettingsChanged, object: self) + // todo: move files in directory + } } } } @@ -76,6 +124,7 @@ class PreferencesWindow: NSWindowController, NSWindowDelegate { // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. self.window?.center() self.window?.makeKeyAndOrderFront(nil) + self.window?.level = .floating if UserDefaults.standard.bool(forKey: PREF_DEBUG_MODE) { debugModeCheckBox.state = .on @@ -100,6 +149,20 @@ class PreferencesWindow: NSWindowController, NSWindowDelegate { portTextField.stringValue = "8000" UserDefaults.standard.set(portTextField.stringValue, forKey: PREF_PORT) } + + generateQRCode() + } + + func generateQRCode() { + imageQRCode.wantsLayer = true + let addresses = getIFAddresses() + if let firstAddr = addresses.first { + let url = "http://\(firstAddr):\(portTextField.stringValue)" + guard let data = url.data(using: String.Encoding.utf8, allowLossyConversion: false) else { + return + } + imageQRCode.image = QRCodeImageWith(data: data, size: imageQRCode.frame.size.width) + } } func windowWillClose(_ notification: Notification) { diff --git a/LomoAgent/QRImage.swift b/LomoAgent/QRImage.swift new file mode 100644 index 0000000..32a79d1 --- /dev/null +++ b/LomoAgent/QRImage.swift @@ -0,0 +1,86 @@ +// +// QRImage.swift +// QRCodeSample +// +// Created by ing.conti on 08/06/2019. +// Copyright © 2019 ing.conti. All rights reserved. +// + + + +#if os(iOS) +import UIKit +typealias QRImage = UIImage +#elseif os(OSX) +import Cocoa +typealias QRImage = NSImage +#endif + +func QRCodeImageWith(string: String, size: CGFloat = 500) -> QRImage?{ + + guard let data = string.data(using: .utf8) else { + return nil + } + + return QRCodeImageWith(data: data, size: size) +} + +func QRCodeImageWith(data: Data, size: CGFloat = 500) -> QRImage?{ + + guard let filter = CIFilter(name: "CIQRCodeGenerator") else { + return nil + } + + filter.setValue(data, forKey: "inputMessage") + guard let ciImage = filter.outputImage else { + return nil + } + + let destRect = CGRect(x: 0, y: 0, width: size, height: size) + + + #if os(iOS) + + let tinyImage = UIImage(ciImage: ciImage) + if (size <= tinyImage.size.width){ + return tinyImage + } + + + // Scale image up: + UIGraphicsBeginImageContext(CGSize(width: size, height: size)) + if let context = UIGraphicsGetCurrentContext(){ + context.interpolationQuality = .none + tinyImage.draw(in: destRect) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image + } + return nil + + #elseif os(OSX) + + let rep = NSCIImageRep(ciImage: ciImage) + let tinyImage = NSImage() + tinyImage.addRepresentation(rep) + if size <= rep.size.width { + return tinyImage as QRImage + } + + // Scale image up: + let nsImage = NSImage(size: NSSize(width: size, height: size)) + nsImage.lockFocus() + + let ctx = NSGraphicsContext.current + ctx?.imageInterpolation = NSImageInterpolation.none + tinyImage.draw(in: destRect) + nsImage.unlockFocus() + return nsImage + + + #endif +} + + +// SEE below for usage on iOS and OSX + diff --git a/LomoAgent/StatusMenuController.swift b/LomoAgent/StatusMenuController.swift index 00a4399..482dc5b 100644 --- a/LomoAgent/StatusMenuController.swift +++ b/LomoAgent/StatusMenuController.swift @@ -55,7 +55,7 @@ class StatusMenuController: NSObject { quitClicked(self) } - @objc func onHomeDirChanged(_ notification: Notification) { + @objc func onSettingsChanged(_ notification: Notification) { stopLomod() startLomod() } @@ -70,8 +70,8 @@ class StatusMenuController: NSObject { aboutWindow = AboutWindow() NotificationCenter.default.addObserver(self, - selector: #selector(onHomeDirChanged(_:)), - name: .NotifyHomeDirChanged, + selector: #selector(onSettingsChanged(_:)), + name: .NotifySettingsChanged, object: nil) NotificationCenter.default.addObserver(self,