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

Grabs are ignored in Qt #3957

Closed
faszkany opened this issue Aug 6, 2023 · 11 comments
Closed

Grabs are ignored in Qt #3957

faszkany opened this issue Aug 6, 2023 · 11 comments
Labels
bug Something isn't working

Comments

@faszkany
Copy link

faszkany commented Aug 6, 2023

Describe the bug
Grabs do not get forwarded or get ignored

To Reproduce
Steps to reproduce the behavior:

  1. xpra start :2
  2. xpra attach ssh://user@host:port/2
  3. dmenu(1), rofi(1) in rofi -show run, Qt in qtcreator and others

System Information (please complete the following information):

  • Server OS: Arch linux
  • Client OS: Arch linux
  • Xpra Server Version v4.4.5-r0 (inside Xephyr)
  • Xpra Client Version v4.4.5-r0

Additional context
grabs getting ignored, same as #3506 described. tested with Qt, dmenu, rofi
the Xpra server is running inside Xephyr, but this should not influence the described behavior.
the Qt issue is mostly visible in dropdowns, which should grab the keyboard.
dmenu and rofi also very visibly misbehave.

@faszkany faszkany added the bug Something isn't working label Aug 6, 2023
@totaam
Copy link
Collaborator

totaam commented Aug 7, 2023

Just like the bug report you linked to, your version is out of date.
Please try with the latest version first.

@faszkany
Copy link
Author

faszkany commented Aug 7, 2023

Confirmed on xpra v6.0-r33984 (g0a2a206e7) beta. Behavior seems a little different in qt, as the initial grab when clicking on a qtcreator menu bar dropdown does get forwarded, but as the pointer moves down to the dropdown from the bar button the grab is lost. rofi and dmenu still misbehave, and so does my test xlib snippet. XGrabKeyboard(display, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) returns GrabSuccess, but the grab somehow gets lost.

@totaam
Copy link
Collaborator

totaam commented Aug 7, 2023

FYI: grabs cannot be forwarded as the window manager (xpra) does not receive the events from the X11 server that would allow it to forward them.
The only thing we can do is to try not to break the grab. Typically, this means careful handling of focus events.
What is your desktop environment and display server?

@faszkany
Copy link
Author

faszkany commented Aug 8, 2023

I see, that is unfortunate. I arrived to the same conclusion after reading X code - no introspection features that would enable direct grab forwarding. The only option left for Xpra would be to speculate when and which client grabbed the devices. This could be achieved by watching out for abnormal FocusIn and FocusOut events and a lot of other painful guessing.
The client display server is Xorg version 21.1.8-2, the server is Xephyr with the same version.
Closing this as the issue is X introspection, thank you for your time

@faszkany faszkany closed this as not planned Won't fix, can't repro, duplicate, stale Aug 8, 2023
@faszkany
Copy link
Author

faszkany commented Aug 8, 2023

Reopening, because I found pain and suffering a way for Xpra to map all client grabs - that is X_GrabPointer X_UngrabPointer X_GrabButton X_UngrabButton X_ChangeActivePointerGrab X_GrabKeyboard X_UngrabKeyboard X_GrabKey X_UngrabKey opcodes 26-34 on my x server (Xproto.h) - without exception on X servers supporting the RECORD extension. RECORD is very flexible and allows for multiple ways to achieve this, perhaps the simplest of those is registering a request callback (with request range set to the mentioned range 26-34) for every client and translating those to the Xpra client-side windows. Another could be to figure out the originating client directly from XRecordInterceptData (thus one callback would be enough), however I have yet to find a way to resolve these to window ids. I am attaching a proof-of-concept C program that depends on development X libraries and xtest. gcc record2.c -lX11 -lXtst -o record2 is how it can be built. (github renamed it to txt)
Please consider spending some time learning how RECORD works in case you are not familiar with it and if you do not have the time point me to the Xpra code where this logic is missing from.
Thank you for your time.

@faszkany faszkany reopened this Aug 8, 2023
@faszkany
Copy link
Author

faszkany commented Aug 8, 2023

Also, xGrabKeyboardReq->grabWindow is not the originator, but what the originator grabbed. It is not very clear to me how this works but the Req seems to be a sub-structure that does not contain information about the originator. Maybe some field of XRecordInterceptData, which contains xGrabKeyboardReq in its data field might have some information about it.

@totaam
Copy link
Collaborator

