diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 2694cff..be1dfa8 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -1,4 +1,5 @@ mod compositor; +mod wlr_layer_shell; mod xdg_activation; mod xdg_decoration; mod xdg_icon; diff --git a/src/handlers/wlr_layer_shell.rs b/src/handlers/wlr_layer_shell.rs new file mode 100644 index 0000000..ae24d14 --- /dev/null +++ b/src/handlers/wlr_layer_shell.rs @@ -0,0 +1,30 @@ +use smithay::{ + desktop::{layer_map_for_output, LayerSurface}, + output::Output, + wayland::shell::wlr_layer::WlrLayerShellHandler, +}; +use wayland_server::protocol::wl_output::WlOutput; + +use crate::State; + +impl WlrLayerShellHandler for State { + fn shell_state(&mut self) -> &mut smithay::wayland::shell::wlr_layer::WlrLayerShellState { + &mut self.wlr_layer_shell_state + } + + fn new_layer_surface( + &mut self, + surface: smithay::wayland::shell::wlr_layer::LayerSurface, + output: Option, + layer: smithay::wayland::shell::wlr_layer::Layer, + namespace: String, + ) { + let output = output + .as_ref() + .and_then(Output::from_resource) + .unwrap_or_else(|| self.space.outputs().next().unwrap().clone()); + let mut map = layer_map_for_output(&output); + map.map_layer(&LayerSurface::new(surface, namespace)) + .unwrap(); + } +} diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 1741669..6aeeb18 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -1,3 +1,5 @@ +use smithay::utils::Point; +use smithay::wayland::seat::WaylandFocus; use smithay::{ desktop::{ find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output, @@ -8,6 +10,7 @@ use smithay::{ pointer::{Focus, GrabStartData as PointerGrabStartData}, Seat, }, + output::Output, reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel, wayland_server::{ @@ -24,6 +27,7 @@ use smithay::{ }, }, }; +use wayland_server::protocol::wl_output; use crate::{ grabs::{MoveSurfaceGrab, ResizeSurfaceGrab}, @@ -82,6 +86,11 @@ impl XdgShellHandler for State { .find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface) .unwrap() .clone(); + + if window.is_fullscreen() { + return; + } + let initial_window_location = self.space.element_location(&window).unwrap(); let grab = MoveSurfaceGrab { @@ -115,6 +124,10 @@ impl XdgShellHandler for State { .find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface) .unwrap() .clone(); + + if window.is_fullscreen() { + return; + } let initial_window_location = self.space.element_location(&window).unwrap(); let initial_window_size = window.window.geometry().size; @@ -169,12 +182,7 @@ impl XdgShellHandler for State { } fn title_changed(&mut self, surface: ToplevelSurface) { - let window = self - .space - .elements() - .filter(|w| w.window.toplevel().is_some()) - .find(|w| *w.window.toplevel().unwrap() == surface) - .unwrap(); + let window = self.get_window_by_surface(&surface); smithay::wayland::compositor::with_states(surface.wl_surface(), |states| { if let Some(data) = states.data_map.get::() { @@ -184,6 +192,154 @@ impl XdgShellHandler for State { } }); } + + // fn new_client(&mut self, client: smithay::wayland::shell::xdg::ShellClient) {} + + // fn client_pong(&mut self, client: smithay::wayland::shell::xdg::ShellClient) {} + + fn maximize_request(&mut self, surface: ToplevelSurface) { + { + let window = self.get_window_by_surface(&surface); + if let Some(loc) = self.space.element_location(&window) { + window.set_saved_location(loc); + } + } + + let window = self.get_window_by_surface(&surface).clone(); + if let Some(loc) = self.space.element_location(&window) { + window.set_saved_location(loc); + } + self.space.relocate_element(&window, Point::new(0, 0)); + + if let Some(output) = window.output.lock().clone() { + let geom = self.space.output_geometry(&output).unwrap(); + + let xdg = window.window.toplevel().unwrap(); + xdg.with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Maximized); + state.size = Some(geom.size); + }); + } + + surface.send_configure(); + } + + fn unmaximize_request(&mut self, surface: ToplevelSurface) { + let window = self.get_window_by_surface(&surface).clone(); + self.space + .relocate_element(&window, window.saved_location()); + + if let Some(output) = window.output.lock().clone() { + let xdg = window.window.toplevel().unwrap(); + xdg.with_pending_state(|state| { + state.states.unset(xdg_toplevel::State::Maximized); + state.size = None; + }); + } + + surface.send_configure(); + } + + fn fullscreen_request( + &mut self, + surface: ToplevelSurface, + mut wl_output: Option, + ) { + // NOTE: This is only one part of the solution. We can set the + // location and configure size here, but the surface should be rendered fullscreen + // independently from its buffer size + let wl_surface = surface.wl_surface(); + let window = self.get_window_by_surface(&surface); + + window.set_fullscreen(true); + + let output_geometry = wl_output + .as_ref() + .and_then(Output::from_resource) + .or_else(|| { + let w = self.space.elements().find(|window| { + window + .window + .wl_surface() + .map(|s| &*s == wl_surface) + .unwrap_or(false) + }); + w.and_then(|w| self.space.outputs_for_element(w).first().cloned()) + }) + .as_ref() + .and_then(|o| self.space.output_geometry(o)); + + if let Some(geometry) = output_geometry { + let output = wl_output + .as_ref() + .and_then(Output::from_resource) + .unwrap_or_else(|| self.space.outputs().next().unwrap().clone()); + let client = match self.display_handle.get_client(wl_surface.id()) { + Ok(client) => client, + Err(_) => return, + }; + for output in output.client_outputs(&client) { + wl_output = Some(output); + } + + surface.with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Fullscreen); + state.size = Some(geometry.size); + state.fullscreen_output = wl_output; + }); + } + + // The protocol demands us to always reply with a configure, + // regardless of we fulfilled the request or not + if surface.is_initial_configure_sent() { + surface.send_configure(); + } else { + // Will be sent during initial configure + } + } + + fn unfullscreen_request(&mut self, surface: ToplevelSurface) { + let window = self.get_window_by_surface(&surface); + + window.set_fullscreen(false); + + let xdg = window.window.toplevel().unwrap(); + xdg.with_pending_state(|state| { + state.states.unset(xdg_toplevel::State::Fullscreen); + state.size = None; + state.fullscreen_output = None; + }); + + surface.send_configure(); + } + + // fn minimize_request(&mut self, surface: ToplevelSurface) {} + + // fn show_window_menu( + // &mut self, + // surface: ToplevelSurface, + // seat: wl_seat::WlSeat, + // serial: Serial, + // location: smithay::utils::Point, + // ) { + // } + + // fn ack_configure( + // &mut self, + // surface: wl_surface::WlSurface, + // configure: smithay::wayland::shell::xdg::Configure, + // ) { + // } + + // fn client_destroyed(&mut self, client: smithay::wayland::shell::xdg::ShellClient) {} + + // fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {} + + // fn popup_destroyed(&mut self, surface: PopupSurface) {} + + // fn app_id_changed(&mut self, surface: ToplevelSurface) {} + + // fn parent_changed(&mut self, surface: ToplevelSurface) {} } fn check_grab( @@ -277,4 +433,12 @@ impl State { state.geometry = state.positioner.get_unconstrained_geometry(target); }); } + + fn get_window_by_surface(&self, surface: &ToplevelSurface) -> &DecoratedWindow { + self.space + .elements() + .filter(|w| w.window.toplevel().is_some()) + .find(|w| *w.window.toplevel().unwrap() == *surface) + .unwrap() + } } diff --git a/src/render.rs b/src/render.rs index 286b9d1..c39013e 100644 --- a/src/render.rs +++ b/src/render.rs @@ -4,7 +4,9 @@ use cairo::{Context, Format, ImageSurface}; use parking_lot::Mutex; use smithay::backend::allocator::Fourcc; use smithay::backend::renderer::element::memory::MemoryRenderBuffer; -use smithay::utils::Transform; +use smithay::desktop::layer_map_for_output; +use smithay::utils::{Logical, Transform}; +use smithay::wayland::commit_timing::CommitTimerBarrierStateUserData; use smithay::{ backend::renderer::{ damage::OutputDamageTracker, @@ -36,7 +38,10 @@ const CSD_BORDER_FRAME_BOTTOM: f64 = 4.0; pub struct DecoratedWindow { pub window: Window, title: Mutex, - decorations: bool, + pub output: Mutex>, + decorations: Mutex, + fullscreen: Mutex, + saved_location: Mutex>, } impl PartialEq for DecoratedWindow { @@ -49,10 +54,17 @@ impl Eq for DecoratedWindow {} impl Clone for DecoratedWindow { fn clone(&self) -> Self { let title = self.title.lock().clone(); + let output = self.output.lock().clone(); + let decorations = self.decorations.lock().clone(); + let fullscreen = self.fullscreen.lock().clone(); + let saved_location = self.saved_location.lock().clone(); Self { window: self.window.clone(), title: Mutex::new(title), - decorations: self.decorations, + output: Mutex::new(output), + decorations: Mutex::new(decorations), + fullscreen: Mutex::new(fullscreen), + saved_location: Mutex::new(saved_location), } } } @@ -63,12 +75,35 @@ impl DecoratedWindow { Self { window, title: Mutex::new(String::new()), - decorations, + output: Mutex::new(None), + decorations: Mutex::new(decorations), + fullscreen: Mutex::new(false), + saved_location: Mutex::new(Point::new(0, 0)), } } + pub fn saved_location(&self) -> Point { + self.saved_location.lock().clone() + } + + pub fn set_saved_location(&self, point: Point) { + *self.saved_location.lock() = point; + } + pub fn set_title(&self, title: String) { + *self.title.lock() = title; + } + + pub fn is_fullscreen(&self) -> bool { + return *self.fullscreen.lock(); + } + + pub fn set_fullscreen(&self, fs: bool) { + // *self.decorations.lock() = !fs; + *self.fullscreen.lock() = fs; + } + fn decorated(&self) -> bool { - let mut b = self.decorations; + let mut b = *self.decorations.lock(); if let Some(toplevel) = self.window.toplevel() { toplevel.with_pending_state(|f| { if let Some(decor) = f.decoration_mode { @@ -79,10 +114,6 @@ impl DecoratedWindow { b } - pub fn set_title(&self, title: String) { - *self.title.lock() = title; - } - fn render_border(&self, width: i32, height: i32) -> (Vec, i32) { let mut surface = ImageSurface::create(Format::ARgb32, width, height).unwrap(); let stride = surface.stride(); @@ -285,10 +316,12 @@ impl SpaceElement for DecoratedWindow { output: &Output, overlap: smithay::utils::Rectangle, ) { + *self.output.lock() = Some(output.clone()); self.window.output_enter(output, overlap) } fn output_leave(&self, output: &Output) { + *self.output.lock() = None; self.window.output_leave(output) } } @@ -309,12 +342,17 @@ where fn render_elements>( &self, renderer: &mut R, - location: Point, + mut location: Point, scale: Scale, alpha: f32, ) -> Vec { let mut elements: Vec = Vec::new(); + if self.is_fullscreen() { + location.x = 0; + location.y = 0; + } + /* Start with actual window elements */ elements.extend( self.window @@ -381,8 +419,6 @@ impl State { framebuffer: &mut GlesTarget, damage_tracker: &mut OutputDamageTracker, ) -> Result<(), Box> { - // let elem = DecorationElement:: - smithay::desktop::space::render_output::<_, OutputElement, _, _>( output, renderer, @@ -394,6 +430,7 @@ impl State { damage_tracker, [0.07, 0.16, 0.286, 1.0], )?; + Ok(()) } } diff --git a/src/state.rs b/src/state.rs index 86537fe..c1e1792 100644 --- a/src/state.rs +++ b/src/state.rs @@ -18,7 +18,7 @@ use smithay::{ fixes::FixesState, output::OutputManagerState, selection::data_device::DataDeviceState, - shell::xdg::{decoration::XdgDecorationState, XdgShellState}, + shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState}, shm::ShmState, socket::ListeningSocketSource, xdg_activation::XdgActivationState, @@ -59,6 +59,9 @@ pub struct State { pub xdg_toplevel_tag_manager_state: XdgToplevelTagManager, pub xdg_system_bell_state: XdgSystemBellState, + // the one wlroots protocol that everyone adopted + pub wlr_layer_shell_state: WlrLayerShellState, + pub output_manager_state: OutputManagerState, pub xwayland_state: XWaylandState, @@ -88,6 +91,8 @@ impl State { let xdg_toplevel_tag_manager_state = XdgToplevelTagManager::new::(&dh); let xdg_system_bell_state = XdgSystemBellState::new::(&dh); + let wlr_layer_shell_state = WlrLayerShellState::new::(&dh); + let shm_state = ShmState::new::(&dh, vec![]); let popups = PopupManager::default(); @@ -149,6 +154,7 @@ impl State { data_device_state, wl_fixes_state, output_manager_state, + wlr_layer_shell_state, // xdg_decoration_state, xdg_activation_state,