Problem in cairo + xcb where alpha blending is cumulative (becoming more opaque with each render)

Submitted by Uli Schlachter on Sept. 27, 2018, 7:56 a.m.

Details

Message ID cb1042fd-1f0c-f1a7-078e-9611bb1aab56@znc.in
State New
Series "Problem in cairo + xcb where alpha blending is cumulative (becoming more opaque with each render)"
Headers show

Commit Message

Uli Schlachter Sept. 27, 2018, 7:56 a.m.
Hi,

On 26.09.2018 07:51, Ryan Flannery wrote:
> The raw transparency piece works fine and is
> easy in cairo + xcb,
sorry to tell you this, but the problem is in the raw transparency part.
You only *think* that you have transparency. Also, "is easy" is the
total opposite of the truth. Again, sorry.

When a new window is mapped, its content is undefined. In practice this
means that the window contains whatever was visible at this position
before. Thus, you think you have transparency when you draw ontop of
this. However, this is wrong.

Try apply the attached patch move.patch. What this patch does is to wait
for the window to become visible and then it moves the window. You will
notice that together with the window, its content is moved. Thus, making
it obvious that your transparency does not work.

The black background that you see is the working case *if your window
had an alpha channel*. Because it does not have an alpha channel, you
only see black.

To create a transparent window in X11, first of all, the user has to be
using a compositing manager (compiz, compton, xcompmgr, ...). Without
this, there is no real transparency. The _NET_WM_CMs selection can be
used to figure out if a compositing manager is running.

Next, you need to create a window with a 32bit visual (and also tell
cairo that your window is using this visual). For this part, I refer you to:

https://stackoverflow.com/questions/3645632/how-to-create-a-window-with-a-bit-depth-of-32

The attached patch fix.patch gives me a window with either a black
background (if no compositing manager is running) or a transparent black
background (if a compositing manager is running; I used compton).

Cheers,
Uli

Patch hide | download patch | download mbox

--- cairo_example.c.orig	2018-09-27 09:37:28.775992348 +0200
+++ cairo_example.c	2018-09-27 09:54:32.519404799 +0200
@@ -119,6 +119,8 @@  main()
    xcb_map_window(xcon, xwindow);
    cairo_clear_background();
    xcb_flush(xcon);
+   sleep(1);
+   xcb_configure_window(xcon, xwindow, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, (uint32_t[]) { 300, 300 });
 
    /* begin main draw loop */
    for (int i = 0; i < 11; i++) {
@@ -137,9 +139,9 @@  main()
 
       /* END: pop group/buffer (exposing it) and render */
       cairo_pop_group_to_source(cairo);
-      /*cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); XXX Makes the background black (?) */
+      cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
       cairo_paint(cairo);
-      /*cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); only done if th eprevious cairo_set_operator() is done */
+      cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
       xcb_flush(xcon);
       sleep(1);
    }
@@ -170,12 +172,12 @@  get_xvisual(xcb_screen_t *screen)
 {
    xcb_depth_iterator_t i = xcb_screen_allowed_depths_iterator(screen);
    for (; i.rem; xcb_depth_next(&i)) {
+     if (i.data->depth != 32)
+       continue;
       xcb_visualtype_iterator_t vi;
       vi = xcb_depth_visuals_iterator(i.data);
       for (; vi.rem; xcb_visualtype_next(&vi)) {
-         if (screen->root_visual == vi.data->visual_id) {
-            return vi.data;
-         }
+        return vi.data;
       }
    }
 
@@ -256,16 +258,13 @@  wm_hints()
          xatoms[NET_WM_STRUT_PARTIAL], XCB_ATOM_CARDINAL, 32, 12, struts);
 	xcb_change_property(xcon, XCB_PROP_MODE_REPLACE, xwindow,
          xatoms[NET_WM_STRUT], XCB_ATOM_CARDINAL, 32, 4, struts);
