tych0 / xcffib

A drop-in replacement for xpyb based on cffi
Apache License 2.0
94 stars 26 forks source link

Fix `List.to_string()` #151

Closed OrichalcumCosmonaut closed 1 year ago

OrichalcumCosmonaut commented 1 year ago

Given this program as an example:

import xcffib
import xcffib.randr
import xcffib.xproto

xconn = xcffib.Connection()
xrandr = xconn(xcffib.randr.key)
try:
    setup = xconn.get_setup()
    for screen in setup.roots:
        scrn_rsrcs = xrandr.GetScreenResources(screen.root).reply()
        for output in scrn_rsrcs.outputs:
            info = xrandr.GetOutputInfo(output, xcffib.XCB_CURRENT_TIME).reply()
            print(info.name.to_string())
finally:
    xconn.disconnect()

A TypeError: 'int' object is not subscriptable error is thrown. With this patch it prints the name of each output as expected:

eDP-1
DP-1
DP-2
DP-3
DP-4
tych0 commented 1 year ago

Huh, I was just looking through this code myself and noticed it was broken. Qtile also calls it in several places though, so I was wondering if somehow I was wrong. But two people can't be wrong, right? Maybe it is just unused in qtile.

tych0 commented 1 year ago

Anyway, thanks for the patch.

tych0 commented 1 year ago

The CI failure is unrelated and just happened on the master CI cron run as well. I'll look into it now.

tych0 commented 1 year ago

Ok, I rebased your branch and added a patch to turn your example into a test case.

It looks like this does break things, and has to do with how things are specified in the xml. One that works is the response from GetAtomName(), which is specified as type=char, whereas the output from GetOutputInfo() is specified as type=BYTE. I'd happily take a patch that changed to_string() to handle both of these cases; feel free to force push to this branch.

OrichalcumCosmonaut commented 1 year ago

Huh, weird. I ended up switching to using XCB directly from Zig because it was a bit hard to find info on how to actually use xpyb, but I might be able to try doing something like that (with the caveat that I don’t usually program in Python).

All I’m trying to make is a little daemon which responds to RandR outputs getting (dis)connected and configuring them appropriately, and I figure interacting with the X server directly instead of shelling out to xrandr makes sense for a daemon. And maybe later a WM, because https://github.com/paperwm/PaperWM looks sorta interesting but I don’t really feel like using GNOME; although in that case i could probably use one of those programmable WMs like xmonad, assuming they’re flexible enough for something like that.

I suppose something naïve like this might work, no idea whether it’s “Pythonic” though:

def to_string(self):
    try:
        return ''.join(chr(i[0]) for i in self)
    except TypeError:
        return ''.join(chr(i) for i in self)
tych0 commented 1 year ago

Looks fine to me, can you update this PR?

tych0 commented 1 year ago

Thanks! I squashed it all into your original commit, and will merge once CI passes.

In terms of implementing xrandr in python, I've been looking at doing roughly the same thing recently, to deal with DPI scaling for a monitor I bought. Here's what I have so far, maybe it will be useful for you: https://gist.github.com/tych0/612788b0556e57ed9bc09983bca1387c My general workflow has been to xtrace xrandr <args> and then try to reproduce the requests (you can see the output of xtrace at the bottom of the gist there).

At some point I plan to integrate this into qtile proper when I actually have it working.