├── docs ├── dshb.png ├── dshb.1.ronn ├── dshb.1 └── index.html ├── dshb.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ └── dshb.xcscheme └── project.pbxproj ├── .gitmodules ├── .gitignore ├── LICENSE ├── dshb ├── WidgetUITitle.swift ├── WidgetCPU.swift ├── WidgetSystem.swift ├── WidgetFan.swift ├── WidgetMemory.swift ├── WidgetBattery.swift ├── WidgetTemperature.swift ├── WidgetUIStat.swift ├── Widget.swift ├── WidgetProcess.swift └── main.swift ├── README.md └── Makefile /docs/dshb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beltex/dshb/HEAD/docs/dshb.png -------------------------------------------------------------------------------- /dshb.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/SMCKit"] 2 | path = libs/SMCKit 3 | url = https://github.com/beltex/SMCKit.git 4 | [submodule "libs/SystemKit"] 5 | path = libs/SystemKit 6 | url = https://github.com/beltex/SystemKit.git 7 | [submodule "libs/CommandLine"] 8 | path = libs/CommandLine 9 | url = https://github.com/jatoben/CommandLine.git 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Based off of Swift.gitignore @ https://github.com/github/gitignore 2 | 3 | # dshb 4 | archive 5 | 6 | # Xcode 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | # Numerous always-ignore extensions 26 | *.diff 27 | *.err 28 | *.orig 29 | *.log 30 | *.rej 31 | *.swo 32 | *.swp 33 | *.zip 34 | *.vi 35 | *~ 36 | 37 | # OS or Editor folders 38 | .DS_Store 39 | ._* 40 | Thumbs.db 41 | .cache 42 | .tmproj 43 | *.esproj 44 | nbproject 45 | *.sublime-project 46 | *.sublime-workspace 47 | /bin 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2017 beltex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /dshb/WidgetUITitle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetUITitle.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Darwin.ncurses 28 | 29 | struct WidgetUITitle { 30 | 31 | let name : String 32 | let nameCount: Int 33 | var window : Window 34 | 35 | fileprivate var padding = String() 36 | 37 | init(name: String, window: Window) { 38 | self.name = name 39 | self.window = window 40 | nameCount = name.characters.count 41 | 42 | generatePadding() 43 | } 44 | 45 | func draw() { 46 | move(window.point.y, window.point.x) 47 | attrset(COLOR_PAIR(WidgetUIColor.title.rawValue)) 48 | addstr(name + padding) 49 | } 50 | 51 | mutating func resize(_ window: Window) { 52 | self.window = window 53 | generatePadding() 54 | draw() 55 | } 56 | 57 | fileprivate mutating func generatePadding() { 58 | var paddingSize = window.length - nameCount 59 | 60 | if paddingSize < 0 { paddingSize = 0 } 61 | 62 | padding = String(repeating: " ", count: paddingSize) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /dshb/WidgetCPU.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetCPU.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | struct WidgetCPU: WidgetType { 28 | 29 | let name = "CPU" 30 | let displayOrder = 1 31 | var title: WidgetUITitle 32 | var stats = [WidgetUIStat]() 33 | 34 | fileprivate static var system = System() 35 | 36 | init(window: Window = Window()) { 37 | title = WidgetUITitle(name: "CPU", window: window) 38 | 39 | for stat in ["System", "User", "Idle", "Nice"] { 40 | stats.append(WidgetUIStat(name: stat, unit: .Percentage, 41 | max: 100.0)) 42 | } 43 | 44 | stats[2].Nominal.color = WidgetUIColor.warningLevelCrisis 45 | stats[2].Crisis.color = WidgetUIColor.warningLevelNominal 46 | } 47 | 48 | mutating func draw() { 49 | let values = WidgetCPU.system.usageCPU() 50 | stats[0].draw(String(Int(values.system)), 51 | percentage: values.system / 100.0) 52 | stats[1].draw(String(Int(values.user)), 53 | percentage: values.user / 100.0) 54 | stats[2].draw(String(Int(values.idle)), 55 | percentage: values.idle / 100.0) 56 | stats[3].draw(String(Int(values.nice)), 57 | percentage: values.nice / 100.0) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /dshb/WidgetSystem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetSystem.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | struct WidgetSystem: WidgetType { 30 | 31 | let name = "System" 32 | let displayOrder = 3 33 | var title: WidgetUITitle 34 | var stats = [WidgetUIStat]() 35 | 36 | init(window: Window = Window()) { 37 | title = WidgetUITitle(name: name, window: window) 38 | 39 | for stat in ["Uptime", "Processes", "Threads", "Load Average", "Mach Factor"] { 40 | stats.append(WidgetUIStat(name: stat, unit: .None, max: 1.0)) 41 | } 42 | } 43 | 44 | mutating func draw() { 45 | let uptime = System.uptime() 46 | stats[0].draw("\(uptime.days)d \(uptime.hrs)h \(uptime.mins)m", 47 | percentage: 0.0) 48 | 49 | let counts = System.processCounts() 50 | stats[1].draw(String(counts.processCount), percentage: 0.0) 51 | stats[2].draw(String(counts.threadCount), percentage: 0.0) 52 | 53 | let loadAverage = System.loadAverage().map 54 | { NSString(format:"%.2f", $0) } 55 | stats[3].draw("\(loadAverage[0]), \(loadAverage[1])," + 56 | "\(loadAverage[2])", percentage: 0.0) 57 | 58 | let machFactor = System.machFactor().map { NSString(format:"%.2f", $0) } 59 | 60 | stats[4].draw("\(machFactor[0]), \(machFactor[1]), \(machFactor[2])", 61 | percentage: 0.0) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /dshb/WidgetFan.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetFan.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | struct WidgetFan: WidgetType { 28 | 29 | let name = "Fan" 30 | let displayOrder = 6 31 | var title: WidgetUITitle 32 | var stats = [WidgetUIStat]() 33 | 34 | init(window: Window = Window()) { 35 | title = WidgetUITitle(name: name, window: window) 36 | 37 | 38 | let fanCount: Int 39 | do { fanCount = try SMCKit.fanCount() } 40 | catch { fanCount = 0 } 41 | 42 | 43 | for index in 0.. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | struct WidgetMemory: WidgetType { 30 | 31 | let name = "Memory" 32 | let displayOrder = 2 33 | var title: WidgetUITitle 34 | var stats = [WidgetUIStat]() 35 | 36 | fileprivate static let maxValueGB = System.physicalMemory(.gigabyte) 37 | fileprivate static let maxValueMB = System.physicalMemory(.megabyte) 38 | 39 | init(window: Window = Window()) { 40 | title = WidgetUITitle(name: name, window: window) 41 | 42 | 43 | for stat in ["Free", "Wired", "Active", "Inactive", "Compressed"] { 44 | stats.append(WidgetUIStat(name: stat, unit: .Gigabyte, 45 | max: WidgetMemory.maxValueGB)) 46 | } 47 | 48 | 49 | stats[0].Nominal.range = 0.45..<1.0 50 | stats[0].Danger.range = 0.2..<0.45 51 | stats[0].Crisis.range = 0..<0.2 52 | } 53 | 54 | mutating func draw() { 55 | let values = System.memoryUsage() 56 | unitCheck(values.free, index: 0) 57 | unitCheck(values.wired, index: 1) 58 | unitCheck(values.active, index: 2) 59 | unitCheck(values.inactive, index: 3) 60 | unitCheck(values.compressed, index: 4) 61 | } 62 | 63 | fileprivate mutating func unitCheck(_ val: Double, index: Int) { 64 | if val < 1.0 { 65 | stats[index].unit = .Megabyte 66 | let value = val * 1000.0 67 | stats[index].draw(String(Int(value)), 68 | percentage: value / WidgetMemory.maxValueMB) 69 | } 70 | else { 71 | stats[index].unit = .Gigabyte 72 | stats[index].draw(NSString(format:"%.2f", val) as String, 73 | percentage: val / WidgetMemory.maxValueGB) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /dshb/WidgetBattery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetBattery.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | struct WidgetBattery: WidgetType { 28 | 29 | let name = "Battery" 30 | let displayOrder = 4 31 | var title: WidgetUITitle 32 | var stats = [WidgetUIStat]() 33 | 34 | init(window: Window = Window()) { 35 | title = WidgetUITitle(name: name, window: window) 36 | 37 | 38 | stats = [WidgetUIStat(name: "Charge", unit: .Percentage, max: 100.0), 39 | WidgetUIStat(name: "Capacity Degradation", 40 | unit: .MilliampereHour, 41 | max: Double(battery.designCapacity())), 42 | WidgetUIStat(name: "Cycles", 43 | unit: .None, 44 | max: Double(battery.designCycleCount())), 45 | // TODO: Better max temp for battery 46 | WidgetUIStat(name: "Temperature", unit: .Celsius, max: 128.0), 47 | WidgetUIStat(name: "Time Remaining", unit: .None, max: 0.0)] 48 | 49 | stats[0].Nominal.range = 0.2..<1.0 50 | stats[0].Danger.range = 0..<0 51 | stats[0].Crisis.range = 0..<0.2 52 | 53 | stats[1].Nominal.color = WidgetUIColor.warningLevelCrisis 54 | stats[1].Crisis.color = WidgetUIColor.warningLevelNominal 55 | } 56 | 57 | mutating func draw() { 58 | let charge = battery.charge() 59 | stats[0].draw(String(charge), percentage: charge / 100.0) 60 | 61 | let maxCapactiy = battery.maxCapactiy() 62 | let cycleCount = battery.cycleCount() 63 | 64 | stats[1].draw(String(maxCapactiy - Int(stats[1].maxValue)), 65 | percentage: Double(maxCapactiy) / stats[1].maxValue) 66 | stats[2].draw(String(cycleCount), 67 | percentage: Double(cycleCount) / stats[2].maxValue) 68 | 69 | let temperature = battery.temperature() 70 | stats[3].draw(String(temperature), percentage: temperature / stats[3].maxValue) 71 | stats[4].draw(battery.timeRemainingFormatted(), percentage: 0.0) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /dshb/WidgetTemperature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetTemperature.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | struct WidgetTemperature: WidgetType { 28 | 29 | let name = "Temperature" 30 | let displayOrder = 5 31 | var title: WidgetUITitle 32 | var stats = [WidgetUIStat]() 33 | 34 | let maxValue = 128.0 35 | fileprivate let sensors: [TemperatureSensor] 36 | 37 | init(window: Window = Window()) { 38 | title = WidgetUITitle(name: name, window: window) 39 | 40 | 41 | do { 42 | // TODO: Add battery temperature from SystemKit? SMC will usually 43 | // have a key for it too (though not always certain which one) 44 | let allKnownSensors = try SMCKit.allKnownTemperatureSensors().sorted 45 | { $0.name < $1.name } 46 | 47 | let allUnknownSensors: [TemperatureSensor] 48 | if CLIUnknownTemperatureSensorsOption.wasSet { 49 | allUnknownSensors = try SMCKit.allUnknownTemperatureSensors() 50 | } else { allUnknownSensors = [ ] } 51 | 52 | sensors = allKnownSensors + allUnknownSensors 53 | } catch { 54 | // TODO: Have some sort of warning message under temperature widget 55 | sensors = [ ] 56 | } 57 | 58 | 59 | for sensor in sensors { 60 | let name: String 61 | if sensor.name == "Unknown" { 62 | name = sensor.name + " (\(sensor.code.toString()))" 63 | } else { name = sensor.name } 64 | 65 | stats.append(WidgetUIStat(name: name, unit: .Celsius, 66 | max: maxValue)) 67 | } 68 | } 69 | 70 | mutating func draw() { 71 | for index in 0..= LINES - 2 { break } 74 | 75 | do { 76 | let value = try SMCKit.temperature(sensors[index].code) 77 | stats[index].draw(String(value), percentage: value / maxValue) 78 | } catch { 79 | stats[index].draw("Error", percentage: 0) 80 | // TODO: stats[i].unit = .None 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dshb 2 | ==== 3 | 4 | A macOS system monitor in Swift, inspired by 5 | top & 6 | [htop](https://github.com/hishamhm/htop). Displays live readings of system CPU & 7 | memory usage, machine temperature sensors, fan speeds, battery information and 8 | other miscellaneous system statistics. The ncurses based TUI (text-based user 9 | interface) uses color coating to imply status and is fully resizable. Stats are 10 | updated at one second intervals while still maintaining low overhead (the 11 | observer effect 12 | is inescapable in this case sadly). 13 | 14 | ![alt text](docs/dshb.png) 15 | 16 | 17 | ### Why? 18 | 19 | - Exploration of Swift. In particular, systems programming and interfacing with 20 | low-level C APIs 21 | 22 | 23 | ### Homebrew :beer: 24 | 25 | ```sh 26 | $ brew install dshb 27 | ``` 28 | 29 | 30 | ### Requirements 31 | 32 | - [Xcode 8.3.3 (Swift 3.1)](https://developer.apple.com/xcode/downloads/) 33 | - macOS 10.9+ 34 | - This is due to Swift 35 | 36 | 37 | ### Clone 38 | 39 | Make sure to use the recursive option on clone to auto init all submodules. 40 | 41 | ```sh 42 | git clone --recursive https://github.com/beltex/dshb 43 | ``` 44 | 45 | Incase you have already cloned the repository, run the following inside the 46 | project directory. 47 | 48 | ```sh 49 | git submodule update --init 50 | ``` 51 | 52 | 53 | ### Install 54 | 55 | This will build dshb from source and place the binary and manual page in your 56 | path. 57 | 58 | ```sh 59 | make install 60 | ``` 61 | 62 | 63 | ### Stack 64 | 65 | - [ncurses](https://www.gnu.org/software/ncurses/ncurses.html) 66 | - For drawing to the terminal (tested with version 5.4 - default on macOS) 67 | - [SystemKit](https://github.com/beltex/SystemKit) 68 | - For almost all statistics 69 | - [SMCKit](https://github.com/beltex/SMCKit) 70 | - For temperature & fan statistics 71 | - [CommandLine](https://github.com/jatoben/CommandLine) 72 | - For the CLI 73 | - [ronn](https://github.com/rtomayko/ronn) 74 | - For generating the manual page 75 | 76 | All Git submodules are built part of the project as simply source files, not 77 | frameworks (which are essentially dynamic libraries). This is because currently, 78 | the Swift runtime dynamic libraries must be packaged with the application in 79 | question. In the case of **dshb**, a single binary is generated which has the 80 | runtime statically linked. Thus, frameworks, which expect to find the libraries 81 | inside the application bundle (`.app`), cannot _"see"_ them. 82 | 83 | For more see: 84 | 85 | - [SwiftInFlux/Runtime Dynamic Libraries](https://github.com/ksm/SwiftInFlux#runtime-dynamic-libraries) 86 | - [SwiftInFlux/Static Libraries](https://github.com/ksm/SwiftInFlux#static-libraries) 87 | 88 | 89 | ### References 90 | 91 | - [top](http://www.opensource.apple.com/source/top/) 92 | - [NCURSES Programming HOWTO](http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/index.html) 93 | - [Programmer's Guide to ncurses](http://www.c-for-dummies.com/ncurses/) 94 | - [Writing Programs with NCURSES](http://invisible-island.net/ncurses/ncurses-intro.html) 95 | 96 | 97 | ### License 98 | 99 | This project is under the **MIT License**. 100 | 101 | 102 | ##### _P.S._ 103 | 104 | Working on this always brought a smile to my face. I hope it brings a smile to 105 | yours too. 106 | [Enjoy](http://hypem.com/track/23j7h/First+Aid+Kit+-+My+Silver+Lining) :) 107 | 108 | -------------------------------------------------------------------------------- /dshb.xcodeproj/xcshareddata/xcschemes/dshb.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 57 | 58 | 59 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /dshb/WidgetUIStat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetUIStat.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | typealias WarningLevel = (range: Range, color: WidgetUIColor) 30 | 31 | 32 | enum Unit: String { 33 | case Celsius = "°C" 34 | case Fahrenheit = "°F" 35 | case Gigabyte = "GB" 36 | case Kelvin = "K" 37 | case Percentage = "%" 38 | case Megabyte = "MB" 39 | case MilliampereHour = " mAh" 40 | case None = "" 41 | case RPM = " RPM" 42 | } 43 | 44 | 45 | struct WidgetUIStat { 46 | 47 | let name: String 48 | 49 | var unit: Unit { 50 | didSet { 51 | unitCount = unit.rawValue.characters.count 52 | } 53 | } 54 | 55 | var maxValue: Double 56 | var window: Window 57 | 58 | var Cool: WarningLevel = (-Double.infinity..<0, WidgetUIColor.warningLevelCool) 59 | var Nominal: WarningLevel = (0..<0.45, WidgetUIColor.warningLevelNominal) 60 | var Danger: WarningLevel = (0.45..<0.75, WidgetUIColor.warningLevelDanger) 61 | var Crisis: WarningLevel = (0.75..]
7 |      [`-h`]
8 |      [`-v`]
9 | 10 | ## DESCRIPTION 11 | 12 | A macOS system monitor in Swift, inspired by top & htop. Displays live readings 13 | of system CPU & memory usage, machine temperature sensors, fan speeds, battery 14 | information and other miscellaneous system statistics. The ncurses based TUI 15 | (text-based user interface) uses color coating to imply status and is fully 16 | resizable. Stats are updated at one second intervals while still maintaining 17 | low overhead (the observer effect is inescapable in this case sadly). 18 | 19 | ## OPTIONS 20 | 21 | List of supported command line options (flags/switchs): 22 | 23 | * `-f`, `--frequency` : 24 | Statistic update frequency in seconds. Default is 1. 25 | 26 | * `-h`, `--help`: 27 | Print the list of options. 28 | 29 | * `-u`, `--unknown-temperature-sensors`: 30 | Show temperature sensors whose hardware mapping is unknown. 31 | 32 | * `-v`, `--version`: 33 | Show dshb version. 34 | 35 | ## COMMANDS 36 | 37 | List of supported interactive commands for use during application runtime: 38 | 39 | * `q`: 40 | Quit. 41 | 42 | ## STATS 43 | 44 | Statistic sections are defined as _"widgets"_ (hence the name _dashboard_ - 45 | metaphorically akin to macOS's Dashboard). Widgets are composed of related 46 | statistics. Here we look at the various widgets in detail. 47 | 48 | * `Battery`: 49 |
50 | Displays information about the state of the battery. If the machine does not 51 | have a battery, this widget will not appear. The information includes 52 | _Charge_, _Capacity Degradation_, _Cycles_ and _Time Remaining_. 53 | 54 | * `CPU`: 55 | Displays CPU usage broken down into _System_, _User_, _Idle_ and _Nice_. 56 | 57 | * `Fan`: 58 | Displays a list of all the fans the machine has with the current speed 59 | (RPM - revolutions per minute) of each. All Intel based Macs to this point 60 | have at least one fan, with the exception of the newly added fanless 61 | MacBook. 62 | 63 | * `Memory`: 64 | Displays memory usage broken down into _Free_, _Wired_, _Active_, 65 | _Inactive_ and _Compressed_. 66 | 67 | * `System`: 68 | Miscellaneous system stats including _Uptime_, _Processes_ & _Threads_ 69 | (global counts), _Load Average_ (1, 5 and 15 minute intervals) and _Mach 70 | Factor_ (5, 30 and 60 second intervals). 71 | 72 | * `Temperature`: 73 | Displays a list of the machine temperature sensors in Celsius. The list 74 | is exhaustive when the (`-u`) switch is used. 75 | 76 | Note that the names of the known sensors may not be mapped to the correct 77 | hardware component. In addition, the maximum temperature of each individual 78 | sensor is not known. Thus, a global max of 128 degrees is used. This is all 79 | due to the fact that the SMC (System Management Controller) has a closed 80 | source driver, and thus information about it's inner workings is limited. 81 | 82 | If a sensor has a value that is very high, constant, and completely 83 | disproportionate to the rest, then there is a chance that it is faulty. This 84 | could be due to bad or damaged hardware (liquid on the logic board). Run the 85 | _Apple Diagnostics_ (_Apple Hardware Test_ for older machines) diagnostics 86 | suite in such a case to confirm. 87 | 88 | Some sensors however report very low values, below zero. The current theory 89 | on this is that at lower temperatures the sensors have inaccurate readings. 90 | It maybe that they are located close to an internal fan, and the airflow is 91 | causing it to be skewed. 92 | 93 | ## ENVIRONMENT 94 | 95 | Due to Swift, dshb requires macOS 10.9 (Mavericks) and above. This implies an 96 | Intel based 64-bit machine. Of course, ncurses is required as well, but it 97 | should be installed by default as a part of macOS, as base install tools such as 98 | top, rely on it as well. 99 | 100 | ## REPOSITORY 101 | 102 | 103 | 104 | All project related matters, including source code, can be found at the GitHub 105 | repository listed above. In particular, the issue tracker, which can be used to 106 | report feedback, feature requests and bugs. 107 | 108 | ## COPYRIGHT 109 | 110 | This project is under the _MIT License_. 111 | 112 | ## AUTHOR 113 | 114 | beltex 115 | 116 | Working on this always brought a smile to my face. I hope it brings a smile to 117 | yours too. Enjoy :) 118 | 119 | ## SEE ALSO 120 | 121 | top(1), htop(1) 122 | -------------------------------------------------------------------------------- /docs/dshb.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "DSHB" "1" "October 2017" "0.2.0" "" 5 | . 6 | .SH "NAME" 7 | \fBdshb\fR \- macOS system monitor 8 | . 9 | .SH "SYNOPSIS" 10 | \fBdshb\fR [\fB\-eu\fR][\fB\-f\fR \fIfrequency\fR] 11 | . 12 | .br 13 | \~\~\~\~\~[\fB\-h\fR] 14 | . 15 | .br 16 | \~\~\~\~\~[\fB\-v\fR] 17 | . 18 | .br 19 | . 20 | .SH "DESCRIPTION" 21 | A macOS system monitor in Swift, inspired by top & htop\. Displays live readings of system CPU & memory usage, machine temperature sensors, fan speeds, battery information and other miscellaneous system statistics\. The ncurses based TUI (text\-based user interface) uses color coating to imply status and is fully resizable\. Stats are updated at one second intervals while still maintaining low overhead (the observer effect is inescapable in this case sadly)\. 22 | . 23 | .SH "OPTIONS" 24 | List of supported command line options (flags/switchs): 25 | . 26 | .TP 27 | \fB\-f\fR, \fB\-\-frequency\fR \fIvalue\fR 28 | Statistic update frequency in seconds\. Default is 1\. 29 | . 30 | .TP 31 | \fB\-h\fR, \fB\-\-help\fR 32 | Print the list of options\. 33 | . 34 | .TP 35 | \fB\-u\fR, \fB\-\-unknown\-temperature\-sensors\fR 36 | Show temperature sensors whose hardware mapping is unknown\. 37 | . 38 | .TP 39 | \fB\-v\fR, \fB\-\-version\fR 40 | Show dshb version\. 41 | . 42 | .SH "COMMANDS" 43 | List of supported interactive commands for use during application runtime: 44 | . 45 | .TP 46 | \fBq\fR 47 | Quit\. 48 | . 49 | .SH "STATS" 50 | Statistic sections are defined as \fI"widgets"\fR (hence the name \fIdashboard\fR \- metaphorically akin to macOS\'s Dashboard)\. Widgets are composed of related statistics\. Here we look at the various widgets in detail\. 51 | . 52 | .TP 53 | \fBBattery\fR 54 | . 55 | .br 56 | Displays information about the state of the battery\. If the machine does not have a battery, this widget will not appear\. The information includes \fICharge\fR, \fICapacity Degradation\fR, \fICycles\fR and \fITime Remaining\fR\. 57 | . 58 | .TP 59 | \fBCPU\fR 60 | Displays CPU usage broken down into \fISystem\fR, \fIUser\fR, \fIIdle\fR and \fINice\fR\. 61 | . 62 | .TP 63 | \fBFan\fR 64 | Displays a list of all the fans the machine has with the current speed (RPM \- revolutions per minute) of each\. All Intel based Macs to this point have at least one fan, with the exception of the newly added fanless MacBook\. 65 | . 66 | .TP 67 | \fBMemory\fR 68 | Displays memory usage broken down into \fIFree\fR, \fIWired\fR, \fIActive\fR, \fIInactive\fR and \fICompressed\fR\. 69 | . 70 | .TP 71 | \fBSystem\fR 72 | Miscellaneous system stats including \fIUptime\fR, \fIProcesses\fR & \fIThreads\fR (global counts), \fILoad Average\fR (1, 5 and 15 minute intervals) and \fIMach Factor\fR (5, 30 and 60 second intervals)\. 73 | . 74 | .TP 75 | \fBTemperature\fR 76 | Displays a list of the machine temperature sensors in Celsius\. The list is exhaustive when the (\fB\-u\fR) switch is used\. 77 | . 78 | .IP 79 | Note that the names of the known sensors may not be mapped to the correct hardware component\. In addition, the maximum temperature of each individual sensor is not known\. Thus, a global max of 128 degrees is used\. This is all due to the fact that the SMC (System Management Controller) has a closed source driver, and thus information about it\'s inner workings is limited\. 80 | . 81 | .IP 82 | If a sensor has a value that is very high, constant, and completely disproportionate to the rest, then there is a chance that it is faulty\. This could be due to bad or damaged hardware (liquid on the logic board)\. Run the \fIApple Diagnostics\fR (\fIApple Hardware Test\fR for older machines) diagnostics suite in such a case to confirm\. 83 | . 84 | .IP 85 | Some sensors however report very low values, below zero\. The current theory on this is that at lower temperatures the sensors have inaccurate readings\. It maybe that they are located close to an internal fan, and the airflow is causing it to be skewed\. 86 | . 87 | .SH "ENVIRONMENT" 88 | Due to Swift, dshb requires macOS 10\.9 (Mavericks) and above\. This implies an Intel based 64\-bit machine\. Of course, ncurses is required as well, but it should be installed by default as a part of macOS, as base install tools such as top, rely on it as well\. 89 | . 90 | .SH "REPOSITORY" 91 | \fIhttps://github\.com/beltex/dshb\fR 92 | . 93 | .P 94 | All project related matters, including source code, can be found at the GitHub repository listed above\. In particular, the issue tracker, which can be used to report feedback, feature requests and bugs\. 95 | . 96 | .SH "COPYRIGHT" 97 | This project is under the \fIMIT License\fR\. 98 | . 99 | .SH "AUTHOR" 100 | beltex \fIhttps://beltex\.github\.io\fR 101 | . 102 | .P 103 | Working on this always brought a smile to my face\. I hope it brings a smile to yours too\. Enjoy :) 104 | . 105 | .SH "SEE ALSO" 106 | top(1), htop(1) 107 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | INSTALL_FOLDER = /usr/local/bin 2 | MANPAGE_FOLDER = /usr/local/share/man/man1 3 | DOCS_FOLDER = docs 4 | XCODE_CONFIG = Release 5 | DSHB_VERSION = 0.2.0 6 | ARCHIVE_FOLDER = archive 7 | ARCHIVE_NAME = dshb-${DSHB_VERSION}-source 8 | SUBMODULES_PATH = libs 9 | SUBMODULES = `find ${SUBMODULES_PATH} -type d -depth 1 | \ 10 | sed 's/${SUBMODULES_PATH}//' | tr -d '/'` 11 | REPO_URL = https://github.com/beltex/dshb 12 | 13 | .PHONY: help install machine release debug build uninstall archive-local \ 14 | archive-remote ronn clean distclean 15 | 16 | help: ._hello 17 | @echo "Usage: make [targets] \ 18 | \n install \tBuilds in release mode, placing the binary & manual page in your path \ 19 | \n uninstall\tDeletes the binary & manual page from your path \ 20 | \n release \tRelease mode build with compiler optimizations & symbol removal \ 21 | \n debug \tDebug mode build \ 22 | \n clean \tCleans the build folder \ 23 | \n distclean\tDeletes the build, bin, & archive folders" 24 | ._hello: 25 | @echo "Hello `whoami`! I'm the Makefile for dshb. Nice to meet you! How" \ 26 | "can I help?\n"; touch ._hello 27 | install: machine release 28 | cp bin/dshb ${INSTALL_FOLDER} 29 | cp ${DOCS_FOLDER}/dshb.1 ${MANPAGE_FOLDER} 30 | du -sh ${INSTALL_FOLDER}/dshb 31 | machine: 32 | @sysctl hw.model; \ 33 | sw_vers; \ 34 | uname -v; \ 35 | ioreg -lbrc AppleSMC | grep smc-version | tr -d "|" | xargs; \ 36 | xcodebuild -version; \ 37 | swiftc -v 38 | release: build 39 | strip bin/dshb 40 | debug: XCODE_CONFIG=Debug 41 | debug: build 42 | build: 43 | $(if $(wildcard .git), git submodule update --init, \ 44 | $(if $(wildcard libs/SMCKit/README.md), , $(call bad-archive))) 45 | xcodebuild -configuration ${XCODE_CONFIG} build 46 | mkdir -p bin 47 | cp build/${XCODE_CONFIG}/dshb bin 48 | define bad-archive 49 | @echo "\n Sorry to say this `whoami` but we have a problem! \n\n\ 50 | Looks like you downloaded an archive of dshb from GitHub via the 'Download ZIP' \n\ 51 | option or one of the 'Source code' archives on the release page. These do not \n\ 52 | contain the Git submodules and thus you can't build dshb. Here's what you can do \n\ 53 | instead though! \n\n\ 54 | [1] Clone the repository via Git \n\n\ 55 | git clone --recursive ${REPO_URL} \n\n\ 56 | [2] Download the 'dshb--source.zip' archive which is complete at \n\n\ 57 | ${REPO_URL}/releases/latest\n"; \ 58 | exit 1 59 | endef 60 | uninstall: 61 | rm ${INSTALL_FOLDER}/dshb 62 | rm ${MANPAGE_FOLDER}/dshb.1 63 | ronn: 64 | ronn --organization=${DSHB_VERSION} --style=toc ${DOCS_FOLDER}/dshb.1.ronn 65 | mv ${DOCS_FOLDER}/dshb.1.html ${DOCS_FOLDER}/index.html 66 | archive-local: 67 | @rm -rf ${ARCHIVE_FOLDER}/*; \ 68 | mkdir -p ${ARCHIVE_FOLDER}; \ 69 | git archive --format zip -o ${ARCHIVE_FOLDER}/tmp HEAD; \ 70 | unzip -d ${ARCHIVE_FOLDER}/${ARCHIVE_NAME} ${ARCHIVE_FOLDER}/tmp; \ 71 | for submodule in ${SUBMODULES}; do \ 72 | cd ${SUBMODULES_PATH}/$$submodule; \ 73 | git archive -o ../../${ARCHIVE_FOLDER}/$$submodule.zip HEAD; \ 74 | cd ../../${ARCHIVE_FOLDER}; \ 75 | unzip -d ${ARCHIVE_NAME}/${SUBMODULES_PATH}/$$submodule \ 76 | $$submodule.zip; \ 77 | cd ..; \ 78 | done; \ 79 | cd ${ARCHIVE_FOLDER}; \ 80 | rm -rf *.zip; \ 81 | zip -r ${ARCHIVE_NAME}.zip ${ARCHIVE_NAME}; \ 82 | rm -rf tmp ${ARCHIVE_NAME} 83 | archive-remote: 84 | @rm -rf ${ARCHIVE_FOLDER}/*; \ 85 | mkdir -p ${ARCHIVE_FOLDER}; \ 86 | git clone --depth 1 --recursive ${REPO_URL} \ 87 | ${ARCHIVE_FOLDER}/${ARCHIVE_NAME}; \ 88 | cd ${ARCHIVE_FOLDER}; \ 89 | zip -r ${ARCHIVE_NAME}.zip ${ARCHIVE_NAME} --exclude \*.git *.git/*; \ 90 | rm -rf ${ARCHIVE_NAME} 91 | clean: 92 | xcodebuild -configuration Debug clean 93 | xcodebuild -configuration Release clean 94 | distclean: 95 | rm -rf bin build ._hello ${ARCHIVE_FOLDER} 96 | -------------------------------------------------------------------------------- /dshb/Widget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Widget.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Darwin.ncurses 28 | 29 | //------------------------------------------------------------------------------ 30 | // MARK: PROPERTIES 31 | //------------------------------------------------------------------------------ 32 | 33 | // Color pair refs 34 | enum WidgetUIColor: Int32 { 35 | case background = 1 36 | case title = 2 37 | case warningLevelCool = 3 38 | case warningLevelNominal = 4 39 | case warningLevelDanger = 5 40 | case warningLevelCrisis = 6 41 | } 42 | 43 | /// Number of pixels between widgets 44 | private let widgetSpacing : Int32 = 1 45 | private let maxWidgetsPerRow: Int32 = 3 46 | 47 | //------------------------------------------------------------------------------ 48 | // MARK: PROTOCOLS 49 | //------------------------------------------------------------------------------ 50 | 51 | protocol WidgetType { 52 | var name: String { get } 53 | var displayOrder: Int { get } 54 | var title: WidgetUITitle { get set } 55 | var stats: [WidgetUIStat] { get set } 56 | 57 | init(window: Window) 58 | 59 | mutating func draw() 60 | mutating func resize(_ window: Window) -> Int32 61 | } 62 | 63 | extension WidgetType { 64 | 65 | mutating func resize(_ window: Window) -> Int32 { 66 | title.resize(window) 67 | 68 | var windowVar = window 69 | windowVar.point.y += 1 // Becuase of title 70 | for index in 0.. maxHeight { 121 | maxHeight = result_pos 122 | } 123 | 124 | widgetRowCount += 1 125 | } 126 | 127 | if CLIExperimentalOption.wasSet { 128 | _ = widgets[widgets.count - 1].resize(Window(length: Int(COLS), point: (x: 0, y: maxHeight))) 129 | } 130 | 131 | refresh() 132 | } 133 | -------------------------------------------------------------------------------- /dshb/WidgetProcess.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetProcess.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Cocoa 28 | import Darwin 29 | 30 | struct WidgetProcess: WidgetType { 31 | 32 | let name = " PID USER COMMAND" 33 | let displayOrder = 7 34 | var title: WidgetUITitle 35 | var stats = [WidgetUIStat]() 36 | 37 | var usernames = [uid_t : String]() 38 | 39 | init(window: Window = Window()) { 40 | title = WidgetUITitle(name: name, window: window) 41 | } 42 | 43 | mutating func draw() { 44 | var list = processList() 45 | 46 | list.sort { $0.kp_proc.p_pid > $1.kp_proc.p_pid } 47 | 48 | for index in 0..= list.count { break } 50 | 51 | var kinfo = list[index] 52 | let command = withUnsafePointer(to: &kinfo.kp_proc.p_comm) { 53 | String(cString: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self)) 54 | } 55 | 56 | let username: String 57 | let uid = kinfo.kp_eproc.e_ucred.cr_uid 58 | 59 | if let index = usernames.index(forKey: uid) { 60 | username = usernames[index].1 61 | } 62 | else { username = getUsername(uid) } 63 | 64 | let tokens = [String(kinfo.kp_proc.p_pid), 65 | username, 66 | command] 67 | 68 | let window = Window(length: title.window.length, point: (0, title.window.point.y + Int32(index) + 1)) 69 | let procStat = WidgetUIProcess( 70 | name: processString(tokens, length: title.window.length), 71 | window: window 72 | ) 73 | 74 | procStat.draw() 75 | } 76 | } 77 | } 78 | 79 | struct WidgetUIProcess { 80 | 81 | let name: String 82 | var window: Window 83 | 84 | init(name: String, window: Window) { 85 | self.name = name 86 | self.window = window 87 | } 88 | 89 | func draw() { 90 | move(window.point.y, window.point.x) 91 | addstr(name) 92 | } 93 | 94 | mutating func resize(_ window: Window) { 95 | self.window = window 96 | draw() 97 | } 98 | } 99 | 100 | 101 | private func processString(_ tokens: [String], length: Int) -> String { 102 | let pidSpace = 6 103 | let userSpace = 16 104 | 105 | let pidCount = tokens[0].characters.count 106 | let userCount = tokens[1].characters.count 107 | 108 | var pidSpaceString = String() 109 | var userSpaceString = String() 110 | 111 | for _ in 0.. [kinfo_proc] { 120 | var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0] 121 | var size = 0 122 | 123 | // First need to need size of process list array 124 | var result = sysctl(&mib, u_int(mib.count), nil, &size, nil, 0) 125 | assert(result == KERN_SUCCESS) 126 | 127 | // Get process list 128 | let procCount = size / MemoryLayout.stride 129 | var procList = [kinfo_proc](repeating: kinfo_proc(), count: procCount) 130 | result = sysctl(&mib, u_int(mib.count), &procList, &size, nil, 0) 131 | assert(result == KERN_SUCCESS) 132 | 133 | return procList 134 | } 135 | 136 | 137 | private func getUsername(_ uid: uid_t) -> String { 138 | let username: String 139 | var userInfo = passwd() 140 | var result: UnsafeMutablePointer? = UnsafeMutablePointer.allocate(capacity: 1) 141 | 142 | // TODO: Can we cache this? 143 | // TODO: Returns -1 on not error 144 | let bufferSize = sysconf(_SC_GETPW_R_SIZE_MAX) 145 | let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) 146 | 147 | // TODO: Check result for nil pointer - indictes not found 148 | // TODO: Add note about not using getpwuid() 149 | if getpwuid_r(uid, &userInfo, buffer, bufferSize, &result) == 0 { 150 | username = String(cString: userInfo.pw_name) 151 | } 152 | else { 153 | username = String() 154 | } 155 | 156 | buffer.deallocate(capacity: bufferSize) 157 | // TODO: Why does this fail? 158 | //result.dealloc(1) 159 | 160 | return username 161 | } 162 | -------------------------------------------------------------------------------- /dshb/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // dshb 4 | // 5 | // The MIT License 6 | // 7 | // Copyright (C) 2014-2017 beltex 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import IOKit 28 | import Darwin.ncurses 29 | 30 | //------------------------------------------------------------------------------ 31 | // MARK: GLOBAL PROPERTIES 32 | //------------------------------------------------------------------------------ 33 | 34 | /// Application version 35 | let dshbVersion = "0.2.0" 36 | 37 | /// Statistic update frequency in seconds. Default is 1 38 | let updateFrequency: UInt64 39 | 40 | /// Does this machine have a battery? 41 | let hasBattery: Bool 42 | 43 | /// Does this machine have a SMC (System Management Controller)? 44 | let hasSMC: Bool 45 | 46 | /// Statistic widgets that are on (displayed) 47 | var widgets: [WidgetType] = [WidgetCPU(), WidgetMemory(), WidgetSystem()] 48 | 49 | //------------------------------------------------------------------------------ 50 | // MARK: COMMAND LINE INTERFACE 51 | //------------------------------------------------------------------------------ 52 | 53 | let CLIExperimentalOption = BoolOption(shortFlag: "e", longFlag: "experimental", 54 | helpMessage: "Turn on experimental features") 55 | let CLIFrequencyOption = IntOption(shortFlag: "f", longFlag: "frequency", 56 | helpMessage: "Statistic update frequency in seconds. Default is 1") 57 | let CLIHelpOption = BoolOption(shortFlag: "h", longFlag: "help", 58 | helpMessage: "Print the list of options") 59 | let CLIUnknownTemperatureSensorsOption = BoolOption(shortFlag: "u", 60 | longFlag: "unknown-temperature-sensors", 61 | helpMessage: "Show temperature sensors whose hardware mapping is unknown") 62 | let CLIVersionOption = BoolOption(shortFlag: "v", longFlag: "version", 63 | helpMessage: "Print dshb version") 64 | 65 | let CLI = CommandLine() 66 | CLI.addOptions(CLIExperimentalOption, CLIFrequencyOption, CLIHelpOption, 67 | CLIUnknownTemperatureSensorsOption, CLIVersionOption) 68 | 69 | do { 70 | try CLI.parse(strict: true) 71 | } catch { 72 | CLI.printUsage(error) 73 | exit(EX_USAGE) 74 | } 75 | 76 | 77 | // Give precedence to help flag 78 | if CLIHelpOption.wasSet { 79 | CLI.printUsage() 80 | exit(EX_OK) 81 | } else if CLIVersionOption.wasSet { 82 | print(dshbVersion) 83 | exit(EX_OK) 84 | } 85 | 86 | 87 | if let customFrequency = CLIFrequencyOption.value { 88 | if customFrequency < 1 { 89 | print("Usage: Statistic update frequency must be >= 1") 90 | exit(EX_USAGE) 91 | } 92 | 93 | updateFrequency = UInt64(customFrequency) 94 | } 95 | else { updateFrequency = 1 } 96 | 97 | 98 | if CLIExperimentalOption.wasSet { widgets.append(WidgetProcess()) } 99 | 100 | //------------------------------------------------------------------------------ 101 | // MARK: NCURSES SETTINGS 102 | //------------------------------------------------------------------------------ 103 | 104 | setlocale(LC_ALL, "") 105 | initscr() // Init window. Must be first 106 | cbreak() 107 | noecho() // Don't echo user input 108 | nonl() // Disable newline mode 109 | intrflush(stdscr, true) // Prevent flush 110 | keypad(stdscr, true) // Enable function and arrow keys 111 | curs_set(0) // Set cursor to invisible 112 | 113 | // Init terminal colours 114 | // TODO: Do has_color() check when we have a way to log the error, print() 115 | // won't work 116 | start_color() 117 | init_pair(Int16(WidgetUIColor.background.rawValue), Int16(COLOR_WHITE), 118 | Int16(use_default_colors())) 119 | init_pair(Int16(WidgetUIColor.title.rawValue), Int16(COLOR_WHITE), 120 | Int16(COLOR_CYAN)) 121 | init_pair(Int16(WidgetUIColor.warningLevelCool.rawValue), Int16(COLOR_BLACK), 122 | Int16(COLOR_BLUE)) 123 | init_pair(Int16(WidgetUIColor.warningLevelNominal.rawValue), Int16(COLOR_BLACK), 124 | Int16(COLOR_GREEN)) 125 | init_pair(Int16(WidgetUIColor.warningLevelDanger.rawValue), Int16(COLOR_BLACK), 126 | Int16(COLOR_YELLOW)) 127 | init_pair(Int16(WidgetUIColor.warningLevelCrisis.rawValue), Int16(COLOR_BLACK), 128 | Int16(COLOR_RED)) 129 | 130 | bkgd(UInt32(COLOR_PAIR(WidgetUIColor.background.rawValue))) 131 | 132 | //------------------------------------------------------------------------------ 133 | // MARK: WIDGET SETUP 134 | //------------------------------------------------------------------------------ 135 | 136 | // Do this before SMC, since temperature widget needs to know about battery 137 | var battery = Battery() 138 | 139 | if battery.open() == kIOReturnSuccess { 140 | // TODO: Could this change during use? Old MacBook with removeable battery? 141 | hasBattery = true 142 | widgets.append(WidgetBattery()) 143 | } else { hasBattery = false } 144 | 145 | 146 | do { 147 | try SMCKit.open() 148 | hasSMC = true 149 | widgets.append(WidgetTemperature()) 150 | 151 | // Due to the new fanless MacBook8,1 152 | if let fanCount = try? SMCKit.fanCount(), fanCount > 0 { 153 | widgets.append(WidgetFan()) 154 | } 155 | } catch { hasSMC = false } 156 | 157 | 158 | widgets.sort { $0.displayOrder < $1.displayOrder } 159 | 160 | drawAllWidgets() 161 | 162 | //------------------------------------------------------------------------------ 163 | // MARK: GCD TIMER SETUP 164 | //------------------------------------------------------------------------------ 165 | 166 | // See comment for background for reference. 167 | // https://github.com/beltex/dshb/issues/16#issuecomment-70699890 168 | 169 | 170 | let qAttr = DispatchQueue.Attributes() 171 | let qLabel = "com.beltex.dshb" 172 | let queue: DispatchQueue 173 | 174 | 175 | if #available(OSX 10.10, *) { 176 | let qos = DispatchQoS(qosClass: DispatchQoS.QoSClass.userInteractive, 177 | relativePriority: 0) 178 | 179 | queue = DispatchQueue(label: qLabel, 180 | qos: qos, 181 | attributes: qAttr) 182 | } else { 183 | queue = DispatchQueue(label: qLabel, 184 | attributes: qAttr) 185 | } 186 | 187 | let source = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: 0), 188 | queue: queue) 189 | 190 | source.scheduleRepeating(deadline: .now(), 191 | interval: Double(updateFrequency), 192 | leeway: .seconds(0)) 193 | 194 | source.setEventHandler { 195 | // TODO: If we call clear() here, can help address display "race condition" 196 | // mentioned in issue #16 (see URL above). Though we'd have to redraw 197 | // titles too 198 | for index in 0.. 2 | 3 | 4 | 5 | 6 | dshb(1) - macOS system monitor 7 | 44 | 50 | 51 | 58 | 59 |
60 | 61 | 74 | 75 |
    76 |
  1. dshb(1)
  2. 77 |
  3. 78 |
  4. dshb(1)
  5. 79 |
