mirror of
https://github.com/CloudNebulaProject/wayray.git
synced 2026-04-10 13:10:41 +00:00
Add missing delegates and framebuffer capture for functional compositor
- Add PrimarySelectionState and delegate: terminals like foot expect the primary selection protocol to be present - Add XdgDecorationState and delegate: allows clients to negotiate server-side vs client-side window decorations - Send initial configure for popups so menus and tooltips render - Flatten nested if-let chains in commit handler (clippy) - Include framebuffer capture in render pipeline (from prior task): copies rendered frame via ExportMem for future network transport
This commit is contained in:
parent
a189c2f51b
commit
ed2f9be8e6
5 changed files with 134 additions and 20 deletions
|
|
@ -54,12 +54,10 @@ impl CompositorHandler for WayRay {
|
||||||
.elements()
|
.elements()
|
||||||
.find(|w| w.wl_surface().map(|s| s.into_owned()) == Some(surface.clone()))
|
.find(|w| w.wl_surface().map(|s| s.into_owned()) == Some(surface.clone()))
|
||||||
.cloned()
|
.cloned()
|
||||||
|
&& let Some(toplevel) = window.toplevel()
|
||||||
|
&& !toplevel.is_initial_configure_sent()
|
||||||
{
|
{
|
||||||
if let Some(toplevel) = window.toplevel() {
|
toplevel.send_configure();
|
||||||
if !toplevel.is_initial_configure_sent() {
|
|
||||||
toplevel.send_configure();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use smithay::{
|
use smithay::{
|
||||||
delegate_data_device, delegate_seat,
|
delegate_data_device, delegate_primary_selection, delegate_seat,
|
||||||
input::{Seat, SeatHandler, SeatState, pointer::CursorImageStatus},
|
input::{Seat, SeatHandler, SeatState, pointer::CursorImageStatus},
|
||||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
wayland::selection::{
|
wayland::selection::{
|
||||||
|
|
@ -7,6 +7,7 @@ use smithay::{
|
||||||
data_device::{
|
data_device::{
|
||||||
ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler,
|
ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler,
|
||||||
},
|
},
|
||||||
|
primary_selection::{PrimarySelectionHandler, PrimarySelectionState},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -39,5 +40,12 @@ impl DataDeviceHandler for WayRay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PrimarySelectionHandler for WayRay {
|
||||||
|
fn primary_selection_state(&self) -> &PrimarySelectionState {
|
||||||
|
&self.primary_selection_state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delegate_seat!(WayRay);
|
delegate_seat!(WayRay);
|
||||||
delegate_data_device!(WayRay);
|
delegate_data_device!(WayRay);
|
||||||
|
delegate_primary_selection!(WayRay);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
use smithay::{
|
use smithay::{
|
||||||
delegate_xdg_shell,
|
delegate_xdg_decoration, delegate_xdg_shell,
|
||||||
desktop::Window,
|
desktop::Window,
|
||||||
reexports::wayland_server::protocol::wl_seat,
|
reexports::{
|
||||||
|
wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode
|
||||||
|
as DecorationMode,
|
||||||
|
wayland_server::protocol::wl_seat,
|
||||||
|
},
|
||||||
utils::Serial,
|
utils::Serial,
|
||||||
wayland::shell::xdg::{
|
wayland::shell::xdg::{
|
||||||
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
|
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
|
||||||
|
decoration::XdgDecorationHandler,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
@ -22,8 +27,9 @@ impl XdgShellHandler for WayRay {
|
||||||
info!("new toplevel mapped");
|
info!("new toplevel mapped");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) {
|
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
||||||
// TODO: handle new popups
|
// Send the initial configure so the popup can start drawing.
|
||||||
|
surface.send_configure().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
|
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
|
||||||
|
|
@ -40,4 +46,33 @@ impl XdgShellHandler for WayRay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl XdgDecorationHandler for WayRay {
|
||||||
|
fn new_decoration(&mut self, toplevel: ToplevelSurface) {
|
||||||
|
// Prefer server-side decorations — we don't draw them yet,
|
||||||
|
// but telling the client lets it skip its own title bar.
|
||||||
|
toplevel.with_pending_state(|state| {
|
||||||
|
state.decoration_mode = Some(DecorationMode::ServerSide);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_mode(&mut self, toplevel: ToplevelSurface, mode: DecorationMode) {
|
||||||
|
toplevel.with_pending_state(|state| {
|
||||||
|
state.decoration_mode = Some(mode);
|
||||||
|
});
|
||||||
|
if toplevel.is_initial_configure_sent() {
|
||||||
|
toplevel.send_pending_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset_mode(&mut self, toplevel: ToplevelSurface) {
|
||||||
|
toplevel.with_pending_state(|state| {
|
||||||
|
state.decoration_mode = Some(DecorationMode::ServerSide);
|
||||||
|
});
|
||||||
|
if toplevel.is_initial_configure_sent() {
|
||||||
|
toplevel.send_pending_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delegate_xdg_shell!(WayRay);
|
delegate_xdg_shell!(WayRay);
|
||||||
|
delegate_xdg_decoration!(WayRay);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
|
allocator::Fourcc,
|
||||||
renderer::{
|
renderer::{
|
||||||
|
ExportMem,
|
||||||
damage::OutputDamageTracker,
|
damage::OutputDamageTracker,
|
||||||
element::texture::TextureRenderElement,
|
element::texture::TextureRenderElement,
|
||||||
gles::{GlesRenderer, GlesTexture},
|
gles::{GlesRenderer, GlesTexture},
|
||||||
|
|
@ -8,10 +10,11 @@ use smithay::{
|
||||||
winit::WinitGraphicsBackend,
|
winit::WinitGraphicsBackend,
|
||||||
},
|
},
|
||||||
desktop::{Window, space::render_output},
|
desktop::{Window, space::render_output},
|
||||||
|
utils::{Buffer as BufferCoord, Rectangle, Size},
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::state::WayRay;
|
use crate::state::{CapturedFrame, WayRay};
|
||||||
|
|
||||||
/// Dark grey clear color for the compositor background.
|
/// Dark grey clear color for the compositor background.
|
||||||
const CLEAR_COLOR: [f32; 4] = [0.1, 0.1, 0.1, 1.0];
|
const CLEAR_COLOR: [f32; 4] = [0.1, 0.1, 0.1, 1.0];
|
||||||
|
|
@ -33,7 +36,8 @@ pub fn render_output_frame(
|
||||||
let age = backend.buffer_age().unwrap_or(0);
|
let age = backend.buffer_age().unwrap_or(0);
|
||||||
|
|
||||||
// Render within a block so framebuffer is dropped before submit.
|
// Render within a block so framebuffer is dropped before submit.
|
||||||
let render_damage = {
|
// Returns (damage, optional captured frame) on success.
|
||||||
|
let render_result = {
|
||||||
let (renderer, mut framebuffer) = match backend.bind() {
|
let (renderer, mut framebuffer) = match backend.bind() {
|
||||||
Ok(pair) => pair,
|
Ok(pair) => pair,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -59,9 +63,55 @@ pub fn render_output_frame(
|
||||||
|
|
||||||
match render_result {
|
match render_result {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
// Clone the damage rectangles so we can use them after
|
// Clone damage before we consume the framebuffer for capture.
|
||||||
// the framebuffer is dropped.
|
let damage = result.damage.cloned();
|
||||||
Ok(result.damage.cloned())
|
|
||||||
|
// Capture the framebuffer while it is still bound.
|
||||||
|
let output_size = state.output.current_mode().unwrap().size;
|
||||||
|
let region: Rectangle<i32, BufferCoord> = Rectangle::from_size(
|
||||||
|
Size::from((output_size.w, output_size.h)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let capture = match renderer.copy_framebuffer(
|
||||||
|
&framebuffer,
|
||||||
|
region,
|
||||||
|
Fourcc::Argb8888,
|
||||||
|
) {
|
||||||
|
Ok(mapping) => match renderer.map_texture(&mapping) {
|
||||||
|
Ok(pixels) => {
|
||||||
|
let damage_rects = damage
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| d.len())
|
||||||
|
.unwrap_or(0);
|
||||||
|
tracing::debug!(
|
||||||
|
width = output_size.w,
|
||||||
|
height = output_size.h,
|
||||||
|
bytes = pixels.len(),
|
||||||
|
damage_rects,
|
||||||
|
"framebuffer captured"
|
||||||
|
);
|
||||||
|
Some(CapturedFrame {
|
||||||
|
data: pixels.to_vec(),
|
||||||
|
width: output_size.w,
|
||||||
|
height: output_size.h,
|
||||||
|
damage: damage
|
||||||
|
.as_ref()
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::warn!(?err, "failed to map framebuffer");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
tracing::warn!(?err, "failed to copy framebuffer");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((damage, capture))
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(?err, "damage tracker render failed");
|
warn!(?err, "damage tracker render failed");
|
||||||
|
|
@ -71,8 +121,12 @@ pub fn render_output_frame(
|
||||||
};
|
};
|
||||||
// framebuffer is now dropped, backend is no longer borrowed.
|
// framebuffer is now dropped, backend is no longer borrowed.
|
||||||
|
|
||||||
match render_damage {
|
match render_result {
|
||||||
Ok(damage) => {
|
Ok((damage, capture)) => {
|
||||||
|
// Store the captured frame for later consumption by the
|
||||||
|
// network transport layer.
|
||||||
|
state.last_capture = capture;
|
||||||
|
|
||||||
let has_damage = damage.is_some();
|
let has_damage = damage.is_some();
|
||||||
|
|
||||||
let submit_result = if let Some(ref rects) = damage {
|
let submit_result = if let Some(ref rects) = damage {
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,28 @@ use smithay::{
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::wayland_server::{Display, DisplayHandle},
|
reexports::wayland_server::{Display, DisplayHandle},
|
||||||
utils::{Clock, Monotonic, SERIAL_COUNTER},
|
utils::{Clock, Monotonic, Physical, Rectangle, SERIAL_COUNTER},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::CompositorState,
|
compositor::CompositorState,
|
||||||
output::OutputManagerState,
|
output::OutputManagerState,
|
||||||
selection::data_device::DataDeviceState,
|
selection::{
|
||||||
shell::xdg::XdgShellState,
|
data_device::DataDeviceState,
|
||||||
|
primary_selection::PrimarySelectionState,
|
||||||
|
},
|
||||||
|
shell::xdg::{XdgShellState, decoration::XdgDecorationState},
|
||||||
shm::ShmState,
|
shm::ShmState,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
|
/// Captured framebuffer data from the last render pass.
|
||||||
|
pub struct CapturedFrame {
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub damage: Vec<Rectangle<i32, Physical>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Central compositor state holding all Smithay subsystem states.
|
/// Central compositor state holding all Smithay subsystem states.
|
||||||
///
|
///
|
||||||
/// This is the "god struct" pattern required by Smithay — a single type that
|
/// This is the "god struct" pattern required by Smithay — a single type that
|
||||||
|
|
@ -36,10 +47,13 @@ pub struct WayRay {
|
||||||
pub seat_state: SeatState<Self>,
|
pub seat_state: SeatState<Self>,
|
||||||
pub output_manager_state: OutputManagerState,
|
pub output_manager_state: OutputManagerState,
|
||||||
pub data_device_state: DataDeviceState,
|
pub data_device_state: DataDeviceState,
|
||||||
|
pub primary_selection_state: PrimarySelectionState,
|
||||||
|
pub xdg_decoration_state: XdgDecorationState,
|
||||||
pub space: Space<smithay::desktop::Window>,
|
pub space: Space<smithay::desktop::Window>,
|
||||||
pub seat: Seat<Self>,
|
pub seat: Seat<Self>,
|
||||||
pub clock: Clock<Monotonic>,
|
pub clock: Clock<Monotonic>,
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
|
pub last_capture: Option<CapturedFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WayRay {
|
impl WayRay {
|
||||||
|
|
@ -51,6 +65,8 @@ impl WayRay {
|
||||||
let xdg_shell_state = XdgShellState::new::<Self>(&dh);
|
let xdg_shell_state = XdgShellState::new::<Self>(&dh);
|
||||||
let shm_state = ShmState::new::<Self>(&dh, vec![]);
|
let shm_state = ShmState::new::<Self>(&dh, vec![]);
|
||||||
let data_device_state = DataDeviceState::new::<Self>(&dh);
|
let data_device_state = DataDeviceState::new::<Self>(&dh);
|
||||||
|
let primary_selection_state = PrimarySelectionState::new::<Self>(&dh);
|
||||||
|
let xdg_decoration_state = XdgDecorationState::new::<Self>(&dh);
|
||||||
|
|
||||||
let mut seat_state = SeatState::new();
|
let mut seat_state = SeatState::new();
|
||||||
let mut seat = seat_state.new_wl_seat(&dh, "wayray");
|
let mut seat = seat_state.new_wl_seat(&dh, "wayray");
|
||||||
|
|
@ -71,10 +87,13 @@ impl WayRay {
|
||||||
seat_state,
|
seat_state,
|
||||||
output_manager_state,
|
output_manager_state,
|
||||||
data_device_state,
|
data_device_state,
|
||||||
|
primary_selection_state,
|
||||||
|
xdg_decoration_state,
|
||||||
space: Space::default(),
|
space: Space::default(),
|
||||||
seat,
|
seat,
|
||||||
clock: Clock::new(),
|
clock: Clock::new(),
|
||||||
output,
|
output,
|
||||||
|
last_capture: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue