diff --git a/Cargo.lock b/Cargo.lock index 6fdffab..2bc01e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1150,7 +1150,9 @@ name = "test_wayland_server" version = "0.1.0" dependencies = [ "smithay", + "wayland-protocols", "wayland-server", + "winit", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 02673e5..1cb9559 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,6 @@ edition = "2021" [dependencies] smithay = { git = "https://github.com/Smithay/smithay.git", branch = "master" } -wayland-server = "0.31.0" \ No newline at end of file +wayland-server = "0.31.0" +wayland-protocols = { version = "0.31.0", features = ["unstable", "staging", "server"] } +winit = { version = "0.29", default-features = false, features = ["wayland", "wayland-dlopen", "x11", "rwh_06"] } \ No newline at end of file diff --git a/README.md b/README.md index bce675c..f61bea7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ - libgdm-devel - libinput-devel - libxkbcommon-devel +- weston-terminal # Usefull links diff --git a/src/main.rs b/src/main.rs index a9ab515..93744a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,90 @@ -use std::sync::Arc; +use std::{os::unix::io::OwnedFd, sync::Arc}; -use smithay::delegate_compositor; -use smithay::reexports::wayland_server::Display; +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}, + 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, +}; -use smithay::wayland::compositor::{CompositorClientState, CompositorHandler, CompositorState}; +impl BufferHandler for App { + fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {} +} -use wayland_server::backend::{ClientData, ClientId, DisconnectReason}; -use wayland_server::protocol::wl_surface::WlSurface; -use wayland_server::{Client, ListeningSocket}; +impl XdgShellHandler for App { + fn xdg_shell_state(&mut self) -> &mut XdgShellState { + &mut self.xdg_shell_state + } -struct App { - compositor_state: CompositorState, + 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) {} } impl CompositorHandler for App { @@ -23,24 +97,139 @@ impl CompositorHandler for App { } fn commit(&mut self, surface: &WlSurface) { - dbg!("Commit", surface); + on_commit_buffer_handler::(surface); } } +impl ShmHandler for App { + fn shm_state(&self) -> &ShmState { + &self.shm_state + } +} + +impl SeatHandler for App { + type KeyboardFocus = WlSurface; + type PointerFocus = WlSurface; + + fn seat_state(&mut self) -> &mut SeatState { + &mut self.seat_state + } + + fn focus_changed(&mut self, _seat: &Seat, _focused: Option<&WlSurface>) {} + fn cursor_image(&mut self, _seat: &Seat, _image: smithay::input::pointer::CursorImageStatus) {} +} + +struct App { + compositor_state: CompositorState, + xdg_shell_state: XdgShellState, + shm_state: ShmState, + seat_state: SeatState, + data_device_state: DataDeviceState, + + seat: Seat, +} + fn main() -> Result<(), Box> { + run_winit() +} + +pub fn run_winit() -> Result<(), Box> { let mut display: Display = Display::new()?; let dh = display.handle(); let compositor_state = CompositorState::new::(&dh); + let shm_state = ShmState::new::(&dh, vec![]); + let mut seat_state = SeatState::new(); + let seat = seat_state.new_wl_seat(&dh, "winit"); - let mut state = App { compositor_state }; + let mut state = { + App { + compositor_state, + xdg_shell_state: XdgShellState::new::(&dh), + shm_state, + seat_state, + data_device_state: DataDeviceState::new::(&dh), + seat, + } + }; let listener = ListeningSocket::bind("wayland-5").unwrap(); - let mut clients = Vec::new(); + let (mut backend, mut winit) = winit::init::()?; + + 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 { - if let Some(stream) = listener.accept().unwrap() { + 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::>>(); + + let mut frame = backend.renderer().render(size, Transform::Flipped180).unwrap(); + frame.clear([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 @@ -52,9 +241,34 @@ fn main() -> Result<(), Box> { 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 + .current::() + .frame_callbacks + .drain(..) + { + callback.done(time); + } + }, + |_, _, &()| true, + ); +} + #[derive(Default)] struct ClientState { compositor_state: CompositorClientState, @@ -69,10 +283,9 @@ impl ClientData for ClientState { } } -impl AsMut for App { - fn as_mut(&mut self) -> &mut CompositorState { - &mut self.compositor_state - } -} - -delegate_compositor!(App); \ No newline at end of file +// 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); \ No newline at end of file