Skip to content

Commit

Permalink
Add niri msg layers
Browse files Browse the repository at this point in the history
  • Loading branch information
YaLTeR committed Nov 12, 2024
1 parent db1faec commit 0e5e764
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 0 deletions.
44 changes: 44 additions & 0 deletions niri-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ pub enum Request {
Workspaces,
/// Request information about open windows.
Windows,
/// Request information about layer-shell surfaces.
Layers,
/// Request information about the configured keyboard layouts.
KeyboardLayouts,
/// Request information about the focused output.
Expand Down Expand Up @@ -119,6 +121,8 @@ pub enum Response {
Workspaces(Vec<Workspace>),
/// Information about open windows.
Windows(Vec<Window>),
/// Information about layer-shell surfaces.
Layers(Vec<LayerSurface>),
/// Information about the keyboard layout.
KeyboardLayouts(KeyboardLayouts),
/// Information about the focused output.
Expand Down Expand Up @@ -767,6 +771,46 @@ pub struct KeyboardLayouts {
pub current_idx: u8,
}

/// A layer-shell layer.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum Layer {
/// The background layer.
Background,
/// The bottom layer.
Bottom,
/// The top layer.
Top,
/// The overlay layer.
Overlay,
}

/// Keyboard interactivity modes for a layer-shell surface.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum LayerSurfaceKeyboardInteractivity {
/// Surface cannot receive keyboard focus.
None,
/// Surface receives keyboard focus whenever possible.
Exclusive,
/// Surface receives keyboard focus on demand, e.g. when clicked.
OnDemand,
}

/// A layer-shell surface.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct LayerSurface {
/// Namespace provided by the layer-shell client.
pub namespace: String,
/// Name of the output the surface is on.
pub output: String,
/// Layer that the surface is on.
pub layer: Layer,
/// The surface's keyboard interactivity mode.
pub keyboard_interactivity: LayerSurfaceKeyboardInteractivity,
}

/// A compositor event.
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
Expand Down
2 changes: 2 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ pub enum Msg {
Workspaces,
/// List open windows.
Windows,
/// List open layer-shell surfaces.
Layers,
/// Get the configured keyboard layouts.
KeyboardLayouts,
/// Print information about the focused output.
Expand Down
67 changes: 67 additions & 0 deletions src/ipc/client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::iter::Peekable;
use std::slice;

use anyhow::{anyhow, bail, Context};
use niri_config::OutputName;
use niri_ipc::socket::Socket;
Expand All @@ -23,6 +26,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
},
Msg::Workspaces => Request::Workspaces,
Msg::Windows => Request::Windows,
Msg::Layers => Request::Layers,
Msg::KeyboardLayouts => Request::KeyboardLayouts,
Msg::EventStream => Request::EventStream,
Msg::RequestError => Request::ReturnError,
Expand Down Expand Up @@ -168,6 +172,69 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
println!();
}
}
Msg::Layers => {
let Response::Layers(mut layers) = response else {
bail!("unexpected response: expected Layers, got {response:?}");
};

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

layers.sort_by(|a, b| {
Ord::cmp(&a.output, &b.output)
.then_with(|| Ord::cmp(&a.layer, &b.layer))
.then_with(|| Ord::cmp(&a.namespace, &b.namespace))
});
let mut iter = layers.iter().peekable();

let print = |surface: &niri_ipc::LayerSurface| {
println!(" Surface:");
println!(" Namespace: \"{}\"", &surface.namespace);

let interactivity = match surface.keyboard_interactivity {
niri_ipc::LayerSurfaceKeyboardInteractivity::None => "none",
niri_ipc::LayerSurfaceKeyboardInteractivity::Exclusive => "exclusive",
niri_ipc::LayerSurfaceKeyboardInteractivity::OnDemand => "on-demand",
};
println!(" Keyboard interactivity: {interactivity}");
};

let print_layer = |iter: &mut Peekable<slice::Iter<niri_ipc::LayerSurface>>,
output: &str,
layer| {
let mut empty = true;
while let Some(surface) = iter.next_if(|s| s.output == output && s.layer == layer) {
empty = false;
println!();
print(surface);
}
if empty {
println!(" (empty)\n");
} else {
println!();
}
};

while let Some(surface) = iter.peek() {
let output = &surface.output;
println!("Output \"{output}\":");

print!(" Background layer:");
print_layer(&mut iter, output, niri_ipc::Layer::Background);

print!(" Bottom layer:");
print_layer(&mut iter, output, niri_ipc::Layer::Bottom);

print!(" Top layer:");
print_layer(&mut iter, output, niri_ipc::Layer::Top);

print!(" Overlay layer:");
print_layer(&mut iter, output, niri_ipc::Layer::Overlay);
}
}
Msg::FocusedOutput => {
let Response::FocusedOutput(output) = response else {
bail!("unexpected response: expected FocusedOutput, got {response:?}");
Expand Down
43 changes: 43 additions & 0 deletions src/ipc/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ use futures_util::{select_biased, AsyncBufReadExt, AsyncWrite, AsyncWriteExt, Fu
use niri_config::OutputName;
use niri_ipc::state::{EventStreamState, EventStreamStatePart as _};
use niri_ipc::{Event, KeyboardLayouts, OutputConfigChanged, Reply, Request, Response, Workspace};
use smithay::desktop::layer_map_for_output;
use smithay::reexports::calloop::generic::Generic;
use smithay::reexports::calloop::{Interest, LoopHandle, Mode, PostAction};
use smithay::reexports::rustix::fs::unlink;
use smithay::wayland::shell::wlr_layer::{KeyboardInteractivity, Layer};

use crate::backend::IpcOutputMap;
use crate::layout::workspace::WorkspaceId;
Expand Down Expand Up @@ -254,6 +256,47 @@ async fn process(ctx: &ClientCtx, request: Request) -> Reply {
let windows = state.windows.windows.values().cloned().collect();
Response::Windows(windows)
}
Request::Layers => {
let (tx, rx) = async_channel::bounded(1);
ctx.event_loop.insert_idle(move |state| {
let mut layers = Vec::new();
for output in state.niri.global_space.outputs() {
let name = output.name();
for surface in layer_map_for_output(output).layers() {
let layer = match surface.layer() {
Layer::Background => niri_ipc::Layer::Background,
Layer::Bottom => niri_ipc::Layer::Bottom,
Layer::Top => niri_ipc::Layer::Top,
Layer::Overlay => niri_ipc::Layer::Overlay,
};
let keyboard_interactivity =
match surface.cached_state().keyboard_interactivity {
KeyboardInteractivity::None => {
niri_ipc::LayerSurfaceKeyboardInteractivity::None
}
KeyboardInteractivity::Exclusive => {
niri_ipc::LayerSurfaceKeyboardInteractivity::Exclusive
}
KeyboardInteractivity::OnDemand => {
niri_ipc::LayerSurfaceKeyboardInteractivity::OnDemand
}
};

layers.push(niri_ipc::LayerSurface {
namespace: surface.namespace().to_owned(),
output: name.clone(),
layer,
keyboard_interactivity,
});
}
}

let _ = tx.send_blocking(layers);
});
let result = rx.recv().await;
let layers = result.map_err(|_| String::from("error getting layers info"))?;
Response::Layers(layers)
}
Request::KeyboardLayouts => {
let state = ctx.event_stream_state.borrow();
let layout = state.keyboard_layouts.keyboard_layouts.clone();
Expand Down

0 comments on commit 0e5e764

Please sign in to comment.