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

X11: Add support for transparent windows #1803

Merged
merged 9 commits into from
Jun 13, 2021

Conversation

psychon
Copy link
Contributor

@psychon psychon commented May 23, 2021

First, some history lesson:

X11 originally did not support transparency. Visuals describe how pixel
values are interpreted, but they can only describe red/green/blue
components. Then, the RENDER extension came and added transparency. Now
we have picture formats. That's basically the same as a visual, but it
also describes an alpha component. There is a mapping between picture
formats and visual. Also, there is a standard ARGB32 format that is
always supported.

Then came the composite extension. Classically, the X11 server "drew
stuff" directly to the output, but there is no alpha compositing
involved. With composite, a composite manager can request drawing to be
redirected to an off-screen pixmap. The composite manager will then
produce the "real" visible screen contents. And in doing so, it can take
alpha values into account.

This commit adds support for transparent windows to the X11 backend. For
that, it has to find the standard ARGB32 render picture format and the
corresponding visual. When a transparent window is created, it checks if
a composite manager is running. If not, nothing changes. If everything
worked out (RENDER is supported, we found the ARGB32 picture format, a
composite manager is running), transparent windows are created with this
visual instead of the root window's visual.

When a window has a different bit depth than its parent window, it has
to "fully define" its rendering. Since it has a different bit depth than
its parent, the X11 server cannot just copy background and window border
from the parent. Thus, when we create an ARGB32 window (which has
depth=32), we are creating a window with a different depth than the root
window (most likely depth=24) and have to provide some additional
settings. Even though no border will be used, we still have to define
its color.

Signed-off-by: Uli Schlachter [email protected]


Since I can only build druid-shell and not druid, this PR only received light testing: I let it open some window and asked xwininfo for the window's depth. I only tested that this produces the expected result with a hardcoded transparent: true and with/without a composite manager. I did not actually test anything involving transparent window background, but I expect that to work since cairo should handle all that is necessary. Hopefully...

First, some history lesson:

X11 originally did not support transparency.  Visuals describe how pixel
values are interpreted, but they can only describe red/green/blue
components. Then, the RENDER extension came and added transparency. Now
we have picture formats. That's basically the same as a visual, but it
also describes an alpha component. There is a mapping between picture
formats and visual. Also, there is a standard ARGB32 format that is
always supported.

Then came the composite extension. Classically, the X11 server "drew
stuff" directly to the output, but there is no alpha compositing
involved.  With composite, a composite manager can request drawing to be
redirected to an off-screen pixmap. The composite manager will then
produce the "real" visible screen contents. And in doing so, it can take
alpha values into account.

This commit adds support for transparent windows to the X11 backend. For
that, it has to find the standard ARGB32 render picture format and the
corresponding visual. When a transparent window is created, it checks if
a composite manager is running. If not, nothing changes. If everything
worked out (RENDER is supported, we found the ARGB32 picture format, a
composite manager is running), transparent windows are created with this
visual instead of the root window's visual.

When a window has a different bit depth than its parent window, it has
to "fully define" its rendering. Since it has a different bit depth than
its parent, the X11 server cannot just copy background and window border
from the parent. Thus, when we create an ARGB32 window (which has
depth=32), we are creating a window with a different depth than the root
window (most likely depth=24) and have to provide some additional
settings. Even though no border will be used, we still have to define
its color.

Signed-off-by: Uli Schlachter <[email protected]>
Signed-off-by: Uli Schlachter <[email protected]>
@maan2003
Copy link
Collaborator

Just tested the transparency example, does not work on kde, xfce or picom (didn't check on others).

Errors
 INFO druid_shell::platform::x11::application: X server supports Present version 1.0
 INFO druid_shell::platform::x11::application: X server supports XFIXES version 5.0
DEBUG druid::localization: available locales [en-US, de-DE, fr-CA], current en-US
DEBUG druid::localization: resolved: [en-US]
 WARN druid_shell::platform::x11::window: WindowBuilder::resizable is currently unimplemented for X11 platforms.
 WARN druid_shell::platform::x11::window: WindowBuilder::show_titlebar is currently unimplemented for X11 platforms.
DEBUG druid::window: TextFieldToken(1) added
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: Match, error_code: 8, sequence: 107, bad_value: 38, minor_opcode: 4, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 108, bad_value: 58720289, minor_opcode: 26, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 109, bad_value: 58720289, minor_opcode: 26, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 110, bad_value: 58720289, minor_opcode: 26, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 112, bad_value: 58720289, minor_opcode: 10, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 114, bad_value: 58720289, minor_opcode: 10, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 116, bad_value: 58720289, minor_opcode: 10, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 118, bad_value: 58720289, minor_opcode: 10, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 143, bad_value: 58720289, minor_opcode: 8, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 146, bad_value: 58720289, minor_opcode: 10, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: RenderPicture, error_code: 143, sequence: 154, bad_value: 58720289, minor_opcode: 24, major_opcode: 139 }
ERROR druid_shell::platform::x11::application: Error handling event: X11 error X11Error { error_kind: Match, error_code: 8, sequence: 156, bad_value: 58720293, minor_opcode: 1, major_opcode: 148 }

