wayland-rs icon indicating copy to clipboard operation
wayland-rs copied to clipboard

assertion failed when after ZwlrDataControlManagerV1.get_data_device

Open WhyNotHugo opened this issue 4 years ago • 10 comments

Running this code fails (I've left the printlns I used to debug since they kinda help pin point down what crashed):

use wayland_client::protocol::wl_seat::WlSeat;
use wayland_client::Display;
use wayland_client::GlobalManager;
use wayland_protocols::wlr::unstable::data_control::v1::client::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1;

fn main() {
    let display = Display::connect_to_env().unwrap();
    let mut event_queue = display.create_event_queue();
    let attached_display = (*display).clone().attach(event_queue.token());
    let globals = GlobalManager::new(&attached_display);

    event_queue
        .sync_roundtrip(&mut (), |_, _, _| unreachable!())
        .unwrap();

    let seat = globals.instantiate_exact::<WlSeat>(1).unwrap();
    let manager = match globals.instantiate_exact::<ZwlrDataControlManagerV1>(1) {
        Err(err) => {
            println!("Compositor doesn't support wlr-data-control-unstable-v1.");
            panic!("{}", err);
        }
        Ok(res) => res,
    };

    event_queue
        .sync_roundtrip(&mut (), |raw_event, _, _| {
            println!("00{}", raw_event.interface)
        })
        .unwrap();
    println!("01");

    manager.create_data_source();

    event_queue
        .sync_roundtrip(&mut (), |raw_event, _, _| {
            println!("10{}", raw_event.interface)
        })
        .unwrap();
    println!("11");

    let data_device = manager.get_data_device(&seat.detach());
    println!("14");

    event_queue
        .sync_roundtrip(&mut (), |raw_event, _, _| {
            println!("20{}", raw_event.interface)
        })
        .unwrap();
    println!("21");

    println!("Hello, world!");

    // https://docs.rs/wayland-client/0.28.5/wayland_client/struct.EventQueue.html
    loop {
        // The dispatch() method returns once it has received some events to dispatch
        // and have emptied the wayland socket from its pending messages, so it needs
        // to be called in a loop. If this method returns an error, your connection to
        // the wayland server is very likely dead. See its documentation for more details.
        event_queue
            .dispatch(&mut (), |raw_event, _, _| {
                /* This closure will be called for every event received by an object not
                assigned to any Filter. If you plan to assign all your objects to Filter,
                the simplest thing to do is to assert this is never called. */
                println!("50{}", raw_event.interface);
                // unreachable!();
            })
            .expect("An error occurred during event dispatching!");
    }
}

Output:

✘ RUST_BACKTRACE=1 cargo run
warning: unused variable: `raw_event`
  --> src/main.rs:60:33
   |
60 |             .dispatch(&mut (), |raw_event, _, _| {
   |                                 ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_raw_event`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `data_device`
  --> src/main.rs:41:9
   |
41 |     let data_device = manager.get_data_device(&seat.detach()); // <<-- the thing I need to use.
   |         ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_data_device`

warning: 2 warnings emitted

    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/clipmon`
00wl_seat
01
11
14
thread 'main' panicked at 'assertion failed: self.map.lock().unwrap().find(id).map(|obj|
                                          obj.is_interface::<I>()).unwrap_or(true)', /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/mod.rs:69:9
stack backtrace:
   0: std::panicking::begin_panic
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:521:12
   1: wayland_client::imp::ProxyMap::get_new
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/mod.rs:69:9
   2: wayland_client::imp::queues::message_to_rawevent::{{closure}}
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/queues.rs:294:59
   3: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &mut F>::call_once
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:280:13
   4: core::option::Option<T>::map
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:453:29
   5: <core::iter::adapters::Map<I,F> as core::iter::traits::iterator::Iterator>::next
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/adapters/mod.rs:924:9
   6: <alloc::vec::Vec<T> as alloc::vec::SpecFromIterNested<T,I>>::from_iter
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec.rs:2082:32
   7: <alloc::vec::Vec<T> as alloc::vec::SpecFromIter<T,I>>::from_iter
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec.rs:2119:9
   8: <alloc::vec::Vec<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec.rs:1959:9
   9: core::iter::traits::iterator::Iterator::collect
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:1670:9
  10: wayland_client::imp::queues::message_to_rawevent
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/queues.rs:272:16
  11: wayland_client::imp::queues::EventQueueInner::dispatch_buffer
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/queues.rs:171:41
  12: wayland_client::imp::queues::EventQueueInner::dispatch_pending
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/queues.rs:201:31
  13: wayland_client::imp::queues::EventQueueInner::dispatch
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/queues.rs:105:28
  14: wayland_client::imp::queues::EventQueueInner::sync_roundtrip
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/rust_imp/queues.rs:231:27
  15: wayland_client::event_queue::EventQueue::sync_roundtrip
             at /home/hugo/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.28.5/src/event_queue.rs:191:9
  16: clipmon::main
             at ./src/main.rs:44:5
  17: core::ops::function::FnOnce::call_once
             at /home/hugo/.local/share/rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

WhyNotHugo avatar Mar 09 '21 18:03 WhyNotHugo

I actually haven't figured out what the above error is. I've tidied up that same code, but it still fails with this assertion.

Any ideas what the failure means?

use wayland_client::protocol::wl_seat::WlSeat;
use wayland_client::Display;
use wayland_client::GlobalManager;
use wayland_protocols::wlr::unstable::data_control::v1::client::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1;

fn main() {
    let display = Display::connect_to_env().unwrap();
    let mut event_queue = display.create_event_queue();
    let attached_display = (*display).clone().attach(event_queue.token());
    let globals = GlobalManager::new(&attached_display);

    // Make a synchronized roundtrip to the wayland server.
    //
    // When this returns it must be true that the server has already
    // sent us all available globals.
    event_queue
        .sync_roundtrip(&mut (), |_, _, _| unreachable!())
        .unwrap();

    let seat = globals.instantiate_exact::<WlSeat>(1).unwrap();
    println!("Have a seat: {:?}", seat);

    event_queue
        .sync_roundtrip(&mut (), |raw_event, _, _| {
            // This is usually the compositor telling us about its capabilities.
            // I don't think we need to do anything about this.
            println!("Got capabilities: {:?}", raw_event)
        })
        .unwrap();

    let manager = match globals.instantiate_exact::<ZwlrDataControlManagerV1>(1) {
        Err(err) => {
            println!("Compositor doesn't support wlr-data-control-unstable-v1.");
            panic!("{}", err);
        }
        Ok(res) => res,
    };

    let data_device = manager.get_data_device(&seat); // <<-- the thing I need to use.
    println!("Got the data device: {:?}", data_device);

    // Crash happens after this line (which just sends the above and gets a reply) ------------
    event_queue
        .sync_roundtrip(&mut (), |_, _, _| unreachable!())
        .unwrap();
    // Crash happens above this line ------------

    unreachable!("This line is never reached :(");
}

WhyNotHugo avatar Sep 23 '21 17:09 WhyNotHugo

The spec of the protocol mentions that the protocol can be used from a privileged client. I am not sure how privileged clients are implemented in the compositor you test with. I would expect that a protocol that is reserved for privileged clients is not advertised for non privileged clients, but i may be wrong. A client that is considered privileged is most likely spawned directly by the compositor and not from the outside for security reasons. Just a quick guess.

cmeissl avatar Sep 23 '21 18:09 cmeissl

Privileged clients are not currently enforced (at least not on sway).

Sway can run nested inside another compositor, maybe I can provide a reproducible environment / example of the failure.

WhyNotHugo avatar Sep 23 '21 18:09 WhyNotHugo

I tried to reproduce and the unreachable of the last sync_rountrip is triggered. This is because the events generated from the instantiated objects are not handled, the callback of sync_rountrip is called as a fallback for this unhandled events.

Here is a sample using quick_assign to receive the events from the objects:

use wayland_client::protocol::wl_seat::WlSeat;
use wayland_client::Display;
use wayland_client::GlobalManager;
use wayland_protocols::wlr::unstable::data_control::v1::client::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1;

fn main() {
    let display = Display::connect_to_env().unwrap();
    let mut event_queue = display.create_event_queue();
    let attached_display = (*display).clone().attach(event_queue.token());
    let globals = GlobalManager::new(&attached_display);

    // Make a synchronized roundtrip to the wayland server.
    //
    // When this returns it must be true that the server has already
    // sent us all available globals.
    event_queue
        .sync_roundtrip(&mut (), |_, _, _| unreachable!())
        .unwrap();

    let seat = globals.instantiate_exact::<WlSeat>(1).unwrap();
    println!("Have a seat: {:?}", seat);

    seat.quick_assign(|_main, event, c| match event {
        wayland_client::protocol::wl_seat::Event::Capabilities { capabilities } => {
            eprint!("Capabilities: {:?}", capabilities)
        }
        wayland_client::protocol::wl_seat::Event::Name { name } => eprint!("Seat name: {}", name),
        _ => unreachable!(),
    });

    event_queue
        .sync_roundtrip(&mut (), |raw_event, _, _| {
            // This is usually the compositor telling us about its capabilities.
            // I don't think we need to do anything about this.
            println!("Got capabilities: {:?}", raw_event)
        })
        .unwrap();

    let manager = match globals.instantiate_exact::<ZwlrDataControlManagerV1>(1) {
        Err(err) => {
            println!("Compositor doesn't support wlr-data-control-unstable-v1.");
            panic!("{}", err);
        }
        Ok(res) => res,
    };

    manager.quick_assign(|_, _, _| {
        // No event to handle
    });

    let data_device = manager.get_data_device(&seat); // <<-- the thing I need to use.

    data_device.quick_assign(|_main, ev, _dispatch_data| {
    match ev {
        wayland_protocols::wlr::unstable::data_control::v1::client::zwlr_data_control_device_v1::Event::DataOffer { id } => eprintln!("Got data offer"),
        wayland_protocols::wlr::unstable::data_control::v1::client::zwlr_data_control_device_v1::Event::Selection { id } => eprintln!("Got selection"),
        wayland_protocols::wlr::unstable::data_control::v1::client::zwlr_data_control_device_v1::Event::Finished => eprintln!("Finished"),
        wayland_protocols::wlr::unstable::data_control::v1::client::zwlr_data_control_device_v1::Event::PrimarySelection { id } => eprintln!("Got primary selection"),
        _ => unreachable!()
    }
});

    println!("Got the data device: {:?}", data_device);

    event_queue.sync_roundtrip(&mut (), |_, _, _| {}).unwrap();

    // Do something with the received events
}

cmeissl avatar Sep 23 '21 19:09 cmeissl

Why do you call:

    manager.quick_assign(|_, _, _| {
        // No event to handle
    });

?

WhyNotHugo avatar Sep 23 '21 19:09 WhyNotHugo

Thanks for the working example, this makes it very clear how to structure event handling too. :+1:

WhyNotHugo avatar Sep 23 '21 19:09 WhyNotHugo

Why do you call:

    manager.quick_assign(|_, _, _| {
        // No event to handle
    });

?

This is just a leftover from checking which objects provide events and to act as an example. This can safely be removed.

cmeissl avatar Sep 23 '21 19:09 cmeissl

Oh. So why does the original snippet crash? Do I need to listen to messages on any object I create individually?

WhyNotHugo avatar Sep 23 '21 19:09 WhyNotHugo

In your second snippet you do not handle the events from data_device. The non-handled events will invoke the fallback callback in sync_rountrip which will panic with unreachable. manager does not provide any events and nothing needs to be handled. You original snippet does not crash for me. I tested it with a nested sway (version 1.6) under gnome (sway running under wayland-1). Handling events with quick_assign provides direct access to the typed event enum which you will most probably need and also enables access to the typed object which received the event. If it still crashes for you running with WAYLAND_DEBUG=1 may show some more info.

cmeissl avatar Sep 23 '21 20:09 cmeissl

My second snippet does not crash on the unrechable, it crashes with:

✘ RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.15s
     Running `target/debug/clipmon`
Have a seat: wl_seat@3[MAIN]
Got capabilities: RawEvent { interface: "wl_seat", opcode: 0, name: "capabilities", args: [Uint(7)] }
Got the data device: zwlr_data_control_device_v1@5[MAIN]
thread 'main' panicked at 'assertion failed: self.map.lock().unwrap().find(id).map(|obj|\n                                          obj.is_interface::<I>()).unwrap_or(true)', /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/mod.rs:75:9
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:515:5
   1: core::panicking::panic_fmt
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/panicking.rs:92:14
   2: core::panicking::panic
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/panicking.rs:50:5
   3: wayland_client::imp::ProxyMap::get_new
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/mod.rs:75:9
   4: wayland_client::imp::queues::message_to_rawevent::{{closure}}
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/queues.rs:290:59
   5: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &mut F>::call_once
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/ops/function.rs:280:13
   6: core::option::Option<T>::map
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/option.rs:489:29
   7: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::next
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/iter/adapters/map.rs:101:9
   8: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter_nested::SpecFromIterNested<T,I>>::from_iter
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/alloc/src/vec/spec_from_iter_nested.rs:23:32
   9: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/alloc/src/vec/spec_from_iter.rs:33:9
  10: <alloc::vec::Vec<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/alloc/src/vec/mod.rs:2449:9
  11: core::iter::traits::iterator::Iterator::collect
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/iter/traits/iterator.rs:1748:9
  12: wayland_client::imp::queues::message_to_rawevent
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/queues.rs:268:16
  13: wayland_client::imp::queues::EventQueueInner::dispatch_buffer
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/queues.rs:168:41
  14: wayland_client::imp::queues::EventQueueInner::dispatch_pending
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/queues.rs:198:31
  15: wayland_client::imp::queues::EventQueueInner::dispatch
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/queues.rs:102:28
  16: wayland_client::imp::queues::EventQueueInner::sync_roundtrip
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/rust_imp/queues.rs:228:27
  17: wayland_client::event_queue::EventQueue::sync_roundtrip
             at /home/hugo/.local/state/cargo/registry/src/github.com-1ecc6299db9ec823/wayland-client-0.29.0/src/event_queue.rs:203:9
  18: clipmon::main
             at ./src/main.rs:43:5
  19: core::ops::function::FnOnce::call_once
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

I would expect it to crash with the unrechable() though.

WhyNotHugo avatar Sep 23 '21 21:09 WhyNotHugo

I don't think this is an issue any more with API changes in recent versions.

WhyNotHugo avatar Oct 27 '22 08:10 WhyNotHugo