diff --git a/.lock b/.lock new file mode 100644 index 000000000..e69de29bb diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/crates.js b/crates.js new file mode 100644 index 000000000..428e15ef4 --- /dev/null +++ b/crates.js @@ -0,0 +1,2 @@ +window.ALL_CRATES = ["niri_ipc"]; +//{"start":21,"fragment_lengths":[10]} \ No newline at end of file diff --git a/help.html b/help.html new file mode 100644 index 000000000..3fe43c671 --- /dev/null +++ b/help.html @@ -0,0 +1 @@ +
pub enum Action {
+Show 83 variants
Quit {
+ skip_confirmation: bool,
+ },
+ PowerOffMonitors {},
+ PowerOnMonitors {},
+ Spawn {
+ command: Vec<String>,
+ },
+ DoScreenTransition {
+ delay_ms: Option<u16>,
+ },
+ Screenshot {},
+ ScreenshotScreen {},
+ ScreenshotWindow {
+ id: Option<u64>,
+ },
+ CloseWindow {
+ id: Option<u64>,
+ },
+ FullscreenWindow {
+ id: Option<u64>,
+ },
+ FocusWindow {
+ id: u64,
+ },
+ FocusColumnLeft {},
+ FocusColumnRight {},
+ FocusColumnFirst {},
+ FocusColumnLast {},
+ FocusColumnRightOrFirst {},
+ FocusColumnLeftOrLast {},
+ FocusWindowOrMonitorUp {},
+ FocusWindowOrMonitorDown {},
+ FocusColumnOrMonitorLeft {},
+ FocusColumnOrMonitorRight {},
+ FocusWindowDown {},
+ FocusWindowUp {},
+ FocusWindowDownOrColumnLeft {},
+ FocusWindowDownOrColumnRight {},
+ FocusWindowUpOrColumnLeft {},
+ FocusWindowUpOrColumnRight {},
+ FocusWindowOrWorkspaceDown {},
+ FocusWindowOrWorkspaceUp {},
+ MoveColumnLeft {},
+ MoveColumnRight {},
+ MoveColumnToFirst {},
+ MoveColumnToLast {},
+ MoveColumnLeftOrToMonitorLeft {},
+ MoveColumnRightOrToMonitorRight {},
+ MoveWindowDown {},
+ MoveWindowUp {},
+ MoveWindowDownOrToWorkspaceDown {},
+ MoveWindowUpOrToWorkspaceUp {},
+ ConsumeOrExpelWindowLeft {
+ id: Option<u64>,
+ },
+ ConsumeOrExpelWindowRight {
+ id: Option<u64>,
+ },
+ ConsumeWindowIntoColumn {},
+ ExpelWindowFromColumn {},
+ CenterColumn {},
+ FocusWorkspaceDown {},
+ FocusWorkspaceUp {},
+ FocusWorkspace {
+ reference: WorkspaceReferenceArg,
+ },
+ FocusWorkspacePrevious {},
+ MoveWindowToWorkspaceDown {},
+ MoveWindowToWorkspaceUp {},
+ MoveWindowToWorkspace {
+ window_id: Option<u64>,
+ reference: WorkspaceReferenceArg,
+ },
+ MoveColumnToWorkspaceDown {},
+ MoveColumnToWorkspaceUp {},
+ MoveColumnToWorkspace {
+ reference: WorkspaceReferenceArg,
+ },
+ MoveWorkspaceDown {},
+ MoveWorkspaceUp {},
+ FocusMonitorLeft {},
+ FocusMonitorRight {},
+ FocusMonitorDown {},
+ FocusMonitorUp {},
+ MoveWindowToMonitorLeft {},
+ MoveWindowToMonitorRight {},
+ MoveWindowToMonitorDown {},
+ MoveWindowToMonitorUp {},
+ MoveColumnToMonitorLeft {},
+ MoveColumnToMonitorRight {},
+ MoveColumnToMonitorDown {},
+ MoveColumnToMonitorUp {},
+ SetWindowHeight {
+ id: Option<u64>,
+ change: SizeChange,
+ },
+ ResetWindowHeight {
+ id: Option<u64>,
+ },
+ SwitchPresetColumnWidth {},
+ SwitchPresetWindowHeight {
+ id: Option<u64>,
+ },
+ MaximizeColumn {},
+ SetColumnWidth {
+ change: SizeChange,
+ },
+ SwitchLayout {
+ layout: LayoutSwitchTarget,
+ },
+ ShowHotkeyOverlay {},
+ MoveWorkspaceToMonitorLeft {},
+ MoveWorkspaceToMonitorRight {},
+ MoveWorkspaceToMonitorDown {},
+ MoveWorkspaceToMonitorUp {},
+ ToggleDebugTint {},
+ DebugToggleOpaqueRegions {},
+ DebugToggleDamage {},
+}
Actions that niri can perform.
+Exit niri.
+Power off all monitors via DPMS.
+Power on all monitors via DPMS.
+Spawn a command.
+Do a screen transition.
+Open the screenshot UI.
+Screenshot the focused screen.
+Screenshot a window.
+Close a window.
+Toggle fullscreen on a window.
+Focus a window by id.
+Focus the column to the left.
+Focus the column to the right.
+Focus the first column.
+Focus the last column.
+Focus the next column to the right, looping if at end.
+Focus the next column to the left, looping if at start.
+Focus the window or the monitor above.
+Focus the window or the monitor below.
+Focus the column or the monitor to the left.
+Focus the column or the monitor to the right.
+Focus the window below.
+Focus the window above.
+Focus the window below or the column to the left.
+Focus the window below or the column to the right.
+Focus the window above or the column to the left.
+Focus the window above or the column to the right.
+Focus the window or the workspace above.
+Focus the window or the workspace above.
+Move the focused column to the left.
+Move the focused column to the right.
+Move the focused column to the start of the workspace.
+Move the focused column to the end of the workspace.
+Move the focused column to the left or to the monitor to the left.
+Move the focused column to the right or to the monitor to the right.
+Move the focused window down in a column.
+Move the focused window up in a column.
+Move the focused window down in a column or to the workspace below.
+Move the focused window up in a column or to the workspace above.
+Consume or expel a window left.
+Consume or expel a window right.
+Consume the window to the right into the focused column.
+Expel the focused window from the column.
+Center the focused column on the screen.
+Focus the workspace below.
+Focus the workspace above.
+Focus a workspace by reference (index or name).
+reference: WorkspaceReferenceArg
Reference (index or name) of the workspace to focus.
+Focus the previous workspace.
+Move the focused window to the workspace below.
+Move the focused window to the workspace above.
+Move a window to a workspace.
+reference: WorkspaceReferenceArg
Reference (index or name) of the workspace to move the window to.
+Move the focused column to the workspace below.
+Move the focused column to the workspace above.
+Move the focused column to a workspace by reference (index or name).
+reference: WorkspaceReferenceArg
Reference (index or name) of the workspace to move the column to.
+Move the focused workspace down.
+Move the focused workspace up.
+Focus the monitor to the left.
+Focus the monitor to the right.
+Focus the monitor below.
+Focus the monitor above.
+Move the focused window to the monitor to the left.
+Move the focused window to the monitor to the right.
+Move the focused window to the monitor below.
+Move the focused window to the monitor above.
+Move the focused column to the monitor to the left.
+Move the focused column to the monitor to the right.
+Move the focused column to the monitor below.
+Move the focused column to the monitor above.
+Change the height of a window.
+change: SizeChange
How to change the height.
+Reset the height of a window back to automatic.
+Switch between preset column widths.
+Switch between preset window heights.
+Toggle the maximized state of the focused column.
+Change the width of the focused column.
+change: SizeChange
How to change the width.
+Switch between keyboard layouts.
+layout: LayoutSwitchTarget
Layout to switch to.
+Show the hotkey overlay.
+Move the focused workspace to the monitor to the left.
+Move the focused workspace to the monitor to the right.
+Move the focused workspace to the monitor below.
+Move the focused workspace to the monitor above.
+Toggle a debug tint on windows.
+Toggle visualization of render element opaque regions.
+Toggle visualization of output damage.
+clone_to_uninit
)pub enum Event {
+ WorkspacesChanged {
+ workspaces: Vec<Workspace>,
+ },
+ WorkspaceActivated {
+ id: u64,
+ focused: bool,
+ },
+ WorkspaceActiveWindowChanged {
+ workspace_id: u64,
+ active_window_id: Option<u64>,
+ },
+ WindowsChanged {
+ windows: Vec<Window>,
+ },
+ WindowOpenedOrChanged {
+ window: Window,
+ },
+ WindowClosed {
+ id: u64,
+ },
+ WindowFocusChanged {
+ id: Option<u64>,
+ },
+ KeyboardLayoutsChanged {
+ keyboard_layouts: KeyboardLayouts,
+ },
+ KeyboardLayoutSwitched {
+ idx: u8,
+ },
+}
A compositor event.
+The workspace configuration has changed.
+A workspace was activated on an output.
+This doesn’t always mean the workspace became focused, just that it’s now the active +workspace on its output. All other workspaces on the same output become inactive.
+An active window changed on a workspace.
+The window configuration has changed.
+A new toplevel window was opened, or an existing toplevel window changed.
+A toplevel window was closed.
+Window focus changed.
+All other windows are no longer focused.
+The configured keyboard layouts have changed.
+keyboard_layouts: KeyboardLayouts
The new keyboard layout configuration.
+The keyboard layout switched.
+clone_to_uninit
)pub enum Layer {
+ Background,
+ Bottom,
+ Top,
+ Overlay,
+}
A layer-shell layer.
+The background layer.
+The bottom layer.
+The top layer.
+The overlay layer.
+clone_to_uninit
)pub enum LayerSurfaceKeyboardInteractivity {
+ None,
+ Exclusive,
+ OnDemand,
+}
Keyboard interactivity modes for a layer-shell surface.
+Surface cannot receive keyboard focus.
+Surface receives keyboard focus whenever possible.
+Surface receives keyboard focus on demand, e.g. when clicked.
+source
. Read moreself
and other
values to be equal, and is used by ==
.clone_to_uninit
)pub enum LayoutSwitchTarget {
+ Next,
+ Prev,
+}
Layout to switch to.
+source
. Read moreclone_to_uninit
)pub enum ModeToSet {
+ Automatic,
+ Specific(ConfiguredMode),
+}
Output mode to set.
+clone_to_uninit
)pub enum OutputAction {
+ Off,
+ On,
+ Mode {
+ mode: ModeToSet,
+ },
+ Scale {
+ scale: ScaleToSet,
+ },
+ Transform {
+ transform: Transform,
+ },
+ Position {
+ position: PositionToSet,
+ },
+ Vrr {
+ vrr: VrrToSet,
+ },
+}
Output actions that niri can perform.
+Turn off the output.
+Turn on the output.
+Set the output mode.
+Set the output scale.
+scale: ScaleToSet
Scale factor to set, or “auto” for automatic selection.
+Set the output transform.
+Set the output position.
+position: PositionToSet
Position to set, or “auto” for automatic selection.
+Set the variable refresh rate mode.
+source
. Read moreclone_to_uninit
)pub enum OutputConfigChanged {
+ Applied,
+ OutputWasMissing,
+}
Output configuration change result.
+The target output was connected and the change was applied.
+The target output was not found, the change will be applied when it is connected.
+source
. Read moreclone_to_uninit
)pub enum PositionToSet {
+ Automatic,
+ Specific(ConfiguredPosition),
+}
Output position to set.
+Position the output automatically.
+Set a specific position.
+source
. Read moreclone_to_uninit
)pub enum Request {
+ Version,
+ Outputs,
+ Workspaces,
+ Windows,
+ Layers,
+ KeyboardLayouts,
+ FocusedOutput,
+ FocusedWindow,
+ Action(Action),
+ Output {
+ output: String,
+ action: OutputAction,
+ },
+ EventStream,
+ ReturnError,
+}
Request from client to niri.
+Request the version string for the running niri instance.
+Request information about connected outputs.
+Request information about workspaces.
+Request information about open windows.
+Request information about layer-shell surfaces.
+Request information about the configured keyboard layouts.
+Request information about the focused output.
+Request information about the focused window.
+Perform an action.
+Change output configuration temporarily.
+The configuration is changed temporarily and not saved into the config file. If the output +configuration subsequently changes in the config file, these temporary changes will be +forgotten.
+Start continuously receiving events from the compositor.
+The compositor should reply with Reply::Ok(Response::Handled)
, then continuously send
+Event
s, one per line.
The event stream will always give you the full current state up-front. For example, the
+first workspace-related event you will receive will be Event::WorkspacesChanged
+containing the full current workspaces state. You do not need to separately send
+Request::Workspaces
when using the event stream.
Where reasonable, event stream state updates are atomic, though this is not always the
+case. For example, a window may end up with a workspace id for a workspace that had already
+been removed. This can happen if the corresponding Event::WorkspacesChanged
arrives
+before the corresponding Event::WindowOpenedOrChanged
.
Respond with an error (for testing error handling).
+clone_to_uninit
)pub enum Response {
+ Handled,
+ Version(String),
+ Outputs(HashMap<String, Output>),
+ Workspaces(Vec<Workspace>),
+ Windows(Vec<Window>),
+ Layers(Vec<LayerSurface>),
+ KeyboardLayouts(KeyboardLayouts),
+ FocusedOutput(Option<Output>),
+ FocusedWindow(Option<Window>),
+ OutputConfigChanged(OutputConfigChanged),
+}
Successful response from niri to client.
+A request that does not need a response was handled successfully.
+The version string for the running niri instance.
+Information about connected outputs.
+Map from output name to output info.
+Information about workspaces.
+Information about open windows.
+Information about layer-shell surfaces.
+Information about the keyboard layout.
+Information about the focused output.
+Information about the focused window.
+Output configuration change result.
+clone_to_uninit
)pub enum ScaleToSet {
+ Automatic,
+ Specific(f64),
+}
Output scale to set.
+source
. Read moreclone_to_uninit
)pub enum SizeChange {
+ SetFixed(i32),
+ SetProportion(f64),
+ AdjustFixed(i32),
+ AdjustProportion(f64),
+}
Change in window or column size.
+Set the size in logical pixels.
+Set the size as a proportion of the working area.
+Add or subtract to the current size in logical pixels.
+Add or subtract to the current size as a proportion of the working area.
+source
. Read moreclone_to_uninit
)pub enum Transform {
+ Normal,
+ _90,
+ _180,
+ _270,
+ Flipped,
+ Flipped90,
+ Flipped180,
+ Flipped270,
+}
Output transform, which goes counter-clockwise.
+Untransformed.
+Rotated by 90°.
+Rotated by 180°.
+Rotated by 270°.
+Flipped horizontally.
+Rotated by 90° and flipped horizontally.
+Flipped vertically.
+Rotated by 270° and flipped horizontally.
+clone_to_uninit
)pub enum WorkspaceReferenceArg {
+ Id(u64),
+ Index(u8),
+ Name(String),
+}
Workspace reference (id, index or name) to operate on.
+Id of the workspace.
+Index of the workspace.
+Name of the workspace.
+source
. Read moreclone_to_uninit
)Types for communicating with niri via IPC.
+After connecting to the niri socket, you can send a single Request
and receive a single
+Reply
, which is a Result
wrapping a Response
. If you requested an event stream, you
+can keep reading Event
s from the socket after the response.
You can use the socket::Socket
helper if you’re fine with blocking communication. However,
+it is a fairly simple helper, so if you need async, or if you’re using a different language,
+you are encouraged to communicate with the socket manually.
socket::SOCKET_PATH_ENV
($NIRI_SOCKET
).Request
on a single line. You can follow
+up with a line break and a flush, or just flush and shutdown the write end of the socket.Reply
.Event
s,
+on a single line each.This crate follows the niri version. It is not API-stable in terms of the Rust semver. In +particular, expect new struct fields and enum variants to be added in patch version bumps.
+Use an exact version requirement to avoid breaking changes:
+[dependencies]
+niri-ipc = "=0.1.10"
+
This crate defines the following features:
+json-schema
: derives the schemars JsonSchema
trait for
+the types.clap
: derives the clap CLI parsing traits for some types. Used internally by niri itself.pub const SOCKET_PATH_ENV: &str = "NIRI_SOCKET";
Name of the environment variable containing the niri IPC socket path.
+pub struct Socket { /* private fields */ }
Helper for blocking communication over the niri socket.
+This struct is used to communicate with the niri IPC server. It handles the socket connection +and serialization/deserialization of messages.
+Connects to the default niri IPC socket.
+This is equivalent to calling Self::connect_to
with the path taken from the
+SOCKET_PATH_ENV
environment variable.
Connects to the niri IPC socket at the given path.
+Sends a request to niri and returns the response.
+Return values:
+Ok(Ok(response))
: successful Response
from niriOk(Err(message))
: error message from niriErr(error)
: error communicating with niriThis method also returns a blocking function that you can call to keep reading Event
s
+after requesting an EventStream
. This function is not useful
+otherwise.
Helpers for keeping track of the event stream state.
+EventStreamState
using Default::default()
, or any individual state part if
+you only care about part of the state.Event
to EventStreamStatePart::apply
on your state.pub struct EventStreamState {
+ pub workspaces: WorkspacesState,
+ pub windows: WindowsState,
+ pub keyboard_layouts: KeyboardLayoutsState,
+}
The full state communicated over the event stream.
+Different parts of the state are not guaranteed to be consistent across every single event
+sent by niri. For example, you may receive the first Event::WindowOpenedOrChanged
for a
+just-opened window after an Event::WorkspaceActiveWindowChanged
for that window. Between
+these two events, the workspace active window id refers to a window that does not yet exist in
+the windows state part.
workspaces: WorkspacesState
State of workspaces.
+windows: WindowsState
State of workspaces.
+keyboard_layouts: KeyboardLayoutsState
State of the keyboard layouts.
+pub struct KeyboardLayoutsState {
+ pub keyboard_layouts: Option<KeyboardLayouts>,
+}
The keyboard layout state communicated over the event stream.
+keyboard_layouts: Option<KeyboardLayouts>
Configured keyboard layouts.
+pub struct WindowsState {
+ pub windows: HashMap<u64, Window>,
+}
The windows state communicated over the event stream.
+windows: HashMap<u64, Window>
Map from a window id to the window.
+pub struct WorkspacesState {
+ pub workspaces: HashMap<u64, Workspace>,
+}
The workspaces state communicated over the event stream.
+workspaces: HashMap<u64, Workspace>
Map from a workspace id to the workspace.
+pub trait EventStreamStatePart {
+ // Required methods
+ fn replicate(&self) -> Vec<Event>;
+ fn apply(&mut self, event: Event) -> Option<Event>;
+}
Part of the state communicated via the event stream.
+pub struct ConfiguredMode {
+ pub width: u16,
+ pub height: u16,
+ pub refresh: Option<f64>,
+}
Output mode as set in the config file.
+width: u16
Width in physical pixels.
+height: u16
Height in physical pixels.
+refresh: Option<f64>
Refresh rate.
+source
. Read moreclone_to_uninit
)pub struct ConfiguredPosition {
+ pub x: i32,
+ pub y: i32,
+}
Output position as set in the config file.
+x: i32
Logical X position.
+y: i32
Logical Y position.
+source
. Read moreclone_to_uninit
)pub struct KeyboardLayouts {
+ pub names: Vec<String>,
+ pub current_idx: u8,
+}
Configured keyboard layouts.
+names: Vec<String>
XKB names of the configured layouts.
+current_idx: u8
Index of the currently active layout in names
.
source
. Read moreclone_to_uninit
)pub struct LayerSurface {
+ pub namespace: String,
+ pub output: String,
+ pub layer: Layer,
+ pub keyboard_interactivity: LayerSurfaceKeyboardInteractivity,
+}
A layer-shell surface.
+namespace: String
Namespace provided by the layer-shell client.
+output: String
Name of the output the surface is on.
+layer: Layer
Layer that the surface is on.
+keyboard_interactivity: LayerSurfaceKeyboardInteractivity
The surface’s keyboard interactivity mode.
+source
. Read moreclone_to_uninit
)pub struct LogicalOutput {
+ pub x: i32,
+ pub y: i32,
+ pub width: u32,
+ pub height: u32,
+ pub scale: f64,
+ pub transform: Transform,
+}
Logical output in the compositor’s coordinate space.
+x: i32
Logical X position.
+y: i32
Logical Y position.
+width: u32
Width in logical pixels.
+height: u32
Height in logical pixels.
+scale: f64
Scale factor.
+transform: Transform
Transform.
+source
. Read moreclone_to_uninit
)pub struct Mode {
+ pub width: u16,
+ pub height: u16,
+ pub refresh_rate: u32,
+ pub is_preferred: bool,
+}
Output mode.
+width: u16
Width in physical pixels.
+height: u16
Height in physical pixels.
+refresh_rate: u32
Refresh rate in millihertz.
+is_preferred: bool
Whether this mode is preferred by the monitor.
+clone_to_uninit
)pub struct Output {
+ pub name: String,
+ pub make: String,
+ pub model: String,
+ pub serial: Option<String>,
+ pub physical_size: Option<(u32, u32)>,
+ pub modes: Vec<Mode>,
+ pub current_mode: Option<usize>,
+ pub vrr_supported: bool,
+ pub vrr_enabled: bool,
+ pub logical: Option<LogicalOutput>,
+}
Connected output.
+name: String
Name of the output.
+make: String
Textual description of the manufacturer.
+model: String
Textual description of the model.
+serial: Option<String>
Serial of the output, if known.
+physical_size: Option<(u32, u32)>
Physical width and height of the output in millimeters, if known.
+modes: Vec<Mode>
Available modes for the output.
+current_mode: Option<usize>
Index of the current mode in Self::modes
.
None
if the output is disabled.
vrr_supported: bool
Whether the output supports variable refresh rate.
+vrr_enabled: bool
Whether variable refresh rate is enabled on the output.
+logical: Option<LogicalOutput>
Logical output information.
+None
if the output is not mapped to any logical output (for example, if it is disabled).
clone_to_uninit
)pub struct VrrToSet {
+ pub vrr: bool,
+ pub on_demand: bool,
+}
Output VRR to set.
+vrr: bool
Whether to enable variable refresh rate.
+on_demand: bool
Only enable when the output shows a window matching the variable-refresh-rate window rule.
+clone_to_uninit
)pub struct Window {
+ pub id: u64,
+ pub title: Option<String>,
+ pub app_id: Option<String>,
+ pub pid: Option<i32>,
+ pub workspace_id: Option<u64>,
+ pub is_focused: bool,
+}
Toplevel window.
+id: u64
Unique id of this window.
+This id remains constant while this window is open.
+Do not assume that window ids will always increase without wrapping, or start at 1. That is +an implementation detail subject to change. For example, ids may change to be randomly +generated for each new window.
+title: Option<String>
Title, if set.
+app_id: Option<String>
Application ID, if set.
+pid: Option<i32>
Process ID that created the Wayland connection for this window, if known.
+Currently, windows created by xdg-desktop-portal-gnome will have a None
PID, but this may
+change in the future.
workspace_id: Option<u64>
Id of the workspace this window is on, if any.
+is_focused: bool
Whether this window is currently focused.
+There can be either one focused window or zero (e.g. when a layer-shell surface has focus).
+clone_to_uninit
)pub struct Workspace {
+ pub id: u64,
+ pub idx: u8,
+ pub name: Option<String>,
+ pub output: Option<String>,
+ pub is_active: bool,
+ pub is_focused: bool,
+ pub active_window_id: Option<u64>,
+}
A workspace.
+id: u64
Unique id of this workspace.
+This id remains constant regardless of the workspace moving around and across monitors.
+Do not assume that workspace ids will always increase without wrapping, or start at 1. That +is an implementation detail subject to change. For example, ids may change to be randomly +generated for each new workspace.
+idx: u8
Index of the workspace on its monitor.
+This is the same index you can use for requests like niri msg action focus-workspace
.
This index will change as you move and re-order workspace. It is merely the workspace’s +current position on its monitor. Workspaces on different monitors can have the same index.
+If you need a unique workspace id that doesn’t change, see Self::id
.
name: Option<String>
Optional name of the workspace.
+output: Option<String>
Name of the output that the workspace is on.
+Can be None
if no outputs are currently connected.
is_active: bool
Whether the workspace is currently active on its output.
+Every output has one active workspace, the one that is currently visible on that output.
+is_focused: bool
Whether the workspace is currently focused.
+There’s only one focused workspace across all outputs.
+active_window_id: Option<u64>
Id of the active window on this workspace, if any.
+clone_to_uninit
)pub type Reply = Result<Response, String>;
Reply from niri to client.
+Every request gets one reply.
+Reply::Err
.Reply::Ok(Response::Handled)
. Kind of like an Ok(())
.Reply::Ok(response)
with one of the other Response
variants.enum Reply {
+ Ok(Response),
+ Err(String),
+}
names
.\nIndex of the current mode in Self::modes
.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nHeight in physical pixels.\nHeight in physical pixels.\nHeight in logical pixels.\nUnique id of this window.\nUnique id of this workspace.\nIndex of the workspace on its monitor.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nWhether the workspace is currently active on its output.\nWhether this window is currently focused.\nWhether the workspace is currently focused.\nWhether this mode is preferred by the monitor.\nThe surface’s keyboard interactivity mode.\nLayer that the surface is on.\nLogical output information.\nTextual description of the manufacturer.\nTextual description of the model.\nAvailable modes for the output.\nName of the output.\nOptional name of the workspace.\nXKB names of the configured layouts.\nNamespace provided by the layer-shell client.\nOnly enable when the output shows a window matching the …\nName of the output that the workspace is on.\nName of the output the surface is on.\nPhysical width and height of the output in millimeters, if …\nProcess ID that created the Wayland connection for this …\nRefresh rate.\nRefresh rate in millihertz.\nScale factor.\nSerial of the output, if known.\nHelper for blocking communication over the niri socket.\nHelpers for keeping track of the event stream state.\nTitle, if set.\nTransform.\nWhether to enable variable refresh rate.\nWhether variable refresh rate is enabled on the output.\nWhether the output supports variable refresh rate.\nWidth in physical pixels.\nWidth in physical pixels.\nWidth in logical pixels.\nId of the workspace this window is on, if any.\nLogical X position.\nLogical X position.\nLogical Y position.\nLogical Y position.\nHow to change the height.\nHow to change the width.\nCommand to spawn.\nDelay in milliseconds for the screen to freeze before …\nId of the window to screenshot.\nId of the window to close.\nId of the window to toggle fullscreen of.\nId of the window to focus.\nId of the window to consume or expel.\nId of the window to consume or expel.\nId of the window whose height to set.\nId of the window whose height to reset.\nId of the window whose height to switch.\nLayout to switch to.\nReference (index or name) of the workspace to focus.\nReference (index or name) of the workspace to move the …\nReference (index or name) of the workspace to move the …\nSkip the “Press Enter to confirm” prompt.\nId of the window to move.\nId of the new active window, if any.\nWhether this workspace also became focused.\nId of the newly active workspace.\nId of the removed window.\nId of the newly focused window, or None
if no window is …\nIndex of the newly active layout.\nThe new keyboard layout configuration.\nThe new or updated window.\nThe new window configuration.\nId of the workspace on which the active window changed.\nThe new workspace configuration.\nMode to set, or “auto” for automatic selection.\nPosition to set, or “auto” for automatic selection.\nScale factor to set, or “auto” for automatic selection.\nTransform to set, counter-clockwise.\nVariable refresh rate mode to set.\nConfiguration to apply.\nOutput name.\nName of the environment variable containing the niri IPC …\nHelper for blocking communication over the niri socket.\nConnects to the default niri IPC socket.\nConnects to the niri IPC socket at the given path.\nReturns the argument unchanged.\nCalls U::from(self)
.\nSends a request to niri and returns the response.\nThe full state communicated over the event stream.\nPart of the state communicated via the event stream.\nThe keyboard layout state communicated over the event …\nThe windows state communicated over the event stream.\nThe workspaces state communicated over the event stream.\nApplies the event to this state.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nCalls U::from(self)
.\nState of the keyboard layouts.\nConfigured keyboard layouts.\nReturns a sequence of events that replicates this state …\nState of workspaces.\nMap from a window id to the window.\nState of workspaces.\nMap from a workspace id to the workspace.")
\ No newline at end of file
diff --git a/settings.html b/settings.html
new file mode 100644
index 000000000..5ff59f9e6
--- /dev/null
+++ b/settings.html
@@ -0,0 +1 @@
+1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +
//! Types for communicating with niri via IPC.
+//!
+//! After connecting to the niri socket, you can send a single [`Request`] and receive a single
+//! [`Reply`], which is a `Result` wrapping a [`Response`]. If you requested an event stream, you
+//! can keep reading [`Event`]s from the socket after the response.
+//!
+//! You can use the [`socket::Socket`] helper if you're fine with blocking communication. However,
+//! it is a fairly simple helper, so if you need async, or if you're using a different language,
+//! you are encouraged to communicate with the socket manually.
+//!
+//! 1. Read the socket filesystem path from [`socket::SOCKET_PATH_ENV`] (`$NIRI_SOCKET`).
+//! 2. Connect to the socket and write a JSON-formatted [`Request`] on a single line. You can follow
+//! up with a line break and a flush, or just flush and shutdown the write end of the socket.
+//! 3. Niri will respond with a single line JSON-formatted [`Reply`].
+//! 4. If you requested an event stream, niri will keep responding with JSON-formatted [`Event`]s,
+//! on a single line each.
+//!
+//! ## Backwards compatibility
+//!
+//! This crate follows the niri version. It is **not** API-stable in terms of the Rust semver. In
+//! particular, expect new struct fields and enum variants to be added in patch version bumps.
+//!
+//! Use an exact version requirement to avoid breaking changes:
+//!
+//! ```toml
+//! [dependencies]
+//! niri-ipc = "=0.1.10"
+//! ```
+//!
+//! ## Features
+//!
+//! This crate defines the following features:
+//! - `json-schema`: derives the [schemars](https://lib.rs/crates/schemars) `JsonSchema` trait for
+//! the types.
+//! - `clap`: derives the clap CLI parsing traits for some types. Used internally by niri itself.
+#![warn(missing_docs)]
+
+use std::collections::HashMap;
+use std::str::FromStr;
+
+use serde::{Deserialize, Serialize};
+
+pub mod socket;
+pub mod state;
+
+/// Request from client to niri.
+#[derive(Debug, Serialize, Deserialize, Clone)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum Request {
+ /// Request the version string for the running niri instance.
+ Version,
+ /// Request information about connected outputs.
+ Outputs,
+ /// Request information about workspaces.
+ 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.
+ FocusedOutput,
+ /// Request information about the focused window.
+ FocusedWindow,
+ /// Perform an action.
+ Action(Action),
+ /// Change output configuration temporarily.
+ ///
+ /// The configuration is changed temporarily and not saved into the config file. If the output
+ /// configuration subsequently changes in the config file, these temporary changes will be
+ /// forgotten.
+ Output {
+ /// Output name.
+ output: String,
+ /// Configuration to apply.
+ action: OutputAction,
+ },
+ /// Start continuously receiving events from the compositor.
+ ///
+ /// The compositor should reply with `Reply::Ok(Response::Handled)`, then continuously send
+ /// [`Event`]s, one per line.
+ ///
+ /// The event stream will always give you the full current state up-front. For example, the
+ /// first workspace-related event you will receive will be [`Event::WorkspacesChanged`]
+ /// containing the full current workspaces state. You *do not* need to separately send
+ /// [`Request::Workspaces`] when using the event stream.
+ ///
+ /// Where reasonable, event stream state updates are atomic, though this is not always the
+ /// case. For example, a window may end up with a workspace id for a workspace that had already
+ /// been removed. This can happen if the corresponding [`Event::WorkspacesChanged`] arrives
+ /// before the corresponding [`Event::WindowOpenedOrChanged`].
+ EventStream,
+ /// Respond with an error (for testing error handling).
+ ReturnError,
+}
+
+/// Reply from niri to client.
+///
+/// Every request gets one reply.
+///
+/// * If an error had occurred, it will be an `Reply::Err`.
+/// * If the request does not need any particular response, it will be
+/// `Reply::Ok(Response::Handled)`. Kind of like an `Ok(())`.
+/// * Otherwise, it will be `Reply::Ok(response)` with one of the other [`Response`] variants.
+pub type Reply = Result<Response, String>;
+
+/// Successful response from niri to client.
+#[derive(Debug, Serialize, Deserialize, Clone)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum Response {
+ /// A request that does not need a response was handled successfully.
+ Handled,
+ /// The version string for the running niri instance.
+ Version(String),
+ /// Information about connected outputs.
+ ///
+ /// Map from output name to output info.
+ Outputs(HashMap<String, Output>),
+ /// Information about workspaces.
+ 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.
+ FocusedOutput(Option<Output>),
+ /// Information about the focused window.
+ FocusedWindow(Option<Window>),
+ /// Output configuration change result.
+ OutputConfigChanged(OutputConfigChanged),
+}
+
+/// 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)]
+#[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"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum Action {
+ /// Exit niri.
+ Quit {
+ /// Skip the "Press Enter to confirm" prompt.
+ #[cfg_attr(feature = "clap", arg(short, long))]
+ skip_confirmation: bool,
+ },
+ /// Power off all monitors via DPMS.
+ PowerOffMonitors {},
+ /// Power on all monitors via DPMS.
+ PowerOnMonitors {},
+ /// Spawn a command.
+ Spawn {
+ /// Command to spawn.
+ #[cfg_attr(feature = "clap", arg(last = true, required = true))]
+ command: Vec<String>,
+ },
+ /// Do a screen transition.
+ DoScreenTransition {
+ /// Delay in milliseconds for the screen to freeze before starting the transition.
+ #[cfg_attr(feature = "clap", arg(short, long))]
+ delay_ms: Option<u16>,
+ },
+ /// Open the screenshot UI.
+ Screenshot {},
+ /// Screenshot the focused screen.
+ ScreenshotScreen {},
+ /// Screenshot a window.
+ #[cfg_attr(feature = "clap", clap(about = "Screenshot the focused window"))]
+ ScreenshotWindow {
+ /// Id of the window to screenshot.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+ },
+ /// Close a window.
+ #[cfg_attr(feature = "clap", clap(about = "Close the focused window"))]
+ CloseWindow {
+ /// Id of the window to close.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+ },
+ /// Toggle fullscreen on a window.
+ #[cfg_attr(
+ feature = "clap",
+ clap(about = "Toggle fullscreen on the focused window")
+ )]
+ FullscreenWindow {
+ /// Id of the window to toggle fullscreen of.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+ },
+ /// Focus a window by id.
+ FocusWindow {
+ /// Id of the window to focus.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: u64,
+ },
+ /// 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 next column to the right, looping if at end.
+ FocusColumnRightOrFirst {},
+ /// Focus the next column to the left, looping if at start.
+ FocusColumnLeftOrLast {},
+ /// Focus the window or the monitor above.
+ FocusWindowOrMonitorUp {},
+ /// Focus the window or the monitor below.
+ FocusWindowOrMonitorDown {},
+ /// Focus the column or the monitor to the left.
+ FocusColumnOrMonitorLeft {},
+ /// Focus the column or the monitor to the right.
+ FocusColumnOrMonitorRight {},
+ /// Focus the window below.
+ FocusWindowDown {},
+ /// Focus the window above.
+ FocusWindowUp {},
+ /// Focus the window below or the column to the left.
+ FocusWindowDownOrColumnLeft {},
+ /// Focus the window below or the column to the right.
+ FocusWindowDownOrColumnRight {},
+ /// Focus the window above or the column to the left.
+ FocusWindowUpOrColumnLeft {},
+ /// Focus the window above or the column to the right.
+ FocusWindowUpOrColumnRight {},
+ /// 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 column to the left or to the monitor to the left.
+ MoveColumnLeftOrToMonitorLeft {},
+ /// Move the focused column to the right or to the monitor to the right.
+ MoveColumnRightOrToMonitorRight {},
+ /// 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 a window left.
+ #[cfg_attr(
+ feature = "clap",
+ clap(about = "Consume or expel the focused window left")
+ )]
+ ConsumeOrExpelWindowLeft {
+ /// Id of the window to consume or expel.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+ },
+ /// Consume or expel a window right.
+ #[cfg_attr(
+ feature = "clap",
+ clap(about = "Consume or expel the focused window right")
+ )]
+ ConsumeOrExpelWindowRight {
+ /// Id of the window to consume or expel.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+ },
+ /// 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 reference (index or name).
+ FocusWorkspace {
+ /// Reference (index or name) of the workspace to focus.
+ #[cfg_attr(feature = "clap", arg())]
+ reference: WorkspaceReferenceArg,
+ },
+ /// Focus the previous workspace.
+ FocusWorkspacePrevious {},
+ /// Move the focused window to the workspace below.
+ MoveWindowToWorkspaceDown {},
+ /// Move the focused window to the workspace above.
+ MoveWindowToWorkspaceUp {},
+ /// Move a window to a workspace.
+ #[cfg_attr(
+ feature = "clap",
+ clap(about = "Move the focused window to a workspace by reference (index or name)")
+ )]
+ MoveWindowToWorkspace {
+ /// Id of the window to move.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ window_id: Option<u64>,
+
+ /// Reference (index or name) of the workspace to move the window to.
+ #[cfg_attr(feature = "clap", arg())]
+ reference: WorkspaceReferenceArg,
+ },
+ /// 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 reference (index or name).
+ MoveColumnToWorkspace {
+ /// Reference (index or name) of the workspace to move the column to.
+ #[cfg_attr(feature = "clap", arg())]
+ reference: WorkspaceReferenceArg,
+ },
+ /// 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 a window.
+ #[cfg_attr(
+ feature = "clap",
+ clap(about = "Change the height of the focused window")
+ )]
+ SetWindowHeight {
+ /// Id of the window whose height to set.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+
+ /// How to change the height.
+ #[cfg_attr(feature = "clap", arg())]
+ change: SizeChange,
+ },
+ /// Reset the height of a window back to automatic.
+ #[cfg_attr(
+ feature = "clap",
+ clap(about = "Reset the height of the focused window back to automatic")
+ )]
+ ResetWindowHeight {
+ /// Id of the window whose height to reset.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+ },
+ /// Switch between preset column widths.
+ SwitchPresetColumnWidth {},
+ /// Switch between preset window heights.
+ SwitchPresetWindowHeight {
+ /// Id of the window whose height to switch.
+ ///
+ /// If `None`, uses the focused window.
+ #[cfg_attr(feature = "clap", arg(long))]
+ id: Option<u64>,
+ },
+ /// 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 {},
+ /// Toggle visualization of render element opaque regions.
+ DebugToggleOpaqueRegions {},
+ /// Toggle visualization of output damage.
+ DebugToggleDamage {},
+}
+
+/// Change in window or column size.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+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),
+}
+
+/// Workspace reference (id, index or name) to operate on.
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum WorkspaceReferenceArg {
+ /// Id of the workspace.
+ Id(u64),
+ /// Index of the workspace.
+ Index(u8),
+ /// Name of the workspace.
+ Name(String),
+}
+
+/// Layout to switch to.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum LayoutSwitchTarget {
+ /// The next configured layout.
+ Next,
+ /// The previous configured layout.
+ Prev,
+}
+
+/// Output actions that niri can perform.
+// Variants in this enum should match the spelling of the ones in niri-config. Most thigs from
+// niri-config should be present here.
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[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"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum OutputAction {
+ /// Turn off the output.
+ Off,
+ /// Turn on the output.
+ On,
+ /// Set the output mode.
+ Mode {
+ /// Mode to set, or "auto" for automatic selection.
+ ///
+ /// Run `niri msg outputs` to see the available modes.
+ #[cfg_attr(feature = "clap", arg())]
+ mode: ModeToSet,
+ },
+ /// Set the output scale.
+ Scale {
+ /// Scale factor to set, or "auto" for automatic selection.
+ #[cfg_attr(feature = "clap", arg())]
+ scale: ScaleToSet,
+ },
+ /// Set the output transform.
+ Transform {
+ /// Transform to set, counter-clockwise.
+ #[cfg_attr(feature = "clap", arg())]
+ transform: Transform,
+ },
+ /// Set the output position.
+ Position {
+ /// Position to set, or "auto" for automatic selection.
+ #[cfg_attr(feature = "clap", command(subcommand))]
+ position: PositionToSet,
+ },
+ /// Set the variable refresh rate mode.
+ Vrr {
+ /// Variable refresh rate mode to set.
+ #[cfg_attr(feature = "clap", command(flatten))]
+ vrr: VrrToSet,
+ },
+}
+
+/// Output mode to set.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum ModeToSet {
+ /// Niri will pick the mode automatically.
+ Automatic,
+ /// Specific mode.
+ Specific(ConfiguredMode),
+}
+
+/// Output mode as set in the config file.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct ConfiguredMode {
+ /// Width in physical pixels.
+ pub width: u16,
+ /// Height in physical pixels.
+ pub height: u16,
+ /// Refresh rate.
+ pub refresh: Option<f64>,
+}
+
+/// Output scale to set.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum ScaleToSet {
+ /// Niri will pick the scale automatically.
+ Automatic,
+ /// Specific scale.
+ Specific(f64),
+}
+
+/// Output position to set.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "clap", derive(clap::Subcommand))]
+#[cfg_attr(feature = "clap", command(subcommand_value_name = "POSITION"))]
+#[cfg_attr(feature = "clap", command(subcommand_help_heading = "Position Values"))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum PositionToSet {
+ /// Position the output automatically.
+ #[cfg_attr(feature = "clap", command(name = "auto"))]
+ Automatic,
+ /// Set a specific position.
+ #[cfg_attr(feature = "clap", command(name = "set"))]
+ Specific(ConfiguredPosition),
+}
+
+/// Output position as set in the config file.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "clap", derive(clap::Args))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct ConfiguredPosition {
+ /// Logical X position.
+ pub x: i32,
+ /// Logical Y position.
+ pub y: i32,
+}
+
+/// Output VRR to set.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "clap", derive(clap::Args))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct VrrToSet {
+ /// Whether to enable variable refresh rate.
+ #[cfg_attr(
+ feature = "clap",
+ arg(
+ value_name = "ON|OFF",
+ action = clap::ArgAction::Set,
+ value_parser = clap::builder::BoolishValueParser::new(),
+ hide_possible_values = true,
+ ),
+ )]
+ pub vrr: bool,
+ /// Only enable when the output shows a window matching the variable-refresh-rate window rule.
+ #[cfg_attr(feature = "clap", arg(long))]
+ pub on_demand: bool,
+}
+
+/// Connected output.
+#[derive(Debug, Serialize, Deserialize, Clone)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct Output {
+ /// Name of the output.
+ pub name: String,
+ /// Textual description of the manufacturer.
+ pub make: String,
+ /// Textual description of the model.
+ pub model: String,
+ /// Serial of the output, if known.
+ pub serial: Option<String>,
+ /// Physical width and height of the output in millimeters, if known.
+ pub physical_size: Option<(u32, u32)>,
+ /// Available modes for the output.
+ pub modes: Vec<Mode>,
+ /// Index of the current mode in [`Self::modes`].
+ ///
+ /// `None` if the output is disabled.
+ pub current_mode: Option<usize>,
+ /// Whether the output supports variable refresh rate.
+ pub vrr_supported: bool,
+ /// Whether variable refresh rate is enabled on the output.
+ pub vrr_enabled: bool,
+ /// Logical output information.
+ ///
+ /// `None` if the output is not mapped to any logical output (for example, if it is disabled).
+ pub logical: Option<LogicalOutput>,
+}
+
+/// Output mode.
+#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct Mode {
+ /// Width in physical pixels.
+ pub width: u16,
+ /// Height in physical pixels.
+ pub height: u16,
+ /// Refresh rate in millihertz.
+ pub refresh_rate: u32,
+ /// Whether this mode is preferred by the monitor.
+ pub is_preferred: bool,
+}
+
+/// Logical output in the compositor's coordinate space.
+#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct LogicalOutput {
+ /// Logical X position.
+ pub x: i32,
+ /// Logical Y position.
+ pub y: i32,
+ /// Width in logical pixels.
+ pub width: u32,
+ /// Height in logical pixels.
+ pub height: u32,
+ /// Scale factor.
+ pub scale: f64,
+ /// Transform.
+ pub transform: Transform,
+}
+
+/// Output transform, which goes counter-clockwise.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum Transform {
+ /// Untransformed.
+ Normal,
+ /// Rotated by 90°.
+ #[serde(rename = "90")]
+ _90,
+ /// Rotated by 180°.
+ #[serde(rename = "180")]
+ _180,
+ /// Rotated by 270°.
+ #[serde(rename = "270")]
+ _270,
+ /// Flipped horizontally.
+ Flipped,
+ /// Rotated by 90° and flipped horizontally.
+ #[cfg_attr(feature = "clap", value(name("flipped-90")))]
+ Flipped90,
+ /// Flipped vertically.
+ #[cfg_attr(feature = "clap", value(name("flipped-180")))]
+ Flipped180,
+ /// Rotated by 270° and flipped horizontally.
+ #[cfg_attr(feature = "clap", value(name("flipped-270")))]
+ Flipped270,
+}
+
+/// Toplevel window.
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct Window {
+ /// Unique id of this window.
+ ///
+ /// This id remains constant while this window is open.
+ ///
+ /// Do not assume that window ids will always increase without wrapping, or start at 1. That is
+ /// an implementation detail subject to change. For example, ids may change to be randomly
+ /// generated for each new window.
+ pub id: u64,
+ /// Title, if set.
+ pub title: Option<String>,
+ /// Application ID, if set.
+ pub app_id: Option<String>,
+ /// Process ID that created the Wayland connection for this window, if known.
+ ///
+ /// Currently, windows created by xdg-desktop-portal-gnome will have a `None` PID, but this may
+ /// change in the future.
+ pub pid: Option<i32>,
+ /// Id of the workspace this window is on, if any.
+ pub workspace_id: Option<u64>,
+ /// Whether this window is currently focused.
+ ///
+ /// There can be either one focused window or zero (e.g. when a layer-shell surface has focus).
+ pub is_focused: bool,
+}
+
+/// Output configuration change result.
+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub enum OutputConfigChanged {
+ /// The target output was connected and the change was applied.
+ Applied,
+ /// The target output was not found, the change will be applied when it is connected.
+ OutputWasMissing,
+}
+
+/// A workspace.
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct Workspace {
+ /// Unique id of this workspace.
+ ///
+ /// This id remains constant regardless of the workspace moving around and across monitors.
+ ///
+ /// Do not assume that workspace ids will always increase without wrapping, or start at 1. That
+ /// is an implementation detail subject to change. For example, ids may change to be randomly
+ /// generated for each new workspace.
+ pub id: u64,
+ /// Index of the workspace on its monitor.
+ ///
+ /// This is the same index you can use for requests like `niri msg action focus-workspace`.
+ ///
+ /// This index *will change* as you move and re-order workspace. It is merely the workspace's
+ /// current position on its monitor. Workspaces on different monitors can have the same index.
+ ///
+ /// If you need a unique workspace id that doesn't change, see [`Self::id`].
+ 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.
+ ///
+ /// Every output has one active workspace, the one that is currently visible on that output.
+ pub is_active: bool,
+ /// Whether the workspace is currently focused.
+ ///
+ /// There's only one focused workspace across all outputs.
+ pub is_focused: bool,
+ /// Id of the active window on this workspace, if any.
+ pub active_window_id: Option<u64>,
+}
+
+/// Configured keyboard layouts.
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
+pub struct KeyboardLayouts {
+ /// XKB names of the configured layouts.
+ pub names: Vec<String>,
+ /// Index of the currently active layout in `names`.
+ 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))]
+pub enum Event {
+ /// The workspace configuration has changed.
+ WorkspacesChanged {
+ /// The new workspace configuration.
+ ///
+ /// This configuration completely replaces the previous configuration. I.e. if any
+ /// workspaces are missing from here, then they were deleted.
+ workspaces: Vec<Workspace>,
+ },
+ /// A workspace was activated on an output.
+ ///
+ /// This doesn't always mean the workspace became focused, just that it's now the active
+ /// workspace on its output. All other workspaces on the same output become inactive.
+ WorkspaceActivated {
+ /// Id of the newly active workspace.
+ id: u64,
+ /// Whether this workspace also became focused.
+ ///
+ /// If `true`, this is now the single focused workspace. All other workspaces are no longer
+ /// focused, but they may remain active on their respective outputs.
+ focused: bool,
+ },
+ /// An active window changed on a workspace.
+ WorkspaceActiveWindowChanged {
+ /// Id of the workspace on which the active window changed.
+ workspace_id: u64,
+ /// Id of the new active window, if any.
+ active_window_id: Option<u64>,
+ },
+ /// The window configuration has changed.
+ WindowsChanged {
+ /// The new window configuration.
+ ///
+ /// This configuration completely replaces the previous configuration. I.e. if any windows
+ /// are missing from here, then they were closed.
+ windows: Vec<Window>,
+ },
+ /// A new toplevel window was opened, or an existing toplevel window changed.
+ WindowOpenedOrChanged {
+ /// The new or updated window.
+ ///
+ /// If the window is focused, all other windows are no longer focused.
+ window: Window,
+ },
+ /// A toplevel window was closed.
+ WindowClosed {
+ /// Id of the removed window.
+ id: u64,
+ },
+ /// Window focus changed.
+ ///
+ /// All other windows are no longer focused.
+ WindowFocusChanged {
+ /// Id of the newly focused window, or `None` if no window is now focused.
+ id: Option<u64>,
+ },
+ /// The configured keyboard layouts have changed.
+ KeyboardLayoutsChanged {
+ /// The new keyboard layout configuration.
+ keyboard_layouts: KeyboardLayouts,
+ },
+ /// The keyboard layout switched.
+ KeyboardLayoutSwitched {
+ /// Index of the newly active layout.
+ idx: u8,
+ },
+}
+
+impl FromStr for WorkspaceReferenceArg {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let reference = if let Ok(index) = s.parse::<i32>() {
+ if let Ok(idx) = u8::try_from(index) {
+ Self::Index(idx)
+ } else {
+ return Err("workspace index must be between 0 and 255");
+ }
+ } else {
+ Self::Name(s.to_string())
+ };
+
+ Ok(reference)
+ }
+}
+
+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""#),
+ }
+ }
+}
+
+impl FromStr for Transform {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "normal" => Ok(Self::Normal),
+ "90" => Ok(Self::_90),
+ "180" => Ok(Self::_180),
+ "270" => Ok(Self::_270),
+ "flipped" => Ok(Self::Flipped),
+ "flipped-90" => Ok(Self::Flipped90),
+ "flipped-180" => Ok(Self::Flipped180),
+ "flipped-270" => Ok(Self::Flipped270),
+ _ => Err(concat!(
+ r#"invalid transform, can be "90", "180", "270", "#,
+ r#""flipped", "flipped-90", "flipped-180" or "flipped-270""#
+ )),
+ }
+ }
+}
+
+impl FromStr for ModeToSet {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s.eq_ignore_ascii_case("auto") {
+ return Ok(Self::Automatic);
+ }
+
+ let mode = s.parse()?;
+ Ok(Self::Specific(mode))
+ }
+}
+
+impl FromStr for ConfiguredMode {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let Some((width, rest)) = s.split_once('x') else {
+ return Err("no 'x' separator found");
+ };
+
+ let (height, refresh) = match rest.split_once('@') {
+ Some((height, refresh)) => (height, Some(refresh)),
+ None => (rest, None),
+ };
+
+ let width = width.parse().map_err(|_| "error parsing width")?;
+ let height = height.parse().map_err(|_| "error parsing height")?;
+ let refresh = refresh
+ .map(str::parse)
+ .transpose()
+ .map_err(|_| "error parsing refresh rate")?;
+
+ Ok(Self {
+ width,
+ height,
+ refresh,
+ })
+ }
+}
+
+impl FromStr for ScaleToSet {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s.eq_ignore_ascii_case("auto") {
+ return Ok(Self::Automatic);
+ }
+
+ let scale = s.parse().map_err(|_| "error parsing scale")?;
+ Ok(Self::Specific(scale))
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +
//! Helper for blocking communication over the niri socket.
+
+use std::env;
+use std::io::{self, BufRead, BufReader, Write};
+use std::net::Shutdown;
+use std::os::unix::net::UnixStream;
+use std::path::Path;
+
+use crate::{Event, Reply, Request};
+
+/// Name of the environment variable containing the niri IPC socket path.
+pub const SOCKET_PATH_ENV: &str = "NIRI_SOCKET";
+
+/// Helper for blocking communication over the niri socket.
+///
+/// This struct is used to communicate with the niri IPC server. It handles the socket connection
+/// and serialization/deserialization of messages.
+pub struct Socket {
+ stream: UnixStream,
+}
+
+impl Socket {
+ /// Connects to the default niri IPC socket.
+ ///
+ /// This is equivalent to calling [`Self::connect_to`] with the path taken from the
+ /// [`SOCKET_PATH_ENV`] environment variable.
+ pub fn connect() -> io::Result<Self> {
+ let socket_path = env::var_os(SOCKET_PATH_ENV).ok_or_else(|| {
+ io::Error::new(
+ io::ErrorKind::NotFound,
+ format!("{SOCKET_PATH_ENV} is not set, are you running this within niri?"),
+ )
+ })?;
+ Self::connect_to(socket_path)
+ }
+
+ /// Connects to the niri IPC socket at the given path.
+ pub fn connect_to(path: impl AsRef<Path>) -> io::Result<Self> {
+ let stream = UnixStream::connect(path.as_ref())?;
+ Ok(Self { stream })
+ }
+
+ /// Sends a request to niri and returns the response.
+ ///
+ /// Return values:
+ ///
+ /// * `Ok(Ok(response))`: successful [`Response`](crate::Response) from niri
+ /// * `Ok(Err(message))`: error message from niri
+ /// * `Err(error)`: error communicating with niri
+ ///
+ /// This method also returns a blocking function that you can call to keep reading [`Event`]s
+ /// after requesting an [`EventStream`][Request::EventStream]. This function is not useful
+ /// otherwise.
+ pub fn send(self, request: Request) -> io::Result<(Reply, impl FnMut() -> io::Result<Event>)> {
+ let Self { mut stream } = self;
+
+ let mut buf = serde_json::to_string(&request).unwrap();
+ stream.write_all(buf.as_bytes())?;
+ stream.shutdown(Shutdown::Write)?;
+
+ let mut reader = BufReader::new(stream);
+
+ buf.clear();
+ reader.read_line(&mut buf)?;
+
+ let reply = serde_json::from_str(&buf)?;
+
+ let events = move || {
+ buf.clear();
+ reader.read_line(&mut buf)?;
+ let event = serde_json::from_str(&buf)?;
+ Ok(event)
+ };
+
+ Ok((reply, events))
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +
//! Helpers for keeping track of the event stream state.
+//!
+//! 1. Create an [`EventStreamState`] using `Default::default()`, or any individual state part if
+//! you only care about part of the state.
+//! 2. Connect to the niri socket and request an event stream.
+//! 3. Pass every [`Event`] to [`EventStreamStatePart::apply`] on your state.
+//! 4. Read the fields of the state as needed.
+
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+
+use crate::{Event, KeyboardLayouts, Window, Workspace};
+
+/// Part of the state communicated via the event stream.
+pub trait EventStreamStatePart {
+ /// Returns a sequence of events that replicates this state from default initialization.
+ fn replicate(&self) -> Vec<Event>;
+
+ /// Applies the event to this state.
+ ///
+ /// Returns `None` after applying the event, and `Some(event)` if the event is ignored by this
+ /// part of the state.
+ fn apply(&mut self, event: Event) -> Option<Event>;
+}
+
+/// The full state communicated over the event stream.
+///
+/// Different parts of the state are not guaranteed to be consistent across every single event
+/// sent by niri. For example, you may receive the first [`Event::WindowOpenedOrChanged`] for a
+/// just-opened window *after* an [`Event::WorkspaceActiveWindowChanged`] for that window. Between
+/// these two events, the workspace active window id refers to a window that does not yet exist in
+/// the windows state part.
+#[derive(Debug, Default)]
+pub struct EventStreamState {
+ /// State of workspaces.
+ pub workspaces: WorkspacesState,
+
+ /// State of workspaces.
+ pub windows: WindowsState,
+
+ /// State of the keyboard layouts.
+ pub keyboard_layouts: KeyboardLayoutsState,
+}
+
+/// The workspaces state communicated over the event stream.
+#[derive(Debug, Default)]
+pub struct WorkspacesState {
+ /// Map from a workspace id to the workspace.
+ pub workspaces: HashMap<u64, Workspace>,
+}
+
+/// The windows state communicated over the event stream.
+#[derive(Debug, Default)]
+pub struct WindowsState {
+ /// Map from a window id to the window.
+ pub windows: HashMap<u64, Window>,
+}
+
+/// The keyboard layout state communicated over the event stream.
+#[derive(Debug, Default)]
+pub struct KeyboardLayoutsState {
+ /// Configured keyboard layouts.
+ pub keyboard_layouts: Option<KeyboardLayouts>,
+}
+
+impl EventStreamStatePart for EventStreamState {
+ fn replicate(&self) -> Vec<Event> {
+ let mut events = Vec::new();
+ events.extend(self.workspaces.replicate());
+ events.extend(self.windows.replicate());
+ events.extend(self.keyboard_layouts.replicate());
+ events
+ }
+
+ fn apply(&mut self, event: Event) -> Option<Event> {
+ let event = self.workspaces.apply(event)?;
+ let event = self.windows.apply(event)?;
+ let event = self.keyboard_layouts.apply(event)?;
+ Some(event)
+ }
+}
+
+impl EventStreamStatePart for WorkspacesState {
+ fn replicate(&self) -> Vec<Event> {
+ let workspaces = self.workspaces.values().cloned().collect();
+ vec![Event::WorkspacesChanged { workspaces }]
+ }
+
+ fn apply(&mut self, event: Event) -> Option<Event> {
+ match event {
+ Event::WorkspacesChanged { workspaces } => {
+ self.workspaces = workspaces.into_iter().map(|ws| (ws.id, ws)).collect();
+ }
+ Event::WorkspaceActivated { id, focused } => {
+ let ws = self.workspaces.get(&id);
+ let ws = ws.expect("activated workspace was missing from the map");
+ let output = ws.output.clone();
+
+ for ws in self.workspaces.values_mut() {
+ let got_activated = ws.id == id;
+ if ws.output == output {
+ ws.is_active = got_activated;
+ }
+
+ if focused {
+ ws.is_focused = got_activated;
+ }
+ }
+ }
+ Event::WorkspaceActiveWindowChanged {
+ workspace_id,
+ active_window_id,
+ } => {
+ let ws = self.workspaces.get_mut(&workspace_id);
+ let ws = ws.expect("changed workspace was missing from the map");
+ ws.active_window_id = active_window_id;
+ }
+ event => return Some(event),
+ }
+ None
+ }
+}
+
+impl EventStreamStatePart for WindowsState {
+ fn replicate(&self) -> Vec<Event> {
+ let windows = self.windows.values().cloned().collect();
+ vec![Event::WindowsChanged { windows }]
+ }
+
+ fn apply(&mut self, event: Event) -> Option<Event> {
+ match event {
+ Event::WindowsChanged { windows } => {
+ self.windows = windows.into_iter().map(|win| (win.id, win)).collect();
+ }
+ Event::WindowOpenedOrChanged { window } => {
+ let (id, is_focused) = match self.windows.entry(window.id) {
+ Entry::Occupied(mut entry) => {
+ let entry = entry.get_mut();
+ *entry = window;
+ (entry.id, entry.is_focused)
+ }
+ Entry::Vacant(entry) => {
+ let entry = entry.insert(window);
+ (entry.id, entry.is_focused)
+ }
+ };
+
+ if is_focused {
+ for win in self.windows.values_mut() {
+ if win.id != id {
+ win.is_focused = false;
+ }
+ }
+ }
+ }
+ Event::WindowClosed { id } => {
+ let win = self.windows.remove(&id);
+ win.expect("closed window was missing from the map");
+ }
+ Event::WindowFocusChanged { id } => {
+ for win in self.windows.values_mut() {
+ win.is_focused = Some(win.id) == id;
+ }
+ }
+ event => return Some(event),
+ }
+ None
+ }
+}
+
+impl EventStreamStatePart for KeyboardLayoutsState {
+ fn replicate(&self) -> Vec<Event> {
+ if let Some(keyboard_layouts) = self.keyboard_layouts.clone() {
+ vec![Event::KeyboardLayoutsChanged { keyboard_layouts }]
+ } else {
+ vec![]
+ }
+ }
+
+ fn apply(&mut self, event: Event) -> Option<Event> {
+ match event {
+ Event::KeyboardLayoutsChanged { keyboard_layouts } => {
+ self.keyboard_layouts = Some(keyboard_layouts);
+ }
+ Event::KeyboardLayoutSwitched { idx } => {
+ let kb = self.keyboard_layouts.as_mut();
+ let kb = kb.expect("keyboard layouts must be set before a layout can be switched");
+ kb.current_idx = idx;
+ }
+ event => return Some(event),
+ }
+ None
+ }
+}
+
fn:
) to \
+ restrict the search to a given item kind.","Accepted kinds are: fn
, mod
, struct
, \
+ enum
, trait
, type
, macro
, \
+ and const
.","Search functions by type signature (e.g., vec -> usize
or \
+ -> vec
or String, enum:Cow -> bool
)","You can look for items with an exact name by putting double quotes around \
+ your request: \"string\"
","Look for functions that accept or return \
+ slices and \
+ arrays by writing \
+ square brackets (e.g., -> [u8]
or [] -> Option
)","Look for items inside another one by searching for a path: vec::Vec
",].map(x=>""+x+"
").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="${value.replaceAll(" ", " ")}
`}else{error[index]=value}});output+=`Takes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
occur, a\ncontainer with the values of each Result
is returned.
Here is an example which increments every integer in a vector,\nchecking for overflow:
\n\nlet v = vec![1, 2];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_add(1).ok_or(\"Overflow!\")\n).collect();\nassert_eq!(res, Ok(vec![2, 3]));
Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:
\n\nlet v = vec![1, 2, 0];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_sub(1).ok_or(\"Underflow!\")\n).collect();\nassert_eq!(res, Err(\"Underflow!\"));
Here is a variation on the previous example, showing that no\nfurther elements are taken from iter
after the first Err
.
let v = vec![3, 2, 1, 10];\nlet mut shared = 0;\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32| {\n shared += x;\n x.checked_sub(2).ok_or(\"Underflow!\")\n}).collect();\nassert_eq!(res, Err(\"Underflow!\"));\nassert_eq!(shared, 6);
Since the third element caused an underflow, no further elements were taken,\nso the final value of shared
is 6 (= 3 + 2 + 1
), not 16.
try_trait_v2
)Residual
type. Read moreReturns a consuming iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(5);\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, [5]);\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, []);
Takes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the product of all elements is returned.
This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err
:
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert_eq!(total, Ok(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert!(total.is_err());
Returns true
if the result is Ok
and the value inside of it matches a predicate.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.is_ok_and(|x| x > 1), true);\n\nlet x: Result<u32, &str> = Ok(0);\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<u32, &str> = Err(\"hey\");\nassert_eq!(x.is_ok_and(|x| x > 1), false);
Returns true
if the result is Err
and the value inside of it matches a predicate.
use std::io::{Error, ErrorKind};\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true);\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, Error> = Ok(123);\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);
Converts from Result<T, E>
to Option<E>
.
Converts self
into an Option<E>
, consuming self
,\nand discarding the success value, if any.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.err(), None);\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.err(), Some(\"Nothing here\"));
Converts from &Result<T, E>
to Result<&T, &E>
.
Produces a new Result
, containing a reference\ninto the original, leaving the original in place.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.as_ref(), Ok(&2));\n\nlet x: Result<u32, &str> = Err(\"Error\");\nassert_eq!(x.as_ref(), Err(&\"Error\"));
Converts from &mut Result<T, E>
to Result<&mut T, &mut E>
.
fn mutate(r: &mut Result<i32, i32>) {\n match r.as_mut() {\n Ok(v) => *v = 42,\n Err(e) => *e = 0,\n }\n}\n\nlet mut x: Result<i32, i32> = Ok(2);\nmutate(&mut x);\nassert_eq!(x.unwrap(), 42);\n\nlet mut x: Result<i32, i32> = Err(13);\nmutate(&mut x);\nassert_eq!(x.unwrap_err(), 0);
Maps a Result<T, E>
to Result<U, E>
by applying a function to a\ncontained Ok
value, leaving an Err
value untouched.
This function can be used to compose the results of two functions.
\nPrint the numbers on each line of a string multiplied by two.
\n\nlet line = \"1\\n2\\n3\\n4\\n\";\n\nfor num in line.lines() {\n match num.parse::<i32>().map(|i| i * 2) {\n Ok(n) => println!(\"{n}\"),\n Err(..) => {}\n }\n}
Returns the provided default (if Err
), or\napplies a function to the contained value (if Ok
).
Arguments passed to map_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else
,\nwhich is lazily evaluated.
let x: Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or(42, |v| v.len()), 42);
Maps a Result<T, E>
to U
by applying fallback function default
to\na contained Err
value, or function f
to a contained Ok
value.
This function can be used to unpack a successful result\nwhile handling an error.
\nlet k = 21;\n\nlet x : Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);\n\nlet x : Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);
Maps a Result<T, E>
to Result<T, F>
by applying a function to a\ncontained Err
value, leaving an Ok
value untouched.
This function can be used to pass through a successful result while handling\nan error.
\nfn stringify(x: u32) -> String { format!(\"error code: {x}\") }\n\nlet x: Result<u32, u32> = Ok(2);\nassert_eq!(x.map_err(stringify), Ok(2));\n\nlet x: Result<u32, u32> = Err(13);\nassert_eq!(x.map_err(stringify), Err(\"error code: 13\".to_string()));
Converts from Result<T, E>
(or &Result<T, E>
) to Result<&<T as Deref>::Target, &E>
.
Coerces the Ok
variant of the original Result
via Deref
\nand returns the new Result
.
let x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&str, &u32> = Ok(\"hello\");\nassert_eq!(x.as_deref(), y);\n\nlet x: Result<String, u32> = Err(42);\nlet y: Result<&str, &u32> = Err(&42);\nassert_eq!(x.as_deref(), y);
Converts from Result<T, E>
(or &mut Result<T, E>
) to Result<&mut <T as DerefMut>::Target, &mut E>
.
Coerces the Ok
variant of the original Result
via DerefMut
\nand returns the new Result
.
let mut s = \"HELLO\".to_string();\nlet mut x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&mut str, &mut u32> = Ok(&mut s);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);\n\nlet mut i = 42;\nlet mut x: Result<String, u32> = Err(42);\nlet y: Result<&mut str, &mut u32> = Err(&mut i);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);
Returns an iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(7);\nassert_eq!(x.iter().next(), Some(&7));\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter().next(), None);
Returns a mutable iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let mut x: Result<u32, &str> = Ok(7);\nmatch x.iter_mut().next() {\n Some(v) => *v = 40,\n None => {},\n}\nassert_eq!(x, Ok(40));\n\nlet mut x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter_mut().next(), None);
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message including the\npassed message, and the content of the Err
.
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
We recommend that expect
messages are used to describe the reason you\nexpect the Result
should be Ok
.
let path = std::env::var(\"IMPORTANT_PATH\")\n .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.
\nFor more detail on expect message styles and the reasoning behind our recommendation please\nrefer to the section on “Common Message\nStyles” in the\nstd::error
module docs.
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message provided by the\nErr
’s value.
Basic usage:
\n\nlet x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
Returns the contained Ok
value or a default
Consumes the self
argument then, if Ok
, returns the contained\nvalue, otherwise if Err
, returns the default value for that\ntype.
Converts a string to an integer, turning poorly-formed strings\ninto 0 (the default value for integers). parse
converts\na string to any other type that implements FromStr
, returning an\nErr
on error.
let good_year_from_input = \"1909\";\nlet bad_year_from_input = \"190blarg\";\nlet good_year = good_year_from_input.parse().unwrap_or_default();\nlet bad_year = bad_year_from_input.parse().unwrap_or_default();\n\nassert_eq!(1909, good_year);\nassert_eq!(0, bad_year);
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a panic message including the\npassed message, and the content of the Ok
.
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a custom panic message provided\nby the Ok
’s value.
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
unwrap_infallible
)Returns the contained Ok
value, but never panics.
Unlike unwrap
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap
as a maintainability safeguard that will fail\nto compile if the error type of the Result
is later changed\nto an error that can actually occur.
\nfn only_good_news() -> Result<String, !> {\n Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
unwrap_infallible
)Returns the contained Err
value, but never panics.
Unlike unwrap_err
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap_err
as a maintainability safeguard that will fail\nto compile if the ok type of the Result
is later changed\nto a type that can actually occur.
\nfn only_bad_news() -> Result<!, String> {\n Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
Returns res
if the result is Ok
, otherwise returns the Err
value of self
.
Arguments passed to and
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<&str, &str> = Ok(\"foo\");\nassert_eq!(x.and(y), Err(\"early error\"));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"not a 2\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Ok(\"different result type\");\nassert_eq!(x.and(y), Ok(\"different result type\"));
Calls op
if the result is Ok
, otherwise returns the Err
value of self
.
This function can be used for control flow based on Result
values.
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {\n x.checked_mul(x).map(|sq| sq.to_string()).ok_or(\"overflowed\")\n}\n\nassert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));\nassert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err(\"overflowed\"));\nassert_eq!(Err(\"not a number\").and_then(sq_then_to_string), Err(\"not a number\"));
Often used to chain fallible operations that may return Err
.
use std::{io::ErrorKind, path::Path};\n\n// Note: on Windows \"/\" maps to \"C:\\\"\nlet root_modified_time = Path::new(\"/\").metadata().and_then(|md| md.modified());\nassert!(root_modified_time.is_ok());\n\nlet should_fail = Path::new(\"/bad/path\").metadata().and_then(|md| md.modified());\nassert!(should_fail.is_err());\nassert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
Returns res
if the result is Err
, otherwise returns the Ok
value of self
.
Arguments passed to or
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<u32, &str> = Ok(2);\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Ok(100);\nassert_eq!(x.or(y), Ok(2));
Calls op
if the result is Err
, otherwise returns the Ok
value of self
.
This function can be used for control flow based on result values.
\nfn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }\nfn err(x: u32) -> Result<u32, u32> { Err(x) }\n\nassert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));\nassert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));\nassert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));\nassert_eq!(Err(3).or_else(err).or_else(err), Err(3));
Returns the contained Ok
value or a provided default.
Arguments passed to unwrap_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else
,\nwhich is lazily evaluated.
let default = 2;\nlet x: Result<u32, &str> = Ok(9);\nassert_eq!(x.unwrap_or(default), 9);\n\nlet x: Result<u32, &str> = Err(\"error\");\nassert_eq!(x.unwrap_or(default), default);
Returns the contained Ok
value, consuming the self
value,\nwithout checking that the value is not an Err
.
Calling this method on an Err
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked(); } // Undefined behavior!
Returns the contained Err
value, consuming the self
value,\nwithout checking that the value is not an Ok
.
Calling this method on an Ok
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
Takes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the sum of all elements is returned.
This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:
\n\nlet f = |&x: &i32| if x < 0 { Err(\"Negative element found\") } else { Ok(x) };\nlet v = vec![1, 2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Ok(3));\nlet v = vec![1, -2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Err(\"Negative element found\"));
try_trait_v2
)?
when not short-circuiting.try_trait_v2
)FromResidual::from_residual
\nas part of ?
when short-circuiting. Read moretry_trait_v2
)Output
type. Read moretry_trait_v2
)?
to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue
)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break
). Read more