31 Commits

Author SHA1 Message Date
eleksir
05eab972d7 Looks like we do not need separators for buttons 2026-03-31 02:07:17 +03:00
eleksir
eeddce7225 Merge branch 'master' of http://git.lan/eleksir/i3status-go 2026-03-31 01:32:28 +03:00
eleksir
56aa0f7e5e Fix rendering in main.go - separators never handled in formally correct way. 2026-03-31 01:26:46 +03:00
eleksir
86596461a1 More efficiently reap zombeis in spawn.go 2026-03-31 01:23:02 +03:00
eleksir
cd57dbc770 More efficient look for leading "{" symbold in stdin.go 2026-03-31 01:18:33 +03:00
eleksir
f2c24bc0ec Render buttons border immedialtely after windo list population 2026-03-31 01:12:32 +03:00
eleksir
bd6aa31c4d Discard unneccessary cast to any 2026-03-31 01:04:47 +03:00
eleksir
f59ac30f51 Handle error in batteries.go in battery.GetAll(), maybe it is bad idea 2026-03-31 01:02:17 +03:00
eleksir
4beda1639e Fix hypotetical file leakage in cputemp.go 2026-03-31 00:59:34 +03:00
eleksir
7a133f3867 Fix possinble division by zero in cputemp.go 2026-03-31 00:57:37 +03:00
eleksir
3f1adbe126 Log error in la.go if unable to open file 2026-03-31 00:55:38 +03:00
eleksir
0d3f805a2d Inconsistent string trimming in batteries.go 2026-03-31 00:53:51 +03:00
eleksir
2855b9e612 Eliminate unused variable in signal.go 2026-03-31 00:51:44 +03:00
eleksir
217948ade6 Inconsistent variable name in ifmon.go 2026-03-31 00:49:52 +03:00
eleksir
c621d03a71 Fix issue while getting windows tree in i3.go 2026-03-31 00:40:10 +03:00
eleksir
13efa0393c Do not exec() if binary path is empty 2026-03-31 00:38:15 +03:00
eleksir
d5f81e9fd6 Remove redundant close of /dev/null in spawn.go 2026-03-31 00:36:12 +03:00
eleksir
8f742709b6 Make delay before execution command in more efficient way 2026-03-31 00:32:02 +03:00
eleksir
40213ba5c3 Read swap statistics only if swap stats enabled 2026-03-31 00:29:09 +03:00
eleksir
deeb0dfbf0 Formally check open() result for /dev/null 2026-03-31 00:27:18 +03:00
eleksir
ad013fb8f5 Cache regex evaluation in i3.go 2026-03-31 00:23:48 +03:00
eleksir
6def26ef02 Add missing modeline to cmd/battery-test/main.go 2026-03-31 00:21:57 +03:00
eleksir
a16816e862 Avoid busy loop in zombies reap procedure. 2026-03-31 00:21:06 +03:00
eleksir
cc6cc192cb Fix battery detection. 2026-03-31 00:19:02 +03:00
eleksir
f59b735f38 Fix possible multiple command execution in rare case. 2026-03-31 00:17:12 +03:00
eleksir
53c7fe292e In runcommand.go logged incorrect variable 2026-03-31 00:14:29 +03:00
eleksir
fe0b0bf6dd Fix inconsistent swap calculation 2026-03-31 00:11:54 +03:00
eleksir
8f3f6c77bd Fix bug - right-most separator never displayed 2026-03-31 00:09:19 +03:00
eleksir
5750894336 Add vim modeline to all *.go files 2026-03-31 00:08:15 +03:00
eleksir
63f1a142d8 Fix couple nasty bugs in collection.go, safer type assertions. 2026-03-30 23:49:06 +03:00
eleksir
9bea626f7c Fix potential panic in i3.go, check interface{} for nil 2026-03-30 23:36:06 +03:00
18 changed files with 179 additions and 140 deletions

View File

