Files
i3status-go/internal/lib/i3.go

231 lines
4.8 KiB
Go

package lib
import (
"log"
"reflect"
"regexp"
"go.i3wm.org/i3"
)
type WinList struct {
Instance Collection
Class Collection
}
// WL contains collections of all windows currently running, collection of their classes and collection of their
// instances.
var WL WinList
// UpdateI3WinList subsribes to all window-related events and send them to I3EventParser() for more detailed parsing.
func (c *MyConfig) UpdateI3WinList() {
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() {
e := i3wm.Event().(*i3.WindowEvent)
go c.I3EventParser(e)
}
log.Fatal(i3wm.Close())
}
// I3EventParser parses i3wm events and updates WL (aka Windows List).
func (c *MyConfig) I3EventParser(e *i3.WindowEvent) {
switch e.Change {
case "new":
var (
count uint64
)
countI, exist := WL.Class.Get(e.Container.WindowProperties.Class)
if exist {
count = interfaceToUint64(countI)
} else {
count = 0
}
count++
WL.Class.Set(e.Container.WindowProperties.Class, count)
countI, exist = WL.Instance.Get(e.Container.WindowProperties.Instance)
if exist {
count = interfaceToUint64(countI)
} else {
count = 0
}
count++
WL.Instance.Set(e.Container.WindowProperties.Instance, count)
c.Channels.UpdateReady <- true
case "close":
countI, exist := WL.Class.Get(e.Container.WindowProperties.Class)
if exist {
count := interfaceToUint64(countI)
if count > 1 {
count--
WL.Class.Set(e.Container.WindowProperties.Class, count)
} else {
WL.Class.Delete(e.Container.WindowProperties.Class)
}
}
countI, exist = WL.Instance.Get(e.Container.WindowProperties.Instance)
if exist {
count := interfaceToUint64(countI)
if count > 1 {
count--
WL.Instance.Set(e.Container.WindowProperties.Instance, count)
} else {
WL.Instance.Delete(e.Container.WindowProperties.Instance)
}
}
c.Channels.UpdateReady <- true
}
}
// interfaceToUint превращает данный интерфейс в Uint64.
// Если может, конечно :) .
func interfaceToUint64(iface interface{}) uint64 {
if iface == nil {
return 0
}
if reflect.TypeOf(iface).Kind() == reflect.Uint64 {
return reflect.ValueOf(iface).Uint()
}
return 0
}
// HasWindows returns true if given Window Class and/or Instance has more than 0 windows according our observations.
func HasWindows(className string, instanceName string) bool {
var (
ret bool
myclass bool
myinstance bool
)
switch {
case className != "" && instanceName != "":
// Since we agree that we use regexp for searching window class and instance, but not yet decide what to do
// with exact match, comment it out and leave it here.
/*
_, myclass = WL.Class.Get(className)
_, myinstance = WL.Instance.Get(instanceName)
*/
myclass = FindWindowClass(className)
myinstance = FindWindowInstance(instanceName)
ret = myclass && myinstance
case className != "":
// _, ret = WL.Class.Get(className)
ret = FindWindowClass(className)
case instanceName != "":
// _, ret = WL.Instance.Get(instanceName)
ret = FindWindowInstance(instanceName)
}
return ret
}
// FindWindowInstance returns true if given regex matces at least one pattern in WL.Class collection.
// Otherwise, returns false.
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()
if re.Match([]byte(keyStr)) { //nolint:mirror
found = true
return false
}
return true
}
WL.Instance.Range(f)
return found
}
// FindWindowClass returns true if given regex matces at least one pattern in WL.Class collection.
// Otherwise, returns false.
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()
if re.Match([]byte(keyStr)) { //nolint:mirror
found = true
return false
}
return true
}
WL.Class.Range(f)
return found
}
// ExtractProps fills WL stricture collections with instance name and class name of given node (x11, that is).
func (c *MyConfig) ExtractProps(n *i3.Node) {
var e i3.WindowEvent
e.Change = "new"
e.Container = *n
c.I3EventParser(&e)
for _, node := range n.Nodes {
c.ExtractProps(node)
}
for _, node := range n.FloatingNodes {
c.ExtractProps(node)
}
}
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */