diff options
| -rw-r--r-- | src/handlers/xdg_shell.rs | 59 | ||||
| -rw-r--r-- | src/input.rs | 5 | ||||
| -rw-r--r-- | src/layout.rs | 160 |
3 files changed, 178 insertions, 46 deletions
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 59d21693..f472047a 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -2,6 +2,7 @@ use smithay::delegate_xdg_shell; use smithay::desktop::{find_popup_root_surface, PopupKind, Window}; use smithay::input::pointer::{Focus, GrabStartData as PointerGrabStartData}; use smithay::input::Seat; +use smithay::output::Output; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::reexports::wayland_server::protocol::{wl_output, wl_seat}; @@ -180,32 +181,22 @@ impl XdgShellHandler for Niri { .capabilities .contains(xdg_toplevel::WmCapabilities::Fullscreen) { - // // 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 output = wl_output - // .as_ref() - // .and_then(Output::from_resource) - // .or_else(|| { - // self.monitor_set - // .find_window_and_space(wl_surface) - // .and_then(|(_window, space)| space.outputs().next().cloned()) - // }); - - // if let Some(output) = output { - // let (window, space) = - // self.monitor_set.find_window_and_space(wl_surface).unwrap(); - // let geometry = space.output_geometry(&output).unwrap(); - - // surface.with_pending_state(|state| { - // state.states.set(xdg_toplevel::State::Fullscreen); - // state.size = Some(geometry.size); - // }); - - // space.map_element(window.clone(), geometry.loc, true); - // } + // 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 + if let Some((window, current_output)) = self + .monitor_set + .find_window_and_output(surface.wl_surface()) + { + if let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) { + if requested_output != current_output { + self.monitor_set + .move_window_to_output(window.clone(), &requested_output); + } + } + + self.monitor_set.set_fullscreen(&window, true); + } } // The protocol demands us to always reply with a configure, @@ -214,20 +205,12 @@ impl XdgShellHandler for Niri { } fn unfullscreen_request(&mut self, surface: ToplevelSurface) { - if !surface - .current_state() - .states - .contains(xdg_toplevel::State::Fullscreen) + if let Some((window, _)) = self + .monitor_set + .find_window_and_output(surface.wl_surface()) { - return; + self.monitor_set.set_fullscreen(&window, false); } - - surface.with_pending_state(|state| { - state.states.unset(xdg_toplevel::State::Fullscreen); - state.size = None; - }); - - surface.send_pending_configure(); } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { diff --git a/src/input.rs b/src/input.rs index e5dba51d..986fc349 100644 --- a/src/input.rs +++ b/src/input.rs @@ -178,8 +178,9 @@ impl Niri { } } Action::ToggleFullscreen => { - if let Some(window) = self.monitor_set.focus() { - // FIXME + let focus = self.monitor_set.focus().cloned(); + if let Some(window) = focus { + self.monitor_set.toggle_fullscreen(&window); } } Action::MoveLeft => { diff --git a/src/layout.rs b/src/layout.rs index 8dba3eed..0f1c08e1 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -42,6 +42,7 @@ use smithay::backend::renderer::gles::GlesRenderer; use smithay::desktop::space::SpaceElement; use smithay::desktop::Window; use smithay::output::Output; +use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::utils::{Logical, Point, Rectangle, Scale, Size}; use smithay::wayland::compositor::{with_states, SurfaceData}; @@ -65,6 +66,7 @@ pub type MonitorRenderElement<R> = pub trait LayoutElement: SpaceElement + PartialEq + Clone { fn request_size(&self, size: Size<i32, Logical>); + fn request_fullscreen(&self, size: Size<i32, Logical>); fn min_size(&self) -> Size<i32, Logical>; fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool; fn send_frame<T, F>( @@ -161,6 +163,9 @@ struct Column<W: LayoutElement> { /// Desired width of this column. width: ColumnWidth, + + /// Whether this column contains a single full-screened window. + is_fullscreen: bool, } impl OutputId { @@ -171,8 +176,17 @@ impl OutputId { impl LayoutElement for Window { fn request_size(&self, size: Size<i32, Logical>) { - self.toplevel() - .with_pending_state(|state| state.size = Some(size)); + self.toplevel().with_pending_state(|state| { + state.size = Some(size); + state.states.unset(xdg_toplevel::State::Fullscreen); + }); + } + + fn request_fullscreen(&self, size: Size<i32, Logical>) { + self.toplevel().with_pending_state(|state| { + state.size = Some(size); + state.states.set(xdg_toplevel::State::Fullscreen); + }); } fn min_size(&self) -> Size<i32, Logical> { @@ -421,7 +435,7 @@ impl<W: LayoutElement> MonitorSet<W> { for ws in &mut mon.workspaces { if ws.has_window(window) { ws.update_window(window); - break; + return; } } } @@ -430,7 +444,7 @@ impl<W: LayoutElement> MonitorSet<W> { for ws in workspaces { if ws.has_window(window) { ws.update_window(window); - break; + return; } } } @@ -818,6 +832,67 @@ impl<W: LayoutElement> MonitorSet<W> { self.add_window(new_idx, workspace_idx, window, true); } } + + pub fn move_window_to_output(&mut self, window: W, output: &Output) { + self.remove_window(&window); + + if let MonitorSet::Normal { monitors, .. } = self { + let new_idx = monitors + .iter() + .position(|mon| &mon.output == output) + .unwrap(); + + let workspace_idx = monitors[new_idx].active_workspace_idx; + // FIXME: activate only if it was already active and focused. + self.add_window(new_idx, workspace_idx, window, true); + } + } + + pub fn set_fullscreen(&mut self, window: &W, is_fullscreen: bool) { + match self { + MonitorSet::Normal { monitors, .. } => { + for mon in monitors { + for ws in &mut mon.workspaces { + if ws.has_window(window) { + ws.set_fullscreen(window, is_fullscreen); + return; + } + } + } + } + MonitorSet::NoOutputs(workspaces) => { + for ws in workspaces { + if ws.has_window(window) { + ws.set_fullscreen(window, is_fullscreen); + return; + } + } + } + } + } + + pub fn toggle_fullscreen(&mut self, window: &W) { + match self { + MonitorSet::Normal { monitors, .. } => { + for mon in monitors { + for ws in &mut mon.workspaces { + if ws.has_window(window) { + ws.toggle_fullscreen(window); + return; + } + } + } + } + MonitorSet::NoOutputs(workspaces) => { + for ws in workspaces { + if ws.has_window(window) { + ws.toggle_fullscreen(window); + return; + } + } + } + } + } } impl MonitorSet<Window> { @@ -1432,7 +1507,12 @@ impl<W: LayoutElement> Workspace<W> { let geom = win.geometry(); // x, y point at the top-left of the window geometry. - let win_pos = Point::from((x, y)) - geom.loc; + let mut win_pos = Point::from((x, y)) - geom.loc; + if col.is_fullscreen { + // FIXME: fullscreen windows are missing left padding + win_pos.x -= PADDING; + win_pos.y -= PADDING; + } if win.is_in_input_region(&(pos - win_pos.to_f64())) { let mut win_pos_within_output = win_pos; win_pos_within_output.x -= view_pos; @@ -1463,6 +1543,51 @@ impl<W: LayoutElement> Workspace<W> { self.columns[self.active_column_idx].toggle_full_width(self.view_size); } + + pub fn set_fullscreen(&mut self, window: &W, is_fullscreen: bool) { + let (mut col_idx, win_idx) = self + .columns + .iter() + .enumerate() + .find_map(|(col_idx, col)| { + col.windows + .iter() + .position(|w| w == window) + .map(|win_idx| (col_idx, win_idx)) + }) + .unwrap(); + + let mut col = &mut self.columns[col_idx]; + + if is_fullscreen && col.windows.len() > 1 { + // This wasn't the only window in its column; extract it into a separate column. + let target_window_was_focused = + self.active_column_idx == col_idx && col.active_window_idx == win_idx; + let window = col.windows.remove(win_idx); + col.active_window_idx = min(col.active_window_idx, col.windows.len() - 1); + col.update_window_sizes(self.view_size); + + col_idx += 1; + self.columns + .insert(col_idx, Column::new(window, self.view_size)); + if self.active_column_idx >= col_idx || target_window_was_focused { + self.active_column_idx += 1; + } + col = &mut self.columns[col_idx]; + } + + col.set_fullscreen(self.view_size, is_fullscreen); + } + + pub fn toggle_fullscreen(&mut self, window: &W) { + let col = self + .columns + .iter_mut() + .find(|col| col.windows.contains(window)) + .unwrap(); + let value = !col.is_fullscreen; + self.set_fullscreen(window, value); + } } impl Workspace<Window> { @@ -1490,7 +1615,13 @@ impl Workspace<Window> { for win in &col.windows { let geom = win.geometry(); - let win_pos = Point::from((x - view_pos, y)) - geom.loc; + let mut win_pos = Point::from((x - view_pos, y)) - geom.loc; + if col.is_fullscreen { + // FIXME: fullscreen windows are missing left padding + win_pos.x -= PADDING; + win_pos.y -= PADDING; + } + rv.extend(win.render_elements( renderer, win_pos.to_physical(1), @@ -1513,6 +1644,7 @@ impl<W: LayoutElement> Column<W> { windows: vec![], active_window_idx: 0, width: ColumnWidth::default(), + is_fullscreen: false, }; rv.add_window(view_size, window); @@ -1539,11 +1671,17 @@ impl<W: LayoutElement> Column<W> { } fn add_window(&mut self, view_size: Size<i32, Logical>, window: W) { + self.is_fullscreen = false; self.windows.push(window); self.update_window_sizes(view_size); } fn update_window_sizes(&mut self, view_size: Size<i32, Logical>) { + if self.is_fullscreen { + self.windows[0].request_fullscreen(view_size); + return; + } + let min_width = self .windows .iter() @@ -1610,6 +1748,10 @@ impl<W: LayoutElement> Column<W> { fn verify_invariants(&self) { assert!(!self.windows.is_empty(), "columns can't be empty"); assert!(self.active_window_idx < self.windows.len()); + + if self.is_fullscreen { + assert_eq!(self.windows.len(), 1); + } } fn toggle_width(&mut self, view_size: Size<i32, Logical>) { @@ -1637,6 +1779,12 @@ impl<W: LayoutElement> Column<W> { }; self.set_width(view_size, width); } + + fn set_fullscreen(&mut self, view_size: Size<i32, Logical>, is_fullscreen: bool) { + assert_eq!(self.windows.len(), 1); + self.is_fullscreen = is_fullscreen; + self.update_window_sizes(view_size); + } } pub fn output_size(output: &Output) -> Size<i32, Logical> { |
