Window Overlay and Position Sync
Soft Works
softworkz at hotmail.com
Mon Sep 30 10:16:08 UTC 2024
Hello,
I hope this is an acceptable question on this ML, please advise in case it wouldn't.
TL/DR
The essential question is about how to synchronize size and position of one top-level window to match another top-level window without getting limited by window manager constraints.
While I'm new to X11 development, coming from Windows, I don't think that this is a beginner's question, as I couldn't find an answer even after ingesting tons of resources, documentation and source code over the past days.
The goal is quite simple: I have a transparent Electron window with browser content and I want to layer this on top of a video window - or rather underlay the video window below the Electron window, in a way that size, position are synchronized and the video window is always one below the Electron window in the stacking order.
This is what I've tried so far:
1. Reparent both windows into a new X11 window
This is how it works on Windows and you're done already, as there's no distinction between top-level and deeper-level windows - composition and painting are always managed by the DWM. If I understood correctly, in case of X11 it's the responsibility of the application to handle composition of child windows inside a top-level window. Maybe that's not quite accurate, but in either case it burns down to the way how the Electron window is implementing transparency.
The result I'm getting from this approach is that the Electron window is still transparent but it's using the background of the (also transparent) top-level window (that I've created) even though the video window is stacked in-between (below the Electron window). So the video cannot be seen.
I've traced this down from the Electron source code, then further to the Chromium source code and there I could see that their window is implemented using a GTK widget class. That widget in turn is using Cairo for copying the visual of the top-level window's background as the Electron window background in order to achieve transparency.
I had looked further into possible ways to override this behavior, but eventually I figured that this would probably never be an ideal solution, because when it comes to things like 4k video playback, I'd really want a solution where the compositing is handled by the system compositor and can possibly be done in hardware.
This brought me to 2..
2. Use and stack two top-level windows
This is working pretty well across common Linux distributions with their default window managers and compositors.
(I should note that I'm not concerned about non-mainstream or customized desktop environments without a compositor or with non-overlapping window management. For those it will fallback to in-browser video playback, even though that's much inferior)
But there's a little caveat with that approach: as it turns out, many window managers on Linux appear to be insanely restrictive with regards to window placement. That wouldn't be an issue normally, but unfortunately these are making a distinction between positioning that is initiated by user actions and positioning set by an application.
There might be reasonable ideas behind this, but I think it's valid to ask the question why an application shouldn't be allowed to synchronize the position of a window to exactly match the position of another window while it's interactively moved around by a user?
To recap in a more illustrative way: The Electron window has a title bar, the video window is frameless. When moving around the Electron window on screen, the video window follows to match size and position (by receiving events for the Electron window and applying the same bounds to the video window) - as long as the Electron window remains within the bounds of the screen.
As soon as it is moved (partially) outside the screen, the video window doesn't follow any further. It remains within the bounds of the screen.
And that limitation is what I've tried to overcome in many different ways - with mixed success:
2.1. Using _NET_MOVERESIZE_WINDOW instead of ConfigureWindow
This appeared to be promising as it involves flags for "source indication" - i.e. whether it's a change initiated by the user or programmatically. Unfortunately it turned out that it doesn't have any effect on the window managers I tried. I looked into the Mutter source code and it completely ignores it.
Using this method still turned out to be useful, because some window managers ignore ConfigureWindow during an ongoing drag operation but that way is still working.
Other window managers totally ignore this message.
2.2. Setting _WM_NORMAL_HINTS and _WM_SIZE_HINTS
Another shine of hope from reading the docs: There are flags in the structure for indicating whether position and size are set by a user (USPositiion, USSize), but it appears that those values aren't used anymore (which I found to be confirmed later in some other docs).
2.3. Enabling override-redirect
The docs mention that setting override-redirect on a window will prevent the window manager from interfering, so I set this on the video window.
This is actually working in a way that the position constraints are no longer applied: the video window properly follows the position and size of the Electron window - but: In that case, the video window is always rendered on top of all other windows, so the Electron window does no longer render above the video window.
I tried RaiseWindow() and _NET_WM_STATE_ABOVE on the Electron window and setting _NET_WM_STATE_BELOW on the video window, without success.
I haven't tried RestackWindows() since even setting "Always on Top" through the window menu didn't bring it above the video window.
2.4. Setting window types DESKTOP and DOCK
While studying the source code for Mutter, I've noticed that it doesn't apply its constraints to windows of type DOCK and DESKTOP and this actually turned out to work for getting around the window constraints. By setting the type of both windows to DOCK and raising the Electron window above, I get them properly stacked on top of all other windows, and by setting type to DESKTOP, they get properly stacked below all other windows.
But the drawback is that the two windows can only be on top or below all other windows but not in-between like normal windows.
So, when these windows are on top and another window is focused, they need to be send all way to the bottom upon loosing focus - again a pretty odd behavior which is undesired.
2.5. Hacking the constraint implementations
I noticed that the constraint implementations of all the window managers I've tested have one thing in common: Once a window has gone beyond the constraint rules, they aren’t being applied anymore. That means, when I configure the video window to have a title bar and bring it to the point where it doesn't move any further to follow the Electron window (which has gone partially outside the screen) - I just need to (manually) move the video window slightly outside of the screen bounds, and from then on, it perfectly follows the main Electron window on all sides of the screen.
Once it gets back to be fully inside the screen area, it's prevented again from being moved outside.
The takeaway is that, it just needs to be slightly moved over the border in some way, each time when moving the main window outside. The following hacks are based on that:
2.5.1. Applying .....
From the Mutter source code I've seen that ....
Works with most window managers.
2.5.2. Setting .....
-----
Works with some, but not all window managers, but not the same as 2.5.1.
(I apologize for redacting, but if it would turn out that this is the only possible way, I wouldn't want anybody getting an idea to "fix" these)
2.5.3. Briefly switching target window during move, using _NET_WM_MOVERESIZE
The idea is to briefly change the mouse movement target to the video window while the user is dragging around the main window, just to move it beyond the constraint limit and then switch it back to the main window. I haven't got this working in a usable way so far.
3. Leads and ideas that I haven't explored yet
- Exploring GrabWindow and the likes
- Can setting specific window classes (WM_CLASS) affect window manager behavior?
- Using window/client groups - could this help in any way?
- Configuring the video window as "output-only"
- Look into "manager selections"
Wrapping Up
===========
Thanks a lot for reading all the way through. While I've come to a point where I got things working as desired (more or less), these hacks are really not the way how I want to go forward. I can't stop thinking that there must be a better way - like "the right way to do this with X11".
I'd be very glad and thankful for any insights and advice on this task.
Best wishes
softworkz
More information about the xorg
mailing list