diff --git a/api/rust/src/input.rs b/api/rust/src/input.rs index 62ae517..9f94b9d 100644 --- a/api/rust/src/input.rs +++ b/api/rust/src/input.rs @@ -52,6 +52,10 @@ impl Input { send_msg(msg).unwrap(); } + /// Set a mousebind. If called with an already existing mousebind, it gets replaced. + /// + /// The mousebind can happen either on button press or release, so you must + /// specify which edge you desire. pub fn mousebind( &self, modifiers: &[Modifier], diff --git a/api/rust/src/input/libinput.rs b/api/rust/src/input/libinput.rs index db3ad45..8b6b7c3 100644 --- a/api/rust/src/input/libinput.rs +++ b/api/rust/src/input/libinput.rs @@ -1,56 +1,111 @@ use crate::{msg::Msg, send_msg}; +/// Libinput settings. +/// +/// Here you can set things like pointer acceleration. #[derive(Clone, Copy)] pub struct Libinput; impl Libinput { + /// Set a libinput setting. + /// + /// This takes a [`LibinputSetting`] containing what you want set. pub fn set(&self, setting: LibinputSetting) { let msg = Msg::SetLibinputSetting(setting); send_msg(msg).unwrap(); } } +/// The acceleration profile. #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] pub enum AccelProfile { + /// Flat pointer acceleration. Flat, + /// Adaptive pointer acceleration. + /// + /// This is the default for most devices. Adaptive, } +/// The click method for a touchpad. #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] pub enum ClickMethod { + /// Use software-button areas to generate button events. ButtonAreas, + /// The number of fingers decides which button press to generate. Clickfinger, } +/// The scroll method for a touchpad. #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] pub enum ScrollMethod { + /// Never send scroll events. NoScroll, + /// Send scroll events when two fingers are logically down on the device. TwoFinger, + /// Send scroll events when a finger moves along the bottom or right edge of a device. Edge, + /// Send scroll events when a button is down and the device moves along a scroll-capable axis. OnButtonDown, } +/// The mapping between finger count and button event for a touchpad. #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] pub enum TapButtonMap { + /// 1/2/3 finger tap is mapped to left/right/middle click. LeftRightMiddle, + /// 1/2/3 finger tap is mapped to left/middle/right click. LeftMiddleRight, } +/// Libinput settings. #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] pub enum LibinputSetting { + /// Set the acceleration profile. AccelProfile(AccelProfile), + /// Set the acceleration speed. + /// + /// This should be a float from -1.0 to 1.0. AccelSpeed(f64), + /// Set the calibration matrix. CalibrationMatrix([f32; 6]), + /// Set the click method. + /// + /// The click method defines when to generate software-emulated buttons, usually on a device + /// that does not have a specific physical button available. ClickMethod(ClickMethod), + /// Set whether or not the device will be disabled while typing. DisableWhileTypingEnabled(bool), + /// Set device left-handedness. LeftHanded(bool), + /// Set whether or not the middle click can be emulated. MiddleEmulationEnabled(bool), + /// Set the rotation angle of a device. RotationAngle(u32), + /// Set the scroll method. ScrollMethod(ScrollMethod), + /// Set whether or not natural scroll is enabled. + /// + /// This reverses the direction of scrolling and is mainly used with touchpads. NaturalScrollEnabled(bool), + /// Set the scroll button. ScrollButton(u32), + /// Set the tap button map, + /// + /// This determines whether taps with 2 and 3 fingers register as right and middle clicks or + /// the reverse. TapButtonMap(TapButtonMap), + /// Set whether or not tap-and-drag is enabled. + /// + /// When enabled, a single-finger tap immediately followed by a finger down results in + /// a button down event, and subsequent finger motion thus triggers a drag. + /// The button is released on finger up. TapDragEnabled(bool), + /// Set whether or not tap drag lock is enabled. + /// + /// When enabled, a finger may be lifted and put back on the touchpad within a timeout and the drag process + /// continues. When disabled, lifting the finger during a tap-and-drag will immediately stop the drag. TapDragLockEnabled(bool), + /// Set whether or not tap-to-click is enabled. TapEnabled(bool), } diff --git a/api/rust/src/lib.rs b/api/rust/src/lib.rs index dc86d32..4231e86 100644 --- a/api/rust/src/lib.rs +++ b/api/rust/src/lib.rs @@ -25,6 +25,7 @@ pub use xkbcommon; /// To that end, you can do `use pinnacle_api::prelude::*` to /// prevent your config file from being cluttered with imports. pub mod prelude { + pub use crate::input::libinput::*; pub use crate::input::Modifier; pub use crate::input::MouseButton; pub use crate::input::MouseEdge; @@ -226,6 +227,7 @@ pub struct Pinnacle { } impl Pinnacle { + /// Quit Pinnacle. pub fn quit(&self) { send_msg(Msg::Quit).unwrap(); } diff --git a/api/rust/src/output.rs b/api/rust/src/output.rs index ab40f57..2fb9720 100644 --- a/api/rust/src/output.rs +++ b/api/rust/src/output.rs @@ -56,6 +56,19 @@ impl Output { .find(|op| op.properties().focused == Some(true)) } + /// Connect a function to be run on all current and future outputs. + /// + /// When called, `connect_for_all` will run `func` with all currently connected outputs. + /// If a new output is connected, `func` will also be called with it. + /// + /// This will *not* be called if it has already been called for a given connector. + /// This means turning your monitor off and on or unplugging and replugging it *to the same port* + /// won't trigger `func`. Plugging it in to a new port *will* trigger `func`. + /// This is intended to prevent duplicate setup. + /// + /// Please note: this function will be run *after* Pinnacle processes your entire config. + /// For example, if you define tags in `func` but toggle them directly after `connect_for_all`, + /// nothing will happen as the tags haven't been added yet. pub fn connect_for_all(&self, mut func: F) where F: FnMut(OutputHandle) + Send + 'static, @@ -247,16 +260,24 @@ enum LeftOrRight { Right, } +/// Horizontal alignment. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum AlignmentHorizontal { + /// Align the outputs such that the left edges are in line. Left, + /// Center the outputs horizontally. Center, + /// Align the outputs such that the right edges are in line. Right, } +/// Vertical alignment. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum AlignmentVertical { + /// Align the outputs such that the top edges are in line. Top, + /// Center the outputs vertically. Center, + /// Align the outputs such that the bottom edges are in line. Bottom, } diff --git a/api/rust/src/process.rs b/api/rust/src/process.rs index e7aea51..4e8d504 100644 --- a/api/rust/src/process.rs +++ b/api/rust/src/process.rs @@ -3,10 +3,16 @@ use crate::{ send_msg, CALLBACK_VEC, }; +/// Process management. #[derive(Clone, Copy)] pub struct Process; impl Process { + /// Spawn a process. + /// + /// This will use Rust's (more specifically `async_process`'s) `Command` to spawn the provided + /// arguments. If you are using any shell syntax like `~`, you may need to spawn a shell + /// instead. If so, you may *also* need to correctly escape the input. pub fn spawn(&self, command: Vec<&str>) -> anyhow::Result<()> { let msg = Msg::Spawn { command: command.into_iter().map(|s| s.to_string()).collect(), @@ -16,6 +22,13 @@ impl Process { send_msg(msg) } + /// Spawn a process with an optional callback for its stdout, stderr, and exit information. + /// + /// `callback` has the following parameters: + /// - `0`: The process's stdout printed this line. + /// - `1`: The process's stderr printed this line. + /// - `2`: The process exited with this code. + /// - `3`: The process exited with this message. pub fn spawn_with_callback(&self, command: Vec<&str>, mut callback: F) -> anyhow::Result<()> where F: FnMut(Option, Option, Option, Option) + Send + 'static, diff --git a/api/rust/src/tag.rs b/api/rust/src/tag.rs index 5c338fd..ba6764b 100644 --- a/api/rust/src/tag.rs +++ b/api/rust/src/tag.rs @@ -6,6 +6,7 @@ use crate::{ request, send_msg, }; +/// Tag management. #[derive(Clone, Copy)] pub struct Tag; @@ -48,6 +49,15 @@ impl Tag { send_msg(msg).unwrap(); } + /// Create a `LayoutCycler` to cycle layouts on tags. + /// + /// Given a slice of layouts, this will create a `LayoutCycler` with two methods; + /// one will cycle forward the layout for the active tag, and one will cycle backward. + /// + /// # Example + /// ``` + /// todo!() + /// ``` pub fn layout_cycler(&self, layouts: &[Layout]) -> LayoutCycler { let mut indices = HashMap::::new(); let layouts = layouts.to_vec(); @@ -174,13 +184,21 @@ impl TagHandle { } } +/// Layouts for tags. #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] pub enum Layout { + /// One master window on the left with all other windows stacked to the right. MasterStack, + /// Windows split in half towards the bottom right corner. Dwindle, + /// Windows split in half in a spiral Spiral, + /// One main corner window in the top left with a column of windows on the right and a row on the bottom. CornerTopLeft, + /// One main corner window in the top right with a column of windows on the left and a row on the bottom. CornerTopRight, + /// One main corner window in the bottom left with a column of windows on the right and a row on the top. CornerBottomLeft, + /// One main corner window in the bottom right with a column of windows on the left and a row on the top. CornerBottomRight, } diff --git a/api/rust/src/window.rs b/api/rust/src/window.rs index 8ea195a..88761e6 100644 --- a/api/rust/src/window.rs +++ b/api/rust/src/window.rs @@ -19,22 +19,27 @@ pub enum WindowId { Some(u32), } +/// Window management. #[derive(Clone, Copy)] pub struct Window { + /// Window rules. pub rules: WindowRules, } impl Window { + /// Get all windows with the class `class`. pub fn get_by_class<'a>(&self, class: &'a str) -> impl Iterator + 'a { self.get_all() .filter(|win| win.properties().class.as_deref() == Some(class)) } + /// Get the currently focused window, or `None` if there isn't one. pub fn get_focused(&self) -> Option { self.get_all() .find(|win| win.properties().focused.is_some_and(|focused| focused)) } + /// Get all windows. pub fn get_all(&self) -> impl Iterator { let RequestResponse::Windows { window_ids } = request(Request::GetWindows) else { unreachable!() @@ -43,6 +48,10 @@ impl Window { window_ids.into_iter().map(WindowHandle) } + /// Begin a window move. + /// + /// This will start a window move grab with the provided button on the window the pointer + /// is currently hovering over. Once `button` is let go, the move will end. pub fn begin_move(&self, button: MouseButton) { let msg = Msg::WindowMoveGrab { button: button as u32, @@ -51,6 +60,10 @@ impl Window { send_msg(msg).unwrap(); } + /// Begin a window resize. + /// + /// This will start a window resize grab with the provided button on the window the + /// pointer is currently hovering over. Once `button` is let go, the resize will end. pub fn begin_resize(&self, button: MouseButton) { let msg = Msg::WindowResizeGrab { button: button as u32, @@ -143,15 +156,34 @@ impl WindowHandle { } } +/// Whether or not a window is floating or tiled. #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize)] pub enum FloatingOrTiled { + /// The window is floating. + /// + /// It can be freely moved around and resized and will not respond to layouts. Floating, + /// The window is tiled. + /// + /// It cannot be resized and can only move by swapping places with other tiled windows. Tiled, } +/// Whether the window is fullscreen, maximized, or neither. +/// +/// These three states are mutually exclusive. Setting a window to maximized while it is fullscreen +/// will make it stop being fullscreen and vice versa. #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum FullscreenOrMaximized { + /// The window is not fullscreen or maximized. Neither, + /// The window is fullscreen. + /// + /// It will be the only rendered window on screen and will fill the output it resides on. + /// Layer surfaces will also not be rendered while a window is fullscreen. Fullscreen, + /// The window is maximized. + /// + /// It will fill up as much space on its output as it can, respecting any layer surfaces. Maximized, } diff --git a/api/rust/src/window/rules.rs b/api/rust/src/window/rules.rs index 1d6a6d0..f8efb14 100644 --- a/api/rust/src/window/rules.rs +++ b/api/rust/src/window/rules.rs @@ -4,10 +4,12 @@ use crate::{msg::Msg, output::OutputHandle, send_msg, tag::TagHandle}; use super::{FloatingOrTiled, FullscreenOrMaximized}; +/// Window rules. #[derive(Clone, Copy)] pub struct WindowRules; impl WindowRules { + /// Add a window rule. pub fn add(&self, cond: WindowRuleCondition, rule: WindowRule) { let msg = Msg::AddWindowRule { cond: cond.0, @@ -18,29 +20,40 @@ impl WindowRules { } } +/// A window rule. +/// +/// This is what will be applied to a window if it meets a [`WindowRuleCondition`]. +/// +/// `WindowRule`s are built using the builder pattern. +/// // TODO: show example #[derive(Default)] pub struct WindowRule(crate::msg::WindowRule); impl WindowRule { + /// Create a new, empty window rule. pub fn new() -> Self { Default::default() } + /// This rule will force windows to open on the provided `output`. pub fn output(mut self, output: &OutputHandle) -> Self { self.0.output = Some(output.0.clone()); self } + /// This rule will force windows to open with the provided `tags`. pub fn tags(mut self, tags: &[TagHandle]) -> Self { self.0.tags = Some(tags.iter().map(|tag| tag.0).collect()); self } + /// This rule will force windows to open either floating or tiled. pub fn floating_or_tiled(mut self, floating_or_tiled: FloatingOrTiled) -> Self { self.0.floating_or_tiled = Some(floating_or_tiled); self } + /// This rule will force windows to open either fullscreen, maximized, or neither. pub fn fullscreen_or_maximized( mut self, fullscreen_or_maximized: FullscreenOrMaximized, @@ -49,45 +62,76 @@ impl WindowRule { self } + /// This rule will force windows to open with a specific size. + /// + /// This will only actually be visible if the window is also floating. pub fn size(mut self, width: NonZeroU32, height: NonZeroU32) -> Self { self.0.size = Some((width, height)); self } + /// This rule will force windows to open at a specific location. + /// + /// This will only actually be visible if the window is also floating. pub fn location(mut self, x: i32, y: i32) -> Self { self.0.location = Some((x, y)); self } } +/// A condition for a [`WindowRule`] to apply to a window. #[derive(Default, Debug)] pub struct WindowRuleCondition(crate::msg::WindowRuleCondition); impl WindowRuleCondition { + /// Create a new, empty `WindowRuleCondition`. pub fn new() -> Self { Default::default() } + /// This condition requires that at least one provided condition is true. pub fn any(mut self, conds: &[WindowRuleCondition]) -> Self { self.0.cond_any = Some(conds.iter().map(|cond| cond.0.clone()).collect()); self } + /// This condition requires that all provided conditions are true. pub fn all(mut self, conds: &[WindowRuleCondition]) -> Self { self.0.cond_all = Some(conds.iter().map(|cond| cond.0.clone()).collect()); self } + /// This condition requires that the window's class matches. + /// + /// When used in a top level condition or inside of [`WindowRuleCondition::all`], + /// *all* classes must match (this is impossible). + /// + /// When used in [`WindowRuleCondition::any`], at least one of the + /// provided classes must match. pub fn class(mut self, classes: &[&str]) -> Self { self.0.class = Some(classes.iter().map(|s| s.to_string()).collect()); self } + /// This condition requires that the window's title matches. + /// + /// When used in a top level condition or inside of [`WindowRuleCondition::all`], + /// *all* titles must match (this is impossible). + /// + /// When used in [`WindowRuleCondition::any`], at least one of the + /// provided titles must match. pub fn title(mut self, titles: &[&str]) -> Self { self.0.title = Some(titles.iter().map(|s| s.to_string()).collect()); self } + /// This condition requires that the window's is opened on the given tags. + /// + /// When used in a top level condition or inside of [`WindowRuleCondition::all`], + /// the window must opne on *all* given tags. + /// + /// When used in [`WindowRuleCondition::any`], the window must open on at least + /// one of the given tags. pub fn tag(mut self, tags: &[TagHandle]) -> Self { self.0.tag = Some(tags.iter().map(|tag| tag.0).collect()); self