mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Add logical output info and preferred modes to IPC
This commit is contained in:
@@ -248,6 +248,10 @@ pub struct Output {
|
|||||||
///
|
///
|
||||||
/// `None` if the output is disabled.
|
/// `None` if the output is disabled.
|
||||||
pub current_mode: Option<usize>,
|
pub current_mode: Option<usize>,
|
||||||
|
/// Logical output information.
|
||||||
|
///
|
||||||
|
/// `None` if the output is not mapped to any logical output (for example, if it is disabled).
|
||||||
|
pub logical: Option<LogicalOutput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Output mode.
|
/// Output mode.
|
||||||
@@ -259,6 +263,49 @@ pub struct Mode {
|
|||||||
pub height: u16,
|
pub height: u16,
|
||||||
/// Refresh rate in millihertz.
|
/// Refresh rate in millihertz.
|
||||||
pub refresh_rate: u32,
|
pub refresh_rate: u32,
|
||||||
|
/// Whether this mode is preferred by the monitor.
|
||||||
|
pub is_preferred: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logical output in the compositor's coordinate space.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||||
|
pub struct LogicalOutput {
|
||||||
|
/// Logical X position.
|
||||||
|
pub x: i32,
|
||||||
|
/// Logical Y position.
|
||||||
|
pub y: i32,
|
||||||
|
/// Width in logical pixels.
|
||||||
|
pub width: u32,
|
||||||
|
/// Height in logical pixels.
|
||||||
|
pub height: u32,
|
||||||
|
/// Scale factor.
|
||||||
|
pub scale: f64,
|
||||||
|
/// Transform.
|
||||||
|
pub transform: Transform,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Output transform, which goes counter-clockwise.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Transform {
|
||||||
|
/// Untransformed.
|
||||||
|
Normal,
|
||||||
|
/// Rotated by 90°.
|
||||||
|
#[serde(rename = "90")]
|
||||||
|
_90,
|
||||||
|
/// Rotated by 180°.
|
||||||
|
#[serde(rename = "180")]
|
||||||
|
_180,
|
||||||
|
/// Rotated by 270°.
|
||||||
|
#[serde(rename = "270")]
|
||||||
|
_270,
|
||||||
|
/// Flipped horizontally.
|
||||||
|
Flipped,
|
||||||
|
/// Rotated by 90° and flipped horizontally.
|
||||||
|
Flipped90,
|
||||||
|
/// Flipped vertically.
|
||||||
|
Flipped180,
|
||||||
|
/// Rotated by 270° and flipped horizontally.
|
||||||
|
Flipped270,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for SizeChange {
|
impl FromStr for SizeChange {
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ pub enum RenderResult {
|
|||||||
Skipped,
|
Skipped,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type IpcOutputMap = HashMap<String, (niri_ipc::Output, Option<Output>)>;
|
pub type IpcOutputMap = HashMap<String, niri_ipc::Output>;
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
pub fn init(&mut self, niri: &mut Niri) {
|
pub fn init(&mut self, niri: &mut Niri) {
|
||||||
|
|||||||
+6
-4
@@ -60,7 +60,7 @@ use crate::frame_clock::FrameClock;
|
|||||||
use crate::niri::{Niri, RedrawState, State};
|
use crate::niri::{Niri, RedrawState, State};
|
||||||
use crate::render_helpers::renderer::AsGlesRenderer;
|
use crate::render_helpers::renderer::AsGlesRenderer;
|
||||||
use crate::render_helpers::{shaders, RenderTarget};
|
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];
|
const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888];
|
||||||
|
|
||||||
@@ -1370,6 +1370,7 @@ impl Tty {
|
|||||||
width: m.size().0,
|
width: m.size().0,
|
||||||
height: m.size().1,
|
height: m.size().1,
|
||||||
refresh_rate: Mode::from(*m).refresh as u32,
|
refresh_rate: Mode::from(*m).refresh as u32,
|
||||||
|
is_preferred: m.mode_type().contains(ModeTypeFlags::PREFERRED),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -1384,14 +1385,14 @@ impl Tty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = niri
|
let logical = niri
|
||||||
.global_space
|
.global_space
|
||||||
.outputs()
|
.outputs()
|
||||||
.find(|output| {
|
.find(|output| {
|
||||||
let tty_state: &TtyOutputState = output.user_data().get().unwrap();
|
let tty_state: &TtyOutputState = output.user_data().get().unwrap();
|
||||||
tty_state.node == *node && tty_state.crtc == crtc
|
tty_state.node == *node && tty_state.crtc == crtc
|
||||||
})
|
})
|
||||||
.cloned();
|
.map(logical_output);
|
||||||
|
|
||||||
let ipc_output = niri_ipc::Output {
|
let ipc_output = niri_ipc::Output {
|
||||||
name: connector_name.clone(),
|
name: connector_name.clone(),
|
||||||
@@ -1400,9 +1401,10 @@ impl Tty {
|
|||||||
physical_size,
|
physical_size,
|
||||||
modes,
|
modes,
|
||||||
current_mode,
|
current_mode,
|
||||||
|
logical,
|
||||||
};
|
};
|
||||||
|
|
||||||
ipc_outputs.insert(connector_name, (ipc_output, output));
|
ipc_outputs.insert(connector_name, ipc_output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use smithay::reexports::winit::window::WindowBuilder;
|
|||||||
use super::{IpcOutputMap, RenderResult};
|
use super::{IpcOutputMap, RenderResult};
|
||||||
use crate::niri::{Niri, RedrawState, State};
|
use crate::niri::{Niri, RedrawState, State};
|
||||||
use crate::render_helpers::{shaders, RenderTarget};
|
use crate::render_helpers::{shaders, RenderTarget};
|
||||||
use crate::utils::get_monotonic_time;
|
use crate::utils::{get_monotonic_time, logical_output};
|
||||||
|
|
||||||
pub struct Winit {
|
pub struct Winit {
|
||||||
config: Rc<RefCell<Config>>,
|
config: Rc<RefCell<Config>>,
|
||||||
@@ -61,7 +61,6 @@ impl Winit {
|
|||||||
let physical_properties = output.physical_properties();
|
let physical_properties = output.physical_properties();
|
||||||
let ipc_outputs = Arc::new(Mutex::new(HashMap::from([(
|
let ipc_outputs = Arc::new(Mutex::new(HashMap::from([(
|
||||||
"winit".to_owned(),
|
"winit".to_owned(),
|
||||||
(
|
|
||||||
niri_ipc::Output {
|
niri_ipc::Output {
|
||||||
name: output.name(),
|
name: output.name(),
|
||||||
make: physical_properties.make,
|
make: physical_properties.make,
|
||||||
@@ -71,11 +70,11 @@ impl Winit {
|
|||||||
width: backend.window_size().w.clamp(0, u16::MAX as i32) as u16,
|
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,
|
height: backend.window_size().h.clamp(0, u16::MAX as i32) as u16,
|
||||||
refresh_rate: 60_000,
|
refresh_rate: 60_000,
|
||||||
|
is_preferred: true,
|
||||||
}],
|
}],
|
||||||
current_mode: Some(0),
|
current_mode: Some(0),
|
||||||
|
logical: Some(logical_output(&output)),
|
||||||
},
|
},
|
||||||
Some(output.clone()),
|
|
||||||
),
|
|
||||||
)])));
|
)])));
|
||||||
|
|
||||||
let damage_tracker = OutputDamageTracker::from_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 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.width = size.w.clamp(0, u16::MAX as i32) as u16;
|
||||||
mode.height = size.h.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;
|
state.niri.ipc_outputs_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use std::collections::HashMap;
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use smithay::utils::Transform;
|
|
||||||
use zbus::fdo::RequestNameFlags;
|
use zbus::fdo::RequestNameFlags;
|
||||||
use zbus::zvariant::{self, OwnedValue, Type};
|
use zbus::zvariant::{self, OwnedValue, Type};
|
||||||
use zbus::{dbus_interface, fdo, SignalContext};
|
use zbus::{dbus_interface, fdo, SignalContext};
|
||||||
@@ -60,11 +59,8 @@ impl DisplayConfig {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
// Take only enabled outputs.
|
// Take only enabled outputs.
|
||||||
.filter_map(|(c, (ipc, output))| {
|
.filter(|(_, output)| output.current_mode.is_some() && output.logical.is_some())
|
||||||
ipc.current_mode?;
|
.map(|(c, output)| {
|
||||||
output.as_ref().map(move |output| (c, (ipc, output)))
|
|
||||||
})
|
|
||||||
.map(|(c, (ipc, output))| {
|
|
||||||
// Loosely matches the check in Mutter.
|
// Loosely matches the check in Mutter.
|
||||||
let is_laptop_panel = matches!(c.get(..4), Some("eDP-" | "LVDS" | "DSI-"));
|
let is_laptop_panel = matches!(c.get(..4), Some("eDP-" | "LVDS" | "DSI-"));
|
||||||
|
|
||||||
@@ -84,7 +80,7 @@ impl DisplayConfig {
|
|||||||
OwnedValue::from(is_laptop_panel),
|
OwnedValue::from(is_laptop_panel),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut modes: Vec<Mode> = ipc
|
let mut modes: Vec<Mode> = output
|
||||||
.modes
|
.modes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| {
|
.map(|m| {
|
||||||
@@ -92,6 +88,7 @@ impl DisplayConfig {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
refresh_rate,
|
refresh_rate,
|
||||||
|
is_preferred,
|
||||||
} = *m;
|
} = *m;
|
||||||
let refresh = refresh_rate as f64 / 1000.;
|
let refresh = refresh_rate as f64 / 1000.;
|
||||||
|
|
||||||
@@ -102,11 +99,14 @@ impl DisplayConfig {
|
|||||||
refresh_rate: refresh,
|
refresh_rate: refresh,
|
||||||
preferred_scale: 1.,
|
preferred_scale: 1.,
|
||||||
supported_scales: vec![1., 2., 3.],
|
supported_scales: vec![1., 2., 3.],
|
||||||
properties: HashMap::new(),
|
properties: HashMap::from([(
|
||||||
|
String::from("is-preferred"),
|
||||||
|
OwnedValue::from(is_preferred),
|
||||||
|
)]),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
modes[ipc.current_mode.unwrap()]
|
modes[output.current_mode.unwrap()]
|
||||||
.properties
|
.properties
|
||||||
.insert(String::from("is-current"), OwnedValue::from(true));
|
.insert(String::from("is-current"), OwnedValue::from(true));
|
||||||
|
|
||||||
@@ -116,23 +116,23 @@ impl DisplayConfig {
|
|||||||
properties,
|
properties,
|
||||||
};
|
};
|
||||||
|
|
||||||
let loc = output.current_location();
|
let logical = output.logical.as_ref().unwrap();
|
||||||
|
|
||||||
let transform = match output.current_transform() {
|
let transform = match logical.transform {
|
||||||
Transform::Normal => 0,
|
niri_ipc::Transform::Normal => 0,
|
||||||
Transform::_90 => 1,
|
niri_ipc::Transform::_90 => 1,
|
||||||
Transform::_180 => 2,
|
niri_ipc::Transform::_180 => 2,
|
||||||
Transform::_270 => 3,
|
niri_ipc::Transform::_270 => 3,
|
||||||
Transform::Flipped => 4,
|
niri_ipc::Transform::Flipped => 4,
|
||||||
Transform::Flipped90 => 5,
|
niri_ipc::Transform::Flipped90 => 5,
|
||||||
Transform::Flipped180 => 6,
|
niri_ipc::Transform::Flipped180 => 6,
|
||||||
Transform::Flipped270 => 7,
|
niri_ipc::Transform::Flipped270 => 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
let logical_monitor = LogicalMonitor {
|
let logical_monitor = LogicalMonitor {
|
||||||
x: loc.x,
|
x: logical.x,
|
||||||
y: loc.y,
|
y: logical.y,
|
||||||
scale: output.current_scale().fractional_scale(),
|
scale: logical.scale,
|
||||||
transform,
|
transform,
|
||||||
is_primary: false,
|
is_primary: false,
|
||||||
monitors: vec![monitor.names.clone()],
|
monitors: vec![monitor.names.clone()],
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use zbus::{dbus_interface, fdo, InterfaceRef, ObjectServer, SignalContext};
|
|||||||
|
|
||||||
use super::Start;
|
use super::Start;
|
||||||
use crate::backend::IpcOutputMap;
|
use crate::backend::IpcOutputMap;
|
||||||
use crate::utils::output_size;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ScreenCast {
|
pub struct ScreenCast {
|
||||||
@@ -49,7 +48,8 @@ struct RecordMonitorProperties {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
output: Output,
|
// FIXME: update on scale changes and whatnot.
|
||||||
|
output: niri_ipc::Output,
|
||||||
cursor_mode: CursorMode,
|
cursor_mode: CursorMode,
|
||||||
was_started: Arc<AtomicBool>,
|
was_started: Arc<AtomicBool>,
|
||||||
to_niri: calloop::channel::Sender<ScreenCastToNiri>,
|
to_niri: calloop::channel::Sender<ScreenCastToNiri>,
|
||||||
@@ -58,6 +58,8 @@ pub struct Stream {
|
|||||||
#[derive(Debug, SerializeDict, Type, Value)]
|
#[derive(Debug, SerializeDict, Type, Value)]
|
||||||
#[zvariant(signature = "dict")]
|
#[zvariant(signature = "dict")]
|
||||||
struct StreamParameters {
|
struct StreamParameters {
|
||||||
|
/// Position of the stream in logical coordinates.
|
||||||
|
position: (i32, i32),
|
||||||
/// Size of the stream in logical coordinates.
|
/// Size of the stream in logical coordinates.
|
||||||
size: (i32, i32),
|
size: (i32, i32),
|
||||||
}
|
}
|
||||||
@@ -65,7 +67,7 @@ struct StreamParameters {
|
|||||||
pub enum ScreenCastToNiri {
|
pub enum ScreenCastToNiri {
|
||||||
StartCast {
|
StartCast {
|
||||||
session_id: usize,
|
session_id: usize,
|
||||||
output: Output,
|
output: String,
|
||||||
cursor_mode: CursorMode,
|
cursor_mode: CursorMode,
|
||||||
signal_ctx: SignalContext<'static>,
|
signal_ctx: SignalContext<'static>,
|
||||||
},
|
},
|
||||||
@@ -160,11 +162,14 @@ impl Session {
|
|||||||
) -> fdo::Result<OwnedObjectPath> {
|
) -> fdo::Result<OwnedObjectPath> {
|
||||||
debug!(connector, ?properties, "record_monitor");
|
debug!(connector, ?properties, "record_monitor");
|
||||||
|
|
||||||
let Some((_, Some(output))) = self.ipc_outputs.lock().unwrap().get(connector).cloned()
|
let Some(output) = self.ipc_outputs.lock().unwrap().get(connector).cloned() else {
|
||||||
else {
|
|
||||||
return Err(fdo::Error::Failed("no such monitor".to_owned()));
|
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);
|
static NUMBER: AtomicUsize = AtomicUsize::new(0);
|
||||||
let path = format!(
|
let path = format!(
|
||||||
"/org/gnome/Mutter/ScreenCast/Stream/u{}",
|
"/org/gnome/Mutter/ScreenCast/Stream/u{}",
|
||||||
@@ -174,7 +179,7 @@ impl Session {
|
|||||||
|
|
||||||
let cursor_mode = properties.cursor_mode.unwrap_or_default();
|
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 {
|
match server.at(&path, stream.clone()).await {
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
let iface = server.interface(&path).await.unwrap();
|
let iface = server.interface(&path).await.unwrap();
|
||||||
@@ -203,8 +208,11 @@ impl Stream {
|
|||||||
|
|
||||||
#[dbus_interface(property)]
|
#[dbus_interface(property)]
|
||||||
async fn parameters(&self) -> StreamParameters {
|
async fn parameters(&self) -> StreamParameters {
|
||||||
let size = output_size(&self.output).into();
|
let logical = self.output.logical.as_ref().unwrap();
|
||||||
StreamParameters { size }
|
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 {
|
impl Stream {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
output: Output,
|
output: niri_ipc::Output,
|
||||||
cursor_mode: CursorMode,
|
cursor_mode: CursorMode,
|
||||||
to_niri: calloop::channel::Sender<ScreenCastToNiri>,
|
to_niri: calloop::channel::Sender<ScreenCastToNiri>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@@ -280,7 +288,7 @@ impl Stream {
|
|||||||
|
|
||||||
let msg = ScreenCastToNiri::StartCast {
|
let msg = ScreenCastToNiri::StartCast {
|
||||||
session_id,
|
session_id,
|
||||||
output: self.output.clone(),
|
output: self.output.name.clone(),
|
||||||
cursor_mode: self.cursor_mode,
|
cursor_mode: self.cursor_mode,
|
||||||
signal_ctx: ctxt,
|
signal_ctx: ctxt,
|
||||||
};
|
};
|
||||||
|
|||||||
+47
-4
@@ -4,7 +4,7 @@ use std::net::Shutdown;
|
|||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context};
|
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;
|
use crate::cli::Msg;
|
||||||
|
|
||||||
@@ -66,6 +66,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
|||||||
physical_size,
|
physical_size,
|
||||||
modes,
|
modes,
|
||||||
current_mode,
|
current_mode,
|
||||||
|
logical,
|
||||||
} = output;
|
} = output;
|
||||||
|
|
||||||
println!(r#"Output "{connector}" ({make} - {model} - {name})"#);
|
println!(r#"Output "{connector}" ({make} - {model} - {name})"#);
|
||||||
@@ -78,9 +79,11 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
refresh_rate,
|
refresh_rate,
|
||||||
|
is_preferred,
|
||||||
} = mode;
|
} = mode;
|
||||||
let refresh = refresh_rate as f64 / 1000.;
|
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 {
|
} else {
|
||||||
println!(" Disabled");
|
println!(" Disabled");
|
||||||
}
|
}
|
||||||
@@ -91,15 +94,55 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
|||||||
println!(" Physical size: unknown");
|
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:");
|
println!(" Available modes:");
|
||||||
for mode in modes {
|
for (idx, mode) in modes.into_iter().enumerate() {
|
||||||
let Mode {
|
let Mode {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
refresh_rate,
|
refresh_rate,
|
||||||
|
is_preferred,
|
||||||
} = mode;
|
} = mode;
|
||||||
let refresh = refresh_rate as f64 / 1000.;
|
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!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-5
@@ -125,11 +125,7 @@ fn process(ctx: &ClientCtx, buf: &str) -> anyhow::Result<Response> {
|
|||||||
|
|
||||||
let response = match request {
|
let response = match request {
|
||||||
Request::Outputs => {
|
Request::Outputs => {
|
||||||
let ipc_outputs = ctx.ipc_outputs.lock().unwrap();
|
let ipc_outputs = ctx.ipc_outputs.lock().unwrap().clone();
|
||||||
let ipc_outputs = ipc_outputs
|
|
||||||
.iter()
|
|
||||||
.map(|(name, (ipc, _))| (name.clone(), ipc.clone()))
|
|
||||||
.collect();
|
|
||||||
Response::Outputs(ipc_outputs)
|
Response::Outputs(ipc_outputs)
|
||||||
}
|
}
|
||||||
Request::Action(action) => {
|
Request::Action(action) => {
|
||||||
|
|||||||
+36
-11
@@ -115,7 +115,8 @@ use crate::ui::hotkey_overlay::HotkeyOverlay;
|
|||||||
use crate::ui::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
use crate::ui::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
||||||
use crate::utils::spawning::CHILD_ENV;
|
use crate::utils::spawning::CHILD_ENV;
|
||||||
use crate::utils::{
|
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::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped, WindowRef};
|
||||||
use crate::{animation, niri_render_elements};
|
use crate::{animation, niri_render_elements};
|
||||||
@@ -459,7 +460,7 @@ impl State {
|
|||||||
self.refresh_pointer_focus();
|
self.refresh_pointer_focus();
|
||||||
foreign_toplevel::refresh(self);
|
foreign_toplevel::refresh(self);
|
||||||
self.niri.refresh_window_rules();
|
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>) {
|
pub fn move_cursor(&mut self, location: Point<f64, Logical>) {
|
||||||
@@ -967,6 +968,28 @@ impl State {
|
|||||||
self.niri.queue_redraw_all();
|
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")]
|
#[cfg(feature = "xdp-gnome-screencast")]
|
||||||
pub fn on_screen_cast_msg(
|
pub fn on_screen_cast_msg(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -997,6 +1020,17 @@ impl State {
|
|||||||
return;
|
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(
|
match pw.start_cast(
|
||||||
to_niri.clone(),
|
to_niri.clone(),
|
||||||
gbm,
|
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")]
|
#[cfg(feature = "dbus")]
|
||||||
pub fn on_ipc_outputs_changed(&self) {
|
pub fn on_ipc_outputs_changed(&self) {
|
||||||
let _span = tracy_client::span!("Niri::on_ipc_outputs_changed");
|
let _span = tracy_client::span!("Niri::on_ipc_outputs_changed");
|
||||||
|
|||||||
+24
-1
@@ -12,7 +12,7 @@ use git_version::git_version;
|
|||||||
use niri_config::Config;
|
use niri_config::Config;
|
||||||
use smithay::output::Output;
|
use smithay::output::Output;
|
||||||
use smithay::reexports::rustix::time::{clock_gettime, ClockId};
|
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 id;
|
||||||
pub mod spawning;
|
pub mod spawning;
|
||||||
@@ -51,6 +51,29 @@ pub fn output_size(output: &Output) -> Size<i32, Logical> {
|
|||||||
.to_logical(output_scale)
|
.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>> {
|
pub fn expand_home(path: &Path) -> anyhow::Result<Option<PathBuf>> {
|
||||||
if let Ok(rest) = path.strip_prefix("~") {
|
if let Ok(rest) = path.strip_prefix("~") {
|
||||||
let dirs = UserDirs::new().context("error retrieving home directory")?;
|
let dirs = UserDirs::new().context("error retrieving home directory")?;
|
||||||
|
|||||||
Reference in New Issue
Block a user