mirror of
https://github.com/YaLTeR/niri.git
synced 2024-08-16 07:40:22 +03:00
Implement niri msg action
This commit is contained in:
parent
3486fa5536
commit
93e16a6582
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
@ -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<niri_ipc::Action> 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<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 {
|
||||
type Err = miette::Error;
|
||||
|
||||
|
@ -8,4 +8,8 @@ edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true, optional = true }
|
||||
serde.workspace = true
|
||||
|
||||
[features]
|
||||
clap = ["dep:clap"]
|
||||
|
@ -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<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.
|
||||
#[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<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
|
||||
// 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.
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
11
src/input.rs
11
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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -22,6 +22,7 @@ pub struct IpcServer {
|
||||
}
|
||||
|
||||
struct ClientCtx {
|
||||
event_loop: LoopHandle<'static, State>,
|
||||
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 {
|
||||
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")?;
|
||||
|
@ -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};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user