80 | 81 |

NAME

82 |

83 | dshb - macOS system monitor 84 |

85 | 86 |

SYNOPSIS

87 | 88 |

dshb [-eu][-f frequency]
89 |      [-h]
90 |      [-v]

91 | 92 |

DESCRIPTION

93 | 94 |

A macOS system monitor in Swift, inspired by top & htop. Displays live readings 95 | of system CPU & memory usage, machine temperature sensors, fan speeds, battery 96 | information and other miscellaneous system statistics. The ncurses based TUI 97 | (text-based user interface) uses color coating to imply status and is fully 98 | resizable. Stats are updated at one second intervals while still maintaining 99 | low overhead (the observer effect is inescapable in this case sadly).

100 | 101 |

OPTIONS

102 | 103 |

List of supported command line options (flags/switchs):

104 | 105 |
106 |
-f, --frequency value

Statistic update frequency in seconds. Default is 1.

107 |
-h, --help

Print the list of options.

108 |
-u, --unknown-temperature-sensors

Show temperature sensors whose hardware mapping is unknown.

109 |
-v, --version

Show dshb version.

110 |
111 | 112 | 113 |

COMMANDS

114 | 115 |

List of supported interactive commands for use during application runtime:

116 | 117 |
118 |
q
Quit.
119 |
120 | 121 | 122 |

