jezek / xgb

The X Go Binding is a low-level API to communicate with the X server. It is modeled on XCB and supports many X extensions.
Other
130 stars 13 forks source link

Trying to get active window PID returns crap #11

Closed 4164696f73 closed 1 year ago

4164696f73 commented 1 year ago

I've been trying to get the active window PID, but all I am getting is a random string. First it was "]", then "", then "h" - it changes with different windows (basically, different PID = different symbol returned).

I assume I'm missing something, but I can't find anything that would help me to get the PID.

Here's the code (basically what is in examples, but changed "_NET_WM_NAME" to "_NET_WM_PID).

// Example get-active-window reads the _NET_ACTIVE_WINDOW property of the root
// window and uses the result (a window id) to get the name of the window.
package main

import (
    "fmt"
    "log"

    "github.com/jezek/xgb"
    "github.com/jezek/xgb/xproto"
)

func main() {
    X, err := xgb.NewConn()
    if err != nil {
        log.Fatal(err)
    }

    // Get the window id of the root window.
    setup := xproto.Setup(X)
    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 {
        log.Fatal(err)
    }

    // Get the atom id (i.e., intern an atom) of "_NET_WM_NAME".
    aname = "_NET_WM_PID"
    nameAtom, err := xproto.InternAtom(X, true, uint16(len(aname)),
        aname).Reply()
    if err != nil {
        log.Fatal(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(X, false, root, activeAtom.Atom,
        xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
    if err != nil {
        log.Fatal(err)
    }
    windowId := xproto.Window(xgb.Get32(reply.Value))
    fmt.Printf("Active window id: %X\n", windowId)

    // 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(X, false, windowId, nameAtom.Atom,
        xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Active window pid: %s\n", string(reply.Value))
}
jezek commented 1 year ago

Hello. I tried your example and my test didn't work, but because in my case the returned window id was 0. I'm pretty sure it's because I tested it on wayland. I have to test it on X11, but it'll take some time (I have to take care of the PR #10.

In meantime you can test, if the PR #10 doesn't fix your issue.

And if not, you can try to check how this kind of example behaves if written in C with xcb (you can take inspiration from test code I wrote when we tried to debug issue #6). I think, I read somewhere, that the filling of _NET_WM_PID is not mandatory for a X11 program and it may be the case, that the active window has no PID.

aarzilli commented 1 year ago

_NET_WM_PID is a uint32

jezek commented 1 year ago

@4164696f73 as @aarzilli and specification says, the _NET_WM_PID is CARDINAL/32 which translates to uint32 in go language. So the last print of your example should be fmt.Printf("Active window pid: %d\n", uint32(reply.Value)).

Does this fix your issue? Can we close this?

jezek commented 1 year ago

@4164696f73 No answer, I assume everything is alright. Closing.

4164696f73 commented 1 year ago

I apologize for the delay. I managed to check your suggestions (finally!) and it ended up working with: fmt.Printf("Active window pid: %d\n", binary.LittleEndian.Uint32(reply.Value)) I couldn't use just uint32(reply.Value) because you can't convert bytes like that, and binary.LittleEndian.Uint32(reply.Value) did the trick.

Sorry again for being absent this long.