Here is a class the replicates most of the functionality of the Xojo™ SYSTEM module
it does NOT support these items at this time
- keyChain
- keyChainCount
- isFunctionAvailable
- penButtonPushed
- penPressure
- penType
and microSeconds and ticks report “time since last reboot”… Xojo reports only “Awake” time
import AppKit
import Foundation
import SystemConfiguration
import AVFoundation
public let system = ds$SYSTEM()
open class ds$SYSTEM : NSObject {
//
public enum LogType { // this is so main app doen't need to import 'os'
case none
case info
case debug
case error
case fault
var category : String {
switch self {
case .none : return ""
case .info : return "INFO : "
case .debug : return "DEBUG : "
case .error : return "ERROR : "
case .fault : return "FAULT : "
}
}
/*
var realLogType : OSLogType {
switch self {
case .none : return .default
case .info : return .info
case .debug : return .debug
case .error : return .error
case .fault : return .fault
}
}
*/
}
//
private var zInterfaces : [networkInterface] = []
private var zLoopBack : networkInterface?
public var random = dsRandom()
public override init() {
super.init()
readNetworkInformation()
}
// MARK: PROPERTIES
public var commandLine : String { return Join(CommandLine.arguments," ") }
public let cursors = ds$CURSORS() // see below
public var fontCount : Int { return NSFontManager.shared.availableFontFamilies.count }
// keyChain : not supported at this time
// keyChainCount : not supported at this time
public var lastFontIndex : Int { fontCount-1 }
public var mouseDown : Bool { return (NSEvent.pressedMouseButtons != 0 ) }
public var mouseX : Int { return Int(NSEvent.mouseLocation.x) }
public var mouseY : Int { return Int(NSEvent.mouseLocation.y) }
// network : SEE BELOW
// networkInterfaceCount : SEE BELOW
// MARK: METHODS
public func Beep() { NSSound.beep() }
public func debugLog(_ msg:String) { print(msg) } // goes to Xcode console only
public func environmentVariable(_ name:String) -> String {
if let value = ProcessInfo.processInfo.environment[name] { return value }
return ""
}
public func fontAT(_ index:Int) -> String {
if index >= 0 && index < fontCount { return NSFontManager.shared.availableFontFamilies[index] }
fatalError("ERROR: Font(index:\(index)) is out of range.")
}
// isFunctionAvailable : not supported at this time
// seems os_log isn't reliable :( so falling back to NSLOG
// public func log(_ type:LogType, _ msg : StaticString) { os_log(type.realLogType, msg) }
// goes to Xcode console AND OS console
public func log(_ type:LogType, _ msg : String) { NSLog(type.category+msg) }
// Note : Xojo™ returns "awake" time, this routine returns actual "Uptime" (ie. since reboot)
public var microSeconds : Double {
var temp = timespec()
if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, &temp) {
fatalError("Could not execute clock_gettime, errno: \(errno)")
}
let upTime = (Double(temp.tv_sec)*1e6)+Double(temp.tv_nsec) / 1e6
//let awake = ProcessInfo.processInfo.systemUptime
return Double(upTime)
}
public func getNetWorkInterface(_ index:Int) -> networkInterface{
if index >= 0 && index < networkInterfaceCount {
return zInterfaces[index]
}
fatalError("ERROR: Font(index:\(index)) is out of range.")
}
// penButtonPushed : not supported at this time
// penPressure : not supported at this time
// penType : not supported at this time
// random : SEE Class Header
public func speak(_ value : String,_ interrupt:Bool=false) {
let utterance = AVSpeechUtterance(string: value)
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)
}
public var ticks : Int { return Int(microSeconds * (1e6/60) ) }
public let OSVersion = ds$Version()
public class ds$Version : NSObject {
public var majorVersion : Int { return ProcessInfo.processInfo.operatingSystemVersion.majorVersion }
public var minorVersion : Int { return ProcessInfo.processInfo.operatingSystemVersion.minorVersion }
public var patchVersion : Int { return ProcessInfo.processInfo.operatingSystemVersion.patchVersion }
public var toString : String { return "\(majorVersion).\(minorVersion).\(patchVersion)" }
}
//
// CURSOR METHODS
//
public class ds$CURSORS : NSObject {
public var arrow : NSCursor { return NSCursor.arrow }
public var closedHand : NSCursor { return NSCursor.closedHand }
public var contextualMenu : NSCursor { return NSCursor.contextualMenu }
public var crosshair : NSCursor { return NSCursor.crosshair }
public var current : NSCursor { return NSCursor.current }
public var currentSystem : NSCursor { return NSCursor.currentSystem ?? NSCursor.arrow }
public var disappearingItem : NSCursor { return NSCursor.disappearingItem}
public var dragCopy : NSCursor { return NSCursor.dragCopy }
public var dragLink : NSCursor { return NSCursor.dragLink }
public var iBeam : NSCursor { return NSCursor.iBeam }
public var iBeamCursorForVerticalLayout : NSCursor { return NSCursor.iBeamCursorForVerticalLayout }
public var openHand : NSCursor { return NSCursor.openHand }
public var operationNotAllowed : NSCursor { return NSCursor.operationNotAllowed}
public var pointingHand : NSCursor { return NSCursor.pointingHand }
public var resizeDown : NSCursor { return NSCursor.resizeDown }
public var resizeLeft : NSCursor { return NSCursor.resizeLeft }
public var resizeLeftRight : NSCursor { return NSCursor.resizeLeftRight }
public var resizeRight : NSCursor { return NSCursor.resizeRight }
public var resizeUp : NSCursor { return NSCursor.resizeUp }
public var resizeUpDown : NSCursor { return NSCursor.resizeUpDown }
public func hide() { NSCursor.hide() }
public func show() { NSCursor.unhide() }
}
//
// NETWORK METHODS
//
public let network = ds$Network()
public class ds$Network : NSObject {
public var isConnected : Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)
guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}) else {
return false
}
var flags: SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
return (isReachable && !needsConnection)
}
public func lookupIPAddress(DNS : String) -> [String] {
var temp : String = DNS
if Left(temp,4).uppercased() != "HTTP" { temp="http://\(temp)" }
let url = URL(string:temp)
var ipList: [String] = []
guard let hostname = url?.host else { return ipList }
guard let host = hostname.withCString({gethostbyname($0)}) else { return ipList }
guard host.pointee.h_length > 0 else { return ipList }
var index = 0
while host.pointee.h_addr_list[index] != nil {
var addr: in_addr = in_addr()
memcpy(&addr.s_addr, host.pointee.h_addr_list[index], Int(host.pointee.h_length))
guard let remoteIPAsC = inet_ntoa(addr) else {
return ipList
}
ipList.append(String.init(cString: remoteIPAsC))
index += 1
}
return ipList
}
}
public var networkInterfaceCount : Int { return zInterfaces.count }
public struct networkInterface {
let name : String
let ip : String
let netmask : String
let macAddress : String
}
public var loopBack : networkInterface? { return zLoopBack }
private func readNetworkInformation() {
// rds : 26Feb2022 - There must be a BETTER way to associate macAddress to IP Address
guard let nic = SCNetworkInterfaceCopyAll() as? [SCNetworkInterface] else { return }
let listOfMacAddress = nic.map(SCNetworkInterfaceGetHardwareAddressString).compactMap { $0 as String? }
//--------------------------------------------------
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while( ptr != nil) { // For each interface ...
let flags = Int32(ptr!.pointee.ifa_flags)
var addr = ptr!.pointee.ifa_addr.pointee
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
// if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if true {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
var mask = ptr!.pointee.ifa_netmask.pointee
// Convert interface address to a human readable string:
let zero = CChar(0)
var hostname = [CChar](repeating: zero, count: Int(NI_MAXHOST))
var netmask = [CChar](repeating: zero, count: Int(NI_MAXHOST))
if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let address = String(cString: hostname)
let name = ptr!.pointee.ifa_name!
let ifname = String(cString: name)
if (getnameinfo(&mask, socklen_t(mask.sa_len), &netmask, socklen_t(netmask.count), nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let netmaskIP = String(cString: netmask)
// associate macAddress
var macAddress : String = ""
for i in(0...nic.count-1) {
let z = nic[i]
let nicName : String = SCNetworkInterfaceGetBSDName(z )! as String
if ifname==nicName {
macAddress = listOfMacAddress[i]
break
}
}
if Left(address,1) != ":" {
let info = networkInterface(name : ifname,
ip : address,
netmask : netmaskIP,
macAddress : macAddress)
if flags & IFF_LOOPBACK > 0 {
zLoopBack = info
} else {
zInterfaces.append(info)
}
}
}
}
}
}
ptr = ptr!.pointee.ifa_next
}
freeifaddrs(ifaddr)
}
}
}