mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement niri msg workspaces
This commit is contained in:
committed by
Ivan Molodetskikh
parent
a2f74c9bff
commit
36d3e70f11
@@ -31,6 +31,8 @@ pub enum Request {
|
||||
/// Configuration to apply.
|
||||
action: OutputAction,
|
||||
},
|
||||
/// Request information about workspaces.
|
||||
Workspaces,
|
||||
/// Respond with an error (for testing error handling).
|
||||
ReturnError,
|
||||
}
|
||||
@@ -60,6 +62,8 @@ pub enum Response {
|
||||
FocusedWindow(Option<Window>),
|
||||
/// Output configuration change result.
|
||||
OutputConfigChanged(OutputConfigChanged),
|
||||
/// Information about workspaces.
|
||||
Workspaces(Vec<Workspace>),
|
||||
}
|
||||
|
||||
/// Actions that niri can perform.
|
||||
@@ -484,6 +488,23 @@ pub enum OutputConfigChanged {
|
||||
OutputWasMissing,
|
||||
}
|
||||
|
||||
/// A workspace.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Workspace {
|
||||
/// Index of the workspace on its monitor.
|
||||
///
|
||||
/// This is the same index you can use for requests like `niri msg action focus-workspace`.
|
||||
pub idx: u8,
|
||||
/// Optional name of the workspace.
|
||||
pub name: Option<String>,
|
||||
/// Name of the output that the workspace is on.
|
||||
///
|
||||
/// Can be `None` if no outputs are currently connected.
|
||||
pub output: Option<String>,
|
||||
/// Whether the workspace is currently active on its output.
|
||||
pub is_active: bool,
|
||||
}
|
||||
|
||||
impl FromStr for WorkspaceReferenceArg {
|
||||
type Err = &'static str;
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ pub enum Msg {
|
||||
#[command(subcommand)]
|
||||
action: OutputAction,
|
||||
},
|
||||
/// List workspaces.
|
||||
Workspaces,
|
||||
/// Request an error from the running niri instance.
|
||||
RequestError,
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
||||
output: output.clone(),
|
||||
action: action.clone(),
|
||||
},
|
||||
Msg::Workspaces => Request::Workspaces,
|
||||
Msg::RequestError => Request::ReturnError,
|
||||
};
|
||||
|
||||
@@ -260,6 +261,54 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
||||
println!("The change will apply when it is connected.");
|
||||
}
|
||||
}
|
||||
Msg::Workspaces => {
|
||||
let Response::Workspaces(mut response) = response else {
|
||||
bail!("unexpected response: expected Workspaces, got {response:?}");
|
||||
};
|
||||
|
||||
if json {
|
||||
let response =
|
||||
serde_json::to_string(&response).context("error formatting response")?;
|
||||
println!("{response}");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if response.is_empty() {
|
||||
println!("No workspaces.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
response.sort_by_key(|ws| ws.idx);
|
||||
response.sort_by(|a, b| a.output.cmp(&b.output));
|
||||
|
||||
let mut current_output = if let Some(output) = response[0].output.as_deref() {
|
||||
println!("Output \"{output}\":");
|
||||
Some(output)
|
||||
} else {
|
||||
println!("No output:");
|
||||
None
|
||||
};
|
||||
|
||||
for ws in &response {
|
||||
if ws.output.as_deref() != current_output {
|
||||
let output = ws.output.as_deref().context(
|
||||
"invalid response: workspace with no output \
|
||||
following a workspace with an output",
|
||||
)?;
|
||||
current_output = Some(output);
|
||||
println!("\nOutput \"{output}\":");
|
||||
}
|
||||
|
||||
let is_active = if ws.is_active { " * " } else { " " };
|
||||
let idx = ws.idx;
|
||||
let name = if let Some(name) = ws.name.as_deref() {
|
||||
format!(" \"{name}\"")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
println!("{is_active}{idx}{name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -198,6 +198,16 @@ async fn process(ctx: &ClientCtx, request: Request) -> Reply {
|
||||
|
||||
Response::OutputConfigChanged(response)
|
||||
}
|
||||
Request::Workspaces => {
|
||||
let (tx, rx) = async_channel::bounded(1);
|
||||
ctx.event_loop.insert_idle(move |state| {
|
||||
let workspaces = state.niri.layout.ipc_workspaces();
|
||||
let _ = tx.send_blocking(workspaces);
|
||||
});
|
||||
let result = rx.recv().await;
|
||||
let workspaces = result.map_err(|_| String::from("error getting workspace info"))?;
|
||||
Response::Workspaces(workspaces)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
|
||||
@@ -2278,6 +2278,41 @@ impl<W: LayoutElement> Layout<W> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ipc_workspaces(&self) -> Vec<niri_ipc::Workspace> {
|
||||
match &self.monitor_set {
|
||||
MonitorSet::Normal {
|
||||
monitors,
|
||||
primary_idx: _,
|
||||
active_monitor_idx: _,
|
||||
} => {
|
||||
let mut workspaces = Vec::new();
|
||||
|
||||
for monitor in monitors {
|
||||
for (idx, workspace) in monitor.workspaces.iter().enumerate() {
|
||||
workspaces.push(niri_ipc::Workspace {
|
||||
idx: u8::try_from(idx + 1).unwrap_or(u8::MAX),
|
||||
name: workspace.name.clone(),
|
||||
output: Some(monitor.output.name()),
|
||||
is_active: monitor.active_workspace_idx == idx,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
workspaces
|
||||
}
|
||||
MonitorSet::NoOutputs { workspaces } => workspaces
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, ws)| niri_ipc::Workspace {
|
||||
idx: u8::try_from(idx + 1).unwrap_or(u8::MAX),
|
||||
name: ws.name.clone(),
|
||||
output: None,
|
||||
is_active: false,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Default for MonitorSet<W> {
|
||||
|
||||
@@ -12,6 +12,7 @@ use _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as
|
||||
use anyhow::{ensure, Context};
|
||||
use calloop::futures::Scheduler;
|
||||
use niri_config::{Config, Key, Modifiers, PreviewRender, TrackLayout, WorkspaceReference};
|
||||
use niri_ipc::Workspace;
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::renderer::damage::OutputDamageTracker;
|
||||
use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
|
||||
@@ -3934,6 +3935,10 @@ impl Niri {
|
||||
self.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ipc_workspaces(&self) -> Vec<Workspace> {
|
||||
self.layout.ipc_workspaces()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientState {
|
||||
|
||||
Reference in New Issue
Block a user