Add documentation

This commit is contained in:
Ottatop 2023-10-19 22:35:12 -05:00
parent f5b626e14d
commit f0e2282445
8 changed files with 189 additions and 0 deletions

View file

@ -52,6 +52,10 @@ impl Input {
send_msg(msg).unwrap(); 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<F>( pub fn mousebind<F>(
&self, &self,
modifiers: &[Modifier], modifiers: &[Modifier],

View file

@ -1,56 +1,111 @@
use crate::{msg::Msg, send_msg}; use crate::{msg::Msg, send_msg};
/// Libinput settings.
///
/// Here you can set things like pointer acceleration.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Libinput; pub struct Libinput;
impl Libinput { impl Libinput {
/// Set a libinput setting.
///
/// This takes a [`LibinputSetting`] containing what you want set.
pub fn set(&self, setting: LibinputSetting) { pub fn set(&self, setting: LibinputSetting) {
let msg = Msg::SetLibinputSetting(setting); let msg = Msg::SetLibinputSetting(setting);
send_msg(msg).unwrap(); send_msg(msg).unwrap();
} }
} }
/// The acceleration profile.
#[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)]
pub enum AccelProfile { pub enum AccelProfile {
/// Flat pointer acceleration.
Flat, Flat,
/// Adaptive pointer acceleration.
///
/// This is the default for most devices.
Adaptive, Adaptive,
} }
/// The click method for a touchpad.
#[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)]
pub enum ClickMethod { pub enum ClickMethod {
/// Use software-button areas to generate button events.
ButtonAreas, ButtonAreas,
/// The number of fingers decides which button press to generate.
Clickfinger, Clickfinger,
} }
/// The scroll method for a touchpad.
#[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)]
pub enum ScrollMethod { pub enum ScrollMethod {
/// Never send scroll events.
NoScroll, NoScroll,
/// Send scroll events when two fingers are logically down on the device.
TwoFinger, TwoFinger,
/// Send scroll events when a finger moves along the bottom or right edge of a device.
Edge, Edge,
/// Send scroll events when a button is down and the device moves along a scroll-capable axis.
OnButtonDown, OnButtonDown,
} }
/// The mapping between finger count and button event for a touchpad.
#[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)]
pub enum TapButtonMap { pub enum TapButtonMap {
/// 1/2/3 finger tap is mapped to left/right/middle click.
LeftRightMiddle, LeftRightMiddle,
/// 1/2/3 finger tap is mapped to left/middle/right click.
LeftMiddleRight, LeftMiddleRight,
} }
/// Libinput settings.
#[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)] #[derive(Debug, PartialEq, Copy, Clone, serde::Serialize)]
pub enum LibinputSetting { pub enum LibinputSetting {
/// Set the acceleration profile.
AccelProfile(AccelProfile), AccelProfile(AccelProfile),
/// Set the acceleration speed.
///
/// This should be a float from -1.0 to 1.0.
AccelSpeed(f64), AccelSpeed(f64),
/// Set the calibration matrix.
CalibrationMatrix([f32; 6]), 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), ClickMethod(ClickMethod),
/// Set whether or not the device will be disabled while typing.
DisableWhileTypingEnabled(bool), DisableWhileTypingEnabled(bool),
/// Set device left-handedness.
LeftHanded(bool), LeftHanded(bool),
/// Set whether or not the middle click can be emulated.
MiddleEmulationEnabled(bool), MiddleEmulationEnabled(bool),
/// Set the rotation angle of a device.
RotationAngle(u32), RotationAngle(u32),
/// Set the scroll method.
ScrollMethod(ScrollMethod), ScrollMethod(ScrollMethod),
/// Set whether or not natural scroll is enabled.
///
/// This reverses the direction of scrolling and is mainly used with touchpads.
NaturalScrollEnabled(bool), NaturalScrollEnabled(bool),
/// Set the scroll button.
ScrollButton(u32), 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), 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), 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), TapDragLockEnabled(bool),
/// Set whether or not tap-to-click is enabled.
TapEnabled(bool), TapEnabled(bool),
} }

View file

