diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend/tty.rs | 2 | ||||
| -rw-r--r-- | src/cli.rs | 17 | ||||
| -rw-r--r-- | src/ipc/client.rs | 9 | ||||
| -rw-r--r-- | src/ipc/server.rs | 6 | ||||
| -rw-r--r-- | src/niri.rs | 161 |
5 files changed, 147 insertions, 48 deletions
diff --git a/src/backend/tty.rs b/src/backend/tty.rs index 327a5c97..4e7055ee 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -2111,7 +2111,7 @@ fn queue_estimated_vblank_timer( fn pick_mode( connector: &connector::Info, - target: Option<niri_config::Mode>, + target: Option<niri_ipc::ConfiguredMode>, ) -> Option<(control::Mode, bool)> { let mut mode = None; let mut fallback = false; @@ -2,7 +2,7 @@ use std::ffi::OsString; use std::path::PathBuf; use clap::{Parser, Subcommand}; -use niri_ipc::Action; +use niri_ipc::{Action, OutputAction}; use crate::utils::version; @@ -63,6 +63,21 @@ pub enum Msg { #[command(subcommand)] action: Action, }, + /// Change output configuration temporarily. + /// + /// The configuration is changed temporarily and not saved into the config file. If the output + /// configuration subsequently changes in the config file, these temporary changes will be + /// forgotten. + Output { + /// Output name. + /// + /// Run `niri msg outputs` to see the output names. + #[arg()] + output: String, + /// Configuration to apply. + #[command(subcommand)] + action: OutputAction, + }, /// Request an error from the running niri instance. RequestError, } diff --git a/src/ipc/client.rs b/src/ipc/client.rs index 1704adfb..3aa5eb22 100644 --- a/src/ipc/client.rs +++ b/src/ipc/client.rs @@ -11,6 +11,10 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { Msg::Outputs => Request::Outputs, Msg::FocusedWindow => Request::FocusedWindow, Msg::Action { action } => Request::Action(action.clone()), + Msg::Output { output, action } => Request::Output { + output: output.clone(), + action: action.clone(), + }, Msg::RequestError => Request::ReturnError, }; @@ -237,6 +241,11 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { bail!("unexpected response: expected Handled, got {response:?}"); }; } + Msg::Output { .. } => { + let Response::Handled = response else { + bail!("unexpected response: expected Handled, got {response:?}"); + }; + } } Ok(()) diff --git a/src/ipc/server.rs b/src/ipc/server.rs index 5e18c16a..59f929d8 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -169,6 +169,12 @@ fn process(ctx: &ClientCtx, request: Request) -> Reply { }); Response::Handled } + Request::Output { output, action } => { + ctx.event_loop.insert_idle(move |state| { + state.apply_transient_output_config(&output, action); + }); + Response::Handled + } }; Ok(response) diff --git a/src/niri.rs b/src/niri.rs index b1e2d917..b6bfb84d 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -138,6 +138,13 @@ const FRAME_CALLBACK_THROTTLE: Option<Duration> = Some(Duration::from_millis(995 pub struct Niri { pub config: Rc<RefCell<Config>>, + /// Output config from the config file. + /// + /// This does not include transient output config changes done via IPC. It is only used when + /// reloading the config from disk to determine if the output configuration should be reloaded + /// (and transient changes dropped). + pub config_file_output_config: Vec<niri_config::Output>, + pub event_loop: LoopHandle<'static, State>, pub scheduler: Scheduler<()>, pub stop_signal: LoopSignal, @@ -858,6 +865,7 @@ impl State { let mut reload_xkb = None; let mut libinput_config_changed = false; let mut output_config_changed = false; + let mut preserved_output_config = None; let mut window_rules_changed = false; let mut debug_config_changed = false; let mut shaders_changed = false; @@ -894,8 +902,15 @@ impl State { libinput_config_changed = true; } - if config.outputs != old_config.outputs { + if config.outputs != self.niri.config_file_output_config { output_config_changed = true; + self.niri + .config_file_output_config + .clone_from(&config.outputs); + } else { + // Output config did not change from the last disk load, so we need to preserve the + // transient changes. + preserved_output_config = Some(mem::take(&mut old_config.outputs)); } if config.binds != old_config.binds { @@ -926,6 +941,10 @@ impl State { *old_config = config; + if let Some(outputs) = preserved_output_config { + old_config.outputs = outputs; + } + // Release the borrow. drop(old_config); @@ -945,51 +964,7 @@ impl State { } if output_config_changed { - let mut resized_outputs = vec![]; - for output in self.niri.global_space.outputs() { - let name = output.name(); - let config = self.niri.config.borrow_mut(); - let config = config.outputs.iter().find(|o| o.name == name); - - let scale = config.map(|c| c.scale).unwrap_or_else(|| { - let size_mm = output.physical_properties().size; - let resolution = output.current_mode().unwrap().size; - guess_monitor_scale(size_mm, resolution) - }); - let scale = scale.clamp(1., 10.).ceil() as i32; - - let mut transform = config - .map(|c| ipc_transform_to_smithay(c.transform)) - .unwrap_or(Transform::Normal); - // FIXME: fix winit damage on other transforms. - if name == "winit" { - transform = Transform::Flipped180; - } - - if output.current_scale().integer_scale() != scale - || output.current_transform() != transform - { - output.change_current_state( - None, - Some(transform), - Some(output::Scale::Integer(scale)), - None, - ); - self.niri.ipc_outputs_changed = true; - resized_outputs.push(output.clone()); - } - } - for output in resized_outputs { - self.niri.output_resized(&output); - } - - self.backend.on_output_config_changed(&mut self.niri); - - self.niri.reposition_outputs(None); - - if let Some(touch) = self.niri.seat.get_touch() { - touch.cancel(self); - } + self.reload_output_config(); } if debug_config_changed { @@ -1032,6 +1007,98 @@ impl State { self.niri.queue_redraw_all(); } + fn reload_output_config(&mut self) { + let mut resized_outputs = vec![]; + for output in self.niri.global_space.outputs() { + let name = output.name(); + let config = self.niri.config.borrow_mut(); + let config = config.outputs.iter().find(|o| o.name == name); + + let scale = config.map(|c| c.scale).unwrap_or_else(|| { + let size_mm = output.physical_properties().size; + let resolution = output.current_mode().unwrap().size; + guess_monitor_scale(size_mm, resolution) + }); + let scale = scale.clamp(1., 10.).ceil() as i32; + + let mut transform = config + .map(|c| ipc_transform_to_smithay(c.transform)) + .unwrap_or(Transform::Normal); + // FIXME: fix winit damage on other transforms. + if name == "winit" { + transform = Transform::Flipped180; + } + + if output.current_scale().integer_scale() != scale + || output.current_transform() != transform + { + output.change_current_state( + None, + Some(transform), + Some(output::Scale::Integer(scale)), + None, + ); + self.niri.ipc_outputs_changed = true; + resized_outputs.push(output.clone()); + } + } + for output in resized_outputs { + self.niri.output_resized(&output); + } + + self.backend.on_output_config_changed(&mut self.niri); + + self.niri.reposition_outputs(None); + + if let Some(touch) = self.niri.seat.get_touch() { + touch.cancel(self); + } + } + + pub fn apply_transient_output_config(&mut self, name: &str, action: niri_ipc::OutputAction) { + { + let mut config = self.niri.config.borrow_mut(); + let config = if let Some(config) = config.outputs.iter_mut().find(|o| o.name == name) { + config + } else { + config.outputs.push(niri_config::Output { + name: String::from(name), + ..Default::default() + }); + config.outputs.last_mut().unwrap() + }; + + match action { + niri_ipc::OutputAction::Off => config.off = true, + niri_ipc::OutputAction::On => config.off = false, + niri_ipc::OutputAction::Mode { mode } => { + config.mode = match mode { + niri_ipc::ModeToSet::Automatic => None, + niri_ipc::ModeToSet::Specific(mode) => Some(mode), + } + } + niri_ipc::OutputAction::Scale { scale } => config.scale = scale, + niri_ipc::OutputAction::Transform { transform } => config.transform = transform, + niri_ipc::OutputAction::Position { position } => { + config.position = match position { + niri_ipc::PositionToSet::Automatic => None, + niri_ipc::PositionToSet::Specific(position) => { + Some(niri_config::Position { + x: position.x, + y: position.y, + }) + } + } + } + niri_ipc::OutputAction::Vrr { enable } => { + config.variable_refresh_rate = enable; + } + } + } + + self.reload_output_config(); + } + pub fn refresh_ipc_outputs(&mut self) { if !self.niri.ipc_outputs_changed { return; @@ -1175,6 +1242,7 @@ impl Niri { let display_handle = display.handle(); let config_ = config.borrow(); + let config_file_output_config = config_.outputs.clone(); let layout = Layout::new(&config_); @@ -1353,6 +1421,7 @@ impl Niri { drop(config_); Self { config, + config_file_output_config, event_loop, scheduler, |
