From 43b2b672541349d5667d5b16232cca42da1bbddc Mon Sep 17 00:00:00 2001 From: Nova Date: Sat, 10 Sep 2022 08:54:15 -0400 Subject: [PATCH] feat(wayland): cursors! --- src/wayland/compositor.rs | 15 +- src/wayland/mod.rs | 74 +++----- src/wayland/panel_item.rs | 379 ++++++++++++++++++++++++-------------- src/wayland/seat.rs | 86 +++++++-- src/wayland/state.rs | 13 +- src/wayland/surface.rs | 233 +++++++++++++++++------ 6 files changed, 522 insertions(+), 278 deletions(-) diff --git a/src/wayland/compositor.rs b/src/wayland/compositor.rs index a54d18a..7825af3 100644 --- a/src/wayland/compositor.rs +++ b/src/wayland/compositor.rs @@ -1,11 +1,9 @@ -use super::state::WaylandState; -use crate::nodes::{core::Node, item::ItemType}; +use super::{state::WaylandState, surface::CoreSurface}; use smithay::{ delegate_compositor, reexports::wayland_server::protocol::wl_surface::WlSurface, wayland::compositor::{self, CompositorHandler, CompositorState}, }; -use std::sync::Arc; impl CompositorHandler for WaylandState { fn compositor_state(&mut self) -> &mut CompositorState { @@ -13,13 +11,10 @@ impl CompositorHandler for WaylandState { } fn commit(&mut self, surface: &WlSurface) { - compositor::with_states(surface, |data| { - if let Some(panel_node) = data.data_map.get::>() { - let item = panel_node.item.get().unwrap(); - if let ItemType::Panel(panel_item) = &item.specialization { - panel_item.resize(&data.data_map); - } - } + compositor::with_states(&surface, |data| { + data.data_map.insert_if_missing_threadsafe(|| { + CoreSurface::new(&self.display, self.display_handle.clone(), &surface) + }) }); } } diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 0627f08..654c63c 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -7,23 +7,15 @@ pub mod surface; pub mod xdg_decoration; pub mod xdg_shell; -use self::{panel_item::PanelItem, state::WaylandState}; -use crate::{nodes::core::Node, wayland::state::ClientState}; +use self::{panel_item::PanelItem, state::WaylandState, surface::CORE_SURFACES}; +use crate::wayland::state::ClientState; use anyhow::{ensure, Result}; use once_cell::sync::OnceCell; use parking_lot::Mutex; use slog::Logger; use smithay::{ - backend::{ - egl::EGLContext, - renderer::{ - gles2::Gles2Renderer, - utils::{import_surface_tree, on_commit_buffer_handler, RendererSurfaceStateUserData}, - }, - }, - desktop::utils::send_frames_surface_tree, + backend::{egl::EGLContext, renderer::gles2::Gles2Renderer}, reexports::wayland_server::{backend::GlobalId, Display, ListeningSocket}, - wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceData}, }; use std::os::unix::prelude::AsRawFd; use std::{ @@ -36,7 +28,6 @@ use std::{ }; use stereokit as sk; use stereokit::StereoKit; -use surface::CoreSurface; use tokio::{ io::unix::AsyncFd, net::UnixListener as AsyncUnixListener, sync::mpsc, task::JoinHandle, }; @@ -70,7 +61,7 @@ pub struct Wayland { display: Arc>>, join_handle: JoinHandle>, renderer: Gles2Renderer, - state: Arc>, + // state: Arc>, } impl Wayland { pub fn new(log: Logger) -> Result { @@ -93,7 +84,8 @@ impl Wayland { let display = Arc::new(Mutex::new(display)); let state = Arc::new(Mutex::new(WaylandState::new( log.clone(), - display_handle.clone(), + display.clone(), + display_handle, ))); let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8); @@ -107,7 +99,7 @@ impl Wayland { display, join_handle, renderer, - state, + // state, }) } @@ -154,47 +146,23 @@ impl Wayland { } pub fn frame(&mut self, sk: &StereoKit) { - let log = self.log.clone(); let time_ms = (sk.time_getf() * 1000.) as u32; - let toplevel_surfaces = self - .state - .lock() - .xdg_shell_state - .toplevel_surfaces(|surfs| surfs.to_vec()); - for surf in toplevel_surfaces { - // Let Smithay handle all the buffer maintenance - on_commit_buffer_handler(surf.wl_surface()); - // Import all surface buffers into textures - import_surface_tree(&mut self.renderer, surf.wl_surface(), &log).unwrap(); - with_states(surf.wl_surface(), |data| { - let mapped = data - .data_map - .get::() - .map(|surface_states| surface_states.borrow().wl_buffer().is_some()) - .unwrap_or(false); - - if mapped && data.data_map.get::().is_some() { - data.data_map.insert_if_missing_threadsafe(CoreSurface::new); - data.data_map.insert_if_missing_threadsafe(|| { - PanelItem::create( - &self.display, - self.display.lock().handle(), - &data.data_map, - surf.wl_surface().clone(), - ) - }); - - if let Some(core_surface) = data.data_map.get::() { - core_surface.update_tex(sk, data, &self.renderer); - if let Some(panel_item) = data.data_map.get::>() { - PanelItem::apply_surface_materials(panel_item, core_surface); - } - } - } - }); - send_frames_surface_tree(surf.wl_surface(), time_ms); + for core_surface in CORE_SURFACES.get_valid_contents() { + core_surface.process( + sk, + &mut self.renderer, + time_ms, + &self.log, + |data| { + PanelItem::on_mapped(&core_surface, data); + }, + |data| { + PanelItem::if_mapped(&core_surface, data); + }, + ); } + self.display.lock().flush_clients().unwrap(); } diff --git a/src/wayland/panel_item.rs b/src/wayland/panel_item.rs index f4b1eb3..d696d46 100644 --- a/src/wayland/panel_item.rs +++ b/src/wayland/panel_item.rs @@ -1,4 +1,4 @@ -use super::{seat::SeatData, state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE}; +use super::{seat::SeatData, surface::CoreSurface}; use crate::{ core::{ client::{Client, INTERNAL_CLIENT}, @@ -7,28 +7,26 @@ use crate::{ nodes::{ core::Node, item::{register_item_ui_flex, Item, ItemSpecialization, ItemType, TypeInfo}, - model::Model, spatial::Spatial, }, }; use anyhow::{anyhow, bail, Result}; use glam::Mat4; use lazy_static::lazy_static; -use libstardustxr::{flex::flexbuffer_from_vector_arguments, flex_to_vec2}; +use libstardustxr::{ + flex::{flexbuffer_from_arguments, flexbuffer_from_vector_arguments}, + flex_to_vec2, +}; use nanoid::nanoid; -use parking_lot::Mutex; use smithay::{ - backend::renderer::utils::RendererSurfaceStateUserData, reexports::wayland_server::{ - backend::ObjectId, protocol::{ wl_keyboard::KeyState, wl_pointer::{Axis, ButtonState}, - wl_surface::WlSurface, }, - Display, DisplayHandle, Resource, + Resource, }, - utils::{user_data::UserDataMap, Logical, Size}, + wayland::{compositor::SurfaceData, shell::xdg::XdgToplevelSurfaceData}, }; use std::{ convert::TryInto, @@ -40,6 +38,7 @@ lazy_static! { type_name: "panel", aliased_local_signals: vec![ "applySurfaceMaterial", + "applyCursorMaterial", "pointerDeactivate", "pointerScroll", "pointerButton", @@ -51,7 +50,7 @@ lazy_static! { "close", ], aliased_local_methods: vec![], - aliased_remote_signals: vec![], + aliased_remote_signals: vec!["resize", "setCursor",], aliased_remote_methods: vec![], ui: Default::default(), items: Registry::new(), @@ -61,20 +60,11 @@ lazy_static! { pub struct PanelItem { node: Weak, - pending_material_applications: Mutex, u32)>>, - display: Weak>>, - dh: DisplayHandle, - pub toplevel_surface_id: ObjectId, + core_surface: Weak, seat_data: SeatData, - size: Mutex>, } impl PanelItem { - pub fn create( - display: &Arc>>, - dh: DisplayHandle, - data: &UserDataMap, - toplevel_surface: WlSurface, - ) -> Arc { + pub fn create(core_surface: &Arc) -> Arc { let node = Arc::new(Node::create( &INTERNAL_CLIENT, "/item/panel/item", @@ -83,30 +73,26 @@ impl PanelItem { )); Spatial::add_to(&node, None, Mat4::IDENTITY).unwrap(); - let seat_data = SeatData::new(&dh, toplevel_surface.client_id().unwrap()); - - let size = data - .get::() - .unwrap() - .borrow() - .surface_size() - .map(Mutex::new) - .unwrap(); + let seat_data = SeatData::new( + &core_surface.dh, + core_surface.wl_surface().client_id().unwrap(), + ); let specialization = ItemType::Panel(PanelItem { node: Arc::downgrade(&node), - pending_material_applications: Mutex::new(Vec::new()), - display: Arc::downgrade(display), - dh, - toplevel_surface_id: toplevel_surface.id(), + core_surface: Arc::downgrade(core_surface), seat_data, - size, }); - Item::add_to(&node, &ITEM_TYPE_INFO_PANEL, specialization); + let item = Item::add_to(&node, &ITEM_TYPE_INFO_PANEL, specialization); + if let ItemType::Panel(panel) = &item.specialization { + let _ = panel.seat_data.panel_item.set(Arc::downgrade(&item)); + } + node.add_local_signal( "applySurfaceMaterial", PanelItem::apply_surface_material_flex, ); + node.add_local_signal("applyCursorMaterial", PanelItem::apply_cursor_material_flex); node.add_local_signal("pointerDeactivate", PanelItem::pointer_deactivate_flex); node.add_local_signal("pointerScroll", PanelItem::pointer_scroll_flex); node.add_local_signal("pointerButton", PanelItem::pointer_button_flex); @@ -123,30 +109,10 @@ impl PanelItem { node } - fn toplevel_surface(&self) -> WlSurface { - WlSurface::from_id(&self.dh, self.toplevel_surface_id.clone()).unwrap() - } - fn flush_clients(&self) { - self.display - .upgrade() - .unwrap() - .lock() - .flush_clients() - .unwrap(); - } - - pub fn resize(&self, data: &UserDataMap) { - if let Some(surface_states) = data.get::() { - if let Some(size) = surface_states.borrow().buffer_size() { - *self.size.lock() = size; - let _ = self.node.upgrade().unwrap().send_remote_signal( - "resize", - &flexbuffer_from_vector_arguments(|vec| { - vec.push(size.w); - vec.push(size.h); - }), - ); - } + fn from_node(node: &Node) -> &PanelItem { + match &node.item.get().unwrap().specialization { + ItemType::Panel(panel_item) => panel_item, + _ => unreachable!(), } } @@ -164,30 +130,125 @@ impl PanelItem { .model .get() .ok_or_else(|| anyhow!("Node is not a model"))?; - let material_idx = flex_vec.idx(1).get_u64()?; + let material_idx = flex_vec.idx(1).get_u64()? as u32; if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { - panel_item - .pending_material_applications - .lock() - .push((model.clone(), material_idx as u32)); + if let Some(core_surface) = panel_item.core_surface.upgrade() { + core_surface.apply_material(model.clone(), material_idx); + } } Ok(()) } - pub fn apply_surface_materials(node: &Node, core_surface: &CoreSurface) { + fn apply_cursor_material_flex( + node: &Node, + calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; + let model_node = calling_client + .scenegraph + .get_node(flex_vec.idx(0).as_str()) + .ok_or_else(|| anyhow!("Model node not found"))?; + let model = model_node + .model + .get() + .ok_or_else(|| anyhow!("Node is not a model"))?; + let material_idx = flex_vec.idx(1).get_u64()? as u32; + if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { - let mut pending_material_applications = panel_item.pending_material_applications.lock(); - for (model, material_idx) in &*pending_material_applications { - let sk_mat = core_surface.sk_mat.get().unwrap().clone(); - model - .pending_material_replacements - .lock() - .insert(*material_idx, sk_mat); + if let Some(cursor) = &*panel_item.seat_data.cursor.lock() { + if let Some(core_surface) = cursor.lock().core_surface.upgrade() { + core_surface.apply_material(model.clone(), material_idx); + } } - pending_material_applications.clear(); } + + Ok(()) + } + + pub fn on_mapped(core_surface: &Arc, surface_data: &SurfaceData) { + if surface_data + .data_map + .get::() + .is_some() + { + surface_data + .data_map + .insert_if_missing_threadsafe(|| PanelItem::create(core_surface)); + } + } + + pub fn if_mapped(core_surface: &Arc, surface_data: &SurfaceData) { + if let Some(panel_node) = surface_data.data_map.get::>() { + let panel_item = PanelItem::from_node(panel_node); + + core_surface.with_data(|core_surface_data| { + if core_surface_data.resized { + panel_item.resize(core_surface); + core_surface_data.resized = false; + } + }); + + panel_item.set_cursor(); + } + } + + pub fn resize(&self, core_surface: &CoreSurface) { + core_surface.with_data(|data| { + if data.resized { + let _ = self.node.upgrade().unwrap().send_remote_signal( + "resize", + &flexbuffer_from_vector_arguments(|vec| { + vec.push(data.size.x); + vec.push(data.size.y); + }), + ); + data.resized = false; + } + }); + } + + pub fn set_cursor(&self) { + let mut cursor_changed = self.seat_data.cursor_changed.lock(); + if !*cursor_changed { + return; + } + let mut data = flexbuffer_from_arguments(|flex| { + flex.build_singleton(()); + }); + + if let Some(cursor) = &*self.seat_data.cursor.lock() { + let cursor = cursor.lock(); + if let Some(core_surface) = cursor.core_surface.upgrade() { + if let Some(mapped_data) = &*core_surface.mapped_data.lock() { + data = flexbuffer_from_vector_arguments(|vec| { + let mut size_vec = vec.start_vector(); + let size = mapped_data.size; + size_vec.push(size.x); + size_vec.push(size.y); + size_vec.end_vector(); + + let mut hotspot_vec = vec.start_vector(); + hotspot_vec.push(cursor.hotspot.x); + hotspot_vec.push(cursor.hotspot.y); + hotspot_vec.end_vector(); + }); + } else { + return; + }; + } else { + return; + } + } + + let _ = self + .node + .upgrade() + .unwrap() + .send_remote_signal("setCursor", &data); + *cursor_changed = false; } fn pointer_deactivate_flex( @@ -195,13 +256,14 @@ impl PanelItem { _calling_client: Arc, _data: &[u8], ) -> Result<()> { - if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { - if *panel_item.seat_data.pointer_active.lock() { + let panel_item = PanelItem::from_node(node); + if *panel_item.seat_data.pointer_active.lock() { + if let Some(core_surface) = panel_item.core_surface.upgrade() { if let Some(pointer) = panel_item.seat_data.pointer() { - pointer.leave(0, &panel_item.toplevel_surface()); + pointer.leave(0, &core_surface.wl_surface()); *panel_item.seat_data.pointer_active.lock() = false; pointer.frame(); - panel_item.flush_clients(); + core_surface.flush_clients(); } } } @@ -212,18 +274,20 @@ impl PanelItem { fn pointer_motion_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { if let Some(pointer) = panel_item.seat_data.pointer() { - let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; - let x = flex_vec.index(0)?.get_f64()?; - let y = flex_vec.index(1)?.get_f64()?; - let mut pointer_active = panel_item.seat_data.pointer_active.lock(); - if *pointer_active { - pointer.motion(0, x, y); - } else { - pointer.enter(0, &panel_item.toplevel_surface(), x, y); - *pointer_active = true; + if let Some(core_surface) = panel_item.core_surface.upgrade() { + let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; + let x = flex_vec.index(0)?.get_f64()?; + let y = flex_vec.index(1)?.get_f64()?; + let mut pointer_active = panel_item.seat_data.pointer_active.lock(); + if *pointer_active { + pointer.motion(0, x, y); + } else { + pointer.enter(0, &core_surface.wl_surface(), x, y); + *pointer_active = true; + } + pointer.frame(); + core_surface.flush_clients(); } - pointer.frame(); - panel_item.flush_clients(); } } @@ -234,23 +298,25 @@ impl PanelItem { if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { if let Some(pointer) = panel_item.seat_data.pointer() { if *panel_item.seat_data.pointer_active.lock() { - let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; - let button = flex_vec.index(0)?.get_u64()? as u32; - let state = flex_vec.index(1)?.get_u64()? as u32; - pointer.button( - 0, - 0, - button, - match state { - 0 => ButtonState::Released, - 1 => ButtonState::Pressed, - _ => { - bail!("Button state is out of bounds") - } - }, - ); - pointer.frame(); - panel_item.flush_clients(); + if let Some(core_surface) = panel_item.core_surface.upgrade() { + let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; + let button = flex_vec.index(0)?.get_u64()? as u32; + let state = flex_vec.index(1)?.get_u64()? as u32; + pointer.button( + 0, + 0, + button, + match state { + 0 => ButtonState::Released, + 1 => ButtonState::Pressed, + _ => { + bail!("Button state is out of bounds") + } + }, + ); + pointer.frame(); + core_surface.flush_clients(); + } } } } @@ -262,24 +328,31 @@ impl PanelItem { if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { if let Some(pointer) = panel_item.seat_data.pointer() { if *panel_item.seat_data.pointer_active.lock() { - let flex = flexbuffers::Reader::get_root(data)?; - if flex.flexbuffer_type().is_null() { - pointer.axis_stop(0, Axis::HorizontalScroll); - pointer.axis_stop(0, Axis::VerticalScroll); - } else { - let flex_vec = flex.get_vector()?; - let axis_continuous_vec = flex_to_vec2!(flex_vec.idx(0)) - .ok_or_else(|| anyhow!("No continuous axis vector!"))?; - pointer.axis(0, Axis::HorizontalScroll, axis_continuous_vec.x as f64); - pointer.axis(0, Axis::VerticalScroll, axis_continuous_vec.y as f64); - if let Some(axis_discrete_vec) = flex_to_vec2!(flex_vec.idx(0)) { - pointer - .axis_discrete(Axis::HorizontalScroll, axis_discrete_vec.x as i32); - pointer.axis_discrete(Axis::VerticalScroll, axis_discrete_vec.y as i32); + if let Some(core_surface) = panel_item.core_surface.upgrade() { + let flex = flexbuffers::Reader::get_root(data)?; + if flex.flexbuffer_type().is_null() { + pointer.axis_stop(0, Axis::HorizontalScroll); + pointer.axis_stop(0, Axis::VerticalScroll); + } else { + let flex_vec = flex.get_vector()?; + let axis_continuous_vec = flex_to_vec2!(flex_vec.idx(0)) + .ok_or_else(|| anyhow!("No continuous axis vector!"))?; + pointer.axis(0, Axis::HorizontalScroll, axis_continuous_vec.x as f64); + pointer.axis(0, Axis::VerticalScroll, axis_continuous_vec.y as f64); + if let Some(axis_discrete_vec) = flex_to_vec2!(flex_vec.idx(0)) { + pointer.axis_discrete( + Axis::HorizontalScroll, + axis_discrete_vec.x as i32, + ); + pointer.axis_discrete( + Axis::VerticalScroll, + axis_discrete_vec.y as i32, + ); + } } + pointer.frame(); + core_surface.flush_clients(); } - pointer.frame(); - panel_item.flush_clients(); } } } @@ -294,15 +367,17 @@ impl PanelItem { ) -> Result<()> { if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { if let Some(keyboard) = panel_item.seat_data.keyboard() { - let mut keyboard_active = panel_item.seat_data.keyboard_active.lock(); - let active = flexbuffers::Reader::get_root(data)?.get_bool()?; - if *keyboard_active != active { - if active { - keyboard.enter(0, &panel_item.toplevel_surface(), vec![]); - } else { - keyboard.leave(0, &panel_item.toplevel_surface()); + if let Some(core_surface) = panel_item.core_surface.upgrade() { + let mut keyboard_active = panel_item.seat_data.keyboard_active.lock(); + let active = flexbuffers::Reader::get_root(data)?.get_bool()?; + if *keyboard_active != active { + if active { + keyboard.enter(0, &core_surface.wl_surface(), vec![]); + } else { + keyboard.leave(0, &core_surface.wl_surface()); + } + *keyboard_active = active; } - *keyboard_active = active; } } } @@ -358,16 +433,40 @@ impl PanelItem { } impl ItemSpecialization for PanelItem { fn serialize_start_data(&self, vec: &mut flexbuffers::VectorBuilder) { - let mut size_vec = vec.start_vector(); - let size = *self.size.lock(); - size_vec.push(size.w as u32); - size_vec.push(size.h as u32); - } -} -impl Drop for PanelItem { - fn drop(&mut self) { - let id = self.seat_data.global_id.get().cloned().unwrap(); - tokio::spawn(async move { GLOBAL_DESTROY_QUEUE.get().unwrap().send(id).await }); + // Panel size + { + let mut size_vec = vec.start_vector(); + self.core_surface.upgrade().unwrap().with_data(|data| { + size_vec.push(data.size.x); + size_vec.push(data.size.y); + }); + } + + // Cursor size and hotspot + if let Some(cursor) = &*self.seat_data.cursor.lock() { + let cursor = cursor.lock(); + if let Some(cursor_core_surface) = cursor.core_surface.upgrade() { + if let Some(mapped_data) = &*cursor_core_surface.mapped_data.lock() { + let mut cursor_vec = vec.start_vector(); + { + let mut cursor_size_vec = cursor_vec.start_vector(); + cursor_size_vec.push(mapped_data.size.x); + cursor_size_vec.push(mapped_data.size.y); + } + { + let mut cursor_hotspot_vec = cursor_vec.start_vector(); + cursor_hotspot_vec.push(cursor.hotspot.x); + cursor_hotspot_vec.push(cursor.hotspot.y); + } + } else { + vec.push(()); + } + } else { + vec.push(()); + } + } else { + vec.push(()); + } } } diff --git a/src/wayland/seat.rs b/src/wayland/seat.rs index 73d7643..77d60f2 100644 --- a/src/wayland/seat.rs +++ b/src/wayland/seat.rs @@ -1,21 +1,30 @@ +use super::{state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE}; +use crate::nodes::item::Item; +use mint::Vector2; use nanoid::nanoid; use once_cell::sync::OnceCell; use parking_lot::Mutex; -use smithay::reexports::wayland_server::{ - backend::{ClientId, GlobalId}, - delegate_dispatch, delegate_global_dispatch, - protocol::{ - wl_keyboard::{self, WlKeyboard}, - wl_pointer::{self, WlPointer}, - wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE}, - wl_touch::{self, WlTouch}, +use smithay::{ + reexports::wayland_server::{ + backend::{ClientId, GlobalId}, + delegate_dispatch, delegate_global_dispatch, + protocol::{ + wl_keyboard::{self, WlKeyboard}, + wl_pointer::{self, WlPointer}, + wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE}, + wl_touch::{self, WlTouch}, + }, + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, }, - Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, + wayland::compositor, }; -use std::ops::Deref; use std::sync::Arc; +use std::{ops::Deref, sync::Weak}; -use super::state::WaylandState; +pub struct Cursor { + pub core_surface: Weak, + pub hotspot: Vector2, +} pub struct SeatDelegate; @@ -26,6 +35,9 @@ impl SeatData { let seat_data = SeatData(Arc::new(SeatDataInner { client, global_id: OnceCell::new(), + panel_item: OnceCell::new(), + cursor: Mutex::new(None), + cursor_changed: Mutex::new(false), pointer: OnceCell::new(), pointer_active: Mutex::new(false), keyboard: OnceCell::new(), @@ -52,6 +64,9 @@ impl Deref for SeatData { pub struct SeatDataInner { client: ClientId, pub global_id: OnceCell, + pub panel_item: OnceCell>, + pub cursor: Mutex>>>, + pub cursor_changed: Mutex, pointer: OnceCell, pub pointer_active: Mutex, keyboard: OnceCell, @@ -69,6 +84,12 @@ impl SeatDataInner { self.touch.get() } } +impl Drop for SeatDataInner { + fn drop(&mut self) { + let id = self.global_id.take().unwrap(); + tokio::spawn(async move { GLOBAL_DESTROY_QUEUE.get().unwrap().send(id).await }); + } +} impl GlobalDispatch for SeatDelegate { fn bind( @@ -123,21 +144,50 @@ delegate_dispatch!(WaylandState: [WlSeat: SeatData] => SeatDelegate); impl Dispatch for SeatDelegate { fn request( - _state: &mut WaylandState, + state: &mut WaylandState, _client: &Client, _resource: &WlPointer, request: ::Request, - _data: &SeatData, - _dh: &DisplayHandle, + seat_data: &SeatData, + dh: &DisplayHandle, _data_init: &mut DataInit<'_, WaylandState>, ) { match request { wl_pointer::Request::SetCursor { serial: _, - surface: _, - hotspot_x: _, - hotspot_y: _, - } => (), + surface, + hotspot_x, + hotspot_y, + } => { + if !*seat_data.pointer_active.lock() { + return; + } + *seat_data.0.cursor_changed.lock() = true; + if let Some(surface) = surface.as_ref() { + compositor::with_states(surface, |data| { + data.data_map.insert_if_missing_threadsafe(|| { + CoreSurface::new(&state.display, dh.clone(), surface) + }); + if !data.data_map.insert_if_missing_threadsafe(|| { + Arc::new(Mutex::new(Cursor { + core_surface: Arc::downgrade( + data.data_map.get::>().unwrap(), + ), + hotspot: Vector2::from([hotspot_x, hotspot_y]), + })) + }) { + let mut cursor = + data.data_map.get::>>().unwrap().lock(); + cursor.hotspot = Vector2::from([hotspot_x, hotspot_y]); + } + }) + } + *seat_data.cursor.lock() = surface.and_then(|surf| { + compositor::with_states(&surf, |data| { + data.data_map.get::>>().cloned() + }) + }); + } wl_pointer::Request::Release => (), _ => unreachable!(), } diff --git a/src/wayland/state.rs b/src/wayland/state.rs index 9b0df8f..f4e15e1 100644 --- a/src/wayland/state.rs +++ b/src/wayland/state.rs @@ -1,10 +1,13 @@ +use std::sync::Arc; + +use parking_lot::Mutex; use slog::Logger; use smithay::{ delegate_output, delegate_shm, reexports::wayland_server::{ backend::{ClientData, ClientId, DisconnectReason}, protocol::wl_output::Subpixel, - DisplayHandle, + Display, DisplayHandle, }, utils::Size, wayland::{ @@ -33,6 +36,7 @@ impl ClientData for ClientState { } pub struct WaylandState { + pub display: Arc>>, pub display_handle: DisplayHandle, pub compositor_state: CompositorState, @@ -46,7 +50,11 @@ pub struct WaylandState { } impl WaylandState { - pub fn new(log: Logger, display_handle: DisplayHandle) -> Self { + pub fn new( + log: Logger, + display: Arc>>, + display_handle: DisplayHandle, + ) -> Self { let compositor_state = CompositorState::new::(&display_handle, log.clone()); let xdg_shell_state = XdgShellState::new::(&display_handle, log.clone()); let xdg_decoration_state = XdgDecorationState::new::(&display_handle, log.clone()); @@ -69,6 +77,7 @@ impl WaylandState { println!("Init Wayland compositor"); WaylandState { + display, display_handle, compositor_state, diff --git a/src/wayland/surface.rs b/src/wayland/surface.rs index ebdc44b..0f7b90c 100644 --- a/src/wayland/surface.rs +++ b/src/wayland/surface.rs @@ -1,17 +1,26 @@ -use std::{fmt::Error, mem, sync::Arc}; - +use super::{shaders::PANEL_SHADER_BYTES, state::WaylandState}; +use crate::{ + core::{destroy_queue, registry::Registry}, + nodes::model::Model, +}; use glam::vec2; -use once_cell::sync::OnceCell; +use mint::Vector2; use parking_lot::Mutex; use send_wrapper::SendWrapper; +use slog::Logger; use smithay::{ backend::renderer::{ gles2::{Gles2Renderer, Gles2Texture}, - utils::RendererSurfaceStateUserData, + utils::{import_surface_tree, on_commit_buffer_handler, RendererSurfaceStateUserData}, Texture, }, - wayland::compositor::SurfaceData, + desktop::utils::send_frames_surface_tree, + reexports::wayland_server::{ + backend::ObjectId, protocol::wl_surface::WlSurface, Display, DisplayHandle, Resource, + }, + wayland::compositor::{self, SurfaceData}, }; +use std::sync::{Arc, Weak}; use stereokit::{ material::{Material, Transparency}, shader::Shader, @@ -19,57 +28,47 @@ use stereokit::{ StereoKit, }; -use crate::core::destroy_queue; +pub static CORE_SURFACES: Registry = Registry::new(); -use super::shaders::PANEL_SHADER_BYTES; - -pub struct CoreSurface { - pub wl_tex: Mutex>>, - sk_tex: OnceCell>, - pub sk_mat: OnceCell>>, +pub struct CoreSurfaceData { + wl_tex: Option>, + sk_tex: Option>, + sk_mat: Option>>, + pub size: Vector2, + pub resized: bool, } - -impl CoreSurface { - pub fn new() -> Self { - CoreSurface { - wl_tex: Mutex::new(None), - sk_tex: OnceCell::new(), - sk_mat: OnceCell::new(), +impl CoreSurfaceData { + fn new(sk: &StereoKit) -> Self { + let sk_tex = SendWrapper::new( + SKTexture::create(sk, TextureType::ImageNoMips, TextureFormat::RGBA32).unwrap(), + ); + let sk_mat = { + let shader = Shader::from_mem(sk, PANEL_SHADER_BYTES).unwrap(); + let mat = Material::create(sk, &shader).unwrap(); + mat.set_parameter("diffuse", &*sk_tex); + mat.set_transparency(Transparency::Blend); + Arc::new(SendWrapper::new(mat)) + }; + CoreSurfaceData { + wl_tex: None, + sk_tex: Some(sk_tex), + sk_mat: Some(sk_mat), + size: Vector2::from([0, 0]), + resized: false, } } - - pub fn update_tex(&self, sk: &StereoKit, data: &SurfaceData, renderer: &Gles2Renderer) { - let sk_tex = self - .sk_tex - .get_or_try_init(|| { - SKTexture::create(sk, TextureType::ImageNoMips, TextureFormat::RGBA32) - .ok_or(Error) - .map(SendWrapper::new) - }) - .unwrap(); - let sk_mat = self - .sk_mat - .get_or_try_init(|| { - let shader = Shader::from_mem(sk, PANEL_SHADER_BYTES).unwrap(); - Material::create(sk, &shader) - .ok_or(Error) - .map(|mat| { - mat.set_parameter("diffuse", &**self.sk_tex.get().unwrap()); - mat.set_transparency(Transparency::Blend); - mat - }) - .map(|mat| Arc::new(SendWrapper::new(mat))) - }) - .unwrap(); - - if let Some(surface_states) = data.data_map.get::() { - *self.wl_tex.lock() = surface_states - .borrow() - .texture(renderer) - .cloned() - .map(SendWrapper::new); + fn update_tex(&mut self, data: &RendererSurfaceStateUserData, renderer: &Gles2Renderer) { + if let Some(surface_size) = data.borrow().surface_size() { + self.size = Vector2::from([surface_size.w as u32, surface_size.h as u32]); } - if let Some(smithay_tex) = self.wl_tex.lock().as_ref() { + self.wl_tex = data + .borrow() + .texture(renderer) + .cloned() + .map(SendWrapper::new); + if let Some(smithay_tex) = self.wl_tex.as_ref() { + let sk_tex = self.sk_tex.as_ref().unwrap(); + let sk_mat = self.sk_mat.as_ref().unwrap(); unsafe { sk_tex.set_native( smithay_tex.tex_id() as usize, @@ -88,10 +87,134 @@ impl CoreSurface { } } } -impl Drop for CoreSurface { +impl Drop for CoreSurfaceData { fn drop(&mut self) { - destroy_queue::add(mem::replace(self.wl_tex.get_mut(), None)); - self.sk_tex.take().map(destroy_queue::add); - self.sk_mat.take().map(destroy_queue::add); + destroy_queue::add(self.wl_tex.take()); + destroy_queue::add(self.sk_tex.take()); + destroy_queue::add(self.sk_mat.take()); + } +} + +pub struct CoreSurface { + display: Weak>>, + pub dh: DisplayHandle, + pub surface_id: ObjectId, + pub mapped_data: Mutex>, + pub pending_material_applications: Mutex, u32)>>, +} + +impl CoreSurface { + pub fn new( + display: &Arc>>, + dh: DisplayHandle, + surface: &WlSurface, + ) -> Arc { + CORE_SURFACES.add(CoreSurface { + display: Arc::downgrade(display), + dh, + surface_id: surface.id(), + mapped_data: Mutex::new(None), + pending_material_applications: Mutex::new(Vec::new()), + }) + } + + pub fn process( + &self, + sk: &StereoKit, + renderer: &mut Gles2Renderer, + time_ms: u32, + log: &Logger, + on_mapped: F, + if_mapped: M, + ) { + // Let Smithay handle all the buffer maintenance + on_commit_buffer_handler(&self.wl_surface()); + // Import all surface buffers into textures + import_surface_tree(renderer, &self.wl_surface(), log).unwrap(); + + let mapped = compositor::with_states(&self.wl_surface(), |data| { + data.data_map + .get::() + .map(|surface_states| surface_states.borrow().wl_buffer().is_some()) + .unwrap_or(false) + }); + + if !mapped { + return; + } + + let mut mapped_data = self.mapped_data.lock(); + let just_mapped = mapped_data.is_none(); + if just_mapped { + *mapped_data = Some(CoreSurfaceData::new(sk)); + } + drop(mapped_data); + self.with_states(|data| { + self.with_data(|mapped_data| { + mapped_data.update_tex( + data.data_map.get::().unwrap(), + renderer, + ); + }); + self.apply_surface_materials(); + + if just_mapped { + on_mapped(data); + } + if_mapped(data); + }); + + send_frames_surface_tree(&self.wl_surface(), time_ms); + } + + pub fn apply_material(&self, model: Arc, material_idx: u32) { + self.pending_material_applications + .lock() + .push((model, material_idx)); + } + + fn apply_surface_materials(&self) { + self.with_data(|mapped_data| { + let mut pending_material_applications = self.pending_material_applications.lock(); + for (model, material_idx) in &*pending_material_applications { + model + .pending_material_replacements + .lock() + .insert(*material_idx, mapped_data.sk_mat.clone().unwrap()); + } + pending_material_applications.clear(); + }); + } + + pub fn wl_surface(&self) -> WlSurface { + WlSurface::from_id(&self.dh, self.surface_id.clone()).unwrap() + } + + pub fn with_states(&self, f: F) -> T + where + F: FnOnce(&SurfaceData) -> T, + { + compositor::with_states(&self.wl_surface(), f) + } + + pub fn with_data(&self, f: F) -> Option + where + F: FnOnce(&mut CoreSurfaceData) -> T, + { + self.mapped_data.lock().as_mut().map(f) + } + + pub fn flush_clients(&self) { + self.display + .upgrade() + .unwrap() + .lock() + .flush_clients() + .unwrap(); + } +} +impl Drop for CoreSurface { + fn drop(&mut self) { + CORE_SURFACES.remove(self); } }