2022-06-09 08:35:02 -04:00
|
|
|
//go:build !windows && !darwin
|
|
|
|
|
2022-06-28 09:25:19 -04:00
|
|
|
package zencmd
|
2022-06-09 08:35:02 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2022-11-14 08:11:14 -05:00
|
|
|
"math"
|
2022-06-09 08:35:02 -04:00
|
|
|
"os/exec"
|
|
|
|
"strconv"
|
2022-06-16 20:55:02 -04:00
|
|
|
"strings"
|
2022-06-09 08:35:02 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// ParseWindowId is internal.
|
|
|
|
func ParseWindowId(id string) int {
|
|
|
|
wid, _ := strconv.ParseUint(id, 0, 64)
|
2022-11-14 08:11:14 -05:00
|
|
|
return int(wid & math.MaxInt)
|
2022-06-09 08:35:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetParentWindowId is internal.
|
2022-06-16 20:55:02 -04:00
|
|
|
func GetParentWindowId(pid int) int {
|
|
|
|
winids, err := getPidToWindowMap()
|
2022-06-09 08:35:02 -04:00
|
|
|
if err != nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2022-06-16 20:55:02 -04:00
|
|
|
ppids, err := getPidToPpidMap()
|
|
|
|
if err != nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
if winid, ok := winids[pid]; ok {
|
|
|
|
id, _ := strconv.Atoi(winid)
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
if ppid, ok := ppids[pid]; ok {
|
|
|
|
pid = ppid
|
|
|
|
} else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getPidToPpidMap() (map[int]int, error) {
|
|
|
|
out, err := exec.Command("ps", "-xo", "pid=,ppid=").Output()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-06-09 08:35:02 -04:00
|
|
|
ppids := map[int]int{}
|
2022-06-16 20:55:02 -04:00
|
|
|
reader := bytes.NewReader(out)
|
2022-06-09 08:35:02 -04:00
|
|
|
for {
|
|
|
|
var pid, ppid int
|
|
|
|
_, err := fmt.Fscan(reader, &pid, &ppid)
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil {
|
2022-06-16 20:55:02 -04:00
|
|
|
return nil, err
|
2022-06-09 08:35:02 -04:00
|
|
|
}
|
|
|
|
ppids[pid] = ppid
|
|
|
|
}
|
2022-06-16 20:55:02 -04:00
|
|
|
return ppids, nil
|
|
|
|
}
|
2022-06-09 08:35:02 -04:00
|
|
|
|
2022-06-16 20:55:02 -04:00
|
|
|
func getPidToWindowMap() (map[int]string, error) {
|
|
|
|
ids, err := getWindowIDs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var pid int
|
|
|
|
winids := map[int]string{}
|
|
|
|
for _, id := range ids {
|
|
|
|
pid, err = getWindowPid(id)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
winids[pid] = id
|
|
|
|
}
|
|
|
|
if err != nil && len(winids) == 0 {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return winids, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getWindowIDs() ([]string, error) {
|
|
|
|
out, err := exec.Command("xprop", "-root", "0i", "\t$0+", "_NET_CLIENT_LIST").Output()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-01-10 09:26:14 -05:00
|
|
|
if _, out, cut := bytes.Cut(out, []byte("\t")); cut {
|
|
|
|
return strings.Split(string(out), ", "), nil
|
2022-06-16 20:55:02 -04:00
|
|
|
}
|
2023-01-10 09:26:14 -05:00
|
|
|
return nil, fmt.Errorf("xprop: unexpected output: %q", out)
|
2022-06-16 20:55:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func getWindowPid(id string) (int, error) {
|
|
|
|
out, err := exec.Command("xprop", "-id", id, "0i", "\t$0", "_NET_WM_PID").Output()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2023-01-10 09:26:14 -05:00
|
|
|
if _, out, cut := bytes.Cut(out, []byte("\t")); cut {
|
|
|
|
return strconv.Atoi(string(out))
|
2022-06-16 20:55:02 -04:00
|
|
|
}
|
2023-01-10 09:26:14 -05:00
|
|
|
return 0, fmt.Errorf("xprop: unexpected output: %q", out)
|
2022-06-09 08:35:02 -04:00
|
|
|
}
|