Skip to content

Commit

Permalink
Add niri msg focused-output
Browse files Browse the repository at this point in the history
  • Loading branch information
rustysec authored and YaLTeR committed May 26, 2024
1 parent 4746a0d commit ae7fb4c
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 90 deletions.
4 changes: 4 additions & 0 deletions niri-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub enum Request {
},
/// Request information about workspaces.
Workspaces,
/// Request information about the focused output.
FocusedOutput,
/// Respond with an error (for testing error handling).
ReturnError,
}
Expand Down Expand Up @@ -64,6 +66,8 @@ pub enum Response {
OutputConfigChanged(OutputConfigChanged),
/// Information about workspaces.
Workspaces(Vec<Workspace>),
/// Information about the focused output.
FocusedOutput(Option<Output>),
}

/// Actions that niri can perform.
Expand Down
2 changes: 2 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub enum Msg {
Workspaces,
/// Print information about the focused window.
FocusedWindow,
/// Print information about the focused output.
FocusedOutput,
/// Perform an action.
Action {
#[command(subcommand)]
Expand Down
203 changes: 113 additions & 90 deletions src/ipc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
Msg::Version => Request::Version,
Msg::Outputs => Request::Outputs,
Msg::FocusedWindow => Request::FocusedWindow,
Msg::FocusedOutput => Request::FocusedOutput,
Msg::Action { action } => Request::Action(action.clone()),
Msg::Output { output, action } => Request::Output {
output: output.clone(),
Expand Down Expand Up @@ -117,96 +118,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
outputs.sort_unstable_by(|a, b| a.0.cmp(&b.0));

for (connector, output) in outputs.into_iter() {
let Output {
name,
make,
model,
physical_size,
modes,
current_mode,
vrr_supported,
vrr_enabled,
logical,
} = output;

println!(r#"Output "{connector}" ({make} - {model} - {name})"#);

if let Some(current) = current_mode {
let mode = *modes
.get(current)
.context("invalid response: current mode does not exist")?;
let Mode {
width,
height,
refresh_rate,
is_preferred,
} = mode;
let refresh = refresh_rate as f64 / 1000.;
let preferred = if is_preferred { " (preferred)" } else { "" };
println!(" Current mode: {width}x{height} @ {refresh:.3} Hz{preferred}");
} else {
println!(" Disabled");
}

if vrr_supported {
let enabled = if vrr_enabled { "enabled" } else { "disabled" };
println!(" Variable refresh rate: supported, {enabled}");
} else {
println!(" Variable refresh rate: not supported");
}

if let Some((width, height)) = physical_size {
println!(" Physical size: {width}x{height} mm");
} else {
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 {
Transform::Normal => "normal",
Transform::_90 => "90° counter-clockwise",
Transform::_180 => "180°",
Transform::_270 => "270° counter-clockwise",
Transform::Flipped => "flipped horizontally",
Transform::Flipped90 => "90° counter-clockwise, flipped horizontally",
Transform::Flipped180 => "flipped vertically",
Transform::Flipped270 => "270° counter-clockwise, flipped horizontally",
};
println!(" Transform: {transform}");
}

println!(" Available 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.;

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}");
}
print_output(connector, output)?;
println!();
}
}
Expand Down Expand Up @@ -239,6 +151,23 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
println!("No window is focused.");
}
}
Msg::FocusedOutput => {
let Response::FocusedOutput(output) = response else {
bail!("unexpected response: expected FocusedOutput, got {response:?}");
};

if json {
let output = serde_json::to_string(&output).context("error formatting response")?;
println!("{output}");
return Ok(());
}

if let Some(output) = output {
print_output(output.name.clone(), output)?;
} else {
println!("No output is focused.");
}
}
Msg::Action { .. } => {
let Response::Handled = response else {
bail!("unexpected response: expected Handled, got {response:?}");
Expand Down Expand Up @@ -313,3 +242,97 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {

Ok(())
}

fn print_output(connector: String, output: Output) -> anyhow::Result<()> {
let Output {
name,
make,
model,
physical_size,
modes,
current_mode,
vrr_supported,
vrr_enabled,
logical,
} = output;

println!(r#"Output "{connector}" ({make} - {model} - {name})"#);

if let Some(current) = current_mode {
let mode = *modes
.get(current)
.context("invalid response: current mode does not exist")?;
let Mode {
width,
height,
refresh_rate,
is_preferred,
} = mode;
let refresh = refresh_rate as f64 / 1000.;
let preferred = if is_preferred { " (preferred)" } else { "" };
println!(" Current mode: {width}x{height} @ {refresh:.3} Hz{preferred}");
} else {
println!(" Disabled");
}

if vrr_supported {
let enabled = if vrr_enabled { "enabled" } else { "disabled" };
println!(" Variable refresh rate: supported, {enabled}");
} else {
println!(" Variable refresh rate: not supported");
}

if let Some((width, height)) = physical_size {
println!(" Physical size: {width}x{height} mm");
} else {
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 {
Transform::Normal => "normal",
Transform::_90 => "90° counter-clockwise",
Transform::_180 => "180°",
Transform::_270 => "270° counter-clockwise",
Transform::Flipped => "flipped horizontally",
Transform::Flipped90 => "90° counter-clockwise, flipped horizontally",
Transform::Flipped180 => "flipped vertically",
Transform::Flipped270 => "270° counter-clockwise, flipped horizontally",
};
println!(" Transform: {transform}");
}

println!(" Available 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.;

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}");
}
Ok(())
}
25 changes: 25 additions & 0 deletions src/ipc/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,31 @@ async fn process(ctx: &ClientCtx, request: Request) -> Reply {
let workspaces = result.map_err(|_| String::from("error getting workspace info"))?;
Response::Workspaces(workspaces)
}
Request::FocusedOutput => {
let (tx, rx) = async_channel::bounded(1);
ctx.event_loop.insert_idle(move |state| {
let active_output = state
.niri
.layout
.active_output()
.map(|output| output.name());

let output = active_output.and_then(|active_output| {
state
.backend
.ipc_outputs()
.lock()
.unwrap()
.get(&active_output)
.cloned()
});

let _ = tx.send_blocking(output);
});
let result = rx.recv().await;
let output = result.map_err(|_| String::from("error getting active output info"))?;
Response::FocusedOutput(output)
}
};

Ok(response)
Expand Down

0 comments on commit ae7fb4c

Please sign in to comment.