I think your build error can solved by commenting out the const generic data impl for array

@psychon
Copy link
Contributor Author

psychon commented May 23, 2021

Thanks for testing. Since the following errors are Picture errors, I guess that Match error is a RENDER CreatePicture request. minor_opcode: 4 matches that theory.

According to https://gitlab.freedesktop.org/xorg/proto/renderproto/-/blob/master/renderproto.txt

It is a Match error
to specify a format with a different depth than the drawable. If
the drawable is a Window then the Red, Green and Blue masks must
match those in the visual for the window else a Match error is
generated.

Since this PR doesn't add any CreatePicture requests, I guess that this comes from cairo. Weird...

I think your build error can solved by commenting out the const generic data impl for array

Thanks. I didn't even look at the code to see how simple it might be to get this to work.

I won't have time to investigate for the next seven days. Afterwards, I'll take a look.

@psychon
Copy link
Contributor Author

psychon commented May 30, 2021

As I already quoted from the spec above:

It is a Match error
to specify a format with a different depth than the drawable. If
the drawable is a Window then the Red, Green and Blue masks must
match those in the visual for the window else a Match error is
generated.

The window in question uses visual 0x72. The picture is supposed to use format 0x26. (Both values taken from the output of xtrace -o /tmp/t cargo run --no-default-features --features=x11 --example=transparency.) According to xdpyinfo -ext RENDER:

  visual:
    visual id:    0x72
    class:    TrueColor
    depth:    32 planes
    available colormap entries:    256 per subfield
    red, green, blue masks:    0xff0000, 0xff00, 0xff
    significant bits in color specification:    8 bits

and

  pict format:
        format id:    0x26
        type:         Direct
        depth:        32
        alpha:        24 mask 0xff
        red:          16 mask 0xff
        green:         8 mask 0xff
        blue:          0 mask 0xff

That looks pretty much the same to me....?
The same output later even says that the two go together (which doesn't really surprise me since the code is using the same table to map from visual to picture):

      visual format:
        visual id:      0x72
        pict format id: 0x26

I really don't know what is going on here and will take a closer look later this week.

The code always called Buffers::new() with a depth of screen.root_depth.
While this value was always correct previously, for transparency we are
using a window with depth=32. The mismatch in depths then caused
RENDER's create_picture to fail down the road.

For clarity, this commit also replaces a use of COPY_DEPTH_FROM_PARENT
with the correct depth. This change should not actually make any
difference in practice.

Signed-off-by: Uli Schlachter <[email protected]>
@psychon
Copy link
Contributor Author

psychon commented May 31, 2021

Urgh. Turns out I was mis-reading "stuff" previously. The code was really using a wrong depth in one place (screen.root_depth). I replaced that with the correct value and now the transparency example actually works fine here with xcompmgr.

Edit: Also: What is the "proper procedure" to handle the obvious git conflicts in CHANGELOG.md? Do you want me to merge master into this PR whenever that occurs? Should I rewrite some git history?

Copy link
Collaborator

@maan2003 maan2003 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working 🎉 . Sorry for being late.

Do you want me to merge master into this PR whenever that occurs? Should I rewrite some git history?

You can do either. We squash and merge so it doesn't matter

&& format.direct.alpha_mask == 0xff
})
// Now find the corresponding visual ID
.and_then(|format| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These chain are getting a bit large. is helpful to extract these into a functions that returns an Option?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. You may decide on the "is it helpful". ;-)

druid-shell/src/platform/x11/util.rs Outdated Show resolved Hide resolved
@psychon
Copy link
Contributor Author

psychon commented Jun 12, 2021

CI fails due to cargo fmt in code that is not touched in this PR. If you want, I can fix that in the PR, but I don't really like touching "random code" outside of the PR just to please CI. I'd rather wait for this to be fixed in master (since this must make all PRs fail...?)

Signed-off-by: Uli Schlachter <[email protected]>
@maan2003
Copy link
Collaborator

CI fails due to cargo fmt in code that is not touched in this PR.

You touched it 😅 (screen.root_depth to depth)

> cargo fmt -- --check

         let buffers = RefCell::new(Buffers::new(
-            conn,
-            id,
-            buf_count,
-            width_px,
-            height_px,
-            depth,
+            conn, id, buf_count, width_px, height_px, depth,
         )?);

Signed-off-by: Uli Schlachter <[email protected]>
@psychon
Copy link
Contributor Author

psychon commented Jun 12, 2021

Argh, sorry. Apparently I am not qualified to read git histories with merges. :-(

@maan2003 maan2003 merged commit a886766 into linebender:master Jun 13, 2021
@psychon psychon deleted the x11-transparent branch June 13, 2021 15:38
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

Successfully merging this pull request may close these issues.

2 participants