`Wrong type parameter passed to Handle::create_global()`: delegating to multiple `struct`s?
As part of my compositor experiment, I'm working on the Engine trait which decouples backend (winit, udev, etc) from compositor.
/// state of compositor. does not contain reference to Engine or Renderer
struct AppState { /*...*/ }
/// implementation of compositor
impl AppState { /*...*/ }
/// provides display handle, event loop handle, renderer, runs main loop
trait Engine { /*...*/ }
Now I want to implement dmabuf support for both WinitEngine and UdevEngine. I need to impl DmabufHandler and invoke delegate_dmabuf!.
In anvil, this is done by implementing DmabufHandler for App<WinitData> and App<UdevData>, but, as pointed out above, in my architecture AppState(=State) and Engine(=run_winit+WinitData/run_udev+UdevData) are decoupled.
So let's see what happens:
/// state for this platform. does not contain reference to AppState.
struct WinitEngine {
renderer: Gles2Renderer
}
impl WinitEngine {
fn new () -> Self {
// ...
let mut dmabuf_state = DmabufState::new();
let dmabuf_global = dmabuf_state.create_global::<Self, _>( /* ... */ );
// ...
}
}
/// implementation of engine api for this platform
impl Engine for WinitEngine { /*...*/ }
/// implementation of dmabuf handling for this platform
impl DmabufHandler for WinitEngine { /*...*/ }
delegate_dmabuf!(WinitEngine)
Here things get tricky. DmabufHandler needs DmabufState and DmabufGlobal from WinitEngine. So I create those (see WinitEngine::new() above); and it compiles then panics with Wrong type parameter passed to Handle::create_global(). -- apparently because all my other delegate_*! are on AppState.
What needs to be done to make global dispatch work with more than one struct?
Wrong type parameter passed to Handle::create_global().
This is because the State parameter passed to create_global must match the generic on Display
It could be useful to add a debug only field inside DisplayHandle which says what the expected type name is.
Yep, just realized that. What is the reasoning behind that constraint though? Is there some protocol-level reason that I only have to attach those globals to one specific type?
The Display has to call the corresponding functions, when it gets protocol messages. So it needs to know, what it has to call, which is why the State is passed to dispatch_clients.
So having multiple types here, doesn't really work.
Okay, that makes sense. I already have a couple workarounds in mind. One basically looks like this:
struct App<E: Engine> {
display: Display<Self>, // maybe Rc<RefCell<Display<Self>>>
engine: E,
state: AppState
}
Having hoisted Display outside of both Engine and State, I suppose I could impl and delegate! on App and be able to "reach into" both AppState and Engine as needed by the impls. This also simplifies my traits somewhat (no need for a State associated type on Engine), so I guess that's what I'm gonna try first.
The other is wackier: create an individual Display instance for every state type that I need to dispatch against, and feed them all from the same Wayland socket and fd. Looking through the source of wayland_server and wayland_backend, it doesn't seem like Display does anything else than pass around protocol messages?
EDIT: Whoops no, #1 won't do. Since AppState doesn't contain a reference to App, I wouldn't be able to pass App to global dispatches inititated from e.g. input handlers. Guess I'll give multiple Displays a go then.
EDIT2: Nope, that's typed, too... The "root" of the state dispatch is the event loop, in particular its callback from the Generic FD. So maybe instead I'll do this:
struct App<E: Engine> {
events: EventLoop<'static, Self>,
display: Display<Self>,
engine: E,
state: AppState
}
That way the engine doesn't own neither the EventLoop nor the Display, I can just pass handles to 'em into it... right? Let's see.