STATS

123 | 124 |

Statistic sections are defined as "widgets" (hence the name dashboard - 125 | metaphorically akin to macOS's Dashboard). Widgets are composed of related 126 | statistics. Here we look at the various widgets in detail.

127 | 128 |
129 |
Battery


130 | Displays information about the state of the battery. If the machine does not 131 | have a battery, this widget will not appear. The information includes 132 | Charge, Capacity Degradation, Cycles and Time Remaining.

133 |
CPU

Displays CPU usage broken down into System, User, Idle and Nice.

134 |
Fan

Displays a list of all the fans the machine has with the current speed 135 | (RPM - revolutions per minute) of each. All Intel based Macs to this point 136 | have at least one fan, with the exception of the newly added fanless 137 | MacBook.

138 |
Memory

Displays memory usage broken down into Free, Wired, Active, 139 | Inactive and Compressed.

140 |
System

Miscellaneous system stats including Uptime, Processes & Threads 141 | (global counts), Load Average (1, 5 and 15 minute intervals) and Mach 142 | Factor (5, 30 and 60 second intervals).

143 |
Temperature

Displays a list of the machine temperature sensors in Celsius. The list 144 | is exhaustive when the (-u) switch is used.

145 | 146 |

Note that the names of the known sensors may not be mapped to the correct 147 | hardware component. In addition, the maximum temperature of each individual 148 | sensor is not known. Thus, a global max of 128 degrees is used. This is all 149 | due to the fact that the SMC (System Management Controller) has a closed 150 | source driver, and thus information about it's inner workings is limited.

151 | 152 |

If a sensor has a value that is very high, constant, and completely 153 | disproportionate to the rest, then there is a chance that it is faulty. This 154 | could be due to bad or damaged hardware (liquid on the logic board). Run the 155 | Apple Diagnostics (Apple Hardware Test for older machines) diagnostics 156 | suite in such a case to confirm.

157 | 158 |

Some sensors however report very low values, below zero. The current theory 159 | on this is that at lower temperatures the sensors have inaccurate readings. 160 | It maybe that they are located close to an internal fan, and the airflow is 161 | causing it to be skewed.

162 |
163 | 164 | 165 |

ENVIRONMENT

166 | 167 |

Due to Swift, dshb requires macOS 10.9 (Mavericks) and above. This implies an 168 | Intel based 64-bit machine. Of course, ncurses is required as well, but it 169 | should be installed by default as a part of macOS, as base install tools such as 170 | top, rely on it as well.

171 | 172 |

REPOSITORY

173 | 174 |

https://github.com/beltex/dshb

175 | 176 |

All project related matters, including source code, can be found at the GitHub 177 | repository listed above. In particular, the issue tracker, which can be used to 178 | report feedback, feature requests and bugs.

179 | 180 | 181 | 182 |

This project is under the MIT License.

183 | 184 |

AUTHOR

185 | 186 |

beltex https://beltex.github.io

187 | 188 |

Working on this always brought a smile to my face. I hope it brings a smile to 189 | yours too. Enjoy :)

