2017-07-14 06:41:48 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/BurntSushi/xgb"
|
|
|
|
"github.com/BurntSushi/xgb/xproto"
|
2017-07-15 03:42:56 +00:00
|
|
|
"github.com/BurntSushi/xgb/xtest"
|
2017-07-14 06:41:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type watcher struct {
|
|
|
|
conn *xgb.Conn
|
|
|
|
root xproto.Window
|
|
|
|
activeAtom, nameAtom xproto.Atom
|
|
|
|
prevWindowName string
|
2017-07-15 03:42:56 +00:00
|
|
|
lastWindowID xproto.Window
|
2017-07-14 06:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewWindowWatcher() *watcher {
|
|
|
|
return &watcher{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *watcher) Setup() error {
|
|
|
|
X, err := xgb.NewConn()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the window id of the root window.
|
|
|
|
setup := xproto.Setup(X)
|
|
|
|
|
2017-07-15 03:42:56 +00:00
|
|
|
if err := xtest.Init(X); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-07-14 06:41:48 +00:00
|
|
|
w.conn = X
|
|
|
|
w.root = setup.DefaultScreen(X).Root
|
|
|
|
|
|
|
|
// Get the atom id (i.e., intern an atom) of "_NET_ACTIVE_WINDOW".
|
|
|
|
aname := "_NET_ACTIVE_WINDOW"
|
|
|
|
activeAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
|
|
|
|
aname).Reply()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Couldn't get _NET_ACTIVE_WINDOW atom: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the atom id (i.e., intern an atom) of "_NET_WM_NAME".
|
|
|
|
aname = "_NET_WM_NAME"
|
|
|
|
nameAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
|
|
|
|
aname).Reply()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Couldn't get _NET_WM_NAME atom: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.activeAtom = activeAtom.Atom
|
|
|
|
w.nameAtom = nameAtom.Atom
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *watcher) Run() {
|
|
|
|
for {
|
|
|
|
w.watch()
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *watcher) watch() {
|
|
|
|
// From github.com/BurntSushi/xgb's examples.
|
|
|
|
reply, err := xproto.GetProperty(w.conn, false, w.root, w.activeAtom,
|
|
|
|
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
windowID := xproto.Window(xgb.Get32(reply.Value))
|
|
|
|
|
|
|
|
reply, err = xproto.GetProperty(w.conn, false, windowID, w.nameAtom,
|
|
|
|
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-07-15 03:42:56 +00:00
|
|
|
w.lastWindowID = windowID
|
|
|
|
|
2017-07-14 06:41:48 +00:00
|
|
|
windowName := string(reply.Value)
|
|
|
|
if w.prevWindowName != windowName {
|
|
|
|
w.prevWindowName = windowName
|
|
|
|
|
|
|
|
w.loadWindowConfiguration(windowName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *watcher) loadWindowConfiguration(windowName string) {
|
|
|
|
if loadedConfiguration == nil {
|
|
|
|
fmt.Println("Window name switched, but no configuration:", windowName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, conf := range loadedConfiguration.Apps {
|
|
|
|
for _, re := range conf.windowTitleRegexps {
|
2019-04-08 05:32:52 +00:00
|
|
|
if *debugMode {
|
|
|
|
fmt.Println("Testing title:", windowName)
|
|
|
|
}
|
2017-07-14 06:41:48 +00:00
|
|
|
if re.MatchString(windowName) {
|
2019-04-08 05:32:52 +00:00
|
|
|
fmt.Printf("Switching configuration for app %q\n", conf.Name)
|
2017-07-14 06:41:48 +00:00
|
|
|
currentConfiguration = conf
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-08 05:32:52 +00:00
|
|
|
|
|
|
|
if !*debugMode {
|
|
|
|
currentConfiguration = nil
|
|
|
|
} else {
|
|
|
|
fmt.Println("Keeping previous config even if window changed")
|
|
|
|
}
|
2017-07-14 06:41:48 +00:00
|
|
|
}
|