@ -25,6 +25,7 @@ pub use xkbcommon;
/// To that end, you can do `use pinnacle_api::prelude::*` to /// To that end, you can do `use pinnacle_api::prelude::*` to
/// prevent your config file from being cluttered with imports. /// prevent your config file from being cluttered with imports.
pub mod prelude { pub mod prelude {
pub use crate::input::libinput::*;
pub use crate::input::Modifier; pub use crate::input::Modifier;
pub use crate::input::MouseButton; pub use crate::input::MouseButton;
pub use crate::input::MouseEdge; pub use crate::input::MouseEdge;
@ -226,6 +227,7 @@ pub struct Pinnacle {
} }
impl Pinnacle { impl Pinnacle {
/// Quit Pinnacle.
pub fn quit(&self) { pub fn quit(&self) {
send_msg(Msg::Quit).unwrap(); send_msg(Msg::Quit).unwrap();
} }

View file

@ -56,6 +56,19 @@ impl Output {
.find(|op| op.properties().focused == Some(true)) .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<F>(&self, mut func: F) pub fn connect_for_all<F>(&self, mut func: F)
where where
F: FnMut(OutputHandle) + Send + 'static, F: FnMut(OutputHandle) + Send + 'static,
@ -247,16 +260,24 @@ enum LeftOrRight {
Right, Right,
} }
/// Horizontal alignment.
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum AlignmentHorizontal { pub enum AlignmentHorizontal {
/// Align the outputs such that the left edges are in line.
Left, Left,
/// Center the outputs horizontally.
Center, Center,
/// Align the outputs such that the right edges are in line.
Right, Right,
} }
/// Vertical alignment.
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum AlignmentVertical { pub enum AlignmentVertical {
/// Align the outputs such that the top edges are in line.
Top, Top,
/// Center the outputs vertically.
Center, Center,
/// Align the outputs such that the bottom edges are in line.
Bottom, Bottom,
} }

View file

@ -3,10 +3,16 @@ use crate::{
send_msg, CALLBACK_VEC, send_msg, CALLBACK_VEC,
}; };
/// Process management.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Process; pub struct Process;
impl 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<()> { pub fn spawn(&self, command: Vec<&str>) -> anyhow::Result<()> {
let msg = Msg::Spawn { let msg = Msg::Spawn {
command: command.into_iter().map(|s| s.to_string()).collect(), command: command.into_iter().map(|s| s.to_string()).collect(),
@ -16,6 +22,13 @@ impl Process {
send_msg(msg) 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<F>(&self, command: Vec<&str>, mut callback: F) -> anyhow::Result<()> pub fn spawn_with_callback<F>(&self, command: Vec<&str>, mut callback: F) -> anyhow::Result<()>
where where
F: FnMut(Option<String>, Option<String>, Option<i32>, Option<String>) + Send + 'static, F: FnMut(Option<String>, Option<String>, Option<i32>, Option<String>) + Send + 'static,

View file

@ -6,6 +6,7 @@ use crate::{
request, send_msg, request, send_msg,
}; };
/// Tag management.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Tag; pub struct Tag;
@ -48,6 +49,15 @@ impl Tag {
send_msg(msg).unwrap(); 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 { pub fn layout_cycler(&self, layouts: &[Layout]) -> LayoutCycler {
let mut indices = HashMap::<TagId, usize>::new(); let mut indices = HashMap::<TagId, usize>::new();
let layouts = layouts.to_vec(); let layouts = layouts.to_vec();
@ -174,13 +184,21 @@ impl TagHandle {
} }
} }
/// Layouts for tags.
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Layout { pub enum Layout {
/// One master window on the left with all other windows stacked to the right.
MasterStack, MasterStack,
/// Windows split in half towards the bottom right corner.
Dwindle, Dwindle,
/// Windows split in half in a spiral
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, CornerTopLeft,
/// One main corner window in the top right with a column of windows on the left and a row on the bottom.
CornerTopRight, CornerTopRight,
/// One main corner window in the bottom left with a column of windows on the right and a row on the top.
CornerBottomLeft, CornerBottomLeft,
/// One main corner window in the bottom right with a column of windows on the left and a row on the top.
CornerBottomRight, CornerBottomRight,
} }

View file

@ -19,22 +19,27 @@ pub enum WindowId {
Some(u32), Some(u32),
} }
/// Window management.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Window { pub struct Window {
/// Window rules.
pub rules: WindowRules, pub rules: WindowRules,
} }
impl Window { impl Window {
/// Get all windows with the class `class`.
pub fn get_by_class<'a>(&self, class: &'a str) -> impl Iterator<Item = WindowHandle> + 'a { pub fn get_by_class<'a>(&self, class: &'a str) -> impl Iterator<Item = WindowHandle> + 'a {
self.get_all() self.get_all()
.filter(|win| win.properties().class.as_deref() == Some(class)) .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<WindowHandle> { pub fn get_focused(&self) -> Option<WindowHandle> {
self.get_all() self.get_all()
.find(|win| win.properties().focused.is_some_and(|focused| focused)) .find(|win| win.properties().focused.is_some_and(|focused| focused))
} }
/// Get all windows.
pub fn get_all(&self) -> impl Iterator<Item = WindowHandle> { pub fn get_all(&self) -> impl Iterator<Item = WindowHandle> {
let RequestResponse::Windows { window_ids } = request(Request::GetWindows) else { let RequestResponse::Windows { window_ids } = request(Request::GetWindows) else {
unreachable!() unreachable!()
@ -43,6 +48,10 @@ impl Window {
window_ids.into_iter().map(WindowHandle) 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) { pub fn begin_move(&self, button: MouseButton) {
let msg = Msg::WindowMoveGrab { let msg = Msg::WindowMoveGrab {
button: button as u32, button: button as u32,
@ -51,6 +60,10 @@ impl Window {
send_msg(msg).unwrap(); 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) { pub fn begin_resize(&self, button: MouseButton) {
let msg = Msg::WindowResizeGrab { let msg = Msg::WindowResizeGrab {
button: button as u32, 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)] #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize)]
pub enum FloatingOrTiled { pub enum FloatingOrTiled {
/// The window is floating.
///
/// It can be freely moved around and resized and will not respond to layouts.
Floating, Floating,
/// The window is tiled.
///
/// It cannot be resized and can only move by swapping places with other tiled windows.
Tiled, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum FullscreenOrMaximized { pub enum FullscreenOrMaximized {
/// The window is not fullscreen or maximized.
Neither, 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, Fullscreen,
/// The window is maximized.
///
/// It will fill up as much space on its output as it can, respecting any layer surfaces.
Maximized, Maximized,
} }

View file

@ -4,10 +4,12 @@ use crate::{msg::Msg, output::OutputHandle, send_msg, tag::TagHandle};
use super::{FloatingOrTiled, FullscreenOrMaximized}; use super::{FloatingOrTiled, FullscreenOrMaximized};
/// Window rules.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct WindowRules; pub struct WindowRules;
impl WindowRules { impl WindowRules {
/// Add a window rule.
pub fn add(&self, cond: WindowRuleCondition, rule: WindowRule) { pub fn add(&self, cond: WindowRuleCondition, rule: WindowRule) {
let msg = Msg::AddWindowRule { let msg = Msg::AddWindowRule {
cond: cond.0, 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)] #[derive(Default)]
pub struct WindowRule(crate::msg::WindowRule); pub struct WindowRule(crate::msg::WindowRule);
impl WindowRule { impl WindowRule {
/// Create a new, empty window rule.
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
/// This rule will force windows to open on the provided `output`.
pub fn output(mut self, output: &OutputHandle) -> Self { pub fn output(mut self, output: &OutputHandle) -> Self {
self.0.output = Some(output.0.clone()); self.0.output = Some(output.0.clone());
self self
} }
/// This rule will force windows to open with the provided `tags`.
pub fn tags(mut self, tags: &[TagHandle]) -> Self { pub fn tags(mut self, tags: &[TagHandle]) -> Self {
self.0.tags = Some(tags.iter().map(|tag| tag.0).collect()); self.0.tags = Some(tags.iter().map(|tag| tag.0).collect());
self self
} }
/// This rule will force windows to open either floating or tiled.
pub fn floating_or_tiled(mut self, floating_or_tiled: FloatingOrTiled) -> Self { pub fn floating_or_tiled(mut self, floating_or_tiled: FloatingOrTiled) -> Self {
self.0.floating_or_tiled = Some(floating_or_tiled); self.0.floating_or_tiled = Some(floating_or_tiled);
self self
} }
/// This rule will force windows to open either fullscreen, maximized, or neither.
pub fn fullscreen_or_maximized( pub fn fullscreen_or_maximized(
mut self, mut self,
fullscreen_or_maximized: FullscreenOrMaximized, fullscreen_or_maximized: FullscreenOrMaximized,
@ -49,45 +62,76 @@ impl WindowRule {
self 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 { pub fn size(mut self, width: NonZeroU32, height: NonZeroU32) -> Self {
self.0.size = Some((width, height)); self.0.size = Some((width, height));
self 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 { pub fn location(mut self, x: i32, y: i32) -> Self {
self.0.location = Some((x, y)); self.0.location = Some((x, y));
self self
} }
} }
/// A condition for a [`WindowRule`] to apply to a window.
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct WindowRuleCondition(crate::msg::WindowRuleCondition); pub struct WindowRuleCondition(crate::msg::WindowRuleCondition);
impl WindowRuleCondition { impl WindowRuleCondition {
/// Create a new, empty `WindowRuleCondition`.
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
/// This condition requires that at least one provided condition is true.
pub fn any(mut self, conds: &[WindowRuleCondition]) -> Self { pub fn any(mut self, conds: &[WindowRuleCondition]) -> Self {
self.0.cond_any = Some(conds.iter().map(|cond| cond.0.clone()).collect()); self.0.cond_any = Some(conds.iter().map(|cond| cond.0.clone()).collect());
self self
} }
/// This condition requires that all provided conditions are true.
pub fn all(mut self, conds: &[WindowRuleCondition]) -> Self { pub fn all(mut self, conds: &[WindowRuleCondition]) -> Self {
self.0.cond_all = Some(conds.iter().map(|cond| cond.0.clone()).collect()); self.0.cond_all = Some(conds.iter().map(|cond| cond.0.clone()).collect());
self 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 { pub fn class(mut self, classes: &[&str]) -> Self {
self.0.class = Some(classes.iter().map(|s| s.to_string()).collect()); self.0.class = Some(classes.iter().map(|s| s.to_string()).collect());
self 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 { pub fn title(mut self, titles: &[&str]) -> Self {
self.0.title = Some(titles.iter().map(|s| s.to_string()).collect()); self.0.title = Some(titles.iter().map(|s| s.to_string()).collect());
self 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 { pub fn tag(mut self, tags: &[TagHandle]) -> Self {
self.0.tag = Some(tags.iter().map(|tag| tag.0).collect()); self.0.tag = Some(tags.iter().map(|tag| tag.0).collect());
self self