Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions src/actor/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,11 +457,6 @@ impl State {
return Ok(false);
}

// If we don't know this window, nothing to verify.
if !self.windows.contains_key(&wid) {
return Ok(false);
}

// Trigger a visible windows refresh. If the window is gone, the reactor
// will detect it via missing membership and tear down state.
*request = Request::GetVisibleWindows;
Expand Down
45 changes: 29 additions & 16 deletions src/actor/reactor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod display_topology;
mod events;
mod main_window;
mod managers;
mod native_tabs;
mod query;
mod replay;
pub mod transaction_manager;
Expand Down Expand Up @@ -65,8 +66,8 @@ type Receiver = actor::Receiver<Event>;
pub use query::ReactorQueryHandle;

pub(crate) use crate::model::reactor::{
AppState, FullscreenSpaceTrack, FullscreenWindowTrack, PendingSpaceChange, WindowFilter,
WindowState,
AppState, FullscreenSpaceTrack, FullscreenWindowTrack, NativeTabMembership, NativeTabRole,
PendingSpaceChange, WindowFilter, WindowState,
};
pub use crate::model::reactor::{
Command, DisplaySelector, DragSession, DragState, MenuState, MissionControlState,
Expand Down Expand Up @@ -238,6 +239,7 @@ pub struct Reactor {
app_manager: managers::AppManager,
layout_manager: managers::LayoutManager,
window_manager: managers::WindowManager,
native_tab_manager: managers::NativeTabManager,
window_server_info_manager: managers::WindowServerInfoManager,
space_manager: managers::SpaceManager,
space_activation_policy: SpaceActivationPolicy,
Expand Down Expand Up @@ -318,6 +320,7 @@ impl Reactor {
visible_windows: HashSet::default(),
observed_window_server_ids: HashSet::default(),
},
native_tab_manager: managers::NativeTabManager::new(),
window_server_info_manager: managers::WindowServerInfoManager {
window_server_info: HashMap::default(),
},
Expand Down Expand Up @@ -972,6 +975,9 @@ impl Reactor {
self.set_login_window_active(false);
}
}
Event::ApplicationMainWindowChanged(pid, wid, quiet) => {
self.handle_native_tab_main_window_changed(pid, wid, quiet);
}
Event::ResyncAppForWindow(wsid) => {
AppEventHandler::handle_resync_app_for_window(self, wsid);
}
Expand Down Expand Up @@ -1315,6 +1321,11 @@ impl Reactor {
let Some(space) = space_opt else {
continue;
};
let is_fullscreen_space = window_server::space_is_fullscreen(space.get())
|| self.space_manager.fullscreen_by_space.contains_key(&space.get());
if is_fullscreen_space {
continue;
}
let Some(display_uuid) = screen.display_uuid_opt() else {
continue;
};
Expand Down Expand Up @@ -1519,14 +1530,19 @@ impl Reactor {
frame: &CGRect,
window_server_id: Option<WindowServerId>,
) -> Option<SpaceId> {
if let Some(space) = window_server_id.and_then(crate::sys::window_server::window_space)
&& (self.space_manager.screen_by_space(space).is_some()
|| crate::sys::window_server::space_is_user(space.get()))
{
if let Some(space) = self.best_space_for_frame(frame) {
return Some(space);
}

self.best_space_for_frame(frame)
if let Some(space) = window_server_id.and_then(crate::sys::window_server::window_space) {
if self.space_manager.screen_by_space(space).is_some()
|| crate::sys::window_server::space_is_user(space.get())
{
return Some(space);
}
}

None
}

fn best_space_for_frame(&self, frame: &CGRect) -> Option<SpaceId> {
Expand Down Expand Up @@ -1682,13 +1698,6 @@ impl Reactor {
self.screen_for_point(window_center).map(|_| window_center)
}

fn has_visible_window_server_ids_for_pid(&self, pid: pid_t) -> bool {
self.window_manager
.visible_windows
.iter()
.any(|wsid| self.window_manager.window_ids.get(wsid).is_some_and(|wid| wid.pid == pid))
}

fn warp_mouse_to_space_center(&self, space: SpaceId) -> bool {
let Some(screen) = self.space_manager.screen_by_space(space) else {
return false;
Expand Down Expand Up @@ -1935,8 +1944,8 @@ impl Reactor {
window.ignore_app_rule = false;
}

let effective_floating = assignment.floating
|| (!assignment.prev_rule_decision && was_floating);
let effective_floating =
assignment.floating || (!assignment.prev_rule_decision && was_floating);
let needs_layout_refresh =
!was_assigned || was_floating != effective_floating || was_ignored;
if needs_layout_refresh {
Expand Down Expand Up @@ -2728,6 +2737,10 @@ impl Reactor {
self.maybe_send_menu_update();
}

fn refresh_all_windows_without_pending_refresh(&mut self) {
self.request_visible_windows_for_apps(false);
}

fn force_refresh_all_windows(&mut self) { self.request_visible_windows_for_apps(true); }

fn request_close_window(&mut self, wid: WindowId) {
Expand Down
88 changes: 83 additions & 5 deletions src/actor/reactor/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl<'a> Animation<'a> {
origin: from.origin,
size: to.size,
};
_ = handle.send(Request::SetWindowFrame(wid, frame, txid, true));
_ = handle.send(Request::SetWindowFrame(wid, frame, txid, false));
}
}

Expand All @@ -97,9 +97,9 @@ impl<'a> Animation<'a> {
// clipped during the animation.
if frame * 2 == self.frames || frame == self.frames {
rect.size = to.size;
_ = handle.send(Request::SetWindowFrame(wid, rect, txid, true));
_ = handle.send(Request::SetWindowFrame(wid, rect, txid, false));
} else {
_ = handle.send(Request::SetWindowPos(wid, rect.origin, txid, true));
_ = handle.send(Request::SetWindowPos(wid, rect.origin, txid, false));
}
}
}
Expand Down Expand Up @@ -163,6 +163,7 @@ impl AnimationManager {
let mut animated_count = 0;
let mut animated_wids_wsids: Vec<u32> = Vec::new();
let mut any_frame_changed = false;
let mut synced_native_tab_wids = Vec::new();

for &(wid, target_frame) in layout {
// Skip applying layout frames and animations for the window currently being dragged.
Expand All @@ -182,8 +183,19 @@ impl AnimationManager {
if target_frame.same_as(current_frame) {
continue;
}
let Some(wsid) = window.info.sys_id else {
debug!(?wid, "Skipping animated layout - window has no server id");
continue;
};
if reactor
.transaction_manager
.get_target_frame(wsid)
.is_some_and(|pending| pending.same_as(target_frame))
{
trace!(?wid, ?target_frame, "Skipping redundant layout request");
continue;
}
any_frame_changed = true;
let wsid = window.info.sys_id.unwrap();
let txid = reactor.transaction_manager.generate_next_txid(wsid);
(current_frame, Some(wsid), txid)
}
Expand All @@ -193,7 +205,7 @@ impl AnimationManager {
}
};

let Some(app_state) = &reactor.app_manager.apps.get(&wid.pid) else {
let Some(app_state) = reactor.app_manager.apps.get(&wid.pid) else {
debug!(?wid, "Skipping for window - app no longer exists");
continue;
};
Expand Down Expand Up @@ -234,6 +246,7 @@ impl AnimationManager {
if let Some(window) = reactor.window_manager.windows.get_mut(&wid) {
window.frame_monotonic = target_frame;
}
synced_native_tab_wids.push(wid);
}

if animated_count > 0 {
Expand All @@ -250,6 +263,9 @@ impl AnimationManager {
anim.run();
}
}
for wid in synced_native_tab_wids {
reactor.handle_native_tab_frame_changed(wid, true);
}

any_frame_changed
}
Expand Down Expand Up @@ -278,6 +294,18 @@ impl AnimationManager {
if target_frame.same_as(current_frame) {
continue;
}
let Some(wsid) = window.info.sys_id else {
debug!(?wid, "Skipping instant layout - window has no server id");
continue;
};
if reactor
.transaction_manager
.get_target_frame(wsid)
.is_some_and(|pending| pending.same_as(target_frame))
{
trace!(?wid, ?target_frame, "Skipping redundant instant layout request");
continue;
}
any_frame_changed = true;
trace!(
?wid,
Expand Down Expand Up @@ -339,9 +367,59 @@ impl AnimationManager {
if let Some(window) = reactor.window_manager.windows.get_mut(wid) {
window.frame_monotonic = *target_frame;
}
reactor.handle_native_tab_frame_changed(*wid, true);
}
}

any_frame_changed
}
}

#[cfg(test)]
mod tests {
use objc2_core_foundation::{CGPoint, CGRect, CGSize};

use super::AnimationManager;
use crate::actor::reactor::testing::{Apps, make_window, screen_params_event};
use crate::actor::reactor::{Reactor, WindowId};
use crate::layout_engine::LayoutEngine;
use crate::sys::screen::SpaceId;

#[test]
fn layout_application_skips_windows_without_window_server_ids() {
let mut apps = Apps::new();
let mut reactor = Reactor::new_for_test(LayoutEngine::new(
&crate::common::config::VirtualWorkspaceSettings::default(),
&crate::common::config::LayoutSettings::default(),
None,
));
let space = SpaceId::new(90);
reactor.handle_event(screen_params_event(
vec![CGRect::new(CGPoint::new(0., 0.), CGSize::new(1000., 1000.))],
vec![Some(space)],
vec![],
));

reactor.handle_events(apps.make_app(1, vec![make_window(1)]));
apps.simulate_until_quiet(&mut reactor);
let _ = apps.requests();

let wid = WindowId::new(1, 1);
let target = CGRect::new(CGPoint::new(300., 50.), CGSize::new(400., 700.));
reactor.window_manager.windows.get_mut(&wid).unwrap().info.sys_id = None;

assert!(!AnimationManager::animate_layout(
&mut reactor,
space,
&[(wid, target)],
false,
None,
));
assert!(!AnimationManager::instant_layout(
&mut reactor,
&[(wid, target)],
None,
));
assert!(apps.requests().is_empty());
}
}
1 change: 1 addition & 0 deletions src/actor/reactor/events/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ impl AppEventHandler {
}

pub fn handle_application_thread_terminated(reactor: &mut Reactor, pid: i32) {
reactor.handle_native_tab_app_terminated(pid);
reactor.app_manager.apps.remove(&pid);
reactor.send_layout_event(LayoutEvent::AppClosed(pid));
}
Expand Down
1 change: 1 addition & 0 deletions src/actor/reactor/events/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ impl CommandEventHandler {
if let Some(state) = reactor.window_manager.windows.get_mut(&window_id) {
state.frame_monotonic = target_frame;
}
reactor.handle_native_tab_frame_changed(window_id, true);

let response = reactor.layout_manager.layout_engine.move_window_to_space(
source_space,
Expand Down
10 changes: 8 additions & 2 deletions src/actor/reactor/events/drag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct DragEventHandler;
impl DragEventHandler {
pub fn handle_mouse_up(reactor: &mut Reactor) {
let mut need_layout_refresh = false;
let dragged_wid = reactor.drag_manager.dragged();

let pending_swap = reactor.get_pending_drag_swap();

Expand Down Expand Up @@ -62,10 +63,15 @@ impl DragEventHandler {
need_layout_refresh = true;
}

if let Some(wid) = dragged_wid {
reactor.handle_native_tab_frame_changed(wid, true);
}

if need_layout_refresh {
let skip_layout_occurred = reactor.drag_manager.skip_layout_for_window.is_some();
let skipped_wid = reactor.drag_manager.skip_layout_for_window;
let _ = reactor.update_layout_or_warn(false, false);
if skip_layout_occurred {
if let Some(skipped_wid) = skipped_wid {
reactor.drag_manager.skip_layout_for_window = Some(skipped_wid);
let _ = reactor.update_layout_or_warn(false, false);
}
}
Expand Down
40 changes: 33 additions & 7 deletions src/actor/reactor/events/space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ impl SpaceEventHandler {
return;
}

if reactor.stage_native_tab_destroy(wsid, sid) {
if let Some(&wid) = reactor.window_manager.window_ids.get(&wsid)
&& let Some(app_state) = reactor.app_manager.apps.get(&wid.pid)
&& let Err(e) = app_state.handle.send(Request::WindowMaybeDestroyed(wid))
{
warn!("Failed to send WindowMaybeDestroyed: {}", e);
}
return;
}

if let Some(&wid) = reactor.window_manager.window_ids.get(&wsid) {
reactor.window_manager.window_ids.remove(&wsid);
reactor.window_server_info_manager.window_server_info.remove(&wsid);
Expand Down Expand Up @@ -90,9 +100,17 @@ impl SpaceEventHandler {
wsid: WindowServerId,
sid: SpaceId,
) {
if reactor.window_server_info_manager.window_server_info.contains_key(&wsid)
|| reactor.window_manager.observed_window_server_ids.contains(&wsid)
let known_before =
reactor.window_server_info_manager.window_server_info.contains_key(&wsid)
|| reactor.window_manager.observed_window_server_ids.contains(&wsid);
let appearance_info = crate::sys::window_server::get_window(wsid);
if let Some(window_server_info) = appearance_info
&& reactor.note_native_tab_appearance(wsid, sid, window_server_info)
{
return;
}

if known_before {
debug!(
?wsid,
"Received WindowServerAppeared for known window - ignoring"
Expand All @@ -103,7 +121,7 @@ impl SpaceEventHandler {
reactor.window_manager.observed_window_server_ids.insert(wsid);
// TODO: figure out why this is happening, we should really know about this app,
// why dont we get notifications that its being launched?
if let Some(window_server_info) = crate::sys::window_server::get_window(wsid) {
if let Some(window_server_info) = appearance_info {
if window_server_info.layer != 0 {
trace!(
?wsid,
Expand Down Expand Up @@ -194,11 +212,19 @@ impl SpaceEventHandler {
.iter()
.filter_map(|screen| screen.space.map(|space| (screen.display_uuid.clone(), space)))
.collect();
let is_fullscreen_space = |space: SpaceId| {
crate::sys::window_server::space_is_fullscreen(space.get())
|| reactor.space_manager.fullscreen_by_space.contains_key(&space.get())
};
// Fullscreen transitions create temporary non-user space IDs for the same display.
// Treat only user->user space-id changes as topology churn.
let display_space_changed =
previous_spaces_by_display.iter().any(|(display_uuid, space)| {
new_spaces_by_display
.get(display_uuid)
.is_some_and(|new_space| new_space != space)
previous_spaces_by_display.iter().any(|(display_uuid, old_space)| {
new_spaces_by_display.get(display_uuid).is_some_and(|new_space| {
new_space != old_space
&& !is_fullscreen_space(*old_space)
&& !is_fullscreen_space(*new_space)
})
});

// IMPORTANT:
Expand Down
Loading
Loading