mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement niri msg action
This commit is contained in:
Generated
+2
@@ -2093,6 +2093,7 @@ dependencies = [
|
|||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"knuffel",
|
"knuffel",
|
||||||
"miette",
|
"miette",
|
||||||
|
"niri-ipc",
|
||||||
"smithay",
|
"smithay",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracy-client",
|
"tracy-client",
|
||||||
@@ -2102,6 +2103,7 @@ dependencies = [
|
|||||||
name = "niri-ipc"
|
name = "niri-ipc"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -12,6 +12,7 @@ repository = "https://github.com/YaLTeR/niri"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
anyhow = "1.0.79"
|
anyhow = "1.0.79"
|
||||||
bitflags = "2.4.2"
|
bitflags = "2.4.2"
|
||||||
|
clap = { version = "4.4.18", features = ["derive"] }
|
||||||
serde = { version = "1.0.196", features = ["derive"] }
|
serde = { version = "1.0.196", features = ["derive"] }
|
||||||
tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] }
|
tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] }
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
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 }
|
async-io = { version = "1.13.0", optional = true }
|
||||||
bitflags = "2.4.2"
|
bitflags = "2.4.2"
|
||||||
calloop = { version = "0.12.4", features = ["executor", "futures-io"] }
|
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"
|
directories = "5.0.1"
|
||||||
futures-util = { version = "0.3.30", default-features = false, features = ["std", "io"] }
|
futures-util = { version = "0.3.30", default-features = false, features = ["std", "io"] }
|
||||||
git-version = "0.3.9"
|
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"] }
|
log = { version = "0.4.20", features = ["max_level_trace", "release_max_level_debug"] }
|
||||||
logind-zbus = { version = "3.1.2", optional = true }
|
logind-zbus = { version = "3.1.2", optional = true }
|
||||||
niri-config = { version = "0.1.1", path = "niri-config" }
|
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 }
|
notify-rust = { version = "4.10.0", optional = true }
|
||||||
pangocairo = "0.19.1"
|
pangocairo = "0.19.1"
|
||||||
pipewire = { version = "0.7.2", optional = true }
|
pipewire = { version = "0.7.2", optional = true }
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ repository.workspace = true
|
|||||||
bitflags.workspace = true
|
bitflags.workspace = true
|
||||||
knuffel = "3.2.0"
|
knuffel = "3.2.0"
|
||||||
miette = "5.10.0"
|
miette = "5.10.0"
|
||||||
|
niri-ipc = { version = "0.1.1", path = "../niri-ipc" }
|
||||||
smithay = { workspace = true, features = ["backend_libinput"] }
|
smithay = { workspace = true, features = ["backend_libinput"] }
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracy-client.workspace = true
|
tracy-client.workspace = true
|
||||||
|
|||||||
+73
-65
@@ -6,6 +6,7 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use miette::{miette, Context, IntoDiagnostic, NarratableReportHandler};
|
use miette::{miette, Context, IntoDiagnostic, NarratableReportHandler};
|
||||||
|
use niri_ipc::{LayoutSwitchTarget, SizeChange};
|
||||||
use smithay::input::keyboard::keysyms::KEY_NoSymbol;
|
use smithay::input::keyboard::keysyms::KEY_NoSymbol;
|
||||||
use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE};
|
use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE};
|
||||||
use smithay::input::keyboard::{Keysym, XkbConfig};
|
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)]
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Quit,
|
Quit,
|
||||||
@@ -578,7 +580,7 @@ pub enum Action {
|
|||||||
SwitchPresetColumnWidth,
|
SwitchPresetColumnWidth,
|
||||||
MaximizeColumn,
|
MaximizeColumn,
|
||||||
SetColumnWidth(#[knuffel(argument, str)] SizeChange),
|
SetColumnWidth(#[knuffel(argument, str)] SizeChange),
|
||||||
SwitchLayout(#[knuffel(argument)] LayoutAction),
|
SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget),
|
||||||
ShowHotkeyOverlay,
|
ShowHotkeyOverlay,
|
||||||
MoveWorkspaceToMonitorLeft,
|
MoveWorkspaceToMonitorLeft,
|
||||||
MoveWorkspaceToMonitorRight,
|
MoveWorkspaceToMonitorRight,
|
||||||
@@ -586,18 +588,76 @@ pub enum Action {
|
|||||||
MoveWorkspaceToMonitorUp,
|
MoveWorkspaceToMonitorUp,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
impl From<niri_ipc::Action> for Action {
|
||||||
pub enum SizeChange {
|
fn from(value: niri_ipc::Action) -> Self {
|
||||||
SetFixed(i32),
|
match value {
|
||||||
SetProportion(f64),
|
niri_ipc::Action::Quit => Self::Quit,
|
||||||
AdjustFixed(i32),
|
niri_ipc::Action::PowerOffMonitors => Self::PowerOffMonitors,
|
||||||
AdjustProportion(f64),
|
niri_ipc::Action::Spawn { command } => Self::Spawn(command),
|
||||||
}
|
niri_ipc::Action::Screenshot => Self::Screenshot,
|
||||||
|
niri_ipc::Action::ScreenshotScreen => Self::ScreenshotScreen,
|
||||||
#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq)]
|
niri_ipc::Action::ScreenshotWindow => Self::ScreenshotWindow,
|
||||||
pub enum LayoutAction {
|
niri_ipc::Action::CloseWindow => Self::CloseWindow,
|
||||||
Next,
|
niri_ipc::Action::FullscreenWindow => Self::FullscreenWindow,
|
||||||
Prev,
|
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)]
|
#[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<Self, Self::Err> {
|
|
||||||
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 {
|
impl FromStr for AccelProfile {
|
||||||
type Err = miette::Error;
|
type Err = miette::Error;
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,8 @@ edition.workspace = true
|
|||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { workspace = true, optional = true }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
clap = ["dep:clap"]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -13,6 +14,8 @@ pub const SOCKET_PATH_ENV: &str = "NIRI_SOCKET";
|
|||||||
pub enum Request {
|
pub enum Request {
|
||||||
/// Request information about connected outputs.
|
/// Request information about connected outputs.
|
||||||
Outputs,
|
Outputs,
|
||||||
|
/// Perform an action.
|
||||||
|
Action(Action),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response from niri to client.
|
/// Response from niri to client.
|
||||||
@@ -24,6 +27,192 @@ pub enum Response {
|
|||||||
Outputs(HashMap<String, Output>),
|
Outputs(HashMap<String, Output>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<String>,
|
||||||
|
},
|
||||||
|
/// 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.
|
/// Connected output.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
@@ -53,3 +242,55 @@ pub struct Mode {
|
|||||||
/// Refresh rate in millihertz.
|
/// Refresh rate in millihertz.
|
||||||
pub refresh_rate: u32,
|
pub refresh_rate: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for SizeChange {
|
||||||
|
type Err = &'static str;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
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<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"next" => Ok(Self::Next),
|
||||||
|
"prev" => Ok(Self::Prev),
|
||||||
|
_ => Err(r#"invalid layout action, can be "next" or "prev""#),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -251,6 +251,9 @@ binds {
|
|||||||
//
|
//
|
||||||
// "Mod" is a special modifier equal to Super when running on a TTY, and to Alt
|
// "Mod" is a special modifier equal to Super when running on a TTY, and to Alt
|
||||||
// when running as a winit window.
|
// 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-?,
|
// Mod-Shift-/, which is usually the same as Mod-?,
|
||||||
// shows a list of important hotkeys.
|
// shows a list of important hotkeys.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::ffi::OsString;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
use niri_ipc::Action;
|
||||||
|
|
||||||
use crate::utils::version;
|
use crate::utils::version;
|
||||||
|
|
||||||
@@ -46,4 +47,9 @@ pub enum Sub {
|
|||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
/// List connected outputs.
|
/// List connected outputs.
|
||||||
Outputs,
|
Outputs,
|
||||||
|
/// Perform an action.
|
||||||
|
Action {
|
||||||
|
#[command(subcommand)]
|
||||||
|
action: Action,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -1,7 +1,8 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashSet;
|
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::{
|
use smithay::backend::input::{
|
||||||
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event,
|
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event,
|
||||||
GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _,
|
GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _,
|
||||||
@@ -273,6 +274,10 @@ impl State {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.do_action(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_action(&mut self, action: Action) {
|
||||||
if self.niri.is_locked() && !allowed_when_locked(&action) {
|
if self.niri.is_locked() && !allowed_when_locked(&action) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -377,8 +382,8 @@ impl State {
|
|||||||
self.niri.seat.get_keyboard().unwrap().with_xkb_state(
|
self.niri.seat.get_keyboard().unwrap().with_xkb_state(
|
||||||
self,
|
self,
|
||||||
|mut state| match action {
|
|mut state| match action {
|
||||||
LayoutAction::Next => state.cycle_next_layout(),
|
LayoutSwitchTarget::Next => state.cycle_next_layout(),
|
||||||
LayoutAction::Prev => state.cycle_prev_layout(),
|
LayoutSwitchTarget::Prev => state.cycle_prev_layout(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -19,8 +19,9 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
|||||||
let mut stream =
|
let mut stream =
|
||||||
UnixStream::connect(socket_path).context("error connecting to {socket_path}")?;
|
UnixStream::connect(socket_path).context("error connecting to {socket_path}")?;
|
||||||
|
|
||||||
let request = match msg {
|
let request = match &msg {
|
||||||
Msg::Outputs => Request::Outputs,
|
Msg::Outputs => Request::Outputs,
|
||||||
|
Msg::Action { action } => Request::Action(action.clone()),
|
||||||
};
|
};
|
||||||
let mut buf = serde_json::to_vec(&request).unwrap();
|
let mut buf = serde_json::to_vec(&request).unwrap();
|
||||||
stream
|
stream
|
||||||
@@ -35,6 +36,14 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
|||||||
.read_to_end(&mut buf)
|
.read_to_end(&mut buf)
|
||||||
.context("error reading IPC response")?;
|
.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")?;
|
let response = serde_json::from_slice(&buf).context("error parsing IPC response")?;
|
||||||
match msg {
|
match msg {
|
||||||
Msg::Outputs => {
|
Msg::Outputs => {
|
||||||
@@ -100,6 +109,7 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> {
|
|||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Msg::Action { .. } => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ pub struct IpcServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ClientCtx {
|
struct ClientCtx {
|
||||||
|
event_loop: LoopHandle<'static, State>,
|
||||||
ipc_outputs: Rc<RefCell<HashMap<String, niri_ipc::Output>>>,
|
ipc_outputs: Rc<RefCell<HashMap<String, niri_ipc::Output>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +86,7 @@ fn on_new_ipc_client(state: &mut State, stream: UnixStream) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let ctx = ClientCtx {
|
let ctx = ClientCtx {
|
||||||
|
event_loop: state.niri.event_loop.clone(),
|
||||||
ipc_outputs: state.backend.ipc_outputs(),
|
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();
|
let ipc_outputs = ctx.ipc_outputs.borrow().clone();
|
||||||
Response::Outputs(ipc_outputs)
|
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")?;
|
let buf = serde_json::to_vec(&response).context("error formatting response")?;
|
||||||
|
|||||||
+2
-1
@@ -34,7 +34,8 @@ use std::mem;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
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::solid::SolidColorRenderElement;
|
||||||
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
|
||||||
use smithay::backend::renderer::element::{AsRenderElements, Id};
|
use smithay::backend::renderer::element::{AsRenderElements, Id};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::cmp::min;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use niri_config::SizeChange;
|
use niri_ipc::SizeChange;
|
||||||
use smithay::backend::renderer::element::utils::{
|
use smithay::backend::renderer::element::utils::{
|
||||||
CropRenderElement, Relocate, RelocateRenderElement,
|
CropRenderElement, Relocate, RelocateRenderElement,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use std::iter::{self, zip};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
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::space::SpaceElement;
|
||||||
use smithay::desktop::{layer_map_for_output, Window};
|
use smithay::desktop::{layer_map_for_output, Window};
|
||||||
use smithay::output::Output;
|
use smithay::output::Output;
|
||||||
|
|||||||
Reference in New Issue
Block a user