diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend/mod.rs | 2 | ||||
| -rw-r--r-- | src/backend/tty.rs | 10 | ||||
| -rw-r--r-- | src/backend/winit.rs | 38 | ||||
| -rw-r--r-- | src/dbus/mutter_display_config.rs | 46 | ||||
| -rw-r--r-- | src/dbus/mutter_screen_cast.rs | 28 | ||||
| -rw-r--r-- | src/ipc/client.rs | 51 | ||||
| -rw-r--r-- | src/ipc/server.rs | 6 | ||||
| -rw-r--r-- | src/niri.rs | 47 | ||||
| -rw-r--r-- | src/utils/mod.rs | 25 |
9 files changed, 177 insertions, 76 deletions
diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 786fdaaa..80986aea 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -31,7 +31,7 @@ pub enum RenderResult { Skipped, } -pub type IpcOutputMap = HashMap<String, (niri_ipc::Output, Option<Output>)>; +pub type IpcOutputMap = HashMap<String, niri_ipc::Output>; impl Backend { pub fn init(&mut self, niri: &mut Niri) { diff --git a/src/backend/tty.rs b/src/backend/tty.rs index a90182a3..783db022 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -60,7 +60,7 @@ use crate::frame_clock::FrameClock; use crate::niri::{Niri, RedrawState, State}; use crate::render_helpers::renderer::AsGlesRenderer; use crate::render_helpers::{shaders, RenderTarget}; -use crate::utils::get_monotonic_time; +use crate::utils::{get_monotonic_time, logical_output}; const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888]; @@ -1370,6 +1370,7 @@ impl Tty { width: m.size().0, height: m.size().1, refresh_rate: Mode::from(*m).refresh as u32, + is_preferred: m.mode_type().contains(ModeTypeFlags::PREFERRED), } }) .collect(); @@ -1384,14 +1385,14 @@ impl Tty { } } - let output = niri + let logical = niri .global_space .outputs() .find(|output| { let tty_state: &TtyOutputState = output.user_data().get().unwrap(); tty_state.node == *node && tty_state.crtc == crtc }) - .cloned(); + .map(logical_output); let ipc_output = niri_ipc::Output { name: connector_name.clone(), @@ -1400,9 +1401,10 @@ impl Tty { physical_size, modes, current_mode, + logical, }; - ipc_outputs.insert(connector_name, (ipc_output, output)); + ipc_outputs.insert(connector_name, ipc_output); } } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 96c0cdf8..65244e81 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -20,7 +20,7 @@ use smithay::reexports::winit::window::WindowBuilder; use super::{IpcOutputMap, RenderResult}; use crate::niri::{Niri, RedrawState, State}; use crate::render_helpers::{shaders, RenderTarget}; -use crate::utils::get_monotonic_time; +use crate::utils::{get_monotonic_time, logical_output}; pub struct Winit { config: Rc<RefCell<Config>>, @@ -61,21 +61,20 @@ impl Winit { let physical_properties = output.physical_properties(); let ipc_outputs = Arc::new(Mutex::new(HashMap::from([( "winit".to_owned(), - ( - niri_ipc::Output { - name: output.name(), - make: physical_properties.make, - model: physical_properties.model, - physical_size: None, - modes: vec![niri_ipc::Mode { - width: backend.window_size().w.clamp(0, u16::MAX as i32) as u16, - height: backend.window_size().h.clamp(0, u16::MAX as i32) as u16, - refresh_rate: 60_000, - }], - current_mode: Some(0), - }, - Some(output.clone()), - ), + niri_ipc::Output { + name: output.name(), + make: physical_properties.make, + model: physical_properties.model, + physical_size: None, + modes: vec![niri_ipc::Mode { + width: backend.window_size().w.clamp(0, u16::MAX as i32) as u16, + height: backend.window_size().h.clamp(0, u16::MAX as i32) as u16, + refresh_rate: 60_000, + is_preferred: true, + }], + current_mode: Some(0), + logical: Some(logical_output(&output)), + }, )]))); let damage_tracker = OutputDamageTracker::from_output(&output); @@ -96,9 +95,14 @@ impl Winit { { let mut ipc_outputs = winit.ipc_outputs.lock().unwrap(); - let mode = &mut ipc_outputs.get_mut("winit").unwrap().0.modes[0]; + let output = ipc_outputs.get_mut("winit").unwrap(); + let mode = &mut output.modes[0]; mode.width = size.w.clamp(0, u16::MAX as i32) as u16; mode.height = size.h.clamp(0, u16::MAX as i32) as u16; + if let Some(logical) = output.logical.as_mut() { + logical.width = size.w as u32; + logical.height = size.h as u32; + } state.niri.ipc_outputs_changed = true; } diff --git a/src/dbus/mutter_display_config.rs b/src/dbus/mutter_display_config.rs index 2e29b923..783c2499 100644 --- a/src/dbus/mutter_display_config.rs +++ b/src/dbus/mutter_display_config.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; use serde::Serialize; -use smithay::utils::Transform; use zbus::fdo::RequestNameFlags; use zbus::zvariant::{self, OwnedValue, Type}; use zbus::{dbus_interface, fdo, SignalContext}; @@ -60,11 +59,8 @@ impl DisplayConfig { .unwrap() .iter() // Take only enabled outputs. - .filter_map(|(c, (ipc, output))| { - ipc.current_mode?; - output.as_ref().map(move |output| (c, (ipc, output))) - }) - .map(|(c, (ipc, output))| { + .filter(|(_, output)| output.current_mode.is_some() && output.logical.is_some()) + .map(|(c, output)| { // Loosely matches the check in Mutter. let is_laptop_panel = matches!(c.get(..4), Some("eDP-" | "LVDS" | "DSI-")); @@ -84,7 +80,7 @@ impl DisplayConfig { OwnedValue::from(is_laptop_panel), ); - let mut modes: Vec<Mode> = ipc + let mut modes: Vec<Mode> = output .modes .iter() .map(|m| { @@ -92,6 +88,7 @@ impl DisplayConfig { width, height, refresh_rate, + is_preferred, } = *m; let refresh = refresh_rate as f64 / 1000.; @@ -102,11 +99,14 @@ impl DisplayConfig { refresh_rate: refresh, preferred_scale: 1., supported_scales: vec![1., 2., 3.], - properties: HashMap::new(), + properties: HashMap::from([( + String::from("is-preferred"), + OwnedValue::from(is_preferred), + )]), } }) .collect(); - modes[ipc.current_mode.unwrap()] + modes[output.current_mode.unwrap()] .properties .insert(String::from("is-current"), OwnedValue::from(true)); @@ -116,23 +116,23 @@ impl DisplayConfig { properties, }; - let loc = output.current_location(); - - let transform = match output.current_transform() { - Transform::Normal => 0, - Transform::_90 => 1, - Transform::_180 => 2, - Transform::_270 => 3, - Transform::Flipped => 4, - Transform::Flipped90 => 5, - Transform::Flipped180 => 6, - Transform::Flipped270 => 7, + let logical = output.logical.as_ref().unwrap(); + + let transform = match logical.transform { + niri_ipc::Transform::Normal => 0, + niri_ipc::Transform::_90 => 1, + niri_ipc::Transform::_180 => 2, + niri_ipc::Transform::_270 => 3, + niri_ipc::Transform::Flipped => 4, + niri_ipc::Transform::Flipped90 => 5, + niri_ipc::Transform::Flipped180 => 6, + niri_ipc::Transform::Flipped270 => 7, }; let logical_monitor = LogicalMonitor { - x: loc.x, - y: loc.y, - scale: output.current_scale().fractional_scale(), + x: logical.x, + y: logical.y, + scale: logical.scale, transform, is_primary: false, monitors: vec![monitor.names.clone()], diff --git a/src/dbus/mutter_screen_cast.rs b/src/dbus/mutter_screen_cast.rs index d9c6c28e..853fe4ae 100644 --- a/src/dbus/mutter_screen_cast.rs +++ b/src/dbus/mutter_screen_cast.rs @@ -11,7 +11,6 @@ use zbus::{dbus_interface, fdo, InterfaceRef, ObjectServer, SignalContext}; use super::Start; use crate::backend::IpcOutputMap; -use crate::utils::output_size; #[derive(Clone)] pub struct ScreenCast { @@ -49,7 +48,8 @@ struct RecordMonitorProperties { #[derive(Clone)] pub struct Stream { - output: Output, + // FIXME: update on scale changes and whatnot. + output: niri_ipc::Output, cursor_mode: CursorMode, was_started: Arc<AtomicBool>, to_niri: calloop::channel::Sender<ScreenCastToNiri>, @@ -58,6 +58,8 @@ pub struct Stream { #[derive(Debug, SerializeDict, Type, Value)] #[zvariant(signature = "dict")] struct StreamParameters { + /// Position of the stream in logical coordinates. + position: (i32, i32), /// Size of the stream in logical coordinates. size: (i32, i32), } @@ -65,7 +67,7 @@ struct StreamParameters { pub enum ScreenCastToNiri { StartCast { session_id: usize, - output: Output, + output: String, cursor_mode: CursorMode, signal_ctx: SignalContext<'static>, }, @@ -160,11 +162,14 @@ impl Session { ) -> fdo::Result<OwnedObjectPath> { debug!(connector, ?properties, "record_monitor"); - let Some((_, Some(output))) = self.ipc_outputs.lock().unwrap().get(connector).cloned() - else { + let Some(output) = self.ipc_outputs.lock().unwrap().get(connector).cloned() else { return Err(fdo::Error::Failed("no such monitor".to_owned())); }; + if output.logical.is_none() { + return Err(fdo::Error::Failed("monitor is disabled".to_owned())); + } + static NUMBER: AtomicUsize = AtomicUsize::new(0); let path = format!( "/org/gnome/Mutter/ScreenCast/Stream/u{}", @@ -174,7 +179,7 @@ impl Session { let cursor_mode = properties.cursor_mode.unwrap_or_default(); - let stream = Stream::new(output, cursor_mode, self.to_niri.clone()); + let stream = Stream::new(output.clone(), cursor_mode, self.to_niri.clone()); match server.at(&path, stream.clone()).await { Ok(true) => { let iface = server.interface(&path).await.unwrap(); @@ -203,8 +208,11 @@ impl Stream { #[dbus_interface(property)] async fn parameters(&self) -> StreamParameters { - let size = output_size(&self.output).into(); - StreamParameters { size } + let logical = self.output.logical.as_ref().unwrap(); + StreamParameters { + position: (logical.x, logical.y), + size: (logical.width as i32, logical.height as i32), + } } } @@ -261,7 +269,7 @@ impl Drop for Session { impl Stream { pub fn new( - output: Output, + output: niri_ipc::Output, cursor_mode: CursorMode, to_niri: calloop::channel::Sender<ScreenCastToNiri>, ) -> Self { @@ -280,7 +288,7 @@ impl Stream { let msg = ScreenCastToNiri::StartCast { session_id, - output: self.output.clone(), + output: self.output.name.clone(), cursor_mode: self.cursor_mode, signal_ctx: ctxt, }; diff --git a/src/ipc/client.rs b/src/ipc/client.rs index 27e1eaa1..18d49fcb 100644 --- a/src/ipc/client.rs +++ b/src/ipc/client.rs @@ -4,7 +4,7 @@ use std::net::Shutdown; use std::os::unix::net::UnixStream; use anyhow::{anyhow, bail, Context}; -use niri_ipc::{Mode, Output, Reply, Request, Response}; +use niri_ipc::{LogicalOutput, Mode, Output, Reply, Request, Response}; use crate::cli::Msg; @@ -66,6 +66,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { physical_size, modes, current_mode, + logical, } = output; println!(r#"Output "{connector}" ({make} - {model} - {name})"#); @@ -78,9 +79,11 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { width, height, refresh_rate, + is_preferred, } = mode; let refresh = refresh_rate as f64 / 1000.; - println!(" Current mode: {width}x{height} @ {refresh:.3} Hz"); + let preferred = if is_preferred { " (preferred)" } else { "" }; + println!(" Current mode: {width}x{height} @ {refresh:.3} Hz{preferred}"); } else { println!(" Disabled"); } @@ -91,15 +94,55 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { println!(" Physical size: unknown"); } + if let Some(logical) = logical { + let LogicalOutput { + x, + y, + width, + height, + scale, + transform, + } = logical; + println!(" Logical position: {x}, {y}"); + println!(" Logical size: {width}x{height}"); + println!(" Scale: {scale}"); + + let transform = match transform { + niri_ipc::Transform::Normal => "normal", + niri_ipc::Transform::_90 => "90° counter-clockwise", + niri_ipc::Transform::_180 => "180°", + niri_ipc::Transform::_270 => "270° counter-clockwise", + niri_ipc::Transform::Flipped => "flipped horizontally", + niri_ipc::Transform::Flipped90 => { + "90° counter-clockwise, flipped horizontally" + } + niri_ipc::Transform::Flipped180 => "flipped vertically", + niri_ipc::Transform::Flipped270 => { + "270° counter-clockwise, flipped horizontally" + } + }; + println!(" Transform: {transform}"); + } + println!(" Available modes:"); - for mode in modes { + for (idx, mode) in modes.into_iter().enumerate() { let Mode { width, height, refresh_rate, + is_preferred, } = mode; let refresh = refresh_rate as f64 / 1000.; - println!(" {width}x{height}@{refresh:.3}"); + + let is_current = Some(idx) == current_mode; + let qualifier = match (is_current, is_preferred) { + (true, true) => " (current, preferred)", + (true, false) => " (current)", + (false, true) => " (preferred)", + (false, false) => "", + }; + + println!(" {width}x{height}@{refresh:.3}{qualifier}"); } println!(); } diff --git a/src/ipc/server.rs b/src/ipc/server.rs index dc314734..43cbb8b4 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -125,11 +125,7 @@ fn process(ctx: &ClientCtx, buf: &str) -> anyhow::Result<Response> { let response = match request { Request::Outputs => { - let ipc_outputs = ctx.ipc_outputs.lock().unwrap(); - let ipc_outputs = ipc_outputs - .iter() - .map(|(name, (ipc, _))| (name.clone(), ipc.clone())) - .collect(); + let ipc_outputs = ctx.ipc_outputs.lock().unwrap().clone(); Response::Outputs(ipc_outputs) } Request::Action(action) => { diff --git a/src/niri.rs b/src/niri.rs index 0cbd0cf3..05ce00e9 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -115,7 +115,8 @@ use crate::ui::hotkey_overlay::HotkeyOverlay; use crate::ui::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement}; use crate::utils::spawning::CHILD_ENV; use crate::utils::{ - center, center_f64, get_monotonic_time, make_screenshot_path, output_size, write_png_rgba8, + center, center_f64, get_monotonic_time, logical_output, make_screenshot_path, output_size, + write_png_rgba8, }; use crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped, WindowRef}; use crate::{animation, niri_render_elements}; @@ -459,7 +460,7 @@ impl State { self.refresh_pointer_focus(); foreign_toplevel::refresh(self); self.niri.refresh_window_rules(); - self.niri.check_ipc_output_changed(); + self.refresh_ipc_outputs(); } pub fn move_cursor(&mut self, location: Point<f64, Logical>) { @@ -967,6 +968,28 @@ impl State { self.niri.queue_redraw_all(); } + pub fn refresh_ipc_outputs(&mut self) { + if !self.niri.ipc_outputs_changed { + return; + } + self.niri.ipc_outputs_changed = false; + + let _span = tracy_client::span!("State::refresh_ipc_outputs"); + + for (name, ipc_output) in self.backend.ipc_outputs().lock().unwrap().iter_mut() { + let logical = self + .niri + .global_space + .outputs() + .find(|output| output.name() == *name) + .map(logical_output); + ipc_output.logical = logical; + } + + #[cfg(feature = "dbus")] + self.niri.on_ipc_outputs_changed(); + } + #[cfg(feature = "xdp-gnome-screencast")] pub fn on_screen_cast_msg( &mut self, @@ -997,6 +1020,17 @@ impl State { return; }; + let Some(output) = self + .niri + .global_space + .outputs() + .find(|out| out.name() == output) + .cloned() + else { + warn!("tried to start a screencast on missing output: {output}"); + return; + }; + match pw.start_cast( to_niri.clone(), gbm, @@ -3438,15 +3472,6 @@ impl Niri { }); } - pub fn check_ipc_output_changed(&mut self) { - if self.ipc_outputs_changed { - self.ipc_outputs_changed = false; - - #[cfg(feature = "dbus")] - self.on_ipc_outputs_changed(); - } - } - #[cfg(feature = "dbus")] pub fn on_ipc_outputs_changed(&self) { let _span = tracy_client::span!("Niri::on_ipc_outputs_changed"); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index acd37b20..aef9a8aa 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -12,7 +12,7 @@ use git_version::git_version; use niri_config::Config; use smithay::output::Output; use smithay::reexports::rustix::time::{clock_gettime, ClockId}; -use smithay::utils::{Logical, Point, Rectangle, Size}; +use smithay::utils::{Logical, Point, Rectangle, Size, Transform}; pub mod id; pub mod spawning; @@ -51,6 +51,29 @@ pub fn output_size(output: &Output) -> Size<i32, Logical> { .to_logical(output_scale) } +pub fn logical_output(output: &Output) -> niri_ipc::LogicalOutput { + let loc = output.current_location(); + let size = output_size(output); + let transform = match output.current_transform() { + Transform::Normal => niri_ipc::Transform::Normal, + Transform::_90 => niri_ipc::Transform::_90, + Transform::_180 => niri_ipc::Transform::_180, + Transform::_270 => niri_ipc::Transform::_270, + Transform::Flipped => niri_ipc::Transform::Flipped, + Transform::Flipped90 => niri_ipc::Transform::Flipped90, + Transform::Flipped180 => niri_ipc::Transform::Flipped180, + Transform::Flipped270 => niri_ipc::Transform::Flipped270, + }; + niri_ipc::LogicalOutput { + x: loc.x, + y: loc.y, + width: size.w as u32, + height: size.h as u32, + scale: output.current_scale().fractional_scale(), + transform, + } +} + pub fn expand_home(path: &Path) -> anyhow::Result<Option<PathBuf>> { if let Ok(rest) = path.strip_prefix("~") { let dirs = UserDirs::new().context("error retrieving home directory")?; |