190 | 191 |

SEE ALSO

192 | 193 |

top(1), htop(1)

194 | 195 | 196 |
    197 |
  1. 0.2.0
  2. 198 |
  3. October 2017
  4. 199 |
  5. dshb(1)
  6. 200 |
201 | 202 |
203 | 204 | 205 | -------------------------------------------------------------------------------- /dshb.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4C08F2981A94F3C3006F9359 /* Widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C08F2971A94F3C3006F9359 /* Widget.swift */; }; 11 | 4C4281FB1A3009040000C114 /* WidgetBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4281FA1A3009040000C114 /* WidgetBattery.swift */; }; 12 | 4C448D381B3058EF00F6416C /* libncurses.5.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C448D371B3058EF00F6416C /* libncurses.5.4.dylib */; }; 13 | 4C53D39D1F85E2FC001225B1 /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C53D39A1F85E2FC001225B1 /* CommandLine.swift */; }; 14 | 4C53D39E1F85E2FC001225B1 /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C53D39B1F85E2FC001225B1 /* Option.swift */; }; 15 | 4C53D39F1F85E2FC001225B1 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C53D39C1F85E2FC001225B1 /* StringExtensions.swift */; }; 16 | 4C8112DD1AFD415200100959 /* WidgetProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8112DC1AFD415200100959 /* WidgetProcess.swift */; }; 17 | 4C81CAC71A05FCFA0078EC22 /* SMC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C81CAC51A05FCFA0078EC22 /* SMC.swift */; }; 18 | 4C81CACB1A05FD140078EC22 /* Battery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C81CAC91A05FD140078EC22 /* Battery.swift */; }; 19 | 4C81CACC1A05FD140078EC22 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C81CACA1A05FD140078EC22 /* System.swift */; }; 20 | 4C87280119C7B41C002D093F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C87280019C7B41C002D093F /* main.swift */; }; 21 | 4C97A53219E4B2F10018EFB6 /* WidgetFan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C97A53119E4B2F10018EFB6 /* WidgetFan.swift */; }; 22 | 4C9A7D2019D98D74004ABADA /* WidgetUITitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9A7D1F19D98D74004ABADA /* WidgetUITitle.swift */; }; 23 | 4C9C0E5119E32921009EF6A9 /* WidgetTemperature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9C0E5019E32921009EF6A9 /* WidgetTemperature.swift */; }; 24 | 4CB246C41A5B916F00CA8AC4 /* WidgetSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB246C31A5B916F00CA8AC4 /* WidgetSystem.swift */; }; 25 | 4CB246C81A5BAADC00CA8AC4 /* WidgetCPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB246C71A5BAADC00CA8AC4 /* WidgetCPU.swift */; }; 26 | 4CB246CA1A5BACF200CA8AC4 /* WidgetMemory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB246C91A5BACF200CA8AC4 /* WidgetMemory.swift */; }; 27 | 4CCB22A219D4F07B0058E62D /* WidgetUIStat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CCB22A119D4F07B0058E62D /* WidgetUIStat.swift */; }; 28 | 4CD184881B87E3EB001CF027 /* IOReturn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD184871B87E3EB001CF027 /* IOReturn.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXCopyFilesBuildPhase section */ 32 | 4C8727FB19C7B41C002D093F /* CopyFiles */ = { 33 | isa = PBXCopyFilesBuildPhase; 34 | buildActionMask = 2147483647; 35 | dstPath = /usr/share/man/man1/; 36 | dstSubfolderSpec = 0; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 1; 40 | }; 41 | /* End PBXCopyFilesBuildPhase section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 4C08F2971A94F3C3006F9359 /* Widget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Widget.swift; sourceTree = ""; }; 45 | 4C4281FA1A3009040000C114 /* WidgetBattery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetBattery.swift; sourceTree = ""; }; 46 | 4C448D371B3058EF00F6416C /* libncurses.5.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libncurses.5.4.dylib; path = ../../../../usr/lib/libncurses.5.4.dylib; sourceTree = ""; }; 47 | 4C53D39A1F85E2FC001225B1 /* CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CommandLine.swift; path = libs/CommandLine/CommandLineKit/CommandLine.swift; sourceTree = ""; }; 48 | 4C53D39B1F85E2FC001225B1 /* Option.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Option.swift; path = libs/CommandLine/CommandLineKit/Option.swift; sourceTree = ""; }; 49 | 4C53D39C1F85E2FC001225B1 /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringExtensions.swift; path = libs/CommandLine/CommandLineKit/StringExtensions.swift; sourceTree = ""; }; 50 | 4C8112DC1AFD415200100959 /* WidgetProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetProcess.swift; sourceTree = ""; }; 51 | 4C81CAC51A05FCFA0078EC22 /* SMC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMC.swift; sourceTree = ""; }; 52 | 4C81CAC91A05FD140078EC22 /* Battery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Battery.swift; sourceTree = ""; }; 53 | 4C81CACA1A05FD140078EC22 /* System.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = ""; }; 54 | 4C8727FD19C7B41C002D093F /* dshb */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dshb; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 4C87280019C7B41C002D093F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 56 | 4C97A53119E4B2F10018EFB6 /* WidgetFan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetFan.swift; sourceTree = ""; }; 57 | 4C9A7D1F19D98D74004ABADA /* WidgetUITitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetUITitle.swift; sourceTree = ""; }; 58 | 4C9C0E5019E32921009EF6A9 /* WidgetTemperature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetTemperature.swift; sourceTree = ""; }; 59 | 4CB246C31A5B916F00CA8AC4 /* WidgetSystem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetSystem.swift; sourceTree = ""; }; 60 | 4CB246C71A5BAADC00CA8AC4 /* WidgetCPU.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetCPU.swift; sourceTree = ""; }; 61 | 4CB246C91A5BACF200CA8AC4 /* WidgetMemory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetMemory.swift; sourceTree = ""; }; 62 | 4CCB22A119D4F07B0058E62D /* WidgetUIStat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetUIStat.swift; sourceTree = ""; }; 63 | 4CD184871B87E3EB001CF027 /* IOReturn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IOReturn.swift; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 4C8727FA19C7B41C002D093F /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | 4C448D381B3058EF00F6416C /* libncurses.5.4.dylib in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 4C81CABE1A05FCA10078EC22 /* libs */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 4CEBF73D1A4E067C0001B326 /* CommandLine */, 82 | 4C81CAC31A05FCFA0078EC22 /* SMCKit */, 83 | 4C81CAC81A05FD140078EC22 /* SystemKit */, 84 | ); 85 | name = libs; 86 | sourceTree = ""; 87 | }; 88 | 4C81CAC31A05FCFA0078EC22 /* SMCKit */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 4C81CAC51A05FCFA0078EC22 /* SMC.swift */, 92 | ); 93 | name = SMCKit; 94 | path = libs/SMCKit/SMCKit; 95 | sourceTree = ""; 96 | }; 97 | 4C81CAC81A05FD140078EC22 /* SystemKit */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 4CD184871B87E3EB001CF027 /* IOReturn.swift */, 101 | 4C81CAC91A05FD140078EC22 /* Battery.swift */, 102 | 4C81CACA1A05FD140078EC22 /* System.swift */, 103 | ); 104 | name = SystemKit; 105 | path = libs/SystemKit/SystemKit; 106 | sourceTree = ""; 107 | }; 108 | 4C8727F419C7B41B002D093F = { 109 | isa = PBXGroup; 110 | children = ( 111 | 4C448D371B3058EF00F6416C /* libncurses.5.4.dylib */, 112 | 4C8727FF19C7B41C002D093F /* dshb */, 113 | 4C81CABE1A05FCA10078EC22 /* libs */, 114 | 4C8727FE19C7B41C002D093F /* Products */, 115 | ); 116 | sourceTree = ""; 117 | }; 118 | 4C8727FE19C7B41C002D093F /* Products */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 4C8727FD19C7B41C002D093F /* dshb */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | 4C8727FF19C7B41C002D093F /* dshb */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 4C87280019C7B41C002D093F /* main.swift */, 130 | 4C08F2971A94F3C3006F9359 /* Widget.swift */, 131 | 4C9A7D1F19D98D74004ABADA /* WidgetUITitle.swift */, 132 | 4CCB22A119D4F07B0058E62D /* WidgetUIStat.swift */, 133 | 4C4281FA1A3009040000C114 /* WidgetBattery.swift */, 134 | 4CB246C71A5BAADC00CA8AC4 /* WidgetCPU.swift */, 135 | 4C97A53119E4B2F10018EFB6 /* WidgetFan.swift */, 136 | 4CB246C91A5BACF200CA8AC4 /* WidgetMemory.swift */, 137 | 4CB246C31A5B916F00CA8AC4 /* WidgetSystem.swift */, 138 | 4C9C0E5019E32921009EF6A9 /* WidgetTemperature.swift */, 139 | 4C8112DC1AFD415200100959 /* WidgetProcess.swift */, 140 | ); 141 | path = dshb; 142 | sourceTree = ""; 143 | }; 144 | 4CEBF73D1A4E067C0001B326 /* CommandLine */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 4C53D39A1F85E2FC001225B1 /* CommandLine.swift */, 148 | 4C53D39B1F85E2FC001225B1 /* Option.swift */, 149 | 4C53D39C1F85E2FC001225B1 /* StringExtensions.swift */, 150 | ); 151 | name = CommandLine; 152 | sourceTree = ""; 153 | }; 154 | /* End PBXGroup section */ 155 | 156 | /* Begin PBXNativeTarget section */ 157 | 4C8727FC19C7B41C002D093F /* dshb */ = { 158 | isa = PBXNativeTarget; 159 | buildConfigurationList = 4C87280419C7B41C002D093F /* Build configuration list for PBXNativeTarget "dshb" */; 160 | buildPhases = ( 161 | 4C8727F919C7B41C002D093F /* Sources */, 162 | 4C8727FA19C7B41C002D093F /* Frameworks */, 163 | 4C8727FB19C7B41C002D093F /* CopyFiles */, 164 | ); 165 | buildRules = ( 166 | ); 167 | dependencies = ( 168 | ); 169 | name = dshb; 170 | productName = dshb; 171 | productReference = 4C8727FD19C7B41C002D093F /* dshb */; 172 | productType = "com.apple.product-type.tool"; 173 | }; 174 | /* End PBXNativeTarget section */ 175 | 176 | /* Begin PBXProject section */ 177 | 4C8727F519C7B41B002D093F /* Project object */ = { 178 | isa = PBXProject; 179 | attributes = { 180 | LastSwiftUpdateCheck = 0700; 181 | LastUpgradeCheck = 0830; 182 | ORGANIZATIONNAME = beltex; 183 | TargetAttributes = { 184 | 4C8727FC19C7B41C002D093F = { 185 | CreatedOnToolsVersion = 6.1; 186 | LastSwiftMigration = 0810; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = 4C8727F819C7B41B002D093F /* Build configuration list for PBXProject "dshb" */; 191 | compatibilityVersion = "Xcode 3.2"; 192 | developmentRegion = English; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | ); 197 | mainGroup = 4C8727F419C7B41B002D093F; 198 | productRefGroup = 4C8727FE19C7B41C002D093F /* Products */; 199 | projectDirPath = ""; 200 | projectRoot = ""; 201 | targets = ( 202 | 4C8727FC19C7B41C002D093F /* dshb */, 203 | ); 204 | }; 205 | /* End PBXProject section */ 206 | 207 | /* Begin PBXSourcesBuildPhase section */ 208 | 4C8727F919C7B41C002D093F /* Sources */ = { 209 | isa = PBXSourcesBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | 4C81CACB1A05FD140078EC22 /* Battery.swift in Sources */, 213 | 4C9C0E5119E32921009EF6A9 /* WidgetTemperature.swift in Sources */, 214 | 4C9A7D2019D98D74004ABADA /* WidgetUITitle.swift in Sources */, 215 | 4C53D39D1F85E2FC001225B1 /* CommandLine.swift in Sources */, 216 | 4CB246CA1A5BACF200CA8AC4 /* WidgetMemory.swift in Sources */, 217 | 4CD184881B87E3EB001CF027 /* IOReturn.swift in Sources */, 218 | 4CB246C81A5BAADC00CA8AC4 /* WidgetCPU.swift in Sources */, 219 | 4C53D39E1F85E2FC001225B1 /* Option.swift in Sources */, 220 | 4C97A53219E4B2F10018EFB6 /* WidgetFan.swift in Sources */, 221 | 4C81CACC1A05FD140078EC22 /* System.swift in Sources */, 222 | 4CB246C41A5B916F00CA8AC4 /* WidgetSystem.swift in Sources */, 223 | 4C81CAC71A05FCFA0078EC22 /* SMC.swift in Sources */, 224 | 4C53D39F1F85E2FC001225B1 /* StringExtensions.swift in Sources */, 225 | 4C08F2981A94F3C3006F9359 /* Widget.swift in Sources */, 226 | 4C8112DD1AFD415200100959 /* WidgetProcess.swift in Sources */, 227 | 4CCB22A219D4F07B0058E62D /* WidgetUIStat.swift in Sources */, 228 | 4C87280119C7B41C002D093F /* main.swift in Sources */, 229 | 4C4281FB1A3009040000C114 /* WidgetBattery.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin XCBuildConfiguration section */ 236 | 4C87280219C7B41C002D093F /* Debug */ = { 237 | isa = XCBuildConfiguration; 238 | buildSettings = { 239 | ALWAYS_SEARCH_USER_PATHS = NO; 240 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 241 | CLANG_CXX_LIBRARY = "libc++"; 242 | CLANG_ENABLE_MODULES = YES; 243 | CLANG_ENABLE_OBJC_ARC = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_CONSTANT_CONVERSION = YES; 246 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_ENUM_CONVERSION = YES; 249 | CLANG_WARN_INFINITE_RECURSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 252 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 253 | CLANG_WARN_UNREACHABLE_CODE = YES; 254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 255 | COPY_PHASE_STRIP = NO; 256 | ENABLE_STRICT_OBJC_MSGSEND = YES; 257 | ENABLE_TESTABILITY = YES; 258 | GCC_C_LANGUAGE_STANDARD = gnu99; 259 | GCC_DYNAMIC_NO_PIC = NO; 260 | GCC_NO_COMMON_BLOCKS = YES; 261 | GCC_OPTIMIZATION_LEVEL = 0; 262 | GCC_PREPROCESSOR_DEFINITIONS = ( 263 | "DEBUG=1", 264 | "$(inherited)", 265 | ); 266 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 267 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 268 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 269 | GCC_WARN_UNDECLARED_SELECTOR = YES; 270 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 271 | GCC_WARN_UNUSED_FUNCTION = YES; 272 | GCC_WARN_UNUSED_VARIABLE = YES; 273 | MACOSX_DEPLOYMENT_TARGET = 10.9; 274 | MTL_ENABLE_DEBUG_INFO = YES; 275 | ONLY_ACTIVE_ARCH = YES; 276 | SDKROOT = macosx; 277 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 278 | }; 279 | name = Debug; 280 | }; 281 | 4C87280319C7B41C002D093F /* Release */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | ALWAYS_SEARCH_USER_PATHS = NO; 285 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 286 | CLANG_CXX_LIBRARY = "libc++"; 287 | CLANG_ENABLE_MODULES = YES; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_WARN_BOOL_CONVERSION = YES; 290 | CLANG_WARN_CONSTANT_CONVERSION = YES; 291 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 292 | CLANG_WARN_EMPTY_BODY = YES; 293 | CLANG_WARN_ENUM_CONVERSION = YES; 294 | CLANG_WARN_INFINITE_RECURSION = YES; 295 | CLANG_WARN_INT_CONVERSION = YES; 296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 297 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 298 | CLANG_WARN_UNREACHABLE_CODE = YES; 299 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 300 | COPY_PHASE_STRIP = YES; 301 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 302 | ENABLE_NS_ASSERTIONS = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu99; 305 | GCC_NO_COMMON_BLOCKS = YES; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | MACOSX_DEPLOYMENT_TARGET = 10.9; 313 | MTL_ENABLE_DEBUG_INFO = NO; 314 | SDKROOT = macosx; 315 | }; 316 | name = Release; 317 | }; 318 | 4C87280519C7B41C002D093F /* Debug */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | PRODUCT_NAME = "$(TARGET_NAME)"; 322 | SWIFT_VERSION = 3.0; 323 | }; 324 | name = Debug; 325 | }; 326 | 4C87280619C7B41C002D093F /* Release */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 331 | SWIFT_VERSION = 3.0; 332 | }; 333 | name = Release; 334 | }; 335 | /* End XCBuildConfiguration section */ 336 | 337 | /* Begin XCConfigurationList section */ 338 | 4C8727F819C7B41B002D093F /* Build configuration list for PBXProject "dshb" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 4C87280219C7B41C002D093F /* Debug */, 342 | 4C87280319C7B41C002D093F /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | 4C87280419C7B41C002D093F /* Build configuration list for PBXNativeTarget "dshb" */ = { 348 | isa = XCConfigurationList; 349 | buildConfigurations = ( 350 | 4C87280519C7B41C002D093F /* Debug */, 351 | 4C87280619C7B41C002D093F /* Release */, 352 | ); 353 | defaultConfigurationIsVisible = 0; 354 | defaultConfigurationName = Release; 355 | }; 356 | /* End XCConfigurationList section */ 357 | }; 358 | rootObject = 4C8727F519C7B41B002D093F /* Project object */; 359 | } 360 | --------------------------------------------------------------------------------