diff --git a/src/actor/reactor/events/command.rs b/src/actor/reactor/events/command.rs index 57e3c953..8aa2fdbd 100644 --- a/src/actor/reactor/events/command.rs +++ b/src/actor/reactor/events/command.rs @@ -213,6 +213,9 @@ impl CommandEventHandler { ReactorCommand::MoveWindowToDisplay { selector, window_id } => { Self::handle_command_reactor_move_window_to_display(reactor, &selector, window_id); } + ReactorCommand::MoveEverythingToDisplay { selector } => { + Self::handle_command_reactor_move_everything_to_display(reactor, &selector); + } } } @@ -506,6 +509,119 @@ impl CommandEventHandler { let _ = reactor.update_layout_or_warn(false, false); } + pub fn handle_command_reactor_move_everything_to_display( + reactor: &mut Reactor, + selector: &DisplaySelector, + ) { + if reactor.is_in_drag() { + warn!("Ignoring move-window-to-display while a drag is active"); + return; + } + + let Some(source_space) = reactor.workspace_command_space() else { + warn!("No source_space could be identified!"); + return; + }; + if !reactor.is_space_active(source_space) { + warn!("Move everything to display ignored: source space is inactive"); + return; + } + + let origin_screen = reactor.space_manager.screen_by_space(source_space); + + let origin_point = + origin_screen.map(|s| s.frame.mid()).or_else(|| reactor.current_screen_center()); + let target_screen = reactor.screen_for_selector(selector, origin_point).cloned(); + + let Some(target_screen) = target_screen else { + warn!( + ?selector, + "Move window to display ignored: target display not found" + ); + return; + }; + let Some(target_space) = target_screen.space else { + warn!( + uuid = ?target_screen.display_uuid, + "Move window to display ignored: display has no active space" + ); + return; + }; + if !reactor.is_space_active(target_space) { + warn!( + ?selector, + ?target_space, + "Move window to display ignored: target display space is inactive" + ); + return; + } + + if target_space == source_space { + return; + } + + let windows = + reactor.layout_manager.layout_engine.windows_in_active_workspace(source_space); + + for window in windows { + let (window_server_id, window_frame) = match reactor.window_manager.windows.get(&window) + { + Some(state) => (state.info.sys_id, state.frame_monotonic), + None => { + warn!(?window, "Move window to display ignored: unknown window"); + continue; + } + }; + let mut target_frame = window_frame; + let size = window_frame.size; + let dest_rect = target_screen.frame; + let mut origin = dest_rect.mid(); + origin.x -= size.width / 2.0; + origin.y -= size.height / 2.0; + let min = dest_rect.min(); + let max = dest_rect.max(); + origin.x = origin.x.max(min.x).min(max.x - size.width); + origin.y = origin.y.max(min.y).min(max.y - size.height); + target_frame.origin = origin; + + if let Some(app) = reactor.app_manager.apps.get(&window.pid) { + if let Some(wsid) = window_server_id { + let txid = reactor.transaction_manager.generate_next_txid(wsid); + reactor.transaction_manager.set_last_sent_txid(wsid, txid); + let _ = app.handle.send(crate::actor::app::Request::SetWindowFrame( + window, + target_frame, + txid, + true, + )); + } else { + let txid = TransactionId::default(); + let _ = app.handle.send(crate::actor::app::Request::SetWindowFrame( + window, + target_frame, + txid, + true, + )); + } + } + + if let Some(state) = reactor.window_manager.windows.get_mut(&window) { + state.frame_monotonic = target_frame; + } + + let response = reactor.layout_manager.layout_engine.move_window_to_space( + source_space, + target_space, + target_screen.frame.size, + window, + ); + + reactor.handle_layout_response(response, None); + } + + let _ = reactor.update_layout_or_warn(false, false); + } + pub fn handle_command_reactor_close_window( reactor: &mut Reactor, window_server_id: Option, diff --git a/src/bin/rift-cli.rs b/src/bin/rift-cli.rs index 8ac4da04..eb1b629d 100644 --- a/src/bin/rift-cli.rs +++ b/src/bin/rift-cli.rs @@ -351,6 +351,18 @@ enum DisplayCommands { #[arg(long)] window_id: Option, }, + /// Move all windows of the current workspace to a display by direction, index, or UUID + MoveEverything { + /// Direction relative to the window's current display (left, right, up, down). + #[arg(long)] + direction: Option, + /// Display index (0-based). + #[arg(long)] + index: Option, + /// Display UUID. + #[arg(long)] + uuid: Option, + }, } #[derive(Subcommand)] @@ -835,6 +847,11 @@ fn map_display_command(cmd: DisplayCommands) -> Result { window_id, }, ))), + DisplayCommands::MoveEverything { direction, index, uuid } => Ok(RiftCommand::Reactor( + reactor::Command::Reactor(reactor::ReactorCommand::MoveEverythingToDisplay { + selector: build_display_selector(direction, index, uuid)?, + }), + )), } } diff --git a/src/model/reactor.rs b/src/model/reactor.rs index 6c4d4a8c..85ff4bde 100644 --- a/src/model/reactor.rs +++ b/src/model/reactor.rs @@ -51,6 +51,9 @@ pub enum ReactorCommand { selector: DisplaySelector, window_id: Option, }, + MoveEverythingToDisplay { + selector: DisplaySelector, + }, } #[derive(Debug, Clone)]