@@ -21,3 +21,5 @@ func main() {
time.Sleep(1 * time.Second)
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -44,7 +44,6 @@ func main() {
// Conf.Values.PA
Conf.Values.SoundVolume = "🔊:0%"
// TODO: Проставить дефолтные значения для глобальных переменных модулей.
Conf.Values.BatteryString = "<span color='" + Conf.Battery.Color +
"' background='" + Conf.Battery.Background +
"' font='" + Conf.Battery.SymbolFont +
@@ -179,24 +178,11 @@ func main() {
// TODO: впилить для первого и последнего батона separator
if Conf.AppButtons.Enabled {
for _, app := range Conf.Apps {
var (
b lib.I3BarOutBlock
sb strings.Builder
)
var b lib.I3BarOutBlock
sb.WriteString("<span color='")
sb.WriteString(app.Color)
sb.WriteString("' background='")
sb.WriteString(app.Background)
sb.WriteString("' font='")
sb.WriteString(app.Font)
sb.WriteString("' size='")
sb.WriteString(app.FontSize)
sb.WriteString("'>")
sb.WriteString(app.FullText)
sb.WriteString("</span>")
b.FullText = sb.String()
b.FullText = app.FullText
b.Background = app.Background
b.Color = app.Color
b.Instance = app.Instance
b.Markup = `pango`
b.Separator = app.Separator
@@ -680,3 +666,5 @@ func PrintToI3bar(c *lib.MyConfig) {
fmt.Println(strings.TrimSuffix(buf.String(), "\n") + ",")
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -57,11 +57,12 @@ func (c *MyConfig) UpdateBatteryInfo() {
continue
}
myCharge = strings.Trim(string(b), "\n")
myCharge = strings.TrimSpace(string(b))
statusFile := filepath.Dir(file) + "/status"
// If file exists.
_, err = os.Stat(statusFile)
if !os.IsNotExist(err) {
b, err := os.ReadFile(statusFile)
@@ -71,7 +72,7 @@ func (c *MyConfig) UpdateBatteryInfo() {
continue
}
myStatus = strings.Trim(string(b), "\n")
myStatus = strings.TrimSpace(string(b))
} else {
continue
}
@@ -104,9 +105,13 @@ func (c *MyConfig) UpdateBatteryInfo() {
}
}
} else {
// In theory, this module should give for each entry its separate err, but in practice it gives
// one single err for all entries, so we cannot detemine whist exatly entry errored.
batteries, _ = battery.GetAll()
var err error
batteries, err = battery.GetAll()
if err != nil {
log.Printf("Unable to get battery info: %s", err)
}
}
var (
@@ -254,3 +259,5 @@ func (c *MyConfig) UpdateBatteryInfo() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -35,3 +35,5 @@ func (c *MyConfig) UpdateClock() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -4,24 +4,19 @@ import (
"sync"
)
type (
// Collection is sync.Map, that stores arbitrary data.
Collection struct {
close chan struct{}
items sync.Map
}
// Collection is sync.Map, that stores arbitrary data.
type Collection struct {
items sync.Map
}
// Item just interface for stoing any data.
item struct {
data any
}
)
// Item just interface for stoing any data.
type item struct {
data any
}
// NewCollection creates new instance of "collection".
func NewCollection() *Collection {
c := &Collection{ //nolint:exhaustruct
close: make(chan struct{}),
}
c := &Collection{}
return c
}
@@ -34,9 +29,12 @@ func (collection *Collection) Get(key any) (any, bool) {
return nil, false
}
item := obj.(item)
it, ok := obj.(item)
if !ok {
return nil, false
}
return item.data, true
return it.data, true
}
// Set saves value to given "collection".
@@ -49,9 +47,12 @@ func (collection *Collection) Set(key any, value any) {
// Range apply function f to all keys/values in collection. Returns immediately on first error.
func (collection *Collection) Range(f func(key, value any) bool) {
fn := func(key, value any) bool {
item := value.(item)
it, ok := value.(item)
if !ok {
return true
}
return f(key, item.data)
return f(key, it.data)
}
collection.items.Range(fn)
@@ -64,8 +65,6 @@ func (collection *Collection) Delete(key any) {
// Close frees memory occupied by collection and deletes collection itself.
func (collection *Collection) Close() {
collection.close <- struct{}{}
collection.items = sync.Map{}
}

View File

@@ -2063,3 +2063,5 @@ func LocateConfFile() (string, error) {
return filepath.Dir(i3ConfigPath) + "/i3status-go.json", nil
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -23,6 +23,7 @@ func (c *MyConfig) UpdateCPUTemperature() {
temperature = make([]int64, n)
tSum int64
tAvg int64
validCount int
)
if Delay == InitialDelay {
@@ -35,48 +36,50 @@ func (c *MyConfig) UpdateCPUTemperature() {
if err != nil {
log.Printf("Unable to open %s: %s", filename, err)
} else {
reader := bufio.NewReader(file)
line, _, err := reader.ReadLine()
if err != nil {
log.Printf("Unable to read from %s: %s", filename, err)
err = file.Close()
if err != nil {
log.Printf("Unable to close %s: %s", filename, err)
}
} else {
err = file.Close()
if err != nil {
log.Printf("Unable to close %s: %s", filename, err)
} else {
temp, err := strconv.ParseInt(string(line), 10, 32)
if err != nil {
log.Printf("Unable to convert string to number from file %s: %s", filename, err)
} else {
if temp > 1000 {
temp /= 1000
}
temperature[i] = temp
}
}
}
continue
}
defer file.Close()
reader := bufio.NewReader(file)
line, _, err := reader.ReadLine()
if err != nil {
log.Printf("Unable to read from %s: %s", filename, err)
continue
}
temp, err := strconv.ParseInt(string(line), 10, 32)
if err != nil {
log.Printf("Unable to convert string to number from file %s: %s", filename, err)
continue
}
if temp > 1000 {
temp /= 1000
}
temperature[i] = temp
validCount++
}
if len(temperature) == 1 {
tAvg = temperature[0]
if validCount == 0 {
continue
}
if validCount == 1 {
for _, t := range temperature {
if t != 0 {
tAvg = t
break
}
}
} else {
for _, t := range temperature {
tSum += t
}
tAvg = tSum / int64(len(temperature))
tAvg = tSum / int64(validCount)
}
if c.Values.CPUTemperature != tAvg {
@@ -85,3 +88,5 @@ func (c *MyConfig) UpdateCPUTemperature() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -27,14 +27,11 @@ func (c MyConfig) RunCron() {
_, err := myCron.NewJob(
gocron.CronJob(job.Time, false),
gocron.NewTask(
// Сука, просто заспавнить бинарь, ну почему это надо делать через жопу-то?
any(
func() bool {
c.Channels.RunChan <- job.Cmd
func() bool {
c.Channels.RunChan <- job.Cmd
return true
},
),
return true
},
),
)
@@ -45,3 +42,5 @@ func (c MyConfig) RunCron() {
myCron.Start()
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -19,15 +19,18 @@ var WL WinList
// UpdateI3WinList subsribes to all window-related events and send them to I3EventParser() for more detailed parsing.
func (c *MyConfig) UpdateI3WinList() {
// TODO: Здесь получить начальный список окон, управляемых i3wm (через GetTree()) и сложить их атрибутику в переменную WL
Tree, err := i3.GetTree()
if err != nil {
log.Printf("Unable to get list of windows: %s", err)
return
}
c.ExtractProps(Tree.Root)
c.Channels.UpdateReady <- true
i3wm := i3.Subscribe(i3.WindowEventType)
for i3wm.Next() {
@@ -106,8 +109,10 @@ func (c *MyConfig) I3EventParser(e *i3.WindowEvent) {
// interfaceToUint превращает данный интерфейс в Uint64.
// Если может, конечно :) .
func interfaceToUint64(iface any) uint64 {
// А теперь мы начинаем дурдом, нам надо превратить ёбанный any в []string.
// Поскольку any может быть чем угодно, перестрахуемся.
if iface == nil {
return 0
}
if reflect.TypeOf(iface).Kind() == reflect.Uint64 {
return reflect.ValueOf(iface).Uint()
}
@@ -152,18 +157,16 @@ func HasWindows(className string, instanceName string) bool {
func FindWindowInstance(regexpStr string) bool {
var found = false
re, err := regexp.Compile(regexpStr)
if err != nil {
log.Printf("Unable to perform regexp match: %s", err)
return false
}
f := func(key any, value any) bool { //nolint: revive
keyStr := reflect.ValueOf(key).String()
re, err := regexp.Compile(regexpStr) // we do not want shit our pants and panic on
// erroneous regexp, we want log it to stderr, that it.
if err != nil {
log.Printf("Unable to perform regexp match: %s", err)
return true
}
if re.Match([]byte(keyStr)) { //nolint:mirror
found = true
@@ -183,18 +186,16 @@ func FindWindowInstance(regexpStr string) bool {
func FindWindowClass(regexpStr string) bool {
var found = false
re, err := regexp.Compile(regexpStr)
if err != nil {
log.Printf("Unable to perform regexp match: %s", err)
return false
}
f := func(key any, value any) bool { //nolint: revive
keyStr := reflect.ValueOf(key).String()
re, err := regexp.Compile(regexpStr) // we do not want shit our pants and panic on
// erroneous regexp, we want log it to stderr, that it.
if err != nil {
log.Printf("Unable to perform regexp match: %s", err)
return true
}
if re.Match([]byte(keyStr)) { //nolint:mirror
found = true
@@ -225,3 +226,5 @@ func (c *MyConfig) ExtractProps(n *i3.Node) {
c.ExtractProps(node)
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -44,8 +44,8 @@ func (c *MyConfig) UpdateIfStatus() {
} else {
switch strings.TrimSpace(string(operstate)) {
case "up":
StatusStr := fmt.Sprintf("<span foreground=\"%s\">⍋</span>", c.NetIf.UpColor)
operstate = []byte(StatusStr)
statusStr := fmt.Sprintf("<span foreground=\"%s\">⍋</span>", c.NetIf.UpColor)
operstate = []byte(statusStr)
case "down":
statusStr := fmt.Sprintf("<span foreground=\"%s\">⍒</span>", c.NetIf.DownColor)
operstate = []byte(statusStr)
@@ -67,3 +67,5 @@ func (c *MyConfig) UpdateIfStatus() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -2,6 +2,7 @@ package lib
import (
"fmt"
"log"
"time"
"github.com/shirou/gopsutil/load"
@@ -22,7 +23,13 @@ func (c *MyConfig) UpdateLaStats() {
ticker.Reset(Delay)
}
l, _ := load.Avg()
l, err := load.Avg()
if err != nil {
log.Printf("Unable to get LA stats: %s", err)
continue
}
lav := fmt.Sprintf("%.2f", l.Load1)
@@ -32,3 +39,5 @@ func (c *MyConfig) UpdateLaStats() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -38,17 +38,17 @@ func (c *MyConfig) UpdateMemStats() {
continue
}
sw, err := mem.SwapMemory()
if err != nil {
log.Printf("Unable to get swap statistics: %s", err)
time.Sleep(1 * time.Second)
continue
}
if c.Mem.ShowSwap {
if c.Values.Memory.Usedpct != uint64(v.UsedPercent) || c.Values.Memory.Shared != v.Shared/1024/1024 || c.Values.Memory.Swap != v.SwapTotal-v.SwapFree {
sw, err := mem.SwapMemory()
if err != nil {
log.Printf("Unable to get swap statistics: %s", err)
time.Sleep(1 * time.Second)
continue
}
if c.Values.Memory.Usedpct != uint64(v.UsedPercent) || c.Values.Memory.Shared != v.Shared/1024/1024 || c.Values.Memory.Swap != sw.Used/1024/1024 {
c.Values.Memory.Usedpct = uint64(v.UsedPercent)
c.Values.Memory.Shared = v.Shared / 1024 / 1024
c.Values.Memory.Swap = sw.Used / 1024 / 1024
@@ -63,3 +63,5 @@ func (c *MyConfig) UpdateMemStats() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -222,3 +222,5 @@ func (c *MyConfig) SVPAHandler() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -31,7 +31,7 @@ func (c *MyConfig) RunCommand() {
outputString += RunProcess(command)
log.Println(c.Values.RunCommandOutput)
log.Println(outputString)
if c.Values.RunCommandOutput != outputString {
c.Values.RunCommandOutput = outputString
@@ -39,3 +39,5 @@ func (c *MyConfig) RunCommand() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -6,8 +6,6 @@ import (
"syscall"
)
var PrintOutput = true
// SigHandler OS signal handler.
func (c *MyConfig) SigHandler() {
for s := range c.Channels.SigChan {
@@ -33,3 +31,5 @@ func (c *MyConfig) SigHandler() {
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -13,10 +13,21 @@ import (
// Spawner forks and execs given program, and also detaches form its control tty.
func (c *MyConfig) Spawner() {
for prg := range c.Channels.RunChan {
<-time.After(25 * time.Millisecond)
time.Sleep(25 * time.Millisecond)
devnullR, _ := os.Open(os.DevNull)
devnullW, _ := os.OpenFile(os.DevNull, os.O_WRONLY|os.O_APPEND, 0644)
devnullR, err := os.Open(os.DevNull)
if err != nil {
log.Printf("Unable to open /dev/null for reading: %s", err)
continue
}
defer devnullR.Close()
devnullW, err := os.OpenFile(os.DevNull, os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Printf("Unable to open /dev/null for writing: %s", err)
continue
}
defer devnullW.Close()
spa := syscall.SysProcAttr{
Setsid: true,
@@ -34,6 +45,7 @@ func (c *MyConfig) Spawner() {
if err != nil {
log.Printf("Unable to spawn %s: %s", prg[0], err)
continue
}
_, err = syscall.ForkExec(bin, prg, &pa)
@@ -41,10 +53,6 @@ func (c *MyConfig) Spawner() {
if err != nil {
log.Printf("Unable to spawn %s: %s", prg[0], err)
}
// Close file errors we don't handle :) at all.
_ = devnullR.Close()
_ = devnullW.Close()
}
}
@@ -53,7 +61,11 @@ func (c *MyConfig) CleanZombies() {
r := syscall.Rusage{}
for {
_, _ = syscall.Wait4(-1, nil, 0, &r)
pid, _ := syscall.Wait4(-1, nil, syscall.WNOHANG, &r)
if pid > 0 {
continue
}
time.Sleep(1 * time.Minute)
}
@@ -96,3 +108,5 @@ func RunProcess(command []string) string {
return strings.TrimRight(stdout.String(), "\n")
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -51,17 +51,11 @@ func (c *MyConfig) ParseStdin() {
continue
}
for i := 0; i < len(buf); i++ {
if strings.HasPrefix(buf, "{") {
break
}
buf = buf[1:]
}
if len(buf) == 0 {
log.Print("String from stdin has zero length, skipping")
buf = strings.TrimLeftFunc(buf, func(r rune) bool {
return r != '{'
})
if !strings.HasPrefix(buf, "{") {
continue
}
@@ -123,12 +117,17 @@ func (c *MyConfig) ParseStdin() {
c.Channels.RunChan <- prg
break
case app.Instance != "" && e.Instance == app.Instance:
prg := append([]string{}, app.Cmd)
prg = append(prg, app.Args...)
c.Channels.RunChan <- prg
break
}
}
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */

View File

@@ -112,3 +112,5 @@ func (c *MyConfig) VPNFileCheck() bool {
return true
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */