mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
- add an option to select a network interface
- update logic when the module is not available (now reader initializing after the module is initialized) - update a Network popup view
This commit is contained in:
@@ -40,7 +40,7 @@ struct Battery_Usage: value_t {
|
||||
}
|
||||
|
||||
public class Battery: Module {
|
||||
private var usageReader: UsageReader = UsageReader()
|
||||
private var usageReader: UsageReader? = nil
|
||||
private let popupView: Popup = Popup()
|
||||
|
||||
public init(_ store: UnsafePointer<Store>?) {
|
||||
@@ -49,15 +49,20 @@ public class Battery: Module {
|
||||
popup: self.popupView,
|
||||
settings: nil
|
||||
)
|
||||
guard self.available else { return }
|
||||
|
||||
self.usageReader.readyCallback = { [unowned self] in
|
||||
self.usageReader = UsageReader()
|
||||
|
||||
self.usageReader?.readyCallback = { [unowned self] in
|
||||
self.readyHandler()
|
||||
}
|
||||
self.usageReader.callbackHandler = { [unowned self] value in
|
||||
self.usageReader?.callbackHandler = { [unowned self] value in
|
||||
self.usageCallback(value)
|
||||
}
|
||||
|
||||
self.addReader(self.usageReader)
|
||||
if let reader = self.usageReader {
|
||||
self.addReader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
public override func isAvailable() -> Bool {
|
||||
|
||||
@@ -35,19 +35,22 @@ public class CPU: Module {
|
||||
private let popupView: Popup = Popup()
|
||||
private var settingsView: Settings
|
||||
|
||||
private var loadReader: LoadReader? = LoadReader()
|
||||
private var loadReader: LoadReader? = nil
|
||||
private let smc: UnsafePointer<SMCService>?
|
||||
|
||||
public init(_ store: UnsafePointer<Store>, _ smc: UnsafePointer<SMCService>) {
|
||||
self.smc = smc
|
||||
self.settingsView = Settings("CPU", store: store)
|
||||
self.loadReader!.store = store
|
||||
|
||||
super.init(
|
||||
store: store,
|
||||
popup: self.popupView,
|
||||
settings: self.settingsView
|
||||
)
|
||||
guard self.available else { return }
|
||||
|
||||
self.loadReader = LoadReader()
|
||||
self.loadReader?.store = store
|
||||
|
||||
self.settingsView.callback = { [unowned self] in
|
||||
self.loadReader?.read()
|
||||
@@ -60,7 +63,9 @@ public class CPU: Module {
|
||||
self.loadCallback(value)
|
||||
}
|
||||
|
||||
self.addReader(self.loadReader!)
|
||||
if let reader = self.loadReader {
|
||||
self.addReader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadCallback(_ value: CPU_Load?) {
|
||||
|
||||
@@ -63,7 +63,7 @@ struct DiskList: value_t {
|
||||
|
||||
public class Disk: Module {
|
||||
private let popupView: Popup = Popup()
|
||||
private var capacityReader: CapacityReader = CapacityReader()
|
||||
private var capacityReader: CapacityReader? = nil
|
||||
private var settingsView: Settings
|
||||
private var selectedDisk: String = ""
|
||||
|
||||
@@ -75,21 +75,26 @@ public class Disk: Module {
|
||||
popup: self.popupView,
|
||||
settings: self.settingsView
|
||||
)
|
||||
guard self.available else { return }
|
||||
|
||||
self.capacityReader = CapacityReader()
|
||||
self.selectedDisk = store!.pointee.string(key: "\(self.config.name)_disk", defaultValue: self.selectedDisk)
|
||||
|
||||
self.capacityReader.readyCallback = { [unowned self] in
|
||||
self.capacityReader?.readyCallback = { [unowned self] in
|
||||
self.readyHandler()
|
||||
}
|
||||
self.capacityReader.callbackHandler = { [unowned self] value in
|
||||
self.capacityReader?.callbackHandler = { [unowned self] value in
|
||||
self.capacityCallback(value: value)
|
||||
}
|
||||
|
||||
self.settingsView.selectedDiskHandler = { [unowned self] value in
|
||||
self.selectedDisk = value
|
||||
self.capacityReader.read()
|
||||
self.capacityReader?.read()
|
||||
}
|
||||
|
||||
self.addReader(self.capacityReader)
|
||||
if let reader = self.capacityReader {
|
||||
self.addReader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
private func capacityCallback(value: DiskList?) {
|
||||
|
||||
@@ -50,7 +50,7 @@ internal class Settings: NSView, Settings_v {
|
||||
rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light)
|
||||
rowTitle.textColor = .textColor
|
||||
|
||||
self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 164, y: -1, width: 140, height: 30))
|
||||
self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 140 - Constants.Settings.margin*2, y: -1, width: 140, height: 30))
|
||||
self.button!.target = self
|
||||
self.button?.action = #selector(self.handleSelection)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public struct RAM_Usage: value_t {
|
||||
|
||||
public class Memory: Module {
|
||||
private let popupView: Popup = Popup()
|
||||
private var usageReader: UsageReader = UsageReader()
|
||||
private var usageReader: UsageReader? = nil
|
||||
|
||||
public init(_ store: UnsafePointer<Store>?) {
|
||||
super.init(
|
||||
@@ -43,15 +43,20 @@ public class Memory: Module {
|
||||
popup: self.popupView,
|
||||
settings: nil
|
||||
)
|
||||
guard self.available else { return }
|
||||
|
||||
self.usageReader.readyCallback = { [unowned self] in
|
||||
self.usageReader = UsageReader()
|
||||
|
||||
self.usageReader?.readyCallback = { [unowned self] in
|
||||
self.readyHandler()
|
||||
}
|
||||
self.usageReader.callbackHandler = { [unowned self] value in
|
||||
self.usageReader?.callbackHandler = { [unowned self] value in
|
||||
self.loadCallback(value: value)
|
||||
}
|
||||
|
||||
self.addReader(self.usageReader)
|
||||
if let reader = self.usageReader {
|
||||
self.addReader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadCallback(value: RAM_Usage?) {
|
||||
|
||||
@@ -12,67 +12,94 @@
|
||||
import Cocoa
|
||||
import StatsKit
|
||||
import ModuleKit
|
||||
import SystemConfiguration
|
||||
|
||||
public enum Network_t: String {
|
||||
case wifi
|
||||
case ethernet
|
||||
case bluetooth
|
||||
case other
|
||||
}
|
||||
|
||||
public struct Network_interface {
|
||||
var displayName: String = ""
|
||||
var BSDName: String = ""
|
||||
var address: String = ""
|
||||
}
|
||||
|
||||
public struct Network_Usage: value_t {
|
||||
var active: Bool = false
|
||||
|
||||
var download: Int64 = 0
|
||||
var upload: Int64 = 0
|
||||
|
||||
var laddr: String? = nil // local ip
|
||||
var paddr: String? = nil // remote ip
|
||||
var iaddr: String? = nil // mac adress
|
||||
var raddr: String? = nil // remote ip
|
||||
|
||||
var interface: Network_interface? = nil
|
||||
var connectionType: Network_t? = nil
|
||||
|
||||
var countryCode: String? = nil
|
||||
var networkName: String? = nil
|
||||
var ssid: String? = nil
|
||||
|
||||
mutating func reset() {
|
||||
self.active = false
|
||||
|
||||
self.download = 0
|
||||
self.upload = 0
|
||||
|
||||
self.laddr = nil
|
||||
self.paddr = nil
|
||||
self.iaddr = nil
|
||||
self.raddr = nil
|
||||
|
||||
self.interface = nil
|
||||
self.connectionType = nil
|
||||
|
||||
self.countryCode = nil
|
||||
self.networkName = nil
|
||||
self.ssid = nil
|
||||
}
|
||||
|
||||
public var widget_value: Double = 0
|
||||
}
|
||||
|
||||
public class Network: Module {
|
||||
private var usageReader: UsageReader = UsageReader()
|
||||
private var usageReader: UsageReader?
|
||||
private let popupView: Popup = Popup()
|
||||
private var settingsView: Settings
|
||||
|
||||
public init(_ store: UnsafePointer<Store>?) {
|
||||
self.usageReader.store = store
|
||||
self.settingsView = Settings("Network", store: store!)
|
||||
|
||||
super.init(
|
||||
store: store,
|
||||
popup: self.popupView,
|
||||
settings: nil
|
||||
settings: self.settingsView
|
||||
)
|
||||
guard self.available else { return }
|
||||
|
||||
self.usageReader.readyCallback = { [unowned self] in
|
||||
self.usageReader = UsageReader()
|
||||
self.usageReader?.store = store
|
||||
|
||||
self.usageReader?.readyCallback = { [unowned self] in
|
||||
self.readyHandler()
|
||||
}
|
||||
self.usageReader.callbackHandler = { [unowned self] value in
|
||||
self.usageReader?.callbackHandler = { [unowned self] value in
|
||||
self.usageCallback(value)
|
||||
}
|
||||
|
||||
self.addReader(self.usageReader)
|
||||
self.settingsView.callback = { [unowned self] in
|
||||
self.usageReader?.getDetails()
|
||||
self.usageReader?.read()
|
||||
}
|
||||
|
||||
if let reader = self.usageReader {
|
||||
self.addReader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
public override func isAvailable() -> Bool {
|
||||
var list: [String] = []
|
||||
for interface in SCNetworkInterfaceCopyAll() as NSArray {
|
||||
if let displayName = SCNetworkInterfaceGetLocalizedDisplayName(interface as! SCNetworkInterface) {
|
||||
list.append(displayName as String)
|
||||
}
|
||||
}
|
||||
return list.count > 0
|
||||
}
|
||||
|
||||
private func usageCallback(_ value: Network_Usage?) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import StatsKit
|
||||
|
||||
internal class Popup: NSView {
|
||||
let dashboardHeight: CGFloat = 90
|
||||
let detailsHeight: CGFloat = 88
|
||||
let detailsHeight: CGFloat = 110
|
||||
|
||||
private var dashboardView: NSView? = nil
|
||||
|
||||
@@ -31,7 +31,8 @@ internal class Popup: NSView {
|
||||
|
||||
private var publicIPField: ValueField? = nil
|
||||
private var localIPField: ValueField? = nil
|
||||
private var networkTypeField: ValueField? = nil
|
||||
private var interfaceField: ValueField? = nil
|
||||
private var ssidField: ValueField? = nil
|
||||
private var macAdressField: ValueField? = nil
|
||||
|
||||
private var initialized: Bool = false
|
||||
@@ -137,19 +138,20 @@ internal class Popup: NSView {
|
||||
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: separator.frame.origin.y - self.detailsHeight, width: self.frame.width, height: self.detailsHeight))
|
||||
|
||||
self.publicIPField = PopupRow(view, n: 3, title: "Public IP:", value: "")
|
||||
self.localIPField = PopupRow(view, n: 2, title: "Local IP:", value: "")
|
||||
self.networkTypeField = PopupRow(view, n: 1, title: "Network:", value: "")
|
||||
self.publicIPField = PopupRow(view, n: 4, title: "Public IP:", value: "")
|
||||
self.localIPField = PopupRow(view, n: 3, title: "Local IP:", value: "")
|
||||
self.interfaceField = PopupRow(view, n: 2, title: "Interface:", value: "")
|
||||
self.ssidField = PopupRow(view, n: 1, title: "Network:", value: "")
|
||||
self.macAdressField = PopupRow(view, n: 0, title: "Physical address:", value: "")
|
||||
|
||||
self.publicIPField?.addTracking()
|
||||
self.localIPField?.addTracking()
|
||||
self.networkTypeField?.addTracking()
|
||||
self.ssidField?.addTracking()
|
||||
self.macAdressField?.addTracking()
|
||||
|
||||
self.publicIPField?.isSelectable = true
|
||||
self.localIPField?.isSelectable = true
|
||||
self.networkTypeField?.isSelectable = true
|
||||
self.ssidField?.isSelectable = true
|
||||
self.macAdressField?.isSelectable = true
|
||||
|
||||
self.addSubview(view)
|
||||
@@ -157,49 +159,38 @@ internal class Popup: NSView {
|
||||
|
||||
public func usageCallback(_ value: Network_Usage) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if !(self.window?.isVisible ?? false) && self.initialized && value.active {
|
||||
if !(self.window?.isVisible ?? false) && self.initialized {
|
||||
return
|
||||
}
|
||||
|
||||
self.uploadValue = value.upload
|
||||
self.downloadValue = value.download
|
||||
self.setUploadDownloadFields()
|
||||
|
||||
if !value.active {
|
||||
self.publicIPField?.stringValue = "No connection"
|
||||
self.localIPField?.stringValue = "No connection"
|
||||
self.networkTypeField?.stringValue = "No connection"
|
||||
self.macAdressField?.stringValue = "No connection"
|
||||
return
|
||||
}
|
||||
|
||||
if var publicIP = value.paddr, self.publicIPField?.stringValue != publicIP {
|
||||
if value.countryCode != nil {
|
||||
publicIP = "\(publicIP) (\(value.countryCode!))"
|
||||
}
|
||||
self.publicIPField?.stringValue = publicIP
|
||||
if let interface = value.interface {
|
||||
self.interfaceField?.stringValue = "\(interface.displayName) (\(interface.BSDName))"
|
||||
self.macAdressField?.stringValue = interface.address
|
||||
} else {
|
||||
self.publicIPField?.stringValue = "Unknown"
|
||||
}
|
||||
if value.laddr != nil && self.localIPField?.stringValue != value.laddr {
|
||||
self.localIPField?.stringValue = value.laddr!
|
||||
}
|
||||
if value.iaddr != nil && self.macAdressField?.stringValue != value.iaddr {
|
||||
self.macAdressField?.stringValue = value.iaddr!
|
||||
self.interfaceField?.stringValue = "Unknown"
|
||||
self.macAdressField?.stringValue = "Unknown"
|
||||
}
|
||||
|
||||
if value.connectionType != nil {
|
||||
var networkType = ""
|
||||
if value.connectionType == .wifi {
|
||||
networkType = "\(value.networkName ?? "unknown") (WiFi)"
|
||||
} else if value.connectionType == .ethernet {
|
||||
networkType = "Ethernet"
|
||||
}
|
||||
|
||||
if self.networkTypeField?.stringValue != networkType {
|
||||
self.networkTypeField?.stringValue = networkType
|
||||
if value.connectionType == .wifi {
|
||||
self.ssidField?.stringValue = value.ssid ?? "Unknown"
|
||||
} else {
|
||||
self.ssidField?.stringValue = "Unavailable"
|
||||
}
|
||||
|
||||
if self.publicIPField?.stringValue != value.raddr {
|
||||
if value.raddr == nil {
|
||||
self.publicIPField?.stringValue = "Unknown"
|
||||
} else {
|
||||
if value.countryCode == nil {
|
||||
self.publicIPField?.stringValue = value.raddr!
|
||||
} else {
|
||||
self.publicIPField?.stringValue = "\(value.raddr!) (\(value.countryCode!))"
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.localIPField?.stringValue != value.laddr {
|
||||
self.localIPField?.stringValue = value.laddr ?? "Unknown"
|
||||
}
|
||||
|
||||
self.initialized = true
|
||||
})
|
||||
@@ -214,7 +205,7 @@ extension ValueField {
|
||||
}
|
||||
|
||||
public override func mouseEntered(with: NSEvent) {
|
||||
guard self.stringValue != "No connection" && self.stringValue != "Unknown" else {
|
||||
guard self.stringValue != "No connection" && self.stringValue != "Unknown" && self.stringValue != "Unavailable" else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -226,7 +217,7 @@ extension ValueField {
|
||||
}
|
||||
|
||||
public override func mouseDown(with: NSEvent) {
|
||||
guard self.stringValue != "No connection" && self.stringValue != "Unknown" else {
|
||||
guard self.stringValue != "No connection" && self.stringValue != "Unknown" && self.stringValue != "Unavailable" else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,15 @@ import Reachability
|
||||
import os.log
|
||||
import CoreWLAN
|
||||
|
||||
struct ipResponse: Decodable {
|
||||
var ip: String
|
||||
var country: String
|
||||
var cc: String
|
||||
}
|
||||
|
||||
internal class UsageReader: Reader<Network_Usage> {
|
||||
public var store: UnsafePointer<Store>? = nil
|
||||
|
||||
private var reachability: Reachability? = nil
|
||||
private var usage: Network_Usage = Network_Usage()
|
||||
|
||||
@@ -27,16 +34,16 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
if let global = SCDynamicStoreCopyValue(nil, "State:/Network/Global/IPv4" as CFString), let name = global["PrimaryInterface"] as? String {
|
||||
return name
|
||||
}
|
||||
return "eth0"
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
private var interfaceID: String {
|
||||
get {
|
||||
return self.store?.pointee.string(key: "network_interface", defaultValue: self.primaryInterface) ?? self.primaryInterface
|
||||
return self.store?.pointee.string(key: "Network_interface", defaultValue: self.primaryInterface) ?? self.primaryInterface
|
||||
}
|
||||
set {
|
||||
self.store?.pointee.set(key: "network_interface", value: newValue)
|
||||
self.store?.pointee.set(key: "Network_interface", value: newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +56,7 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
}
|
||||
|
||||
self.reachability!.whenReachable = { _ in
|
||||
self.usage.reset()
|
||||
self.readInformation()
|
||||
self.getDetails()
|
||||
}
|
||||
self.reachability!.whenUnreachable = { _ in
|
||||
self.usage.reset()
|
||||
@@ -59,14 +65,6 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
}
|
||||
|
||||
public override func read() {
|
||||
guard self.reachability?.connection != .unavailable else {
|
||||
if self.usage.active {
|
||||
self.usage.reset()
|
||||
self.callback(self.usage)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
|
||||
var upload: Int64 = 0
|
||||
var download: Int64 = 0
|
||||
@@ -80,14 +78,14 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
continue
|
||||
}
|
||||
|
||||
if let ip = getLocalIP(pointer!), self.usage.laddr != ip {
|
||||
self.usage.laddr = ip
|
||||
}
|
||||
|
||||
if let info = getBytesInfo(pointer!) {
|
||||
upload += info.upload
|
||||
download += info.download
|
||||
}
|
||||
|
||||
if let ip = getLocalIP(pointer!), self.usage.laddr != ip {
|
||||
self.usage.laddr = ip
|
||||
}
|
||||
}
|
||||
freeifaddrs(interfaceAddresses)
|
||||
|
||||
@@ -96,52 +94,53 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
self.usage.download = download - self.usage.download
|
||||
}
|
||||
|
||||
if self.usage.upload < 0 {
|
||||
self.usage.upload = 0
|
||||
}
|
||||
if self.usage.download < 0 {
|
||||
self.usage.download = 0
|
||||
}
|
||||
|
||||
self.callback(self.usage)
|
||||
|
||||
self.usage.upload = upload
|
||||
self.usage.download = download
|
||||
}
|
||||
|
||||
private func readInformation() {
|
||||
guard self.reachability != nil && self.reachability!.connection != .unavailable else { return }
|
||||
public func getDetails() {
|
||||
self.usage.reset()
|
||||
|
||||
self.usage.active = true
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
self.usage.paddr = self.getPublicIP()
|
||||
self.getPublicIP()
|
||||
}
|
||||
|
||||
if self.reachability!.connection == .wifi {
|
||||
self.usage.connectionType = .wifi
|
||||
if let interface = CWWiFiClient.shared().interface() {
|
||||
self.usage.networkName = interface.ssid()
|
||||
self.usage.countryCode = interface.countryCode()
|
||||
self.usage.iaddr = interface.hardwareAddress()
|
||||
if self.interfaceID != "" {
|
||||
for interface in SCNetworkInterfaceCopyAll() as NSArray {
|
||||
if let bsdName = SCNetworkInterfaceGetBSDName(interface as! SCNetworkInterface),
|
||||
bsdName as String == self.interfaceID,
|
||||
let type = SCNetworkInterfaceGetInterfaceType(interface as! SCNetworkInterface),
|
||||
let displayName = SCNetworkInterfaceGetLocalizedDisplayName(interface as! SCNetworkInterface),
|
||||
let address = SCNetworkInterfaceGetHardwareAddressString(interface as! SCNetworkInterface) {
|
||||
self.usage.interface = Network_interface(displayName: displayName as String, BSDName: bsdName as String, address: address as String)
|
||||
|
||||
switch type {
|
||||
case kSCNetworkInterfaceTypeEthernet:
|
||||
self.usage.connectionType = .ethernet
|
||||
case kSCNetworkInterfaceTypeIEEE80211, kSCNetworkInterfaceTypeWWAN:
|
||||
self.usage.connectionType = .wifi
|
||||
case kSCNetworkInterfaceTypeBluetooth:
|
||||
self.usage.connectionType = .bluetooth
|
||||
default:
|
||||
self.usage.connectionType = .other
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.usage.connectionType = .ethernet
|
||||
self.usage.iaddr = getMacAddress()
|
||||
}
|
||||
}
|
||||
|
||||
private func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> (upload: Int64, download: Int64)? {
|
||||
let pointer = infoPointer
|
||||
|
||||
let addr = pointer.pointee.ifa_addr.pointee
|
||||
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
|
||||
var networkData: UnsafeMutablePointer<if_data>? = nil
|
||||
|
||||
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
|
||||
return (upload: Int64(networkData?.pointee.ifi_obytes ?? 0), download: Int64(networkData?.pointee.ifi_ibytes ?? 0))
|
||||
}
|
||||
|
||||
private func getBytesInfo(_ pointer: UnsafeMutablePointer<ifaddrs>) -> (upload: Int64, download: Int64)? {
|
||||
let addr = pointer.pointee.ifa_addr.pointee
|
||||
|
||||
guard addr.sa_family == UInt8(AF_LINK) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let data: UnsafeMutablePointer<if_data>? = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
|
||||
return (upload: Int64(data?.pointee.ifi_obytes ?? 0), download: Int64(data?.pointee.ifi_ibytes ?? 0))
|
||||
if let interface = CWWiFiClient.shared().interface(), self.usage.connectionType == .wifi {
|
||||
self.usage.ssid = interface.ssid()
|
||||
self.usage.countryCode = interface.countryCode()
|
||||
}
|
||||
}
|
||||
|
||||
private func getLocalIP(_ pointer: UnsafeMutablePointer<ifaddrs>) -> String? {
|
||||
@@ -157,73 +156,35 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
return String(cString: ip)
|
||||
}
|
||||
|
||||
private func getPublicIP() -> String? {
|
||||
let url = URL(string: "https://api.ipify.org")
|
||||
private func getPublicIP() {
|
||||
let url = URL(string: "https://api.myip.com")
|
||||
var address: String? = nil
|
||||
|
||||
do {
|
||||
if let url = url {
|
||||
address = try String(contentsOf: url)
|
||||
if address!.contains("<") {
|
||||
address = nil
|
||||
|
||||
if address != nil {
|
||||
let jsonData = address!.data(using: .utf8)
|
||||
let response: ipResponse = try JSONDecoder().decode(ipResponse.self, from: jsonData!)
|
||||
|
||||
self.usage.countryCode = response.cc
|
||||
self.usage.raddr = response.ip
|
||||
}
|
||||
}
|
||||
} catch let error {
|
||||
os_log(.error, log: log, "get public ip %s", "\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func getBytesInfo(_ pointer: UnsafeMutablePointer<ifaddrs>) -> (upload: Int64, download: Int64)? {
|
||||
let addr = pointer.pointee.ifa_addr.pointee
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/31835418/how-to-get-mac-address-from-os-x-with-swift
|
||||
private func getMacAddress() -> String? {
|
||||
var macAddressAsString : String?
|
||||
if let intfIterator = findEthernetInterfaces() {
|
||||
if let macAddress = getMACAddress(intfIterator) {
|
||||
macAddressAsString = macAddress.map( { String(format:"%02x", $0) } ).joined(separator: ":")
|
||||
}
|
||||
IOObjectRelease(intfIterator)
|
||||
}
|
||||
return macAddressAsString
|
||||
}
|
||||
|
||||
private func findEthernetInterfaces() -> io_iterator_t? {
|
||||
let matchingDictUM = IOServiceMatching("IOEthernetInterface");
|
||||
if matchingDictUM == nil {
|
||||
guard addr.sa_family == UInt8(AF_LINK) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let matchingDict = matchingDictUM! as NSMutableDictionary
|
||||
matchingDict["IOPropertyMatch"] = [ "IOPrimaryInterface" : true]
|
||||
|
||||
var matchingServices : io_iterator_t = 0
|
||||
if IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &matchingServices) != KERN_SUCCESS {
|
||||
return nil
|
||||
}
|
||||
|
||||
return matchingServices
|
||||
}
|
||||
|
||||
private func getMACAddress(_ intfIterator : io_iterator_t) -> [UInt8]? {
|
||||
var macAddress : [UInt8]?
|
||||
var intfService = IOIteratorNext(intfIterator)
|
||||
|
||||
while intfService != 0 {
|
||||
var controllerService : io_object_t = 0
|
||||
if IORegistryEntryGetParentEntry(intfService, kIOServicePlane, &controllerService) == KERN_SUCCESS {
|
||||
let dataUM = IORegistryEntryCreateCFProperty(controllerService, "IOMACAddress" as CFString, kCFAllocatorDefault, 0)
|
||||
if dataUM != nil {
|
||||
let data = (dataUM!.takeRetainedValue() as! CFData) as Data
|
||||
macAddress = [0, 0, 0, 0, 0, 0]
|
||||
data.copyBytes(to: &macAddress!, count: macAddress!.count)
|
||||
}
|
||||
IOObjectRelease(controllerService)
|
||||
}
|
||||
|
||||
IOObjectRelease(intfService)
|
||||
intfService = IOIteratorNext(intfIterator)
|
||||
}
|
||||
|
||||
return macAddress
|
||||
let data: UnsafeMutablePointer<if_data>? = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
|
||||
return (upload: Int64(data?.pointee.ifi_obytes ?? 0), download: Int64(data?.pointee.ifi_ibytes ?? 0))
|
||||
}
|
||||
}
|
||||
|
||||
106
Modules/Net/settings.swift
Normal file
106
Modules/Net/settings.swift
Normal file
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// settings.swift
|
||||
// Net
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 06/07/2020.
|
||||
// Using Swift 5.0.
|
||||
// Running on macOS 10.15.
|
||||
//
|
||||
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import StatsKit
|
||||
import ModuleKit
|
||||
import SystemConfiguration
|
||||
|
||||
internal class Settings: NSView, Settings_v {
|
||||
public var callback: (() -> Void) = {}
|
||||
|
||||
private let title: String
|
||||
private let store: UnsafePointer<Store>
|
||||
private var button: NSPopUpButton?
|
||||
|
||||
private var list: [Network_interface] = []
|
||||
|
||||
public init(_ title: String, store: UnsafePointer<Store>) {
|
||||
self.title = title
|
||||
self.store = store
|
||||
|
||||
super.init(frame: CGRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: Constants.Settings.width - (Constants.Settings.margin*2), height: 0))
|
||||
|
||||
for interface in SCNetworkInterfaceCopyAll() as NSArray {
|
||||
if let bsdName = SCNetworkInterfaceGetBSDName(interface as! SCNetworkInterface),
|
||||
let displayName = SCNetworkInterfaceGetLocalizedDisplayName(interface as! SCNetworkInterface) {
|
||||
self.list.append(Network_interface(displayName: displayName as String, BSDName: bsdName as String))
|
||||
}
|
||||
}
|
||||
|
||||
self.wantsLayer = true
|
||||
self.canDrawConcurrently = true
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func load(widget: widget_t) {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
self.addNetworkSelector()
|
||||
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: 30 + (Constants.Settings.margin*2)))
|
||||
}
|
||||
|
||||
private func addNetworkSelector() {
|
||||
let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: self.frame.width, height: 29))
|
||||
|
||||
let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: (view.frame.height - 16)/2, width: view.frame.width - 52, height: 17), "Network interface")
|
||||
rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light)
|
||||
rowTitle.textColor = .textColor
|
||||
|
||||
self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 200 - Constants.Settings.margin*2, y: -1, width: 200, height: 30))
|
||||
self.button!.target = self
|
||||
self.button?.action = #selector(self.handleSelection)
|
||||
|
||||
let selectedInterface = self.store.pointee.string(key: "\(self.title)_interface", defaultValue: "")
|
||||
let menu = NSMenu()
|
||||
let autodetection = NSMenuItem(title: "Autodetection", action: nil, keyEquivalent: "")
|
||||
menu.addItem(autodetection)
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
self.list.forEach { (interface: Network_interface) in
|
||||
let interfaceMenu = NSMenuItem(title: "\(interface.displayName) (\(interface.BSDName))", action: nil, keyEquivalent: "")
|
||||
interfaceMenu.identifier = NSUserInterfaceItemIdentifier(rawValue: interface.BSDName)
|
||||
menu.addItem(interfaceMenu)
|
||||
if selectedInterface != "" && selectedInterface == interface.BSDName {
|
||||
interfaceMenu.state = .on
|
||||
}
|
||||
}
|
||||
|
||||
if selectedInterface == "" {
|
||||
self.button?.selectItem(withTitle: "Autodetection")
|
||||
}
|
||||
|
||||
self.button?.menu = menu
|
||||
|
||||
view.addSubview(rowTitle)
|
||||
view.addSubview(self.button!)
|
||||
|
||||
self.addSubview(view)
|
||||
}
|
||||
|
||||
@objc func handleSelection(_ sender: NSPopUpButton) {
|
||||
guard let item = sender.selectedItem else { return }
|
||||
|
||||
if item.title == "Autodetection" {
|
||||
self.store.pointee.remove("\(self.title)_interface")
|
||||
} else {
|
||||
if let bsdName = item.identifier?.rawValue {
|
||||
self.store.pointee.set(key: "\(self.title)_interface", value: bsdName)
|
||||
}
|
||||
}
|
||||
|
||||
self.callback()
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ public class Sensors: Module {
|
||||
popup: self.popupView,
|
||||
settings: self.settingsView
|
||||
)
|
||||
guard self.available else { return }
|
||||
|
||||
self.checkIfNoSensorsEnabled()
|
||||
self.popupView.setup(self.sensorsReader.list)
|
||||
|
||||
Reference in New Issue
Block a user