From 9f65bc386e7cc0a25abede089242fc86cc166cdc Mon Sep 17 00:00:00 2001 From: Seaotatop Date: Fri, 23 Jun 2023 09:44:00 -0500 Subject: [PATCH] Fix flickering when swapping tiled windows --- README.md | 2 - src/backend/winit.rs | 6 ++- src/grab/move_grab.rs | 18 +++++---- src/handlers.rs | 41 +++++++++++++++++--- src/layout/automatic.rs | 78 +++++++++++++++++++++++++++++--------- src/window.rs | 32 ++++++++++++---- src/window/window_state.rs | 32 +++++++++++++++- 7 files changed, 166 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 1a29006..6388eec 100644 --- a/README.md +++ b/README.md @@ -100,5 +100,3 @@ You can find the rest of the controls in the [`example_config`](api/lua/example_ ## A Small Note This is currently just a summer project I'm working on, but I hope that I can work on it enough that it becomes somewhat usable! If development slows down during the rest of the year, it's because :star:university:star:. - -Also the only layout is kinda wonk right now if you close all but one window diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 1b259d4..d53ee20 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -375,7 +375,11 @@ pub fn run_winit() -> Result<(), Box> { TimeoutAction::ToDuration(Duration::from_millis(6)) })?; - event_loop.run(None, &mut CalloopData { display, state }, |_data| {})?; + event_loop.run( + Some(Duration::from_millis(6)), + &mut CalloopData { display, state }, + |_data| {}, + )?; Ok(()) } diff --git a/src/grab/move_grab.rs b/src/grab/move_grab.rs index 33d4d61..904f5c3 100644 --- a/src/grab/move_grab.rs +++ b/src/grab/move_grab.rs @@ -40,14 +40,20 @@ impl PointerGrab> for MoveSurfaceGrab> { return; } - let tiled = WindowState::with_state(&self.window, |state| { - matches!(state.floating, Float::Tiled(_)) - }); + data.space.raise_element(&self.window, false); + + // tracing::info!("window geo is: {:?}", self.window.geometry()); + // tracing::info!("loc is: {:?}", data.space.element_location(&self.window)); + + let tiled = WindowState::with_state(&self.window, |state| state.floating.is_tiled()); if tiled { + // INFO: this is being used instead of space.element_under(event.location) because that + // | uses the bounding box, which is different from the actual geometry let window_under = data .space .elements() + .rev() .find(|&win| { if let Some(loc) = data.space.element_location(win) { let size = win.geometry().size; @@ -64,15 +70,13 @@ impl PointerGrab> for MoveSurfaceGrab> { return; } - let window_under_floating = WindowState::with_state(&window_under, |state| { - matches!(state.floating, Float::Floating) - }); + let window_under_floating = + WindowState::with_state(&window_under, |state| state.floating.is_floating()); if window_under_floating { return; } - tracing::info!("{:?}, {:?}", self.window.geometry(), self.window.bbox()); data.swap_window_positions(&self.window, &window_under); } } else { diff --git a/src/handlers.rs b/src/handlers.rs index bb515d8..94e9fef 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -32,8 +32,8 @@ use smithay::{ dmabuf, fractional_scale::{self, FractionalScaleHandler}, shell::xdg::{ - PopupSurface, PositionerState, ToplevelSurface, XdgPopupSurfaceData, XdgShellHandler, - XdgShellState, XdgToplevelSurfaceData, + Configure, PopupSurface, PositionerState, ToplevelSurface, XdgPopupSurfaceData, + XdgShellHandler, XdgShellState, XdgToplevelSurfaceData, }, shm::{ShmHandler, ShmState}, }, @@ -43,6 +43,7 @@ use crate::{ backend::Backend, layout::Layout, state::{ClientState, State}, + window::window_state::{WindowResizeState, WindowState}, }; impl BufferHandler for State { @@ -86,8 +87,6 @@ impl CompositorHandler for State { } fn commit(&mut self, surface: &WlSurface) { - // println!("CompositorHandler commit()"); - utils::on_commit_buffer_handler::(surface); if !compositor::is_sync_subsurface(surface) { @@ -105,6 +104,17 @@ impl CompositorHandler for State { ensure_initial_configure(surface, self); crate::grab::resize_grab::handle_commit(self, surface); + + if let Some(window) = self.window_for_surface(surface) { + WindowState::with_state(&window, |state| { + if let WindowResizeState::WaitingForCommit(new_pos) = state.resize_state { + // tracing::info!("Committing, new location"); + state.resize_state = WindowResizeState::Idle; + self.space.map_element(window.clone(), new_pos, false); + } + // state.resize_state + }); + } } fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { @@ -127,13 +137,14 @@ fn ensure_initial_configure(surface: &WlSurface, state: &mut State XdgShellHandler for State { } } + fn ack_configure(&mut self, surface: WlSurface, configure: Configure) { + // TODO: add serial to WaitingForAck + if let Some(window) = self.window_for_surface(&surface) { + WindowState::with_state(&window, |state| { + if let WindowResizeState::WaitingForAck(serial, new_loc) = state.resize_state { + match &configure { + Configure::Toplevel(configure) => { + // tracing::info!("acking before serial check"); + if configure.serial >= serial { + // tracing::info!("acking, serial >="); + state.resize_state = WindowResizeState::WaitingForCommit(new_loc); + } + } + Configure::Popup(_) => todo!(), + } + } + }); + } + } + // TODO: impl the rest of the fns in XdgShellHandler } delegate_xdg_shell!(@ State); diff --git a/src/layout/automatic.rs b/src/layout/automatic.rs index af320a7..7673af2 100644 --- a/src/layout/automatic.rs +++ b/src/layout/automatic.rs @@ -1,9 +1,13 @@ -use smithay::desktop::Window; +use smithay::{ + desktop::Window, + utils::SERIAL_COUNTER, + wayland::{compositor, shell::xdg::XdgToplevelSurfaceData}, +}; use crate::{ backend::Backend, state::State, - window::window_state::{Float, WindowState}, + window::window_state::{WindowResizeState, WindowState}, }; use super::{Direction, Layout}; @@ -14,11 +18,11 @@ impl Layout { mut windows: Vec, side: Direction, ) { - windows.retain(|win| { - WindowState::with_state(win, |state| matches!(state.floating, Float::Tiled(_))) - }); + windows.retain(|win| WindowState::with_state(win, |state| state.floating.is_tiled())); match side { Direction::Left => { + let serial = SERIAL_COUNTER.next_serial(); + let window_count = windows.len(); if window_count == 0 { return; @@ -37,9 +41,31 @@ impl Layout { tl_state.size = Some(state.space.output_geometry(&output).unwrap().size); }); - state - .space - .map_element(window, output.current_location(), false); + WindowState::with_state(&window, |state| { + state.resize_state = + WindowResizeState::WaitingForAck(serial, output.current_location()); + }); + + // state.loop_handle.insert_idle(move |_calloop_data| { + // window.toplevel().send_pending_configure(); + // }); + let initial_configure_sent = + compositor::with_states(window.toplevel().wl_surface(), |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }); + if initial_configure_sent { + window.toplevel().send_pending_configure(); + } + + // state + // .space + // .map_element(window, output.current_location(), false); return; } @@ -55,9 +81,14 @@ impl Layout { tl_state.size = Some(size); }); - state - .space - .map_element(first_window.clone(), output.current_location(), false); + // state + // .space + // .map_element(first_window.clone(), output.current_location(), false); + + WindowState::with_state(first_window, |state| { + state.resize_state = + WindowResizeState::WaitingForAck(serial, output.current_location()); + }); let window_count = windows.len() as i32; let height = output_size.h / window_count; @@ -73,22 +104,33 @@ impl Layout { let mut new_loc = output.current_location(); new_loc.x = x; - new_loc.y = i as i32 * height; + new_loc.y = (i as i32) * height; - state.space.map_element(win.clone(), new_loc, false); + // state.space.map_element(win.clone(), new_loc, false); + WindowState::with_state(win, |state| { + state.resize_state = WindowResizeState::WaitingForAck(serial, new_loc); + }); } } - state.backend_data.reset_buffers(&output); - // INFO: We send configures when the event loop is idle so // | CompositorHandler::commit() sends the initial configure // TODO: maybe check if the initial configure was sent instead? - state.loop_handle.insert_idle(|_calloop_data| { - for win in windows { + for win in windows { + let initial_configure_sent = + compositor::with_states(win.toplevel().wl_surface(), |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }); + if initial_configure_sent { win.toplevel().send_pending_configure(); } - }); + } } Direction::Right => todo!(), Direction::Top => todo!(), diff --git a/src/window.rs b/src/window.rs index 474425f..d6eddcf 100644 --- a/src/window.rs +++ b/src/window.rs @@ -3,17 +3,24 @@ use std::cell::RefCell; use smithay::{ desktop::Window, reexports::wayland_server::protocol::wl_surface::WlSurface, + utils::SERIAL_COUNTER, wayland::{compositor, seat::WaylandFocus}, }; -use crate::{backend::Backend, layout::Layout, state::State}; +use crate::{ + backend::Backend, layout::Layout, state::State, window::window_state::WindowResizeState, +}; use self::window_state::{Float, WindowState}; pub mod window_state; pub trait SurfaceState: Default + 'static { - /// Access the [SurfaceState] associated with a [WlSurface] + /// Access the [SurfaceState] associated with a [WlSurface]. + /// + /// # Panics + /// + /// This function will panic if you use it within itself due to the use of a [RefCell]. fn with_state(wl_surface: &WlSurface, function: F) -> T where F: FnOnce(&mut Self) -> T, @@ -39,8 +46,10 @@ impl State { pub fn swap_window_positions(&mut self, win1: &Window, win2: &Window) { let win1_loc = self.space.element_location(win1).unwrap(); // TODO: handle unwraps let win2_loc = self.space.element_location(win2).unwrap(); - let win1_geo = self.space.element_geometry(win1).unwrap(); - let win2_geo = self.space.element_geometry(win2).unwrap(); + let win1_geo = win1.geometry(); + let win2_geo = win2.geometry(); + // tracing::info!("win1: {:?}, {:?}", win1_loc, win1_geo); + // tracing::info!("win2: {:?}, {:?}", win2_loc, win2_geo); win1.toplevel().with_pending_state(|state| { state.size = Some(win2_geo.size); @@ -49,11 +58,18 @@ impl State { state.size = Some(win1_geo.size); }); - self.space.map_element(win1.clone(), win2_loc, false); - self.space.map_element(win2.clone(), win1_loc, false); + let serial = win1.toplevel().send_configure(); + WindowState::with_state(win1, |state| { + state.resize_state = WindowResizeState::WaitingForAck(serial, win2_loc); + }); - win1.toplevel().send_pending_configure(); - win2.toplevel().send_pending_configure(); + let serial = win2.toplevel().send_configure(); + WindowState::with_state(win2, |state| { + state.resize_state = WindowResizeState::WaitingForAck(serial, win1_loc); + }); + + // self.space.map_element(win1.clone(), win2_loc, false); + // self.space.map_element(win2.clone(), win1_loc, false); } } diff --git a/src/window/window_state.rs b/src/window/window_state.rs index 7f1cafd..e83d1c0 100644 --- a/src/window/window_state.rs +++ b/src/window/window_state.rs @@ -1,12 +1,21 @@ -use std::{borrow::BorrowMut, cell::RefCell}; +use std::cell::RefCell; use smithay::{ desktop::Window, - utils::{Logical, Point, Size}, + utils::{Logical, Point, Serial, Size}, }; pub struct WindowState { pub floating: Float, + pub resize_state: WindowResizeState, +} + +#[derive(Debug, Default)] +pub enum WindowResizeState { + #[default] + Idle, + WaitingForAck(Serial, Point), + WaitingForCommit(Point), } pub enum Float { @@ -15,6 +24,24 @@ pub enum Float { Floating, } +impl Float { + /// Returns `true` if the float is [`Tiled`]. + /// + /// [`Tiled`]: Float::Tiled + #[must_use] + pub fn is_tiled(&self) -> bool { + matches!(self, Self::Tiled(..)) + } + + /// Returns `true` if the float is [`Floating`]. + /// + /// [`Floating`]: Float::Floating + #[must_use] + pub fn is_floating(&self) -> bool { + matches!(self, Self::Floating) + } +} + impl WindowState { pub fn new() -> Self { Default::default() @@ -43,6 +70,7 @@ impl Default for WindowState { Self { // TODO: get this from a config file instead of hardcoding floating: Float::Tiled(None), + resize_state: Default::default(), } } }