From 93e16a6582f8e80ac4079ac527637861696ed7b1 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 10 Feb 2024 09:33:32 +0400 Subject: [PATCH] Implement niri msg action --- Cargo.lock | 2 + Cargo.toml | 5 +- niri-config/Cargo.toml | 1 + niri-config/src/lib.rs | 138 ++++++++++---------- niri-ipc/Cargo.toml | 4 + niri-ipc/src/lib.rs | 241 +++++++++++++++++++++++++++++++++++ resources/default-config.kdl | 3 + src/cli.rs | 6 + src/input.rs | 11 +- src/ipc/client.rs | 12 +- src/ipc/server.rs | 9 ++ src/layout/mod.rs | 3 +- src/layout/monitor.rs | 2 +- src/layout/workspace.rs | 3 +- 14 files changed, 366 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 817c41b..d00655e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2093,6 +2093,7 @@ dependencies = [ "bitflags 2.4.2", "knuffel", "miette", + "niri-ipc", "smithay", "tracing", "tracy-client", @@ -2102,6 +2103,7 @@ dependencies = [ name = "niri-ipc" version = "0.1.1" dependencies = [ + "clap", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 9ebd4b0..a3fcbda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/YaLTeR/niri" [workspace.dependencies] anyhow = "1.0.79" bitflags = "2.4.2" +clap = { version = "4.4.18", features = ["derive"] } serde = { version = "1.0.196", features = ["derive"] } tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } @@ -45,7 +46,7 @@ async-channel = { version = "2.1.1", optional = true } async-io = { version = "1.13.0", optional = true } bitflags = "2.4.2" calloop = { version = "0.12.4", features = ["executor", "futures-io"] } -clap = { version = "4.4.18", features = ["derive", "string"] } +clap = { workspace = true, features = ["string"] } directories = "5.0.1" futures-util = { version = "0.3.30", default-features = false, features = ["std", "io"] } git-version = "0.3.9" @@ -55,7 +56,7 @@ libc = "0.2.153" log = { version = "0.4.20", features = ["max_level_trace", "release_max_level_debug"] } logind-zbus = { version = "3.1.2", optional = true } niri-config = { version = "0.1.1", path = "niri-config" } -niri-ipc = { version = "0.1.1", path = "niri-ipc" } +niri-ipc = { version = "0.1.1", path = "niri-ipc", features = ["clap"] } notify-rust = { version = "4.10.0", optional = true } pangocairo = "0.19.1" pipewire = { version = "0.7.2", optional = true } diff --git a/niri-config/Cargo.toml b/niri-config/Cargo.toml index 4123cb0..8bff3a3 100644 --- a/niri-config/Cargo.toml +++ b/niri-config/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true bitflags.workspace = true knuffel = "3.2.0" miette = "5.10.0" +niri-ipc = { version = "0.1.1", path = "../niri-ipc" } smithay = { workspace = true, features = ["backend_libinput"] } tracing.workspace = true tracy-client.workspace = true diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 333a018..f32ca74 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -6,6 +6,7 @@ use std::str::FromStr; use bitflags::bitflags; use miette::{miette, Context, IntoDiagnostic, NarratableReportHandler}; +use niri_ipc::{LayoutSwitchTarget, SizeChange}; use smithay::input::keyboard::keysyms::KEY_NoSymbol; use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE}; use smithay::input::keyboard::{Keysym, XkbConfig}; @@ -512,6 +513,7 @@ bitflags! { } } +// Remember to add new actions to the CLI enum too. #[derive(knuffel::Decode, Debug, Clone, PartialEq)] pub enum Action { Quit, @@ -578,7 +580,7 @@ pub enum Action { SwitchPresetColumnWidth, MaximizeColumn, SetColumnWidth(#[knuffel(argument, str)] SizeChange), - SwitchLayout(#[knuffel(argument)] LayoutAction), + SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget), ShowHotkeyOverlay, MoveWorkspaceToMonitorLeft, MoveWorkspaceToMonitorRight, @@ -586,18 +588,76 @@ pub enum Action { MoveWorkspaceToMonitorUp, } -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum SizeChange { - SetFixed(i32), - SetProportion(f64), - AdjustFixed(i32), - AdjustProportion(f64), -} - -#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq)] -pub enum LayoutAction { - Next, - Prev, +impl From for Action { + fn from(value: niri_ipc::Action) -> Self { + match value { + niri_ipc::Action::Quit => Self::Quit, + niri_ipc::Action::PowerOffMonitors => Self::PowerOffMonitors, + niri_ipc::Action::Spawn { command } => Self::Spawn(command), + niri_ipc::Action::Screenshot => Self::Screenshot, + niri_ipc::Action::ScreenshotScreen => Self::ScreenshotScreen, + niri_ipc::Action::ScreenshotWindow => Self::ScreenshotWindow, + niri_ipc::Action::CloseWindow => Self::CloseWindow, + niri_ipc::Action::FullscreenWindow => Self::FullscreenWindow, + niri_ipc::Action::FocusColumnLeft => Self::FocusColumnLeft, + niri_ipc::Action::FocusColumnRight => Self::FocusColumnRight, + niri_ipc::Action::FocusColumnFirst => Self::FocusColumnFirst, + niri_ipc::Action::FocusColumnLast => Self::FocusColumnLast, + niri_ipc::Action::FocusWindowDown => Self::FocusWindowDown, + niri_ipc::Action::FocusWindowUp => Self::FocusWindowUp, + niri_ipc::Action::FocusWindowOrWorkspaceDown => Self::FocusWindowOrWorkspaceDown, + niri_ipc::Action::FocusWindowOrWorkspaceUp => Self::FocusWindowOrWorkspaceUp, + niri_ipc::Action::MoveColumnLeft => Self::MoveColumnLeft, + niri_ipc::Action::MoveColumnRight => Self::MoveColumnRight, + niri_ipc::Action::MoveColumnToFirst => Self::MoveColumnToFirst, + niri_ipc::Action::MoveColumnToLast => Self::MoveColumnToLast, + niri_ipc::Action::MoveWindowDown => Self::MoveWindowDown, + niri_ipc::Action::MoveWindowUp => Self::MoveWindowUp, + niri_ipc::Action::MoveWindowDownOrToWorkspaceDown => { + Self::MoveWindowDownOrToWorkspaceDown + } + niri_ipc::Action::MoveWindowUpOrToWorkspaceUp => Self::MoveWindowUpOrToWorkspaceUp, + niri_ipc::Action::ConsumeOrExpelWindowLeft => Self::ConsumeOrExpelWindowLeft, + niri_ipc::Action::ConsumeOrExpelWindowRight => Self::ConsumeOrExpelWindowRight, + niri_ipc::Action::ConsumeWindowIntoColumn => Self::ConsumeWindowIntoColumn, + niri_ipc::Action::ExpelWindowFromColumn => Self::ExpelWindowFromColumn, + niri_ipc::Action::CenterColumn => Self::CenterColumn, + niri_ipc::Action::FocusWorkspaceDown => Self::FocusWorkspaceDown, + niri_ipc::Action::FocusWorkspaceUp => Self::FocusWorkspaceUp, + niri_ipc::Action::FocusWorkspace { index } => Self::FocusWorkspace(index), + niri_ipc::Action::MoveWindowToWorkspaceDown => Self::MoveWindowToWorkspaceDown, + niri_ipc::Action::MoveWindowToWorkspaceUp => Self::MoveWindowToWorkspaceUp, + niri_ipc::Action::MoveWindowToWorkspace { index } => Self::MoveWindowToWorkspace(index), + niri_ipc::Action::MoveColumnToWorkspaceDown => Self::MoveColumnToWorkspaceDown, + niri_ipc::Action::MoveColumnToWorkspaceUp => Self::MoveColumnToWorkspaceUp, + niri_ipc::Action::MoveColumnToWorkspace { index } => Self::MoveColumnToWorkspace(index), + niri_ipc::Action::MoveWorkspaceDown => Self::MoveWorkspaceDown, + niri_ipc::Action::MoveWorkspaceUp => Self::MoveWorkspaceUp, + niri_ipc::Action::FocusMonitorLeft => Self::FocusMonitorLeft, + niri_ipc::Action::FocusMonitorRight => Self::FocusMonitorRight, + niri_ipc::Action::FocusMonitorDown => Self::FocusMonitorDown, + niri_ipc::Action::FocusMonitorUp => Self::FocusMonitorUp, + niri_ipc::Action::MoveWindowToMonitorLeft => Self::MoveWindowToMonitorLeft, + niri_ipc::Action::MoveWindowToMonitorRight => Self::MoveWindowToMonitorRight, + niri_ipc::Action::MoveWindowToMonitorDown => Self::MoveWindowToMonitorDown, + niri_ipc::Action::MoveWindowToMonitorUp => Self::MoveWindowToMonitorUp, + niri_ipc::Action::MoveColumnToMonitorLeft => Self::MoveColumnToMonitorLeft, + niri_ipc::Action::MoveColumnToMonitorRight => Self::MoveColumnToMonitorRight, + niri_ipc::Action::MoveColumnToMonitorDown => Self::MoveColumnToMonitorDown, + niri_ipc::Action::MoveColumnToMonitorUp => Self::MoveColumnToMonitorUp, + niri_ipc::Action::SetWindowHeight { change } => Self::SetWindowHeight(change), + niri_ipc::Action::SwitchPresetColumnWidth => Self::SwitchPresetColumnWidth, + niri_ipc::Action::MaximizeColumn => Self::MaximizeColumn, + niri_ipc::Action::SetColumnWidth { change } => Self::SetColumnWidth(change), + niri_ipc::Action::SwitchLayout { layout } => Self::SwitchLayout(layout), + niri_ipc::Action::ShowHotkeyOverlay => Self::ShowHotkeyOverlay, + niri_ipc::Action::MoveWorkspaceToMonitorLeft => Self::MoveWorkspaceToMonitorLeft, + niri_ipc::Action::MoveWorkspaceToMonitorRight => Self::MoveWorkspaceToMonitorRight, + niri_ipc::Action::MoveWorkspaceToMonitorDown => Self::MoveWorkspaceToMonitorDown, + niri_ipc::Action::MoveWorkspaceToMonitorUp => Self::MoveWorkspaceToMonitorUp, + niri_ipc::Action::ToggleDebugTint => Self::ToggleDebugTint, + } + } } #[derive(knuffel::Decode, Debug, Default, PartialEq)] @@ -718,58 +778,6 @@ impl FromStr for Key { } } -impl FromStr for SizeChange { - type Err = miette::Error; - - fn from_str(s: &str) -> Result { - match s.split_once('%') { - Some((value, empty)) => { - if !empty.is_empty() { - return Err(miette!("trailing characters after '%' are not allowed")); - } - - match value.bytes().next() { - Some(b'-' | b'+') => { - let value = value - .parse() - .into_diagnostic() - .context("error parsing value")?; - Ok(Self::AdjustProportion(value)) - } - Some(_) => { - let value = value - .parse() - .into_diagnostic() - .context("error parsing value")?; - Ok(Self::SetProportion(value)) - } - None => Err(miette!("value is missing")), - } - } - None => { - let value = s; - match value.bytes().next() { - Some(b'-' | b'+') => { - let value = value - .parse() - .into_diagnostic() - .context("error parsing value")?; - Ok(Self::AdjustFixed(value)) - } - Some(_) => { - let value = value - .parse() - .into_diagnostic() - .context("error parsing value")?; - Ok(Self::SetFixed(value)) - } - None => Err(miette!("value is missing")), - } - } - } - } -} - impl FromStr for AccelProfile { type Err = miette::Error; diff --git a/niri-ipc/Cargo.toml b/niri-ipc/Cargo.toml index 21207ac..9bd2e83 100644 --- a/niri-ipc/Cargo.toml +++ b/niri-ipc/Cargo.toml @@ -8,4 +8,8 @@ edition.workspace = true repository.workspace = true [dependencies] +clap = { workspace = true, optional = true } serde.workspace = true + +[features] +clap = ["dep:clap"] diff --git a/niri-ipc/src/lib.rs b/niri-ipc/src/lib.rs index 129e8f5..5131388 100644 --- a/niri-ipc/src/lib.rs +++ b/niri-ipc/src/lib.rs @@ -2,6 +2,7 @@ #![warn(missing_docs)] use std::collections::HashMap; +use std::str::FromStr; use serde::{Deserialize, Serialize}; @@ -13,6 +14,8 @@ pub const SOCKET_PATH_ENV: &str = "NIRI_SOCKET"; pub enum Request { /// Request information about connected outputs. Outputs, + /// Perform an action. + Action(Action), } /// Response from niri to client. @@ -24,6 +27,192 @@ pub enum Response { Outputs(HashMap), } +/// Actions that niri can perform. +// Variants in this enum should match the spelling of the ones in niri-config. Most, but not all, +// variants from niri-config should be present here. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "clap", derive(clap::Parser))] +#[cfg_attr(feature = "clap", command(subcommand_value_name = "ACTION"))] +#[cfg_attr(feature = "clap", command(subcommand_help_heading = "Actions"))] +pub enum Action { + /// Exit niri. + Quit, + /// Power off all monitors via DPMS. + PowerOffMonitors, + /// Spawn a command. + Spawn { + /// Command to spawn. + #[cfg_attr(feature = "clap", arg(last = true, required = true))] + command: Vec, + }, + /// Open the screenshot UI. + Screenshot, + /// Screenshot the focused screen. + ScreenshotScreen, + /// Screenshot the focused window. + ScreenshotWindow, + /// Close the focused window. + CloseWindow, + /// Toggle fullscreen on the focused window. + FullscreenWindow, + /// Focus the column to the left. + FocusColumnLeft, + /// Focus the column to the right. + FocusColumnRight, + /// Focus the first column. + FocusColumnFirst, + /// Focus the last column. + FocusColumnLast, + /// Focus the window below. + FocusWindowDown, + /// Focus the window above. + FocusWindowUp, + /// Focus the window or the workspace above. + FocusWindowOrWorkspaceDown, + /// Focus the window or the workspace above. + FocusWindowOrWorkspaceUp, + /// Move the focused column to the left. + MoveColumnLeft, + /// Move the focused column to the right. + MoveColumnRight, + /// Move the focused column to the start of the workspace. + MoveColumnToFirst, + /// Move the focused column to the end of the workspace. + MoveColumnToLast, + /// Move the focused window down in a column. + MoveWindowDown, + /// Move the focused window up in a column. + MoveWindowUp, + /// Move the focused window down in a column or to the workspace below. + MoveWindowDownOrToWorkspaceDown, + /// Move the focused window up in a column or to the workspace above. + MoveWindowUpOrToWorkspaceUp, + /// Consume or expel the focused window left. + ConsumeOrExpelWindowLeft, + /// Consume or expel the focused window right. + ConsumeOrExpelWindowRight, + /// Consume the window to the right into the focused column. + ConsumeWindowIntoColumn, + /// Expel the focused window from the column. + ExpelWindowFromColumn, + /// Center the focused column on the screen. + CenterColumn, + /// Focus the workspace below. + FocusWorkspaceDown, + /// Focus the workspace above. + FocusWorkspaceUp, + /// Focus a workspace by index. + FocusWorkspace { + /// Index of the workspace to focus. + #[cfg_attr(feature = "clap", arg())] + index: u8, + }, + /// Move the focused window to the workspace below. + MoveWindowToWorkspaceDown, + /// Move the focused window to the workspace above. + MoveWindowToWorkspaceUp, + /// Move the focused window to a workspace by index. + MoveWindowToWorkspace { + /// Index of the target workspace. + #[cfg_attr(feature = "clap", arg())] + index: u8, + }, + /// Move the focused column to the workspace below. + MoveColumnToWorkspaceDown, + /// Move the focused column to the workspace above. + MoveColumnToWorkspaceUp, + /// Move the focused column to a workspace by index. + MoveColumnToWorkspace { + /// Index of the target workspace. + #[cfg_attr(feature = "clap", arg())] + index: u8, + }, + /// Move the focused workspace down. + MoveWorkspaceDown, + /// Move the focused workspace up. + MoveWorkspaceUp, + /// Focus the monitor to the left. + FocusMonitorLeft, + /// Focus the monitor to the right. + FocusMonitorRight, + /// Focus the monitor below. + FocusMonitorDown, + /// Focus the monitor above. + FocusMonitorUp, + /// Move the focused window to the monitor to the left. + MoveWindowToMonitorLeft, + /// Move the focused window to the monitor to the right. + MoveWindowToMonitorRight, + /// Move the focused window to the monitor below. + MoveWindowToMonitorDown, + /// Move the focused window to the monitor above. + MoveWindowToMonitorUp, + /// Move the focused column to the monitor to the left. + MoveColumnToMonitorLeft, + /// Move the focused column to the monitor to the right. + MoveColumnToMonitorRight, + /// Move the focused column to the monitor below. + MoveColumnToMonitorDown, + /// Move the focused column to the monitor above. + MoveColumnToMonitorUp, + /// Change the height of the focused window. + SetWindowHeight { + /// How to change the height. + #[cfg_attr(feature = "clap", arg())] + change: SizeChange, + }, + /// Switch between preset column widths. + SwitchPresetColumnWidth, + /// Toggle the maximized state of the focused column. + MaximizeColumn, + /// Change the width of the focused column. + SetColumnWidth { + /// How to change the width. + #[cfg_attr(feature = "clap", arg())] + change: SizeChange, + }, + /// Switch between keyboard layouts. + SwitchLayout { + /// Layout to switch to. + #[cfg_attr(feature = "clap", arg())] + layout: LayoutSwitchTarget, + }, + /// Show the hotkey overlay. + ShowHotkeyOverlay, + /// Move the focused workspace to the monitor to the left. + MoveWorkspaceToMonitorLeft, + /// Move the focused workspace to the monitor to the right. + MoveWorkspaceToMonitorRight, + /// Move the focused workspace to the monitor below. + MoveWorkspaceToMonitorDown, + /// Move the focused workspace to the monitor above. + MoveWorkspaceToMonitorUp, + /// Toggle a debug tint on windows. + ToggleDebugTint, +} + +/// Change in window or column size. +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] +pub enum SizeChange { + /// Set the size in logical pixels. + SetFixed(i32), + /// Set the size as a proportion of the working area. + SetProportion(f64), + /// Add or subtract to the current size in logical pixels. + AdjustFixed(i32), + /// Add or subtract to the current size as a proportion of the working area. + AdjustProportion(f64), +} + +/// Layout to switch to. +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] +pub enum LayoutSwitchTarget { + /// The next configured layout. + Next, + /// The previous configured layout. + Prev, +} + /// Connected output. #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Output { @@ -53,3 +242,55 @@ pub struct Mode { /// Refresh rate in millihertz. pub refresh_rate: u32, } + +impl FromStr for SizeChange { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.split_once('%') { + Some((value, empty)) => { + if !empty.is_empty() { + return Err("trailing characters after '%' are not allowed"); + } + + match value.bytes().next() { + Some(b'-' | b'+') => { + let value = value.parse().map_err(|_| "error parsing value")?; + Ok(Self::AdjustProportion(value)) + } + Some(_) => { + let value = value.parse().map_err(|_| "error parsing value")?; + Ok(Self::SetProportion(value)) + } + None => Err("value is missing"), + } + } + None => { + let value = s; + match value.bytes().next() { + Some(b'-' | b'+') => { + let value = value.parse().map_err(|_| "error parsing value")?; + Ok(Self::AdjustFixed(value)) + } + Some(_) => { + let value = value.parse().map_err(|_| "error parsing value")?; + Ok(Self::SetFixed(value)) + } + None => Err("value is missing"), + } + } + } + } +} + +impl FromStr for LayoutSwitchTarget { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "next" => Ok(Self::Next), + "prev" => Ok(Self::Prev), + _ => Err(r#"invalid layout action, can be "next" or "prev""#), + } + } +} diff --git a/resources/default-config.kdl b/resources/default-config.kdl index 7f89bec..2cee1c6 100644 --- a/resources/default-config.kdl +++ b/resources/default-config.kdl @@ -251,6 +251,9 @@ binds { // // "Mod" is a special modifier equal to Super when running on a TTY, and to Alt // when running as a winit window. + // + // Most actions that you can bind here can also be invoked programmatically with + // `niri msg action do-something`. // Mod-Shift-/, which is usually the same as Mod-?, // shows a list of important hotkeys. diff --git a/src/cli.rs b/src/cli.rs index 8dd8927..2632f1e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,6 +2,7 @@ use std::ffi::OsString; use std::path::PathBuf; use clap::{Parser, Subcommand}; +use niri_ipc::Action; use crate::utils::version; @@ -46,4 +47,9 @@ pub enum Sub { pub enum Msg { /// List connected outputs. Outputs, + /// Perform an action. + Action { + #[command(subcommand)] + action: Action, + }, } diff --git a/src/input.rs b/src/input.rs index 507e447..0f586ab 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,7 +1,8 @@ use std::any::Any; use std::collections::HashSet; -use niri_config::{Action, Binds, LayoutAction, Modifiers}; +use niri_config::{Action, Binds, Modifiers}; +use niri_ipc::LayoutSwitchTarget; use smithay::backend::input::{ AbsolutePositionEvent, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event, GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, @@ -273,6 +274,10 @@ impl State { return; } + self.do_action(action); + } + + pub fn do_action(&mut self, action: Action) { if self.niri.is_locked() && !allowed_when_locked(&action) { return; } @@ -377,8 +382,8 @@ impl State { self.niri.seat.get_keyboard().unwrap().with_xkb_state( self, |mut state| match action { - LayoutAction::Next => state.cycle_next_layout(), - LayoutAction::Prev => state.cycle_prev_layout(), + LayoutSwitchTarget::Next => state.cycle_next_layout(), + LayoutSwitchTarget::Prev => state.cycle_prev_layout(), }, ); } diff --git a/src/ipc/client.rs b/src/ipc/client.rs index 7754187..b2004a7 100644 --- a/src/ipc/client.rs +++ b/src/ipc/client.rs @@ -19,8 +19,9 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { let mut stream = UnixStream::connect(socket_path).context("error connecting to {socket_path}")?; - let request = match msg { + let request = match &msg { Msg::Outputs => Request::Outputs, + Msg::Action { action } => Request::Action(action.clone()), }; let mut buf = serde_json::to_vec(&request).unwrap(); stream @@ -35,6 +36,14 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { .read_to_end(&mut buf) .context("error reading IPC response")?; + if matches!(msg, Msg::Action { .. }) { + if buf.is_empty() { + return Ok(()); + } else { + bail!("unexpected response: expected no response, got {buf:?}"); + } + } + let response = serde_json::from_slice(&buf).context("error parsing IPC response")?; match msg { Msg::Outputs => { @@ -100,6 +109,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { println!(); } } + Msg::Action { .. } => unreachable!(), } Ok(()) diff --git a/src/ipc/server.rs b/src/ipc/server.rs index d493e86..bedfe48 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -22,6 +22,7 @@ pub struct IpcServer { } struct ClientCtx { + event_loop: LoopHandle<'static, State>, ipc_outputs: Rc>>, } @@ -85,6 +86,7 @@ fn on_new_ipc_client(state: &mut State, stream: UnixStream) { }; let ctx = ClientCtx { + event_loop: state.niri.event_loop.clone(), ipc_outputs: state.backend.ipc_outputs(), }; @@ -115,6 +117,13 @@ async fn handle_client(ctx: ClientCtx, stream: Async<'_, UnixStream>) -> anyhow: let ipc_outputs = ctx.ipc_outputs.borrow().clone(); Response::Outputs(ipc_outputs) } + Request::Action(action) => { + let action = niri_config::Action::from(action); + ctx.event_loop.insert_idle(move |state| { + state.do_action(action); + }); + return Ok(()); + } }; let buf = serde_json::to_vec(&response).context("error formatting response")?; diff --git a/src/layout/mod.rs b/src/layout/mod.rs index c4bcc61..93b1fe1 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -34,7 +34,8 @@ use std::mem; use std::rc::Rc; use std::time::Duration; -use niri_config::{self, CenterFocusedColumn, Config, SizeChange, Struts}; +use niri_config::{self, CenterFocusedColumn, Config, Struts}; +use niri_ipc::SizeChange; use smithay::backend::renderer::element::solid::SolidColorRenderElement; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::element::{AsRenderElements, Id}; diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index e67f5bf..cb99279 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -2,7 +2,7 @@ use std::cmp::min; use std::rc::Rc; use std::time::Duration; -use niri_config::SizeChange; +use niri_ipc::SizeChange; use smithay::backend::renderer::element::utils::{ CropRenderElement, Relocate, RelocateRenderElement, }; diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 0e576b7..64bc4bc 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -3,7 +3,8 @@ use std::iter::{self, zip}; use std::rc::Rc; use std::time::Duration; -use niri_config::{CenterFocusedColumn, PresetWidth, SizeChange, Struts}; +use niri_config::{CenterFocusedColumn, PresetWidth, Struts}; +use niri_ipc::SizeChange; use smithay::desktop::space::SpaceElement; use smithay::desktop::{layer_map_for_output, Window}; use smithay::output::Output;