231 lines
4.7 KiB
Go
231 lines
4.7 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 any) 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: */
|