mirror of
https://github.com/YaLTeR/niri.git
synced 2024-10-26 20:04:05 +03:00
Wrap mapped windows in a Mapped
This commit is contained in:
parent
f31e105043
commit
3963f537a4
@ -17,8 +17,7 @@ use smithay::wayland::shm::{ShmHandler, ShmState};
|
||||
use smithay::{delegate_compositor, delegate_shm};
|
||||
|
||||
use crate::niri::{ClientState, State};
|
||||
use crate::utils::clone2;
|
||||
use crate::window::{InitialConfigureState, Unmapped};
|
||||
use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};
|
||||
|
||||
impl CompositorHandler for State {
|
||||
fn compositor_state(&mut self) -> &mut CompositorState {
|
||||
@ -109,22 +108,22 @@ impl CompositorHandler for State {
|
||||
|
||||
window.on_commit();
|
||||
|
||||
let (width, is_full_width, output) =
|
||||
let (rules, width, is_full_width, output) =
|
||||
if let InitialConfigureState::Configured {
|
||||
rules,
|
||||
width,
|
||||
is_full_width,
|
||||
output,
|
||||
..
|
||||
} = state
|
||||
{
|
||||
// Check that the output is still connected.
|
||||
let output =
|
||||
output.filter(|o| self.niri.layout.monitor_for_output(o).is_some());
|
||||
|
||||
(width, is_full_width, output)
|
||||
(rules, width, is_full_width, output)
|
||||
} else {
|
||||
error!("window map must happen after initial configure");
|
||||
(None, false, None)
|
||||
(ResolvedWindowRules::default(), None, false, None)
|
||||
};
|
||||
|
||||
let parent = window
|
||||
@ -141,29 +140,30 @@ impl CompositorHandler for State {
|
||||
.filter(|(_, parent_output)| {
|
||||
output.is_none() || output.as_ref() == Some(*parent_output)
|
||||
})
|
||||
.map(|(window, _)| window.clone());
|
||||
.map(|(mapped, _)| mapped.window.clone());
|
||||
|
||||
let window = window.clone();
|
||||
let win = window.clone();
|
||||
let mapped = Mapped::new(window, rules);
|
||||
let window = mapped.window.clone();
|
||||
|
||||
let output = if let Some(p) = parent {
|
||||
// Open dialogs immediately to the right of their parent window.
|
||||
self.niri
|
||||
.layout
|
||||
.add_window_right_of(&p, win, width, is_full_width)
|
||||
.add_window_right_of(&p, mapped, width, is_full_width)
|
||||
} else if let Some(output) = &output {
|
||||
self.niri
|
||||
.layout
|
||||
.add_window_on_output(output, win, width, is_full_width);
|
||||
.add_window_on_output(output, mapped, width, is_full_width);
|
||||
Some(output)
|
||||
} else {
|
||||
self.niri.layout.add_window(win, width, is_full_width)
|
||||
self.niri.layout.add_window(mapped, width, is_full_width)
|
||||
};
|
||||
|
||||
if let Some(output) = output.cloned() {
|
||||
self.niri.layout.start_open_animation_for_window(&window);
|
||||
|
||||
let new_active_window = self.niri.layout.active_window().map(|(w, _)| w);
|
||||
let new_active_window =
|
||||
self.niri.layout.active_window().map(|(m, _)| &m.window);
|
||||
if new_active_window == Some(&window) {
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
}
|
||||
@ -183,8 +183,9 @@ impl CompositorHandler for State {
|
||||
}
|
||||
|
||||
// This is a commit of a previously-mapped root or a non-toplevel root.
|
||||
if let Some(win_out) = self.niri.layout.find_window_and_output(surface) {
|
||||
let (window, output) = clone2(win_out);
|
||||
if let Some((mapped, output)) = self.niri.layout.find_window_and_output(surface) {
|
||||
let window = mapped.window.clone();
|
||||
let output = output.clone();
|
||||
|
||||
window.on_commit();
|
||||
|
||||
@ -224,7 +225,9 @@ impl CompositorHandler for State {
|
||||
|
||||
// This is a commit of a non-root or a non-toplevel root.
|
||||
let root_window_output = self.niri.layout.find_window_and_output(&root_surface);
|
||||
if let Some((window, output)) = root_window_output.map(clone2) {
|
||||
if let Some((mapped, output)) = root_window_output {
|
||||
let window = mapped.window.clone();
|
||||
let output = output.clone();
|
||||
window.on_commit();
|
||||
self.niri.layout.update_window(&window);
|
||||
self.niri.queue_redraw(output);
|
||||
|
@ -144,7 +144,7 @@ impl InputMethodHandler for State {
|
||||
self.niri
|
||||
.layout
|
||||
.find_window_and_output(parent)
|
||||
.map(|(window, _)| window.geometry())
|
||||
.map(|(mapped, _)| mapped.window.geometry())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
@ -333,25 +333,24 @@ impl ForeignToplevelHandler for State {
|
||||
}
|
||||
|
||||
fn activate(&mut self, wl_surface: WlSurface) {
|
||||
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||
let window = window.clone();
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.activate_window(&window);
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, wl_surface: WlSurface) {
|
||||
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||
window.toplevel().expect("no x11 support").send_close();
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||
mapped.toplevel().send_close();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>) {
|
||||
if let Some((window, current_output)) = self.niri.layout.find_window_and_output(&wl_surface)
|
||||
if let Some((mapped, current_output)) = self.niri.layout.find_window_and_output(&wl_surface)
|
||||
{
|
||||
if !window
|
||||
if !mapped
|
||||
.toplevel()
|
||||
.expect("no x11 support")
|
||||
.current_state()
|
||||
.capabilities
|
||||
.contains(xdg_toplevel::WmCapabilities::Fullscreen)
|
||||
@ -359,7 +358,7 @@ impl ForeignToplevelHandler for State {
|
||||
return;
|
||||
}
|
||||
|
||||
let window = window.clone();
|
||||
let window = mapped.window.clone();
|
||||
|
||||
if let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) {
|
||||
if &requested_output != current_output {
|
||||
@ -374,8 +373,8 @@ impl ForeignToplevelHandler for State {
|
||||
}
|
||||
|
||||
fn unset_fullscreen(&mut self, wl_surface: WlSurface) {
|
||||
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||
let window = window.clone();
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.set_fullscreen(&window, false);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ use smithay::{
|
||||
|
||||
use crate::layout::workspace::ColumnWidth;
|
||||
use crate::niri::{PopupGrabState, State};
|
||||
use crate::utils::clone2;
|
||||
use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped};
|
||||
|
||||
fn window_matches(role: &XdgToplevelSurfaceRoleAttributes, m: &Match) -> bool {
|
||||
@ -213,9 +212,7 @@ impl XdgShellHandler for State {
|
||||
}
|
||||
|
||||
let layout_focus = self.niri.layout.focus();
|
||||
if Some(&root)
|
||||
!= layout_focus.map(|win| win.toplevel().expect("no x11 support").wl_surface())
|
||||
{
|
||||
if Some(&root) != layout_focus.map(|win| win.toplevel().wl_surface()) {
|
||||
let _ = PopupManager::dismiss_popup(&root, &popup);
|
||||
return;
|
||||
}
|
||||
@ -278,12 +275,12 @@ impl XdgShellHandler for State {
|
||||
) {
|
||||
let requested_output = wl_output.as_ref().and_then(Output::from_resource);
|
||||
|
||||
if let Some((window, current_output)) = self
|
||||
if let Some((mapped, current_output)) = self
|
||||
.niri
|
||||
.layout
|
||||
.find_window_and_output(toplevel.wl_surface())
|
||||
{
|
||||
let window = window.clone();
|
||||
let window = mapped.window.clone();
|
||||
|
||||
if let Some(requested_output) = requested_output {
|
||||
if &requested_output != current_output {
|
||||
@ -358,12 +355,12 @@ impl XdgShellHandler for State {
|
||||
}
|
||||
|
||||
fn unfullscreen_request(&mut self, toplevel: ToplevelSurface) {
|
||||
if let Some((window, _)) = self
|
||||
if let Some((mapped, _)) = self
|
||||
.niri
|
||||
.layout
|
||||
.find_window_and_output(toplevel.wl_surface())
|
||||
{
|
||||
let window = window.clone();
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.set_fullscreen(&window, false);
|
||||
|
||||
// A configure is required in response to this event regardless if there are pending
|
||||
@ -453,14 +450,16 @@ impl XdgShellHandler for State {
|
||||
.layout
|
||||
.find_window_and_output(surface.wl_surface());
|
||||
|
||||
let Some((window, output)) = win_out.map(clone2) else {
|
||||
let Some((mapped, output)) = win_out else {
|
||||
// I have no idea how this can happen, but I saw it happen once, in a weird interaction
|
||||
// involving laptop going to sleep and resuming.
|
||||
error!("toplevel missing from both unmapped_windows and layout");
|
||||
return;
|
||||
};
|
||||
let window = mapped.window.clone();
|
||||
let output = output.clone();
|
||||
|
||||
let active_window = self.niri.layout.active_window().map(|(w, _)| w);
|
||||
let active_window = self.niri.layout.active_window().map(|(m, _)| &m.window);
|
||||
let was_active = active_window == Some(&window);
|
||||
|
||||
self.niri.layout.remove_window(&window);
|
||||
@ -733,8 +732,8 @@ impl State {
|
||||
};
|
||||
|
||||
// Figure out if the root is a window or a layer surface.
|
||||
if let Some((window, output)) = self.niri.layout.find_window_and_output(&root) {
|
||||
self.unconstrain_window_popup(popup, window, output);
|
||||
if let Some((mapped, output)) = self.niri.layout.find_window_and_output(&root) {
|
||||
self.unconstrain_window_popup(popup, &mapped.window, output);
|
||||
} else if let Some((layer_surface, output)) = self.niri.layout.outputs().find_map(|o| {
|
||||
let map = layer_map_for_output(o);
|
||||
let layer_surface = map.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)?;
|
||||
@ -814,6 +813,8 @@ impl State {
|
||||
if let InitialConfigureState::Configured { rules, .. } = &mut unmapped.state {
|
||||
*rules = resolve();
|
||||
}
|
||||
} else if let Some(mapped) = self.niri.layout.find_window_mut(toplevel.wl_surface()) {
|
||||
mapped.rules = resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
src/input.rs
25
src/input.rs
@ -379,21 +379,24 @@ impl State {
|
||||
}
|
||||
Action::ScreenshotWindow => {
|
||||
let active = self.niri.layout.active_window();
|
||||
if let Some((window, output)) = active {
|
||||
if let Some((mapped, output)) = active {
|
||||
self.backend.with_primary_renderer(|renderer| {
|
||||
if let Err(err) = self.niri.screenshot_window(renderer, output, window) {
|
||||
if let Err(err) =
|
||||
self.niri
|
||||
.screenshot_window(renderer, output, &mapped.window)
|
||||
{
|
||||
warn!("error taking screenshot: {err:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Action::CloseWindow => {
|
||||
if let Some(window) = self.niri.layout.focus() {
|
||||
window.toplevel().expect("no x11 support").send_close();
|
||||
if let Some(mapped) = self.niri.layout.focus() {
|
||||
mapped.toplevel().send_close();
|
||||
}
|
||||
}
|
||||
Action::FullscreenWindow => {
|
||||
let focus = self.niri.layout.focus().cloned();
|
||||
let focus = self.niri.layout.focus().map(|m| m.window.clone());
|
||||
if let Some(window) = focus {
|
||||
self.niri.layout.toggle_fullscreen(&window);
|
||||
// FIXME: granular
|
||||
@ -1017,8 +1020,8 @@ impl State {
|
||||
let button_state = event.state();
|
||||
|
||||
if ButtonState::Pressed == button_state {
|
||||
if let Some(window) = self.niri.window_under_cursor() {
|
||||
let window = window.clone();
|
||||
if let Some(mapped) = self.niri.window_under_cursor() {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.activate_window(&window);
|
||||
|
||||
// FIXME: granular.
|
||||
@ -1177,8 +1180,8 @@ impl State {
|
||||
tool.tip_down(serial, event.time_msec());
|
||||
|
||||
if let Some(pos) = self.niri.tablet_cursor_location {
|
||||
if let Some(window) = self.niri.window_under(pos) {
|
||||
let window = window.clone();
|
||||
if let Some(mapped) = self.niri.window_under(pos) {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.activate_window(&window);
|
||||
|
||||
// FIXME: granular.
|
||||
@ -1535,8 +1538,8 @@ impl State {
|
||||
.output_under(touch_location)
|
||||
.next()
|
||||
.cloned();
|
||||
if let Some(window) = self.niri.window_under(touch_location) {
|
||||
let window = window.clone();
|
||||
if let Some(mapped) = self.niri.window_under(touch_location) {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.activate_window(&window);
|
||||
|
||||
// FIXME: granular.
|
||||
|
@ -38,21 +38,14 @@ use niri_config::{CenterFocusedColumn, Config, Struts};
|
||||
use niri_ipc::SizeChange;
|
||||
use smithay::backend::renderer::element::solid::SolidColorRenderElement;
|
||||
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
||||
use smithay::backend::renderer::element::{AsRenderElements, Id};
|
||||
use smithay::desktop::space::SpaceElement;
|
||||
use smithay::desktop::Window;
|
||||
use smithay::backend::renderer::element::Id;
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;
|
||||
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, Transform};
|
||||
use smithay::wayland::compositor::{send_surface_state, with_states};
|
||||
use smithay::wayland::shell::xdg::SurfaceCachedState;
|
||||
use smithay::utils::{Logical, Point, Scale, Size, Transform};
|
||||
|
||||
use self::monitor::Monitor;
|
||||
pub use self::monitor::MonitorRenderElement;
|
||||
use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Workspace};
|
||||
use crate::niri::WindowOffscreenId;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::utils::output_size;
|
||||
@ -229,148 +222,6 @@ impl Options {
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutElement for Window {
|
||||
type Id = Self;
|
||||
|
||||
fn id(&self) -> &Self::Id {
|
||||
self
|
||||
}
|
||||
|
||||
fn size(&self) -> Size<i32, Logical> {
|
||||
self.geometry().size
|
||||
}
|
||||
|
||||
fn buf_loc(&self) -> Point<i32, Logical> {
|
||||
Point::from((0, 0)) - self.geometry().loc
|
||||
}
|
||||
|
||||
fn is_in_input_region(&self, point: Point<f64, Logical>) -> bool {
|
||||
let surface_local = point + self.geometry().loc.to_f64();
|
||||
SpaceElement::is_in_input_region(self, &surface_local)
|
||||
}
|
||||
|
||||
fn render<R: NiriRenderer>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
location: Point<i32, Logical>,
|
||||
scale: Scale<f64>,
|
||||
) -> Vec<LayoutElementRenderElement<R>> {
|
||||
let buf_pos = location - self.geometry().loc;
|
||||
self.render_elements(
|
||||
renderer,
|
||||
buf_pos.to_physical_precise_round(scale),
|
||||
scale,
|
||||
1.,
|
||||
)
|
||||
}
|
||||
|
||||
fn request_size(&self, size: Size<i32, Logical>) {
|
||||
self.toplevel()
|
||||
.expect("no x11 support")
|
||||
.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()
|
||||
.expect("no x11 support")
|
||||
.with_pending_state(|state| {
|
||||
state.size = Some(size);
|
||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||
});
|
||||
}
|
||||
|
||||
fn min_size(&self) -> Size<i32, Logical> {
|
||||
with_states(
|
||||
self.toplevel().expect("no x11 support").wl_surface(),
|
||||
|state| {
|
||||
let curr = state.cached_state.current::<SurfaceCachedState>();
|
||||
curr.min_size
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn max_size(&self) -> Size<i32, Logical> {
|
||||
with_states(
|
||||
self.toplevel().expect("no x11 support").wl_surface(),
|
||||
|state| {
|
||||
let curr = state.cached_state.current::<SurfaceCachedState>();
|
||||
curr.max_size
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool {
|
||||
self.toplevel().expect("no x11 support").wl_surface() == wl_surface
|
||||
}
|
||||
|
||||
fn set_preferred_scale_transform(&self, scale: i32, transform: Transform) {
|
||||
self.with_surfaces(|surface, data| {
|
||||
send_surface_state(surface, data, scale, transform);
|
||||
});
|
||||
}
|
||||
|
||||
fn has_ssd(&self) -> bool {
|
||||
self.toplevel()
|
||||
.expect("no x11 support")
|
||||
.current_state()
|
||||
.decoration_mode
|
||||
== Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
|
||||
}
|
||||
|
||||
fn output_enter(&self, output: &Output) {
|
||||
let overlap = Rectangle::from_loc_and_size((0, 0), (i32::MAX, i32::MAX));
|
||||
SpaceElement::output_enter(self, output, overlap)
|
||||
}
|
||||
|
||||
fn output_leave(&self, output: &Output) {
|
||||
SpaceElement::output_leave(self, output)
|
||||
}
|
||||
|
||||
fn set_offscreen_element_id(&self, id: Option<Id>) {
|
||||
let data = self.user_data().get_or_insert(WindowOffscreenId::default);
|
||||
data.0.replace(id);
|
||||
}
|
||||
|
||||
fn set_activated(&self, active: bool) {
|
||||
Window::set_activated(self, active);
|
||||
}
|
||||
|
||||
fn set_bounds(&self, bounds: Size<i32, Logical>) {
|
||||
self.toplevel()
|
||||
.expect("no x11 support")
|
||||
.with_pending_state(|state| {
|
||||
state.bounds = Some(bounds);
|
||||
});
|
||||
}
|
||||
|
||||
fn send_pending_configure(&self) {
|
||||
self.toplevel()
|
||||
.expect("no x11 support")
|
||||
.send_pending_configure();
|
||||
}
|
||||
|
||||
fn is_fullscreen(&self) -> bool {
|
||||
self.toplevel()
|
||||
.expect("no x11 support")
|
||||
.current_state()
|
||||
.states
|
||||
.contains(xdg_toplevel::State::Fullscreen)
|
||||
}
|
||||
|
||||
fn is_pending_fullscreen(&self) -> bool {
|
||||
self.toplevel()
|
||||
.expect("no x11 support")
|
||||
.with_pending_state(|state| state.states.contains(xdg_toplevel::State::Fullscreen))
|
||||
}
|
||||
|
||||
fn refresh(&self) {
|
||||
SpaceElement::refresh(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Layout<W> {
|
||||
pub fn new(config: &Config) -> Self {
|
||||
Self::with_options(Options::from_config(config))
|
||||
@ -792,6 +643,29 @@ impl<W: LayoutElement> Layout<W> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_window_mut(&mut self, wl_surface: &WlSurface) -> Option<&mut W> {
|
||||
match &mut self.monitor_set {
|
||||
MonitorSet::Normal { monitors, .. } => {
|
||||
for mon in monitors {
|
||||
for ws in &mut mon.workspaces {
|
||||
if let Some(window) = ws.find_wl_surface_mut(wl_surface) {
|
||||
return Some(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MonitorSet::NoOutputs { workspaces } => {
|
||||
for ws in workspaces {
|
||||
if let Some(window) = ws.find_wl_surface_mut(wl_surface) {
|
||||
return Some(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_window_and_output(&self, wl_surface: &WlSurface) -> Option<(&W, &Output)> {
|
||||
if let MonitorSet::Normal { monitors, .. } = &self.monitor_set {
|
||||
for mon in monitors {
|
||||
@ -1866,6 +1740,7 @@ mod tests {
|
||||
use proptest::prelude::*;
|
||||
use proptest_derive::Arbitrary;
|
||||
use smithay::output::{Mode, PhysicalProperties, Subpixel};
|
||||
use smithay::utils::Rectangle;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -121,6 +121,10 @@ impl<W: LayoutElement> Tile<W> {
|
||||
&self.window
|
||||
}
|
||||
|
||||
pub fn window_mut(&mut self) -> &mut W {
|
||||
&mut self.window
|
||||
}
|
||||
|
||||
pub fn into_window(self) -> W {
|
||||
self.window
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ 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::send_surface_state;
|
||||
|
||||
use super::tile::{Tile, TileRenderElement};
|
||||
use super::{LayoutElement, Options};
|
||||
@ -299,6 +300,13 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
.map(Tile::window)
|
||||
}
|
||||
|
||||
pub fn windows_mut(&mut self) -> impl Iterator<Item = &mut W> + '_ {
|
||||
self.columns
|
||||
.iter_mut()
|
||||
.flat_map(|col| col.tiles.iter_mut())
|
||||
.map(Tile::window_mut)
|
||||
}
|
||||
|
||||
pub fn set_output(&mut self, output: Option<Output>) {
|
||||
if self.output == output {
|
||||
return;
|
||||
@ -407,7 +415,11 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
|
||||
pub fn configure_new_window(&self, window: &Window, width: Option<ColumnWidth>) {
|
||||
if let Some(output) = self.output.as_ref() {
|
||||
set_preferred_scale_transform(window, output);
|
||||
let scale = output.current_scale().integer_scale();
|
||||
let transform = output.current_transform();
|
||||
window.with_surfaces(|surface, data| {
|
||||
send_surface_state(surface, data, scale, transform);
|
||||
});
|
||||
}
|
||||
|
||||
window
|
||||
@ -584,6 +596,10 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
self.windows().find(|win| win.is_wl_surface(wl_surface))
|
||||
}
|
||||
|
||||
pub fn find_wl_surface_mut(&mut self, wl_surface: &WlSurface) -> Option<&mut W> {
|
||||
self.windows_mut().find(|win| win.is_wl_surface(wl_surface))
|
||||
}
|
||||
|
||||
/// Computes the X position of the windows in the given column, in logical coordinates.
|
||||
fn column_x(&self, column_idx: usize) -> i32 {
|
||||
let mut x = 0;
|
||||
|
32
src/niri.rs
32
src/niri.rs
@ -114,7 +114,7 @@ use crate::utils::spawning::CHILD_ENV;
|
||||
use crate::utils::{
|
||||
center, center_f64, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8,
|
||||
};
|
||||
use crate::window::Unmapped;
|
||||
use crate::window::{Mapped, Unmapped};
|
||||
use crate::{animation, niri_render_elements};
|
||||
|
||||
const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.];
|
||||
@ -138,7 +138,7 @@ pub struct Niri {
|
||||
|
||||
// Each workspace corresponds to a Space. Each workspace generally has one Output mapped to it,
|
||||
// however it may have none (when there are no outputs connected) or mutiple (when mirroring).
|
||||
pub layout: Layout<Window>,
|
||||
pub layout: Layout<Mapped>,
|
||||
|
||||
// This space does not actually contain any windows, but all outputs are mapped into it
|
||||
// according to their global position.
|
||||
@ -631,7 +631,7 @@ impl State {
|
||||
self.niri
|
||||
.layout
|
||||
.focus()
|
||||
.map(|win| win.toplevel().expect("no x11 support").wl_surface().clone())
|
||||
.map(|win| win.toplevel().wl_surface().clone())
|
||||
.map(|surface| KeyboardFocus::Layout {
|
||||
surface: Some(surface),
|
||||
})
|
||||
@ -1591,7 +1591,7 @@ impl Niri {
|
||||
///
|
||||
/// The cursor may be inside the window's activation region, but not within the window's input
|
||||
/// region.
|
||||
pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<&Window> {
|
||||
pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<&Mapped> {
|
||||
if self.is_locked() || self.screenshot_ui.is_open() {
|
||||
return None;
|
||||
}
|
||||
@ -1618,7 +1618,7 @@ impl Niri {
|
||||
///
|
||||
/// The cursor may be inside the window's activation region, but not within the window's input
|
||||
/// region.
|
||||
pub fn window_under_cursor(&self) -> Option<&Window> {
|
||||
pub fn window_under_cursor(&self) -> Option<&Mapped> {
|
||||
let pos = self.seat.get_pointer().unwrap().current_location();
|
||||
self.window_under(pos)
|
||||
}
|
||||
@ -1684,8 +1684,9 @@ impl Niri {
|
||||
let window_under = || {
|
||||
self.layout
|
||||
.window_under(output, pos_within_output)
|
||||
.and_then(|(window, win_pos_within_output)| {
|
||||
.and_then(|(mapped, win_pos_within_output)| {
|
||||
let win_pos_within_output = win_pos_within_output?;
|
||||
let window = &mapped.window;
|
||||
window
|
||||
.surface_under(
|
||||
pos_within_output - win_pos_within_output.to_f64(),
|
||||
@ -2409,7 +2410,8 @@ impl Niri {
|
||||
// The reason to do this at all is that it keeps track of whether the surface is visible or
|
||||
// not in a unified way with the pointer surfaces, which makes the logic elsewhere simpler.
|
||||
|
||||
for win in self.layout.windows_for_output(output) {
|
||||
for mapped in self.layout.windows_for_output(output) {
|
||||
let win = &mapped.window;
|
||||
let offscreen_id = win
|
||||
.user_data()
|
||||
.get_or_insert(WindowOffscreenId::default)
|
||||
@ -2480,8 +2482,8 @@ impl Niri {
|
||||
// We can unconditionally send the current output's feedback to regular and layer-shell
|
||||
// surfaces, as they can only be displayed on a single output at a time. Even if a surface
|
||||
// is currently invisible, this is the DMABUF feedback that it should know about.
|
||||
for win in self.layout.windows_for_output(output) {
|
||||
win.send_dmabuf_feedback(
|
||||
for mapped in self.layout.windows_for_output(output) {
|
||||
mapped.window.send_dmabuf_feedback(
|
||||
output,
|
||||
|_, _| Some(output.clone()),
|
||||
|surface, _| {
|
||||
@ -2600,8 +2602,8 @@ impl Niri {
|
||||
|
||||
let frame_callback_time = get_monotonic_time();
|
||||
|
||||
for win in self.layout.windows_for_output(output) {
|
||||
win.send_frame(
|
||||
for mapped in self.layout.windows_for_output(output) {
|
||||
mapped.window.send_frame(
|
||||
output,
|
||||
frame_callback_time,
|
||||
FRAME_CALLBACK_THROTTLE,
|
||||
@ -2666,8 +2668,8 @@ impl Niri {
|
||||
|
||||
let frame_callback_time = get_monotonic_time();
|
||||
|
||||
self.layout.with_windows(|win, _| {
|
||||
win.send_frame(
|
||||
self.layout.with_windows(|mapped, _| {
|
||||
mapped.window.send_frame(
|
||||
output,
|
||||
frame_callback_time,
|
||||
FRAME_CALLBACK_THROTTLE,
|
||||
@ -2746,8 +2748,8 @@ impl Niri {
|
||||
);
|
||||
}
|
||||
|
||||
for win in self.layout.windows_for_output(output) {
|
||||
win.take_presentation_feedback(
|
||||
for mapped in self.layout.windows_for_output(output) {
|
||||
mapped.window.take_presentation_feedback(
|
||||
&mut feedback,
|
||||
surface_primary_scanout_output,
|
||||
|surface, _| {
|
||||
|
@ -95,8 +95,8 @@ pub fn refresh(state: &mut State) {
|
||||
// Save the focused window for last, this way when the focus changes, we will first deactivate
|
||||
// the previous window and only then activate the newly focused window.
|
||||
let mut focused = None;
|
||||
state.niri.layout.with_windows(|window, output| {
|
||||
let wl_surface = window.toplevel().expect("no x11 support").wl_surface();
|
||||
state.niri.layout.with_windows(|mapped, output| {
|
||||
let wl_surface = mapped.toplevel().wl_surface();
|
||||
|
||||
with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
@ -107,7 +107,7 @@ pub fn refresh(state: &mut State) {
|
||||
.unwrap();
|
||||
|
||||
if state.niri.keyboard_focus.surface() == Some(wl_surface) {
|
||||
focused = Some((window.clone(), output.cloned()));
|
||||
focused = Some((mapped.window.clone(), output.cloned()));
|
||||
} else {
|
||||
refresh_toplevel(protocol_state, wl_surface, &role, output, false);
|
||||
}
|
||||
|
@ -20,10 +20,6 @@ pub mod watcher;
|
||||
|
||||
pub static IS_SYSTEMD_SERVICE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub fn clone2<T: Clone, U: Clone>(t: (&T, &U)) -> (T, U) {
|
||||
(t.0.clone(), t.1.clone())
|
||||
}
|
||||
|
||||
pub fn version() -> String {
|
||||
format!(
|
||||
"{} ({})",
|
||||
|
159
src/window/mapped.rs
Normal file
159
src/window/mapped.rs
Normal file
@ -0,0 +1,159 @@
|
||||
use smithay::backend::renderer::element::{AsRenderElements as _, Id};
|
||||
use smithay::desktop::space::SpaceElement as _;
|
||||
use smithay::desktop::Window;
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;
|
||||
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, Transform};
|
||||
use smithay::wayland::compositor::{send_surface_state, with_states};
|
||||
use smithay::wayland::shell::xdg::{SurfaceCachedState, ToplevelSurface};
|
||||
|
||||
use super::ResolvedWindowRules;
|
||||
use crate::layout::{LayoutElement, LayoutElementRenderElement};
|
||||
use crate::niri::WindowOffscreenId;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mapped {
|
||||
pub window: Window,
|
||||
|
||||
/// Up-to-date rules.
|
||||
pub rules: ResolvedWindowRules,
|
||||
}
|
||||
|
||||
impl Mapped {
|
||||
pub fn new(window: Window, rules: ResolvedWindowRules) -> Self {
|
||||
Self { window, rules }
|
||||
}
|
||||
|
||||
pub fn toplevel(&self) -> &ToplevelSurface {
|
||||
self.window.toplevel().expect("no X11 support")
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutElement for Mapped {
|
||||
type Id = Window;
|
||||
|
||||
fn id(&self) -> &Self::Id {
|
||||
&self.window
|
||||
}
|
||||
|
||||
fn size(&self) -> Size<i32, Logical> {
|
||||
self.window.geometry().size
|
||||
}
|
||||
|
||||
fn buf_loc(&self) -> Point<i32, Logical> {
|
||||
Point::from((0, 0)) - self.window.geometry().loc
|
||||
}
|
||||
|
||||
fn is_in_input_region(&self, point: Point<f64, Logical>) -> bool {
|
||||
let surface_local = point + self.window.geometry().loc.to_f64();
|
||||
self.window.is_in_input_region(&surface_local)
|
||||
}
|
||||
|
||||
fn render<R: NiriRenderer>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
location: Point<i32, Logical>,
|
||||
scale: Scale<f64>,
|
||||
) -> Vec<LayoutElementRenderElement<R>> {
|
||||
let buf_pos = location - self.window.geometry().loc;
|
||||
self.window.render_elements(
|
||||
renderer,
|
||||
buf_pos.to_physical_precise_round(scale),
|
||||
scale,
|
||||
1.,
|
||||
)
|
||||
}
|
||||
|
||||
fn request_size(&self, size: Size<i32, Logical>) {
|
||||
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> {
|
||||
with_states(self.toplevel().wl_surface(), |state| {
|
||||
let curr = state.cached_state.current::<SurfaceCachedState>();
|
||||
curr.min_size
|
||||
})
|
||||
}
|
||||
|
||||
fn max_size(&self) -> Size<i32, Logical> {
|
||||
with_states(self.toplevel().wl_surface(), |state| {
|
||||
let curr = state.cached_state.current::<SurfaceCachedState>();
|
||||
curr.max_size
|
||||
})
|
||||
}
|
||||
|
||||
fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool {
|
||||
self.toplevel().wl_surface() == wl_surface
|
||||
}
|
||||
|
||||
fn set_preferred_scale_transform(&self, scale: i32, transform: Transform) {
|
||||
self.window.with_surfaces(|surface, data| {
|
||||
send_surface_state(surface, data, scale, transform);
|
||||
});
|
||||
}
|
||||
|
||||
fn has_ssd(&self) -> bool {
|
||||
self.toplevel().current_state().decoration_mode
|
||||
== Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
|
||||
}
|
||||
|
||||
fn output_enter(&self, output: &Output) {
|
||||
let overlap = Rectangle::from_loc_and_size((0, 0), (i32::MAX, i32::MAX));
|
||||
self.window.output_enter(output, overlap)
|
||||
}
|
||||
|
||||
fn output_leave(&self, output: &Output) {
|
||||
self.window.output_leave(output)
|
||||
}
|
||||
|
||||
fn set_offscreen_element_id(&self, id: Option<Id>) {
|
||||
let data = self
|
||||
.window
|
||||
.user_data()
|
||||
.get_or_insert(WindowOffscreenId::default);
|
||||
data.0.replace(id);
|
||||
}
|
||||
|
||||
fn set_activated(&self, active: bool) {
|
||||
self.window.set_activated(active);
|
||||
}
|
||||
|
||||
fn set_bounds(&self, bounds: Size<i32, Logical>) {
|
||||
self.toplevel().with_pending_state(|state| {
|
||||
state.bounds = Some(bounds);
|
||||
});
|
||||
}
|
||||
|
||||
fn send_pending_configure(&self) {
|
||||
self.toplevel().send_pending_configure();
|
||||
}
|
||||
|
||||
fn is_fullscreen(&self) -> bool {
|
||||
self.toplevel()
|
||||
.current_state()
|
||||
.states
|
||||
.contains(xdg_toplevel::State::Fullscreen)
|
||||
}
|
||||
|
||||
fn is_pending_fullscreen(&self) -> bool {
|
||||
self.toplevel()
|
||||
.with_pending_state(|state| state.states.contains(xdg_toplevel::State::Fullscreen))
|
||||
}
|
||||
|
||||
fn refresh(&self) {
|
||||
self.window.refresh();
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
use crate::layout::workspace::ColumnWidth;
|
||||
|
||||
pub mod mapped;
|
||||
pub use mapped::Mapped;
|
||||
|
||||
pub mod unmapped;
|
||||
pub use unmapped::{InitialConfigureState, Unmapped};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user