-
-   /* remove window from window manager tabbing */
-   const uint32_t val[] = { 1 };
-   xcb_change_window_attributes(xcon, xwindow,
-         XCB_CW_OVERRIDE_REDIRECT, val);
 }
 
 void
 setup_xcb()
 {
+   xcb_colormap_t colormap;
+
    xcon = xcb_connect(NULL, &default_screen);
    if (xcb_connection_has_error(xcon)) {
       xcb_disconnect(xcon);
@@ -278,23 +277,29 @@  setup_xcb()
    if (NULL == (xvisual = get_xvisual(xscreen)))
       errx(1, "Failed to retrieve X visual context");
 
-   static uint32_t valwin[2] = {
+   colormap = xcb_generate_id(xcon);
+   xcb_create_colormap(xcon, XCB_COLORMAP_ALLOC_NONE, colormap, xscreen->root, xvisual->visual_id);
+
+   uint32_t valwin[5] = {
       XCB_NONE,
-      XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS
+      0,
+      1,
+      XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS,
+      colormap
    };
 
    xwindow = xcb_generate_id(xcon);
    xcb_create_window(
          xcon,
-         XCB_COPY_FROM_PARENT,
+         32,
          xwindow,
          xscreen->root,
          x, y,
          y, h,
          1,    /* border width */
          XCB_WINDOW_CLASS_INPUT_OUTPUT,
-         xscreen->root_visual,
-         XCB_CW_EVENT_MASK | XCB_CW_BACK_PIXMAP,
+         xvisual->visual_id,
+         XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
          valwin);
 
    wm_hints();

Comments

Ryan Flannery Sept. 28, 2018, 2:24 a.m.
On Thu, Sep 27, 2018 at 3:56 AM, Uli Schlachter <psychon@znc.in> wrote:

> Hi,
>
> On 26.09.2018 07:51, Ryan Flannery wrote:
> > The raw transparency piece works fine and is
> > easy in cairo + xcb,
> sorry to tell you this, but the problem is in the raw transparency part.
> You only *think* that you have transparency. Also, "is easy" is the
> total opposite of the truth. Again, sorry.
>
> When a new window is mapped, its content is undefined. In practice this
> means that the window contains whatever was visible at this position
> before. Thus, you think you have transparency when you draw ontop of
> this. However, this is wrong.


THANK YOU!

I was digging into a totally different path. After reading this, I dove
into what you described and now have a good handle on the issue. I can now
detect if it's supported and apply the alpha blending only when it is. Also
I learned what compositing window managers are and now have these sweet
transparent urxvt terminals :D (though I doubt I'll keep them).

Many thanks again. You saved me many more hours and headaches before I
would have arrived at this.


>
>
Try apply the attached patch move.patch. What this patch does is to wait
> for the window to become visible and then it moves the window. You will
> notice that together with the window, its content is moved. Thus, making
> it obvious that your transparency does not work.
>
> The black background that you see is the working case *if your window
> had an alpha channel*. Because it does not have an alpha channel, you
> only see black.
>
> To create a transparent window in X11, first of all, the user has to be
> using a compositing manager (compiz, compton, xcompmgr, ...). Without
> this, there is no real transparency. The _NET_WM_CMs selection can be
> used to figure out if a compositing manager is running.
>
> Next, you need to create a window with a 32bit visual (and also tell
> cairo that your window is using this visual). For this part, I refer you
> to:
>
> https://stackoverflow.com/questions/3645632/how-to-
> create-a-window-with-a-bit-depth-of-32
>
> The attached patch fix.patch gives me a window with either a black
> background (if no compositing manager is running) or a transparent black
> background (if a compositing manager is running; I used compton).


Thanks for the patch and stackoverflow link. That both solved the issue in
my example program and also cleaned up a couple things, like applying
OVERRIDE_REDIRECT when I create the window rather than later.

Many thanks,
-Ryan