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

How do I use SendExtensionEvent? #158

Open
elParaguayo opened this issue Nov 5, 2023 · 9 comments
Open

How do I use SendExtensionEvent? #158

elParaguayo opened this issue Nov 5, 2023 · 9 comments

Comments

@elParaguayo
Copy link
Contributor

I've got an open PR in qtile (qtile/qtile#4557) using PointerBarriers from xfixes and xinput. I'd like to write a test for this and have had limited success using FakeInput from xtest. I was hoping I could just send a synthetic event but have had no joy with that either.

I see xinput has SendExtensionEvent which sounds promising but I'm not clear how to use it. Do I need to put the event into a buffer object rather than passing the packed data? I also have no idea what classes (https://gitlab.freedesktop.org/xorg/proto/xcbproto/-/blob/master/src/xinput.xml?ref_type=heads#L2725) are meant to be.

Lastly, I'm also concerned that this won't at all as the notes on xcbproto suggest we can't send xge events which would include the BarrierHitEvent.

Any tips would be greatly appreciated.

@tych0
Copy link
Owner

tych0 commented Nov 5, 2023

Yeah, I agree that it looks like BarrierHitEvent won't work since its opcode is too high. What problems were you having with SendEvent, though? The man page says it should work with extension events too.

@elParaguayo
Copy link
Contributor Author

Thanks for the reply.

The problem is my handle_BarrierHit method is not called at all after sending the event. Current code is here: https://github.com/elParaguayo/qtile/blob/pointer-barrier-test/test/backend/x11/test_pointer_barriers.py

I'm not entirely sure what I should put for the mask value here (I've tried with xcffib.xinput.XIEventMask.BarrierHit and that doesn't work either).

@tych0
Copy link
Owner

tych0 commented Nov 5, 2023

Hum, I'll take a little bit later today. I think you have to use xproto.EventMask.* with SendEvent, but it's not obvious to me which one of those barrier corresponds to:

https://tronche.com/gui/x/xlib/events/mask.html

Maybe try to set all the bits and see?

@elParaguayo
Copy link
Contributor Author

Didn't work with all of those set either sadly.

@elParaguayo
Copy link
Contributor Author

I've got an idea what the problem might be. Let me test something.

@elParaguayo
Copy link
Contributor Author

elParaguayo commented Nov 5, 2023

Didn't work. I tried to use the GeGenericEvent (that gets hoisted to the correct event by xcffib) and send that instead but it's still not working. That's also an xge event though...

@tych0
Copy link
Owner

tych0 commented Nov 6, 2023

Yeah, just reading the code it looks like it maybe won't work, in spite of what the man page says. Sorry, I didn't get a chance to look last night, perhaps later this week.

@elParaguayo
Copy link
Contributor Author

elParaguayo commented Nov 6, 2023

No problem at all. Appreciate you taking a look. Happy to work on a fix if there is a problem within xcffib.

@tych0
Copy link
Owner

tych0 commented Nov 17, 2023

Hi, just wanted to say that I have not forgotten about this :). Here is a smaller xcffib test case:

diff --git a/test/test_barrier_hit.py b/test/test_barrier_hit.py
new file mode 100644
index 0000000..718cc7a
--- /dev/null
+++ b/test/test_barrier_hit.py
@@ -0,0 +1,40 @@
+import xcffib
+import xcffib.xproto
+import xcffib.xinput
+
+from .conftest import XcffibTest
+
+class TestPythonCode:
+
+    def test_send_BarrierHitEvent(self, xcffib_test):
+        conn = xcffib_test
+        barrier = 5
+        event = xcffib.xinput.BarrierHitEvent.synthetic(
+            2,
+            xcffib.xproto.Time.CurrentTime,
+            1,
+            conn.default_screen.root,
+            conn.default_screen.root,
+            barrier,
+            0,
+            0,
+            11,
+            799 << 16,
+            100 << 16,
+            (0, 0),
+            (0, 0)
+        )
+        mask = xcffib.xproto.EventMask.NoEvent
+        xinput = xcffib_test.conn(xcffib.xinput.key)
+        print(conn.conn._event_offsets.offsets)
+        xinput.SendExtensionEventChecked(
+            conn.default_screen.root,
+            1, # device id
+            True,
+            1,
+            1,
+            [event.pack()],
+            [66+24]
+        ).check()
+        conn.conn.core.GetInputFocus().reply()
+        assert False

Which I've been using with these (manual) patches to the bindings:

--- xinput.py	2023-11-17 07:34:29.110212168 -0700
+++ xinput.py.patched	2023-11-17 07:34:38.778375457 -0700
@@ -3896,11 +3896,11 @@
         unpacker.pad(FP3232)
         self.dy = FP3232(unpacker)
         self.bufsize = unpacker.offset - base
     def pack(self):
         buf = io.BytesIO()
-        buf.write(struct.pack("=B", 25))
+        buf.write(struct.pack("=B", 66+25))
         buf.write(struct.pack("=x2xHIIIIIIIH2xii", self.deviceid, self.time, self.eventid, self.root, self.event, self.barrier, self.dtime, self.flags, self.sourceid, self.root_x, self.root_y))
         buf.write(self.dx.pack() if hasattr(self.dx, "pack") else FP3232.synthetic(*self.dx).pack())
         buf.write(self.dy.pack() if hasattr(self.dy, "pack") else FP3232.synthetic(*self.dy).pack())
         buf_len = len(buf.getvalue())
         if buf_len < 32:
@@ -4333,9 +4333,9 @@
         buf.write(xcffib.pack_list(barriers, BarrierReleasePointerInfo))
         return self.send_request(61, buf, is_checked=is_checked)
     def SendExtensionEvent(self, destination, device_id, propagate, num_classes, num_events, events, classes, is_checked=False):
         buf = io.BytesIO()
         buf.write(struct.pack("=xx2xIBBHB3x", destination, device_id, propagate, num_classes, num_events))
-        buf.write(xcffib.pack_list(events, EventForSend))
+        buf.write(xcffib.pack_list(events, BarrierHitEvent))
         buf.write(xcffib.pack_list(classes, "I"))
         return self.send_request(31, buf, is_checked=is_checked)
 xcffib._add_ext(key, xinputExtension, _events, _errors)

I've managed to crash xtrace,

[433633.963184] xtrace[249393]: segfault at c ip 00005558f33296f7 sp 00007ffd89f4d5f0 error 4 in xtrace[5558f3326000+10000]

so I guess they're not quite as good as the actual x server, which renders a LengthError. In any case, the x server really doesn't accept variable length events here, so the comment from the xml is right.

googling around a little bit, these patches do not seem like they ever landed: https://xorg-devel.x.narkive.com/OPQdsFiP/handling-genericevents-in-xsendevent#post9

so maybe it's just not possible right now? anyway, I will keep digging. maybe we need to write some xserver code to make it work.

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