totaam commented Aug 9, 2023

I had not thought of using the record extension!
Let me see what I can do.
The recommended communication model for a Record application is to open two connections to the server—one connection for recording control and one connection for reading recorded protocol data.
That's on top of the main UI thread we already have!

@totaam
Copy link
Collaborator

totaam commented Dec 31, 2023

The commit above adds a simple test tool exercising the Cython record extension module.

WAYLAND_DISPLAY= DISPLAY=:100 python3 ./xpra/x11/bindings/record_tool.py 
found XRecord extension version 1.13
start of X11 record data
from-client event type=ChangeActivePointerGrab {'cursor': 12582952, 'event-mask': 12, 'time': 94900809}
from-client event type=UngrabKey {'window': 1100, 'key': 0, 'modifiers': 32768}
from-client event type=ChangeActivePointerGrab {'cursor': 12582952, 'event-mask': 12, 'time': 94901262}
from-client event type=UngrabKey {'window': 1100, 'key': 0, 'modifiers': 32768}
from-client event type=ChangeActivePointerGrab {'cursor': 12582952, 'event-mask': 12, 'time': 94901614}
from-client event type=UngrabKey {'window': 1100, 'key': 0, 'modifiers': 32768}

This code could easily be executed in a separate thread - or the file descriptor could be added to Gio so that it is handled by the existing main loop.
We should probably bail out if client_swapped is set - but recent X11 servers don't enable this feature anyway.
The unmarshalling of C structures might be easier using a ctypes wrapper or just python-xlib.

@faszkany as you pointed out, most events have a grabWindow but this is not the originator.
And I don't see any way of getting the information about which client requested the grab.
Even setting up individual record contexts (XRecordClientSpec) would not give us this information.

So at this point, I am a bit stuck as I don't see a way of using this data reliably. Any ideas?
I am keeping this code for now, it may also be useful as a tracing / debugging tool too.

@faszkany
Copy link
Author

faszkany commented Jan 4, 2024

Setting up individual RCs is indeed unfeasible due to X not providing client enumeration and callback (e.g. client finalizer) features. However, to my best understanding, Xpra doesn't need to identify the originator client (as grabs are effectively locks), and could use a single dummy client on the Xpra client-end to forward grabs. Please correct me if this isn't possible or straightforward and I will figure out something else.

Unfortunately, reliably checking grab state without side-effects is difficult on X, as the only core protocol requests available are the interfering grabs themselves. Pairs of the grabs and ungrabs could still be used with the SYNC extension to ensure they are processed back-to-back. This could be used to poll grab state, because we always know when a grab starts, only the end is ambiguous.

I also had the idea of creating trapping mechanisms with fake X Input devices and windows, where a grab change would immediately make the X server send an event notifying of the changes related to the window. This might not work, as the way grabs interact with MPX is still a mystery to me.

X11 is infinitely complex and there should be other ways as well, for now I will put together Xephyr PoCs of these and post the results later.

@totaam
Copy link
Collaborator

totaam commented Jan 4, 2024

Please correct me if this isn't possible or straightforward and I will figure out something else.

You're probably right, I am juggling with too many things at the same time and not doing any of them well enough!

My concern was with the data that will need to be provided to the client side to forward that grab and have it behave as expected.
Ignoring all the platform constraints and availability for a minute - as that's another can of worms, the Gdk implementation of a keyboard grab looks like this:
https://lazka.github.io/pgi-docs/Gdk-3.0/functions.html#Gdk.keyboard_grab
For pointer grabs:
https://lazka.github.io/pgi-docs/Gdk-3.0/functions.html#Gdk.pointer_grab
The impedance mismatch will leave a lot to figure out!

reliably checking grab state without side-effects is difficult on X

Do we really care that much?
I would think that as long as we break the grabs reliably, users are unlikely to care.
We used to have sticky grabs that users couldn't get out of, now that was a problem!

trapping mechanisms with fake X Input devices and windows

Clever! The big downside is that this would be another back channel via the device mechanism, with all the code + setup + packaging + configuration that entails.

for now I will put together Xephyr PoCs of these and post the results later

I am usually capable of taking PoC code and beating it into shape.

@totaam
Copy link
Collaborator

totaam commented May 31, 2024

Not heard back.

@totaam totaam closed this as not planned Won't fix, can't repro, duplicate, stale May 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants