Skip to content

Instantly share code, notes, and snippets.

@mmstick
Last active August 29, 2015 14:06
Show Gist options
  • Save mmstick/9d4389ee875aa9ceb62a to your computer and use it in GitHub Desktop.
Save mmstick/9d4389ee875aa9ceb62a to your computer and use it in GitHub Desktop.
i3 status bar programmed in Go -- works with both laptops and desktops now. If a battery is present, it will print battery information. Otherwise, it will not print battery information. It will also automatically detect the currently active network connection and use statistics from that to notify the user how much data has been downloaded and u…
package main
import (
"fmt"
"io/ioutil"
"os"
"os/user"
"runtime"
"strconv"
"strings"
"syscall"
"time"
)
var sprintf = fmt.Sprintf
var refreshRate = 1 * time.Second
// Contains system information
type systemInfo struct {
kernel, cpuModel, host, user string // Static
uptime, cpufreqs, memStat, transferStat, batteryInfo, date string // Dynamic
memTotal int
}
// This is used to get a string from the kernel utsname
func utsnameToString(unameArray [65]int8) string {
var byteString [65]byte
var indexLength int
for ; unameArray[indexLength] != 0; indexLength++ {
byteString[indexLength] = uint8(unameArray[indexLength])
}
return string(byteString[0:indexLength])
}
// Returns kernel version information
func kernelVersion() string {
var utsname syscall.Utsname
_ = syscall.Uname(&utsname)
return utsnameToString(utsname.Release)
}
// Returns a newline-delimited string slice of the file
func parseFile(file string) []string {
cached, _ := ioutil.ReadFile(file)
return strings.Split(string(cached), "\n")
}
// Sets the CPU Model
func getCPUModel() string {
return parseFile("/proc/cpuinfo")[4][13:]
}
// Returns the amount of memory installed in the system
func installedMemory() int {
mem := parseFile("/proc/meminfo")[0]
totalMem, _ := strconv.Atoi(mem[17 : len(mem)-3])
return totalMem / 1024
}
// Returns the memory currently available as an int
func memAvailable() int {
mem := parseFile("/proc/meminfo")[2]
memAvailable, _ := strconv.Atoi(mem[18 : len(mem)-3])
return memAvailable / 1024
}
// Returns a string indicating memory usage out of total available memory
func memoryStatistics(memTotal *int, memStat *string, done chan bool) {
*memStat = sprintf("RAM: %d/%dMB", *memTotal-memAvailable(), *memTotal)
done <- true
}
// Returns the current time
func currentTime(date *string, done chan bool) {
*date = time.Now().Format(time.RFC1123)
done <- true
}
// Returns the number of CPU cores in the system
func parseCPUCount(count string) int {
cores, _ := strconv.Atoi(count[11:])
return cores
}
// Parses CPU frequency information
func parseFrequency(frequency string) string {
frequency = frequency[11 : len(frequency)-4]
if len(frequency) < 4 {
frequency = " " + frequency
}
return frequency + "MHz"
}
// Obtains the printf format for the current core frequency
func getFrequencyFormat(index *int, lastCore int) string {
if *index == lastCore {
return "%s"
} else {
return "%s "
}
}
// Returns the frequency of the current core
func getCoreFrequency(cpuInfo []string, index *int, format *string) string {
return sprintf(*format, parseFrequency(cpuInfo[*index*27+7]))
}
// Returns a string containing the frequencies of each CPU core
func getCPUFrequencies(cpufreqs *string, done chan bool) {
cpuInfo := parseFile("/proc/cpuinfo")
numCPUs := parseCPUCount(cpuInfo[len(cpuInfo)-17]) + 1
var cpuFrequencies string
for index := 0; index < numCPUs; index++ {
format := getFrequencyFormat(&index, numCPUs-1)
cpuFrequencies += getCoreFrequency(cpuInfo, &index, &format)
}
*cpufreqs = "Cores:" + cpuFrequencies
done <- true
}
// Returns the status of the battery: [C]harging, [F]ull, or [D]ischarging.
func batteryStatus() byte {
return parseFile("/sys/class/power_supply/BAT1/status")[0][0]
}
// Returns the current battery life in percent.
func batteryCharge() string {
return parseFile("/sys/class/power_supply/BAT1/capacity")[0]
}
// Returns a string indicating that the battery is currently charging.
func batteryIsCharging() string {
return sprintf("BAT Charging: %s%%", batteryCharge())
}
// Returns a string indicating that the battery is currently discharging.
func batteryIsDischarging() string {
return sprintf("BAT: %s%%", batteryCharge())
}
// Checks the battery status and returns information based on that information.
func batteryInformation(batteryStat *string, done chan bool) {
switch batteryStatus() {
case 'C':
*batteryStat = batteryIsCharging()
case 'F':
*batteryStat = "BAT Full"
default:
*batteryStat = batteryIsDischarging()
}
done <- true
}
// Takes an uptime value and divides it by a scale (days/hours/minutes).
// After determining the amount of time in that scale, it subtracts that amount
// from uptime and returns the time in days/hours/minutes
func getTimeScale(time **int, scale int) int {
var output int
if **time > scale {
output = **time / scale
**time -= output * scale
}
return output
}
// Adds an extra zero in case the time only has one digit
func formatTime(time int) string {
output := strconv.Itoa(time)
if len(output) == 1 {
output = "0" + output
}
return output
}
// Returns the time formatted in days and subtracts that from time.
func getDays(time *int) string {
return formatTime(getTimeScale(&time, 86400))
}
// Returns the time formatted in hours and subtracts that from time.
func getHours(time *int) string {
return formatTime(getTimeScale(&time, 3600))
}
// Returns the time formatted in minutes and subtracts that from time.
func getMinutes(time *int) string {
return formatTime(getTimeScale(&time, 60))
}
// Returns the time formatted in seconds.
func getSeconds(time *int) string {
return formatTime(*time)
}
// Takes the uptime integer and converts it into a human readable format.
// Ex: 01:21:18:57 for days:hours:seconds:minutes
func humanReadableTime(time int) string {
return sprintf("%s:%s:%s:%s", getDays(&time), getHours(&time),
getMinutes(&time), getSeconds(&time))
}
// Converts a string that we know is an integer to an integer
func strToInt(input string) int {
output, _ := strconv.Atoi(input)
return output
}
// Open /proc/uptime and return the value in integer format.
func parseUptime() int {
parsedInfo := strings.SplitAfter(parseFile("/proc/uptime")[0], " ")
return strToInt(parsedInfo[0][0 : len(parsedInfo[0])-4])
}
// Returns the current uptime in days:hours:minutes:seconds format
func getUptime(uptime *string, done chan bool) {
*uptime = sprintf("Uptime: %s", humanReadableTime(parseUptime()))
done <- true
}
// Returns the hostname
func getHost() string {
host, _ := os.Hostname()
return host
}
// Returns true if the connection is not a loopback address.
func connectionIsNotLoopback(connection *string) bool {
if *connection == "lo" {
return false
} else {
return true
}
}
// Returns true if the connection is up
func connectionIsUp(connection *string) bool {
if parseFile("/sys/class/net/" + *connection + "/operstate")[0][0] == 'u' {
return true
} else {
return false
}
}
// Returns true if the connection is active and isn't a loopback address.
func connectionIsActive(connection string) bool {
if connectionIsNotLoopback(&connection) && connectionIsUp(&connection) {
return true
} else {
return false
}
}
// Returns the currently active connection name.
func getCurrentNetwork() string {
var activeConnection string
networkConnections, _ := ioutil.ReadDir("/sys/class/net/")
for _, connection := range networkConnections {
if connectionIsActive(connection.Name()) {
activeConnection = connection.Name()
}
}
return activeConnection
}
// Pads digits with spaces so that the status bar always has the same number of
// characters.
func padDigits(number int) string {
numberString := sprintf("%d", number)
switch len(numberString) {
case 1:
return " " + numberString
case 2:
return " " + numberString
case 3:
return " " + numberString
default:
return numberString
}
}
// Formats the bytes into their respectiev scales
func formatBytes(bytes int) string {
switch {
case bytes > 10737418240:
return sprintf("%sGiB", padDigits(bytes/1073741824))
case bytes > 10485760:
return sprintf("%sMiB", padDigits(bytes/1048576))
case bytes > 10240:
return sprintf("%sKiB", padDigits(bytes/1024))
default:
return sprintf("%s B", padDigits(bytes))
}
}
// Returns the transfer statistics directory name.
func transferDir() string {
return sprintf("/sys/class/net/%s/statistics/", getCurrentNetwork())
}
// Returns the contents of the file as an integer variable
func fileAsInt(file string) int {
return strToInt(parseFile(file)[0])
}
// Returns the amount of bytes downloaded since boot.
func downloadInformation() string {
return formatBytes(fileAsInt(transferDir() + "rx_bytes"))
}
// Returns the amount of bytes uploaded since boot.
func uploadInformation() string {
return formatBytes(fileAsInt(transferDir() + "tx_bytes"))
}
// Returns RX/TX transfer statistics
func transferStatistics(transferStat *string, done chan bool) {
*transferStat = sprintf("D:%s U:%s", downloadInformation(),
uploadInformation())
done <- true
}
// Returns the user's name
func getUsername() string {
currentUser, _ := user.Current()
return currentUser.Username
}
// If a battery exists in the system, return true and the number of concurrent
// jobs to perform.
func ifBatteryExists() (bool, int) {
battery, _ := ioutil.ReadDir("/sys/class/power_supply")
if len(battery) > 0 {
return true, 6
} else {
return false, 5
}
}
// Returns the printf format for formatting the status bar. If a battery exists,
// this will return an additional string element.
func statusBarFormat(batteryExists bool) string {
if batteryExists {
return "%s@%s | %s | %s | %s %s | %s | %s | %s | %s\n"
} else {
return "%s@%s | %s | %s | %s %s | %s | %s | %s\n"
}
}
// Returns system information that does not need to be dynamically updated.
func getStaticSystemInformation() *systemInfo {
info := systemInfo{
kernel: kernelVersion(),
cpuModel: getCPUModel(),
memTotal: installedMemory(),
host: getHost(),
user: getUsername(),
}
return &info
}
// Adds dynamic system information to the systemInfo struct and checks for
// the existence of a battery.
func getDynamicSystemInformation(info systemInfo) (*systemInfo, bool) {
batteryExists, jobs := ifBatteryExists()
synchronize := make(chan bool, jobs)
if batteryExists {
go batteryInformation(&info.batteryInfo, synchronize)
}
go getUptime(&info.uptime, synchronize)
go getCPUFrequencies(&info.cpufreqs, synchronize)
go memoryStatistics(&info.memTotal, &info.memStat, synchronize)
go transferStatistics(&info.transferStat, synchronize)
go currentTime(&info.date, synchronize)
for jobCount := 0; jobCount < jobs; jobCount++ {
<-synchronize
}
return &info, batteryExists
}
// Refreshes the status bar
func refreshBar(info *systemInfo, batteryExists bool) {
if batteryExists {
fmt.Printf(statusBarFormat(true), info.user, info.host, info.kernel,
info.uptime, info.cpuModel, info.cpufreqs, info.memStat,
info.transferStat, info.batteryInfo, info.date)
} else {
fmt.Printf(statusBarFormat(false), info.user, info.host, info.kernel,
info.uptime, info.cpuModel, info.cpufreqs, info.memStat,
info.transferStat, info.date)
}
}
func main() {
runtime.GOMAXPROCS(6)
system := getStaticSystemInformation()
for {
go refreshBar(getDynamicSystemInformation(*system))
time.Sleep(refreshRate)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment