Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xkb implementation seems to behave differently from its C counterpart #181

Open
acid-bong opened this issue Feb 18, 2025 · 9 comments
Open

Comments

@acid-bong
Copy link

xcffib v1.7.1

I'm trying to reimplement the event loop that https://github.com/xkbmon/xkbmon does, but can't replicate the behaviour, specifically -- the filtering of events. I'm only interested in those that signal about the keyboard layout change, and xkbmon does it like this (the comments are added by me):

    xcb_generic_event_t *event;
    while ((event = xcb_wait_for_event(connection))) {
        // filter 1: it selects the `pad0` field of `event`
        // xcffib's event doesn't have that field
        switch (event->pad0) {
            // and thus can't be identified as a StateNotify type
            // all events have type NewKeyboardNotifyEvent
            case XCB_XKB_STATE_NOTIFY: {
                xcb_xkb_state_notify_event_t *ne = (void *)event;
                // filter 2: it bitwise-compares `changed` field to a constant
                // good news: the constant is present under xcffib.xkb.StatePart.GroupState
                // bad news: the `changed` field doesn't hold any value except 0, thus events can't be filtered by it
                if (ne->changed & XCB_XKB_STATE_PART_GROUP_STATE) {
                    print_layout(connection);
                }

                break;
            }
            default:
                break;
        }
        free(event);
    }

At the moment, the only reliable way to check if the layout has changed is to check the value of event.oldMaxKeyCode, which gets the same index as the result of xkb.GetState(xcffib.xkb.ID.UseCoreKbd).reply().lockedGroup (where xkb = conn(xcffib.xkb.key)). On one hand, it might be somewhat cheaper to use that value myself to get the layout by index than to rerun GetState(), on the other -- it still differs from the C implementation.

Btw, the code I come up with (with some help from, Lord forgive me, ChatGPT (except from the event filter, I found it out myself)) is such: https://pastebin.com/F2QQiUL0

@tych0
Copy link
Owner

tych0 commented Feb 19, 2025

IIUC you're not using the API in exactly the same way (i.e. checking changed masks), which is perhaps why it's not doing the same thing. Does something like this (untested) help?

~ diff -U5 sample.py.orig sample.py
--- sample.py.orig	2025-02-19 07:15:35.736883265 -0700
+++ sample.py	2025-02-19 07:20:34.049897473 -0700
@@ -37,19 +37,16 @@
         0,
         xcffib.xkb.EventType.StateNotify,
         0,
         0,
         0,
-    )
+    ).check()

-    old_index = xkb.GetState(xcffib.xkb.ID.UseCoreKbd).reply().lockedGroup
     print_layout(conn, index)

     while event := conn.wait_for_event():
-        new_index = event.oldMaxKeyCode
-        if new_index != old_index:
+        if isinstance(event, xcffib.xkb.StateNotifyEvent) and event.changed & xcffib.xkb.StatePart.GroupState:
             print_layout(conn, index)
-            old_index = new_index


 if __name__ == "__main__":
-    main()
\ No newline at end of file
+    main()

@acid-bong
Copy link
Author

acid-bong commented Feb 19, 2025

Sadly, adding .check() to xkb.SelectEvents() makes it worse:

Traceback (most recent call last):
  File "/tmp/./xkb.py", line 69, in <module>
    main()
  File "/tmp/./xkb.py", line 44, in main
    ).check()
      ^^^^^^^
  File "/nix/store/77iryi5f198r99gk79dj5n1mw4xnb73h-python3-3.12.8-env/lib/python3.12/site-packages/xcffib/__init__.py", line 339, in check
    assert self.is_checked and self.reply_type is None, (
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: Request is not void and checked

Maybe there's a different thing that requires checking?

@acid-bong
Copy link
Author

Btw, if I don't retrieve a layout index or the symbols string, conn.wait_for_event() hangs forever and can only be killed with SIGKILL

@tych0
Copy link
Owner

tych0 commented Feb 19, 2025

Oh, sorry, you can drop the .check(), that was me being pedantic.

@tych0
Copy link
Owner

tych0 commented Feb 19, 2025

Yeah, waiting for event should only give you the events you've selected, so i think that's expected. You'll still need the SelectEvents bit

@acid-bong
Copy link
Author

Ight, sorry for the pause. The SelectEvents isn't what i'm complaining about, but the event contents: changed attr doesn't take any values other than 0, and the type isn't xcffib.xkb.StateNotifyEvent. That's where it differs from the C impl

@tych0
Copy link
Owner

tych0 commented Feb 23, 2025

What is the type?

@acid-bong
Copy link
Author

acid-bong commented Feb 23, 2025

NewKeyboardNotifyEvent (I thought i mentioned it earlier, but apparently i didn't, my bad)

@tych0
Copy link
Owner

tych0 commented Feb 23, 2025

Ok, and if you xtrace it, do you see the events you expect? I can imagine NewKeyboardNotifyEvents are sent, but perhaps in addition to StateChangeNotify?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants