diff --git a/cmd/zenity/main.go b/cmd/zenity/main.go index d61d36b..60473ad 100644 --- a/cmd/zenity/main.go +++ b/cmd/zenity/main.go @@ -452,10 +452,13 @@ func loadFlags() []zenity.Option { if attach != "" { opts = append(opts, zenity.Attach(zenutil.ParseWindowId(attach))) } else if modal { - if pid := zenutil.GetParentWindowId(); pid != 0 { - opts = append(opts, zenity.Attach(pid)) + if id := zenutil.GetParentWindowId(os.Getppid()); id != 0 { + opts = append(opts, zenity.Attach(id)) } } + if modal { + opts = append(opts, zenity.Modal()) + } // Message options diff --git a/internal/zenutil/window_darwin.go b/internal/zenutil/window_darwin.go index 51c74df..acdbcee 100644 --- a/internal/zenutil/window_darwin.go +++ b/internal/zenutil/window_darwin.go @@ -1,7 +1,6 @@ package zenutil import ( - "os" "strconv" "golang.org/x/sys/unix" @@ -16,8 +15,7 @@ func ParseWindowId(id string) any { } // GetParentWindowId is internal. -func GetParentWindowId() int { - pid := os.Getppid() +func GetParentWindowId(pid int) int { for { kinfo, err := unix.SysctlKinfoProc("kern.proc.pid", pid) if err != nil { diff --git a/internal/zenutil/window_unix.go b/internal/zenutil/window_unix.go index d1598da..a95cd3c 100644 --- a/internal/zenutil/window_unix.go +++ b/internal/zenutil/window_unix.go @@ -8,6 +8,7 @@ import ( "io" "os/exec" "strconv" + "strings" ) // ParseWindowId is internal. @@ -17,14 +18,38 @@ func ParseWindowId(id string) int { } // GetParentWindowId is internal. -func GetParentWindowId() int { - buf, err := exec.Command("ps", "-xo", "pid=,ppid=").CombinedOutput() +func GetParentWindowId(pid int) int { + winids, err := getPidToWindowMap() if err != nil { return 0 } + 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 + } + ppids := map[int]int{} - reader := bytes.NewReader(buf) + reader := bytes.NewReader(out) for { var pid, ppid int _, err := fmt.Fscan(reader, &pid, &ppid) @@ -32,11 +57,58 @@ func GetParentWindowId() int { break } if err != nil { - return 0 + return nil, err } ppids[pid] = ppid } - - // Find the relevant pid and window id. - return 0 + return ppids, nil +} + +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 + } + + if i := bytes.IndexByte(out, '\t'); i < 0 { + return nil, fmt.Errorf("xprop: unexpected output: %q", out) + } else { + out = out[i+1:] + } + return strings.Split(string(out), ", "), nil +} + +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 + } + + if i := bytes.IndexByte(out, '\t'); i < 0 { + return 0, fmt.Errorf("xprop: unexpected output: %q", out) + } else { + out = out[i+1:] + } + return strconv.Atoi(string(out)) } diff --git a/internal/zenutil/window_windows.go b/internal/zenutil/window_windows.go index f661732..14eef03 100644 --- a/internal/zenutil/window_windows.go +++ b/internal/zenutil/window_windows.go @@ -9,6 +9,6 @@ func ParseWindowId(id string) uintptr { } // GetParentWindowId is internal. -func GetParentWindowId() int { +func GetParentWindowId(pid int) int { return 0 }