wayland

Attaching None to a surface influences surface configuration


My application creates a popup which at some point needs to be closed and then reappear again. I tried to implement it by attaching a buffer to the surface after which I attach a none to the surface and then finally attach the same buffer as before. The issue is that when I reattach the buffer I get this error: layer_surface has never been configured.
I scrolled through the docs a little but did not find anything which describes this behavior. Did I misunderstand something or is this a possible bug?

Example:

use std::{
    fs::File,
    io::{Seek, SeekFrom, Write},
    os::fd::AsFd,
};

use wayland_client::{
    delegate_noop,
    globals::{registry_queue_init, GlobalListContents},
    protocol::{
        wl_buffer::WlBuffer,
        wl_compositor::WlCompositor,
        wl_output::WlOutput,
        wl_registry::{self, WlRegistry},
        wl_shm::{Format, WlShm},
        wl_shm_pool::WlShmPool,
        wl_surface::WlSurface,
    },
    Connection, Dispatch, QueueHandle,
};
use wayland_protocols_wlr::layer_shell::v1::client::{
    zwlr_layer_shell_v1::{Layer, ZwlrLayerShellV1},
    zwlr_layer_surface_v1::{self, Anchor, KeyboardInteractivity, ZwlrLayerSurfaceV1},
};

struct Delegate;
impl Dispatch<WlRegistry, GlobalListContents> for Delegate {
    fn event(
        _: &mut Self,
        _: &WlRegistry,
        _event: wl_registry::Event,
        _: &GlobalListContents,
        _: &Connection,
        _: &QueueHandle<Self>,
    ) {
    }
}

impl Dispatch<ZwlrLayerSurfaceV1, ()> for Delegate {
    fn event(
        _: &mut Self,
        layer_surface: &ZwlrLayerSurfaceV1,
        event: <ZwlrLayerSurfaceV1 as wayland_client::Proxy>::Event,
        _: &(),
        _: &Connection,
        _: &QueueHandle<Self>,
    ) {
        println!("test");
        if let zwlr_layer_surface_v1::Event::Configure { serial, .. } = event {
            layer_surface.ack_configure(serial);
        }
    }
}

delegate_noop!(Delegate: ignore WlOutput);
delegate_noop!(Delegate: ignore WlShm);
delegate_noop!(Delegate: ignore WlShmPool);
delegate_noop!(Delegate: ignore WlBuffer);
delegate_noop!(Delegate: ignore WlCompositor);
delegate_noop!(Delegate: ignore WlSurface);
delegate_noop!(Delegate: ignore ZwlrLayerShellV1);

fn main() {
    let conn = Connection::connect_to_env().unwrap();
    let (globals, mut event_queue) = registry_queue_init::<Delegate>(&conn).unwrap();
    let qh = event_queue.handle();

    let mut outputs = vec![];
    for global in globals.contents().clone_list() {
        if let "wl_output" = &global.interface[..] {
            let output: WlOutput = globals
                .registry()
                .bind(global.name, global.version, &qh, ());
            outputs.push(output);
        }
    }

    let output = &outputs[0];

    let mut overlay = tempfile::tempfile().unwrap();

    let shm: WlShm = globals.bind(&qh, 1..=1, ()).unwrap();
    let compositor: WlCompositor = globals.bind(&qh, 1..=4, ()).unwrap();
    let layer_shell: ZwlrLayerShellV1 = globals.bind(&qh, 1..=1, ()).unwrap();

    let (width, height) = (1920, 1080);

    let shm_pool = shm.create_pool(overlay.as_fd(), width * height * 4, &qh, ());

    let buffer = shm_pool.create_buffer(0, width, height, width * 4, Format::Abgr8888, &qh, ());
    let surface = compositor.create_surface(&qh, ());
    let layer_surface = layer_shell.get_layer_surface(
        &surface,
        Some(output),
        Layer::Overlay,
        "ScreenshotUtil".to_string(),
        &qh,
        (),
    );

    layer_surface.set_size(width as u32, height as u32);
    layer_surface.set_anchor(Anchor::Bottom);
    layer_surface.set_margin(0, 0, 0, 0);
    layer_surface.set_keyboard_interactivity(KeyboardInteractivity::None);
    layer_surface.set_exclusive_zone(-1);

    draw_background([100, 44, 44, 44], &mut overlay);
    surface.commit();

    event_queue.blocking_dispatch(&mut Delegate).unwrap();

    surface.attach(Some(&buffer), 0, 0);
    surface.commit();

    surface.attach(None, 0, 0);
    surface.commit();

    surface.attach(Some(&buffer), 0, 0);
    surface.commit();

    loop {
        event_queue.blocking_dispatch(&mut Delegate).unwrap();
    }
}

fn draw_background(background: [u8; 4], file: &mut File) {
    file.seek(SeekFrom::Start(0)).unwrap();

    let cashed = u32::from_ne_bytes(background);
    let compressed_buf = vec![cashed; 1920 * 1080];
    let uncompressed_buf = bytemuck::cast_slice::<u32, u8>(&compressed_buf[..]);

    file.write_all(uncompressed_buf).unwrap();
}

Solution

  • The answer is no it is not a bug, and yes it is described in the docs. https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1

    Unmapping a layer_surface means that the surface cannot be shown by the compositor until it is explicitly mapped again. The layer_surface returns to the state it had right after layer_shell.get_layer_surface. The client can re-map the surface by performing a commit without any buffer attached, waiting for a configure event and handling it as usual.