feat: rudimentary implementation with Layer Shell
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
use bevy::prelude::*;
|
||||
use smithay_client_toolkit::{
|
||||
delegate_seat,
|
||||
reexports::client::QueueHandle,
|
||||
seat::{SeatHandler, SeatState},
|
||||
};
|
||||
|
||||
use crate::WaylandState;
|
||||
|
||||
pub struct InputHandlerPlugin;
|
||||
impl Plugin for InputHandlerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let globals = app.world().non_send_resource();
|
||||
let queue_handle: &QueueHandle<WaylandState> = app.world().non_send_resource();
|
||||
let seat_state = SeatState::new(globals, queue_handle);
|
||||
|
||||
app.insert_non_send_resource(seat_state);
|
||||
}
|
||||
}
|
||||
|
||||
impl SeatHandler for WaylandState {
|
||||
fn seat_state(&mut self) -> &mut SeatState {
|
||||
self.world_mut()
|
||||
.non_send_resource_mut::<SeatState>()
|
||||
.into_inner()
|
||||
}
|
||||
|
||||
fn new_seat(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
|
||||
) {
|
||||
}
|
||||
|
||||
fn new_capability(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
|
||||
_capability: smithay_client_toolkit::seat::Capability,
|
||||
) {
|
||||
}
|
||||
|
||||
fn remove_capability(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
|
||||
_capability: smithay_client_toolkit::seat::Capability,
|
||||
) {
|
||||
}
|
||||
|
||||
fn remove_seat(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
|
||||
) {
|
||||
}
|
||||
}
|
||||
delegate_seat!(WaylandState);
|
||||
@@ -0,0 +1,75 @@
|
||||
use bevy::prelude::*;
|
||||
use smithay_client_toolkit::{
|
||||
delegate_layer,
|
||||
reexports::client::{globals::GlobalList, QueueHandle},
|
||||
shell::{
|
||||
wlr_layer::{Layer, LayerShell, LayerShellHandler},
|
||||
WaylandSurface,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{surface_handler::WaylandSurfaces, WaylandState};
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct LayerShellWindow {}
|
||||
#[derive(Component)]
|
||||
struct LayerShellRoleAssigned;
|
||||
|
||||
pub struct LayerShellPlugin;
|
||||
impl Plugin for LayerShellPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, assign_layer_shell_role);
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_layer_shell_role(
|
||||
mut commands: Commands,
|
||||
wayland_surfaces: NonSend<WaylandSurfaces>,
|
||||
queue_handle: NonSend<QueueHandle<WaylandState>>,
|
||||
globals: NonSend<GlobalList>,
|
||||
layer_shell_windows: Query<
|
||||
(Entity, &Window, &LayerShellWindow),
|
||||
Without<LayerShellRoleAssigned>,
|
||||
>,
|
||||
) {
|
||||
for (entity, _window, _layer_shell_settings) in &layer_shell_windows {
|
||||
let window_wrapper = wayland_surfaces.get_window_wrapper(entity);
|
||||
let surface = window_wrapper
|
||||
.expect("tried to assign role before creating surface!")
|
||||
.wl_surface();
|
||||
|
||||
let layer_shell =
|
||||
LayerShell::bind(&globals, &queue_handle).expect("layer shell not available!");
|
||||
let layer = layer_shell.create_layer_surface(
|
||||
&queue_handle,
|
||||
surface.clone(),
|
||||
Layer::Top,
|
||||
Some("simple_layer"),
|
||||
None,
|
||||
);
|
||||
layer.commit();
|
||||
Box::leak(Box::new(layer));
|
||||
commands.entity(entity).insert(LayerShellRoleAssigned);
|
||||
}
|
||||
}
|
||||
|
||||
impl LayerShellHandler for WaylandState {
|
||||
fn closed(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_layer: &smithay_client_toolkit::shell::wlr_layer::LayerSurface,
|
||||
) {
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_layer: &smithay_client_toolkit::shell::wlr_layer::LayerSurface,
|
||||
_configure: smithay_client_toolkit::shell::wlr_layer::LayerSurfaceConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
}
|
||||
}
|
||||
delegate_layer!(WaylandState);
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
use bevy::{app::PluginsState, prelude::*};
|
||||
use smithay_client_toolkit::{
|
||||
delegate_registry,
|
||||
output::OutputState,
|
||||
reexports::{
|
||||
calloop::EventLoop,
|
||||
calloop_wayland_source::WaylandSource,
|
||||
client::{globals::registry_queue_init, Connection},
|
||||
},
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
seat::SeatState,
|
||||
};
|
||||
|
||||
mod input_handler;
|
||||
pub mod layer_shell;
|
||||
mod output_handler;
|
||||
mod surface_handler;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WaylandPlugin;
|
||||
impl Plugin for WaylandPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let connection =
|
||||
Connection::connect_to_env().expect("failed to connect to wayland socket!");
|
||||
let event_loop =
|
||||
EventLoop::<WaylandState>::try_new().expect("failed to create event_loop!");
|
||||
let (globals, event_queue) = registry_queue_init::<WaylandState>(&connection)
|
||||
.expect("failed to init registry queue");
|
||||
|
||||
let qh = event_queue.handle();
|
||||
let loop_handle = event_loop.handle();
|
||||
WaylandSource::new(connection.clone(), event_queue)
|
||||
.insert(loop_handle.clone())
|
||||
.expect("failed to insert wayland source to event loop");
|
||||
|
||||
app.insert_non_send_resource(RegistryState::new(&globals));
|
||||
app.insert_non_send_resource(connection.clone());
|
||||
app.insert_non_send_resource(globals);
|
||||
app.insert_non_send_resource(qh);
|
||||
|
||||
app.add_plugins((
|
||||
output_handler::OutputHandlerPlugin,
|
||||
input_handler::InputHandlerPlugin,
|
||||
surface_handler::SurfaceHandlerPlugin,
|
||||
layer_shell::LayerShellPlugin,
|
||||
));
|
||||
app.set_runner(|app| runner(app, event_loop));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runner(mut app: App, mut event_loop: EventLoop<'_, WaylandState>) -> AppExit {
|
||||
if app.plugins_state() == PluginsState::Ready {
|
||||
app.finish();
|
||||
app.cleanup();
|
||||
}
|
||||
|
||||
let mut state = WaylandState(app);
|
||||
loop {
|
||||
// TODO: Error handling
|
||||
let _ = event_loop.dispatch(None, &mut state);
|
||||
|
||||
if state.plugins_state() == PluginsState::Cleaned {
|
||||
state.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct WaylandState(App);
|
||||
impl ProvidesRegistryState for WaylandState {
|
||||
fn registry(&mut self) -> &mut smithay_client_toolkit::registry::RegistryState {
|
||||
self.world_mut()
|
||||
.non_send_resource_mut::<RegistryState>()
|
||||
.into_inner()
|
||||
}
|
||||
registry_handlers!(OutputState, SeatState);
|
||||
}
|
||||
delegate_registry!(WaylandState);
|
||||
@@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
use bevy::prelude::*;
|
||||
use smithay_client_toolkit::{
|
||||
delegate_output,
|
||||
output::{OutputHandler, OutputState},
|
||||
reexports::client::QueueHandle,
|
||||
};
|
||||
|
||||
use crate::WaylandState;
|
||||
|
||||
pub struct OutputHandlerPlugin;
|
||||
impl Plugin for OutputHandlerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let globals = app.world().non_send_resource();
|
||||
let queue_handle: &QueueHandle<WaylandState> = app.world().non_send_resource();
|
||||
let output_state = OutputState::new(globals, queue_handle);
|
||||
|
||||
app.insert_non_send_resource(output_state);
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputHandler for WaylandState {
|
||||
fn output_state(&mut self) -> &mut OutputState {
|
||||
self.world_mut()
|
||||
.non_send_resource_mut::<OutputState>()
|
||||
.into_inner()
|
||||
}
|
||||
|
||||
fn new_output(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
info!("new output was added");
|
||||
}
|
||||
|
||||
fn update_output(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn output_destroyed(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
delegate_output!(WaylandState);
|
||||
@@ -0,0 +1,194 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bevy::{
|
||||
ecs::entity::EntityHashMap,
|
||||
prelude::*,
|
||||
window::{RawHandleWrapper, RawHandleWrapperHolder, WindowCreated, WindowWrapper},
|
||||
};
|
||||
use raw_window_handle::{
|
||||
DisplayHandle, HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||
WaylandDisplayHandle, WaylandWindowHandle, WindowHandle,
|
||||
};
|
||||
use smithay_client_toolkit::{
|
||||
compositor::{CompositorHandler, CompositorState},
|
||||
delegate_compositor,
|
||||
reexports::client::{
|
||||
backend::ObjectId, protocol::wl_surface::WlSurface, Connection, Proxy, QueueHandle,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::WaylandState;
|
||||
|
||||
pub struct SurfaceHandlerPlugin;
|
||||
impl Plugin for SurfaceHandlerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let queue_handle: &QueueHandle<WaylandState> = app.world().non_send_resource();
|
||||
let globals = app.world().non_send_resource();
|
||||
app.insert_non_send_resource(
|
||||
CompositorState::bind(globals, queue_handle).expect("failed to bind compositor!"),
|
||||
);
|
||||
app.insert_non_send_resource(WaylandSurfaces::default());
|
||||
app.add_systems(PreUpdate, create_windows);
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositorHandler for WaylandState {
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_new_factor: i32,
|
||||
) {
|
||||
}
|
||||
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_new_transform: smithay_client_toolkit::reexports::client::protocol::wl_output::Transform,
|
||||
) {
|
||||
}
|
||||
|
||||
fn frame(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_output: &smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_conn: &smithay_client_toolkit::reexports::client::Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_output: &smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
delegate_compositor!(WaylandState);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WaylandSurfaces {
|
||||
windows: HashMap<ObjectId, WindowWrapper<WaylandSurface>>,
|
||||
entity_to_surface: EntityHashMap<ObjectId>,
|
||||
surface_to_entity: HashMap<ObjectId, Entity>,
|
||||
|
||||
_not_send_sync: core::marker::PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
impl WaylandSurfaces {
|
||||
pub fn create_surface(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
queue_handle: &QueueHandle<WaylandState>,
|
||||
connection: Connection,
|
||||
compositor_state: &CompositorState,
|
||||
) -> &WindowWrapper<WaylandSurface> {
|
||||
let wl_surface = compositor_state.create_surface(queue_handle);
|
||||
let wayland_surface = WaylandSurface::new(wl_surface, connection);
|
||||
let surface_id = wayland_surface.id();
|
||||
self.windows
|
||||
.insert(wayland_surface.id(), WindowWrapper::new(wayland_surface));
|
||||
|
||||
self.entity_to_surface.insert(entity, surface_id.clone());
|
||||
self.surface_to_entity.insert(surface_id.clone(), entity);
|
||||
self.windows.get(&surface_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_window_wrapper(&self, entity: Entity) -> Option<&WindowWrapper<WaylandSurface>> {
|
||||
self.entity_to_surface
|
||||
.get(&entity)
|
||||
.map(|surface_id| self.windows.get(surface_id))?
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaylandSurface {
|
||||
surface: WlSurface,
|
||||
connection: Connection,
|
||||
}
|
||||
|
||||
impl WaylandSurface {
|
||||
pub fn new(surface: WlSurface, connection: Connection) -> Self {
|
||||
Self {
|
||||
surface,
|
||||
connection,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wl_surface(&self) -> &WlSurface {
|
||||
&self.surface
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ObjectId {
|
||||
self.surface.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasWindowHandle for WaylandSurface {
|
||||
fn window_handle(
|
||||
&self,
|
||||
) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError>
|
||||
{
|
||||
let raw_window_handle = RawWindowHandle::Wayland(WaylandWindowHandle::new(
|
||||
core::ptr::NonNull::new(self.wl_surface().id().as_ptr() as *mut _).unwrap(),
|
||||
));
|
||||
unsafe { Ok(WindowHandle::borrow_raw(raw_window_handle)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDisplayHandle for WaylandSurface {
|
||||
fn display_handle(
|
||||
&self,
|
||||
) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
|
||||
let raw_display_handle = RawDisplayHandle::Wayland(WaylandDisplayHandle::new(
|
||||
core::ptr::NonNull::new(self.connection.backend().display_ptr() as *mut _).unwrap(),
|
||||
));
|
||||
unsafe { Ok(DisplayHandle::borrow_raw(raw_display_handle)) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_windows(
|
||||
mut commands: Commands,
|
||||
mut wayland_surfaces: NonSendMut<WaylandSurfaces>,
|
||||
compositor_state: NonSend<CompositorState>,
|
||||
connection: NonSend<Connection>,
|
||||
queue_handle: NonSend<QueueHandle<WaylandState>>,
|
||||
bevy_windows: Query<(Entity, Option<&RawHandleWrapperHolder>), With<Window>>,
|
||||
mut window_created_event: EventWriter<WindowCreated>,
|
||||
) {
|
||||
for (entity, handle_holder) in &bevy_windows {
|
||||
if wayland_surfaces.get_window_wrapper(entity).is_some() {
|
||||
continue;
|
||||
}
|
||||
println!("Creating Window");
|
||||
|
||||
let surface = wayland_surfaces.create_surface(
|
||||
entity,
|
||||
&queue_handle,
|
||||
connection.clone(),
|
||||
&compositor_state,
|
||||
);
|
||||
let mut wrapper: Option<_> = None;
|
||||
if let Ok(handle_wrapper) = RawHandleWrapper::new(surface) {
|
||||
wrapper = Some(handle_wrapper.clone());
|
||||
if let Some(handle_holder) = handle_holder {
|
||||
*handle_holder.0.lock().unwrap() = Some(handle_wrapper);
|
||||
}
|
||||
}
|
||||
commands.entity(entity).insert(wrapper.unwrap());
|
||||
window_created_event.write(WindowCreated { window: entity });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user