smithay/examples/minimal.rs
Christian Meissl 801d90a144 renderer: introduce Color32F
this replaces all occurrences of [f32; 4] with an explicit
color type
2024-08-20 10:47:03 -06:00

299 lines
9.2 KiB
Rust

use std::{os::unix::io::OwnedFd, sync::Arc};
use ::winit::platform::pump_events::PumpStatus;
use smithay::{
backend::{
input::{InputEvent, KeyboardKeyEvent},
renderer::{
element::{
surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement},
Kind,
},
gles::GlesRenderer,
utils::{draw_render_elements, on_commit_buffer_handler},
Color32F, Frame, Renderer,
},
winit::{self, WinitEvent},
},
delegate_compositor, delegate_data_device, delegate_seat, delegate_shm, delegate_xdg_shell,
input::{keyboard::FilterResult, Seat, SeatHandler, SeatState},
reexports::wayland_server::{protocol::wl_seat, Display},
utils::{Rectangle, Serial, Transform},
wayland::{
buffer::BufferHandler,
compositor::{
with_surface_tree_downward, CompositorClientState, CompositorHandler, CompositorState,
SurfaceAttributes, TraversalAction,
},
selection::{
data_device::{ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler},
SelectionHandler,
},
shell::xdg::{PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState},
shm::{ShmHandler, ShmState},
},
};
use wayland_protocols::xdg::shell::server::xdg_toplevel;
use wayland_server::{
backend::{ClientData, ClientId, DisconnectReason},
protocol::{
wl_buffer,
wl_surface::{self, WlSurface},
},
Client, ListeningSocket,
};
impl BufferHandler for App {
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
}
impl XdgShellHandler for App {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.xdg_shell_state
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Activated);
});
surface.send_configure();
}
fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) {
// Handle popup creation here
}
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
// Handle popup grab here
}
fn reposition_request(&mut self, _surface: PopupSurface, _positioner: PositionerState, _token: u32) {
// Handle popup reposition here
}
}
impl SelectionHandler for App {
type SelectionUserData = ();
}
impl DataDeviceHandler for App {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
}
}
impl ClientDndGrabHandler for App {}
impl ServerDndGrabHandler for App {
fn send(&mut self, _mime_type: String, _fd: OwnedFd, _seat: Seat<Self>) {}
}
impl CompositorHandler for App {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
&client.get_data::<ClientState>().unwrap().compositor_state
}
fn commit(&mut self, surface: &WlSurface) {
on_commit_buffer_handler::<Self>(surface);
}
}
impl ShmHandler for App {
fn shm_state(&self) -> &ShmState {
&self.shm_state
}
}
impl SeatHandler for App {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
type TouchFocus = WlSurface;
fn seat_state(&mut self) -> &mut SeatState<Self> {
&mut self.seat_state
}
fn focus_changed(&mut self, _seat: &Seat<Self>, _focused: Option<&WlSurface>) {}
fn cursor_image(&mut self, _seat: &Seat<Self>, _image: smithay::input::pointer::CursorImageStatus) {}
}
struct App {
compositor_state: CompositorState,
xdg_shell_state: XdgShellState,
shm_state: ShmState,
seat_state: SeatState<Self>,
data_device_state: DataDeviceState,
seat: Seat<Self>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
} else {
tracing_subscriber::fmt().init();
}
run_winit()
}
pub fn run_winit() -> Result<(), Box<dyn std::error::Error>> {
let mut display: Display<App> = Display::new()?;
let dh = display.handle();
let compositor_state = CompositorState::new::<App>(&dh);
let shm_state = ShmState::new::<App>(&dh, vec![]);
let mut seat_state = SeatState::new();
let seat = seat_state.new_wl_seat(&dh, "winit");
let mut state = {
App {
compositor_state,
xdg_shell_state: XdgShellState::new::<App>(&dh),
shm_state,
seat_state,
data_device_state: DataDeviceState::new::<App>(&dh),
seat,
}
};
let listener = ListeningSocket::bind("wayland-5").unwrap();
let mut clients = Vec::new();
let (mut backend, mut winit) = winit::init::<GlesRenderer>()?;
let start_time = std::time::Instant::now();
let keyboard = state.seat.add_keyboard(Default::default(), 200, 200).unwrap();
std::env::set_var("WAYLAND_DISPLAY", "wayland-5");
std::process::Command::new("weston-terminal").spawn().ok();
loop {
let status = winit.dispatch_new_events(|event| match event {
WinitEvent::Resized { .. } => {}
WinitEvent::Input(event) => match event {
InputEvent::Keyboard { event } => {
keyboard.input::<(), _>(
&mut state,
event.key_code(),
event.state(),
0.into(),
0,
|_, _, _| {
//
FilterResult::Forward
},
);
}
InputEvent::PointerMotionAbsolute { .. } => {
if let Some(surface) = state.xdg_shell_state.toplevel_surfaces().iter().next().cloned() {
let surface = surface.wl_surface().clone();
keyboard.set_focus(&mut state, Some(surface), 0.into());
};
}
_ => {}
},
_ => (),
});
match status {
PumpStatus::Continue => (),
PumpStatus::Exit(_) => return Ok(()),
};
backend.bind().unwrap();
let size = backend.window_size();
let damage = Rectangle::from_loc_and_size((0, 0), size);
let elements = state
.xdg_shell_state
.toplevel_surfaces()
.iter()
.flat_map(|surface| {
render_elements_from_surface_tree(
backend.renderer(),
surface.wl_surface(),
(0, 0),
1.0,
1.0,
Kind::Unspecified,
)
})
.collect::<Vec<WaylandSurfaceRenderElement<GlesRenderer>>>();
let mut frame = backend.renderer().render(size, Transform::Flipped180).unwrap();
frame.clear(Color32F::new(0.1, 0.0, 0.0, 1.0), &[damage]).unwrap();
draw_render_elements(&mut frame, 1.0, &elements, &[damage]).unwrap();
// We rely on the nested compositor to do the sync for us
let _ = frame.finish().unwrap();
for surface in state.xdg_shell_state.toplevel_surfaces() {
send_frames_surface_tree(surface.wl_surface(), start_time.elapsed().as_millis() as u32);
}
if let Some(stream) = listener.accept()? {
println!("Got a client: {:?}", stream);
let client = display
.handle()
.insert_client(stream, Arc::new(ClientState::default()))
.unwrap();
clients.push(client);
}
display.dispatch_clients(&mut state)?;
display.flush_clients()?;
// It is important that all events on the display have been dispatched and flushed to clients before
// swapping buffers because this operation may block.
backend.submit(Some(&[damage])).unwrap();
}
}
pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
with_surface_tree_downward(
surface,
(),
|_, _, &()| TraversalAction::DoChildren(()),
|_surf, states, &()| {
// the surface may not have any user_data if it is a subsurface and has not
// yet been commited
for callback in states
.cached_state
.get::<SurfaceAttributes>()
.current()
.frame_callbacks
.drain(..)
{
callback.done(time);
}
},
|_, _, &()| true,
);
}
#[derive(Default)]
struct ClientState {
compositor_state: CompositorClientState,
}
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {
println!("initialized");
}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
println!("disconnected");
}
}
// Macros used to delegate protocol handling to types in the app state.
delegate_xdg_shell!(App);
delegate_compositor!(App);
delegate_shm!(App);
delegate_seat!(App);
delegate_data_device!(App);