-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfocused_unix.go
85 lines (73 loc) · 2.49 KB
/
focused_unix.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//+build !windows
package main
import (
"fmt"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
"strings"
)
func isRipcordFocused() (bool, error) {
if wmClasses, err := activeWindowClasses(); err != nil {
return false, err
} else {
for _, wmClass := range wmClasses {
if wmClass == "Ripcord" {
return true, nil
}
}
}
return false, nil
}
// reuse connection
var xConn *xgb.Conn
// adapted from https://github.com/BurntSushi/xgb/blob/master/examples/get-active-window/main.go
func activeWindowClasses() ([]string, error) {
if xConn == nil {
var err error
xConn, err = xgb.NewConn()
if err != nil {
return nil, fmt.Errorf("couldn't connect to X: %w", err)
}
}
// Get the window id of the root window.
setup := xproto.Setup(xConn)
root := setup.DefaultScreen(xConn).Root
// Get the atom id (i.e., intern an atom) of "_NET_ACTIVE_WINDOW".
aname := "_NET_ACTIVE_WINDOW"
activeAtom, err := xproto.InternAtom(xConn, true, uint16(len(aname)),
aname).Reply()
if err != nil {
return nil, fmt.Errorf("couldn't get atom of %s: %w", aname, err)
}
// Get the atom id (i.e., intern an atom) of "_NET_WM_NAME".
aname = "WM_CLASS"
classAtom, err := xproto.InternAtom(xConn, true, uint16(len(aname)),
aname).Reply()
if err != nil {
return nil, fmt.Errorf("couldn't get atom of %s: %w", aname, err)
}
// Get the actual value of _NET_ACTIVE_WINDOW.
// Note that 'reply.Value' is just a slice of bytes, so we use an
// XGB helper function, 'Get32', to pull an unsigned 32-bit integer out
// of the byte slice. We then convert it to an X resource id so it can
// be used to get the name of the window in the next GetProperty request.
reply, err := xproto.GetProperty(xConn, false, root, activeAtom.Atom,
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
if err != nil {
return nil, fmt.Errorf("couldn't get active window: %w", err)
}
windowId := xproto.Window(xgb.Get32(reply.Value))
// Now get the value of _NET_WM_NAME for the active window.
// Note that this time, we simply convert the resulting byte slice,
// reply.Value, to a string.
reply, err = xproto.GetProperty(xConn, false, windowId, classAtom.Atom,
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
if err != nil {
return nil, fmt.Errorf("couldn't get active window class: %w", err)
}
// extract null terminated strings from byte buffer
classes := strings.Split(string(reply.Value), "\000")
// last one is empty because the buffer ends with null
classes = classes[:len(classes)-1]
return classes, nil
}