diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 0d3ffae..2d79177 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -853,38 +853,6 @@ impl State { device_id: node, }); - // Run any connected callbacks - { - let clone = output.clone(); - self.loop_handle.insert_idle(|data| { - crate::state::schedule( - data, - |dt| dt.state.api_state.stream.is_some(), - move |dt| { - let stream = dt - .state - .api_state - .stream - .as_ref() - .expect("Stream doesn't exist"); - let mut stream = stream.lock().expect("Couldn't lock stream"); - for callback_id in dt.state.output_callback_ids.iter() { - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::CallCallback { - callback_id: *callback_id, - args: Some(Args::ConnectForAllOutputs { - output_name: clone.name(), - }), - }, - ) - .expect("Send to client failed"); - } - }, - ) - }); - } - let allocator = GbmAllocator::new( device.gbm.clone(), GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT, @@ -980,6 +948,35 @@ impl State { device.surfaces.insert(crtc, surface); self.schedule_initial_render(node, crtc, self.loop_handle.clone()); + + // Run any connected callbacks + { + let clone = output.clone(); + self.schedule( + |dt| dt.state.api_state.stream.is_some(), + move |dt| { + let stream = dt + .state + .api_state + .stream + .as_ref() + .expect("Stream doesn't exist"); + let mut stream = stream.lock().expect("Couldn't lock stream"); + for callback_id in dt.state.output_callback_ids.iter() { + crate::api::send_to_client( + &mut stream, + &OutgoingMsg::CallCallback { + callback_id: *callback_id, + args: Some(Args::ConnectForAllOutputs { + output_name: clone.name(), + }), + }, + ) + .expect("Send to client failed"); + } + }, + ); + } } fn connector_disconnected( diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index eba89fc..162b7e3 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -1,11 +1,8 @@ -use std::time::Duration; - use smithay::{ delegate_xdg_shell, desktop::{ - find_popup_root_surface, layer_map_for_output, utils::surface_primary_scanout_output, - PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window, - WindowSurfaceType, + find_popup_root_surface, layer_map_for_output, PopupKeyboardGrab, PopupKind, + PopupPointerGrab, PopupUngrabStrategy, Window, WindowSurfaceType, }, input::{pointer::Focus, Seat}, output::Output, @@ -91,49 +88,74 @@ impl XdgShellHandler for State { // note to self: don't reorder this // TODO: fix it so that reordering this doesn't break stuff self.windows.push(window.clone()); - // self.space.map_element(window.clone(), (0, 0), true); - if let Some(focused_output) = self.focus_state.focused_output.clone() { - self.update_windows(&focused_output); - BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); - tracing::debug!( - "blocker {}", - BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) - ); - for win in windows_on_output.iter() { - if let Some(surf) = win.wl_surface() { - compositor::add_blocker(&surf, crate::window::WindowBlocker); + + self.space.map_element(window.clone(), (0, 0), true); + + let win_clone = window.clone(); + self.schedule( + move |_data| { + if let WindowElement::Wayland(window) = &win_clone { + let initial_configure_sent = + compositor::with_states(window.toplevel().wl_surface(), |states| { + states + .data_map + .get::() + .expect("XdgToplevelSurfaceData wasn't in surface's data map") + .lock() + .expect("Failed to lock Mutex") + .initial_configure_sent + }); + + initial_configure_sent + } else { + true } - } - let clone = window.clone(); - self.loop_handle.insert_idle(|data| { - crate::state::schedule_on_commit(data, vec![clone], move |data| { - BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst); + }, + |data| { + if let Some(focused_output) = data.state.focus_state.focused_output.clone() { + data.state.update_windows(&focused_output); + BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); tracing::debug!( "blocker {}", BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) ); - for client in windows_on_output - .iter() - .filter_map(|win| win.wl_surface()?.client()) - { - data.state - .client_compositor_state(&client) - .blocker_cleared(&mut data.state, &data.display.handle()) + for win in windows_on_output.iter() { + if let Some(surf) = win.wl_surface() { + compositor::add_blocker(&surf, crate::window::WindowBlocker); + } } - }) - }); - } - self.loop_handle.insert_idle(move |data| { - data.state - .seat - .get_keyboard() - .expect("Seat had no keyboard") // FIXME: actually handle error - .set_focus( - &mut data.state, - Some(FocusTarget::Window(window)), - SERIAL_COUNTER.next_serial(), - ); - }); + let clone = window.clone(); + data.state.loop_handle.insert_idle(|data| { + crate::state::schedule_on_commit(data, vec![clone], move |data| { + BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst); + tracing::debug!( + "blocker {}", + BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) + ); + for client in windows_on_output + .iter() + .filter_map(|win| win.wl_surface()?.client()) + { + data.state + .client_compositor_state(&client) + .blocker_cleared(&mut data.state, &data.display.handle()) + } + }) + }); + } + data.state.loop_handle.insert_idle(move |data| { + data.state + .seat + .get_keyboard() + .expect("Seat had no keyboard") // FIXME: actually handle error + .set_focus( + &mut data.state, + Some(FocusTarget::Window(window)), + SERIAL_COUNTER.next_serial(), + ); + }); + }, + ); } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { @@ -262,16 +284,6 @@ impl XdgShellHandler for State { // tracing::debug!("acked configure, new loc is {:?}", new_loc); state.loc_request_state = LocationRequestState::Acknowledged(new_loc); - if let Some(focused_output) = - self.focus_state.focused_output.clone() - { - window.send_frame( - &focused_output, - self.clock.now(), - Some(Duration::ZERO), - surface_primary_scanout_output, - ); - } } } Configure::Popup(_) => todo!(), diff --git a/src/layout.rs b/src/layout.rs index 080ee6e..1477b97 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -114,9 +114,17 @@ impl State { window.with_state(|state| { if let LocationRequestState::Sent(loc) = state.loc_request_state { match &window { - WindowElement::Wayland(window) => { - let serial = window.toplevel().send_configure(); - state.loc_request_state = LocationRequestState::Requested(serial, loc); + WindowElement::Wayland(win) => { + // If the above didn't cause any change to size or other state, simply + // map the window. + if !win.toplevel().has_pending_changes() { + state.loc_request_state = LocationRequestState::Idle; + self.space.map_element(window.clone(), loc, false); + } else { + let serial = win.toplevel().send_configure(); + state.loc_request_state = + LocationRequestState::Requested(serial, loc); + } } WindowElement::X11(surface) => { // already configured, just need to map diff --git a/src/state.rs b/src/state.rs index 9774f93..3a1da88 100644 --- a/src/state.rs +++ b/src/state.rs @@ -159,22 +159,6 @@ where on_commit(data); } -// Schedule something to be done when `condition` returns true. -pub fn schedule(data: &mut CalloopData, condition: F1, run: F2) -where - F1: Fn(&mut CalloopData) -> bool + 'static, - F2: FnOnce(&mut CalloopData) + 'static, -{ - if !condition(data) { - data.state.loop_handle.insert_idle(|data| { - schedule(data, condition, run); - }); - return; - } - - run(data); -} - impl State { pub fn init( backend: Backend, @@ -393,6 +377,35 @@ impl State { xdisplay: None, }) } + + /// Schedule `run` to run when `condition` returns true. + /// + /// This will continually reschedule `run` in the event loop if `condition` returns false. + pub fn schedule(&self, condition: F1, run: F2) + where + F1: Fn(&mut CalloopData) -> bool + 'static, + F2: FnOnce(&mut CalloopData) + 'static, + { + self.loop_handle.insert_idle(|data| { + Self::schedule_inner(data, condition, run); + }); + } + + // Schedule something to be done when `condition` returns true. + fn schedule_inner(data: &mut CalloopData, condition: F1, run: F2) + where + F1: Fn(&mut CalloopData) -> bool + 'static, + F2: FnOnce(&mut CalloopData) + 'static, + { + if !condition(data) { + data.state.loop_handle.insert_idle(|data| { + Self::schedule_inner(data, condition, run); + }); + return; + } + + run(data); + } } fn get_config_dir() -> PathBuf {