Add man pages

beansprout(1) is basically just the README and beansprout(5) is basically
just docs/CONFIGURATION.md.

By default, the man pages are generated if scdoc is availabled, but they
can also be explicitly disabled with -Dman-pages.
This commit is contained in:
Ben Buhse 2026-02-25 16:02:58 -06:00
commit 09f43674b5
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
5 changed files with 451 additions and 1 deletions

View file

@ -35,6 +35,12 @@ Only needed at build-time:
|--------------------|------------------------------|---------------------| |--------------------|------------------------------|---------------------|
| wayland-protocols | `dev-libs/wayland-protocols` | `wayland-protocols` | | wayland-protocols | `dev-libs/wayland-protocols` | `wayland-protocols` |
Optional (for building man pages):
| Dependency | Gentoo Name | Debian/Ubuntu Name |
|--------------------|------------------------------|---------------------|
| scdoc | `app-text/scdoc` | `scdoc` |
#### Note for Gentoo Users #### Note for Gentoo Users
Beansprout is available in my personal [ebuild repo](https://codeberg.org/bwbuhse/beansprout). Beansprout is available in my personal [ebuild repo](https://codeberg.org/bwbuhse/beansprout).

View file

@ -1,7 +1,7 @@
version = 1 version = 1
[[annotations]] [[annotations]]
path = ["README.md", "docs/**"] path = ["README.md", "man/**", "docs/**"]
SPDX-FileCopyrightText = "2026 Ben Buhse <me@benbuhse.email>" SPDX-FileCopyrightText = "2026 Ben Buhse <me@benbuhse.email>"
SPDX-License-Identifier = "CC-BY-4.0" SPDX-License-Identifier = "CC-BY-4.0"

View file

@ -11,6 +11,15 @@ pub fn build(b: *std.Build) void {
const strip = b.option(bool, "strip", "Omit debug information") orelse false; const strip = b.option(bool, "strip", "Omit debug information") orelse false;
const pie = b.option(bool, "pie", "Build a Position Independent Executable") orelse false; const pie = b.option(bool, "pie", "Build a Position Independent Executable") orelse false;
const man_pages = b.option(
bool,
"man-pages",
"Set to true to build man pages. Requires scdoc. Defaults to true if scdoc is found.",
) orelse scdoc_found: {
// Default to true if scdoc is available; else false.
_ = b.findProgram(&.{"scdoc"}, &.{}) catch break :scdoc_found false;
break :scdoc_found true;
};
// Wayland // Wayland
const scanner = Scanner.create(b, .{}); const scanner = Scanner.create(b, .{});
@ -75,6 +84,20 @@ pub fn build(b: *std.Build) void {
b.installArtifact(beansprout); b.installArtifact(beansprout);
const man_step = b.step("man", "Build man pages");
if (man_pages) {
inline for (.{ .{ "beansprout", "1" }, .{ "beansprout", "5" } }) |page| {
const scdoc = b.addSystemCommand(&.{ "/bin/sh", "-c", "scdoc < man/" ++ page[0] ++ "." ++ page[1] ++ ".scd" });
scdoc.addFileArg(b.path("man/" ++ page[0] ++ "." ++ page[1] ++ ".scd"));
const stdout = scdoc.captureStdOut();
const install = b.addInstallFile(stdout, "share/man/man" ++ page[1] ++ "/" ++ page[0] ++ "." ++ page[1]);
b.getInstallStep().dependOn(&install.step);
man_step.dependOn(&install.step);
}
} else {
man_step.dependOn(&b.addFail("man pages disabled; scdoc not found or -Dman-pages=false was set").step);
}
const exe_unit_tests = b.addTest(.{ const exe_unit_tests = b.addTest(.{
.root_module = beansprout.root_module, .root_module = beansprout.root_module,
}); });

60
man/beansprout.1.scd Normal file
View file

@ -0,0 +1,60 @@
BEANSPROUT(1)
# NAME
beansprout - tiling window manager for river
# SYNOPSIS
*beansprout* [_options_]
# DESCRIPTION
*beansprout* is a tiling window manager for the *river*(1) Wayland compositor.
It communicates with river using the river-window-management-v1 protocol and
other River-specific Wayland protocols.
*beansprout* uses a primary/stack tiling layout inspired by dwm with a
customizable ratio and primary count. It uses a 32-bit tag system rather than
workspaces. Each output has its own tags, primary count, and ratio.
# OPTIONS
*-h*
Print a help message and exit.
*-version*
Print the version number and exit.
*-log-level* [*error*|*warning*|*info*|*debug*]
Set the log level. (Default: *warning*)
# USAGE
You can either add *beansprout* to the river init file or directly run:
river -c beansprout
If you need to exit river, for example if *beansprout* crashes, you can use
river's hardcoded *Ctrl+Alt+Delete* keybind.
# CONFIGURATION
*beansprout* is configured with a KDL file located at
_$XDG_CONFIG_HOME/beansprout/config.kdl_. If _$XDG_CONFIG_HOME_ is not set,
this defaults to _~/.config/beansprout/config.kdl_.
See *beansprout*(5) for a full configuration reference.
If the config file is missing, *beansprout* falls back to built-in defaults
(which will be missing keybinds). If an individual node or block is invalid,
*beansprout* will try to ignore the error and continue.
# AUTHORS
Maintained by Ben Buhse <me@benbuhse.email>. For more information about
beansprout's development, see <https://codeberg.org/beansprout/beansprout>.
# SEE ALSO
*beansprout*(5), *river*(1)

361
man/beansprout.5.scd Normal file
View file

@ -0,0 +1,361 @@
BEANSPROUT(5)
# NAME
beansprout - configuration file
# DESCRIPTION
*beansprout*(1) is configured with a KDL file located at
_$XDG_CONFIG_HOME/beansprout/config.kdl_. If _$XDG_CONFIG_HOME_ is not set,
this defaults to _~/.config/beansprout/config.kdl_.
All configuration is applied top-down, so later options overwrite earlier ones.
Any node can have a *host=* property to restrict it to a specific hostname.
This is useful for sharing a config file across machines. Nodes without a
*host=* property apply to all hosts.
# GENERAL SETTINGS
*attach_mode* *top*|*bottom*
Whether new windows go to the top or bottom of the window stack.
(Default: *top*)
*primary_count* _count_
Number of windows in the primary stack. (Default: 1)
*primary_ratio* _ratio_
Proportion of output width taken by the primary stack. Must be between
0.10 and 0.90. (Default: 0.55)
*primary_side* *left*|*right*
Which side of the output the primary stack is on. (Default: *left*)
*single_window_ratio* _ratio_
Proportion of output width taken when a single tiled window is visible.
Must be between 0.10 and 1.00. When less than 1.0, the window is
centered. (Default: 1.00)
*focus_follows_pointer* _bool_
Whether mousing over a new window should move focus.
(Default: *true*)
*pointer_warp_on_focus_change* _bool_
Whether the pointer should warp to the center of newly-focused windows.
(Default: *true*)
*wallpaper_image_path* _path_
Path to image to use as wallpaper. The same image is displayed on all
outputs, scaled separately. If not set, the background is a blank black
screen.
# BORDERS
Border settings are placed inside a *borders* block:
```
borders {
width 2
color_focused "0x89b4fa"
color_unfocused "0x1e1e2e"
}
```
*width* _pixels_
Border width in pixels. (Default: 2)
*color_focused* _color_
Border color of the focused window in 0xRRGGBB or 0xRRGGBBAA hex
format. (Default: 0x89b4fa)
*color_unfocused* _color_
Border color of unfocused windows in 0xRRGGBB or 0xRRGGBBAA hex
format. (Default: 0x1e1e2e)
# WINDOW RULES
Window rules let you set properties on windows when they first appear, based
on their app_id and/or title. Rules are placed inside a *window_rules* block:
```
window_rules {
float app_id="firefox" title="Picture-in-Picture"
float title="*Preferences*"
no_float app_id="mpv"
tags 0x0004 app_id="Slack"
}
```
## Rule Types
*float* [*app_id=*_pattern_] [*title=*_pattern_]
Make matching windows float.
*no_float* [*app_id=*_pattern_] [*title=*_pattern_]
Keep matching windows tiled.
*tags* _bitmask_ [*app_id=*_pattern_] [*title=*_pattern_]
Assign matching windows to specific tags. The argument is a 32-bit
integer representing the tag bitfield.
## Matching
Each rule can have an *app_id=* and/or *title=* property. Both support glob
patterns using *\** as a wildcard. A rule with neither property matches all
windows. If both are specified, both must match.
Rules are evaluated top-to-bottom. Each matching rule applies only the
properties it specifies, so multiple rules can contribute different properties
to the same window. For the same property, later rules override earlier ones.
Rules are applied once during window initialization. Title changes after
initialization do not re-trigger rules.
# BAR
The bar is an optional widget that shows the time. It is only created when a
*bar* block is present:
```
bar {
position top
}
```
*fonts* _fontconfig_
Comma-separated FontConfig font string. (Default: "monospace:size=14")
*text_color* _color_
Text color. (Default: 0xcdd6f4)
*background_color* _color_
Background color. (Default: 0x1e1e2e)
*position* *top*|*bottom*
Bar position. (Default: *top*)
The bar also supports *margins* and *anchors* child blocks; see *TAG OVERLAY*
for their format.
# TAG OVERLAY
The tag overlay is an optional widget that briefly shows tag state when
switching tags. It is only created when a *tag_overlay* block is present:
```
tag_overlay {
tag_amount 10
}
```
*border_width* _pixels_
Widget border width in pixels. (Default: 2)
*tag_amount* _count_
Number of displayed tags, 1-32. (Default: 9)
*tags_per_row* _count_
Tags per row, 1-32. (Default: 32)
*square_size* _pixels_
Size of tag squares in pixels. (Default: 40)
*square_inner_padding* _pixels_
Padding around occupied indicator. (Default: 10)
*square_padding* _pixels_
Padding around tag squares. (Default: 15)
*square_border_width* _pixels_
Border width of tag squares. (Default: 1)
*timeout* _milliseconds_
Display duration in milliseconds. (Default: 500)
*background_color*, *border_color*, *square_active_background_color*,
*square_active_border_color*, *square_active_occupied_color*,
*square_inactive_background_color*, *square_inactive_border_color*,
*square_inactive_occupied_color*
Color settings in 0xRRGGBB or 0xRRGGBBAA hex format. Defaults follow
the Catppuccin Mocha theme.
## Anchors
The *anchors* child block controls which edge(s) of the screen the overlay
attaches to. Each direction is a boolean. Default: unanchored (centered).
*top*, *right*, *bottom*, *left*
## Margins
The *margins* child block sets pixel offsets from the anchored edge(s). All
default to 0.
*top*, *right*, *bottom*, *left*
# KEYBINDS
Keyboard bindings are placed inside a *keybinds* block. Each binding has the
form:
_command_ _modifiers_ _keysym_ [_arguments..._]
## Modifiers
Modifiers are combined with *+*. They are case-insensitive.
- *Mod4* or *Super* - Super/Windows key
- *Shift* - Shift key
- *Ctrl* - Control key
- *Mod1* or *Alt* - Alt key
- *Mod3* - Mod3 key
- *Mod5* - Mod5 key
- *None* - No modifier (e.g., for media keys)
## Keysyms
Keysym names follow the XKB naming convention (case-insensitive). A full list
can typically be found at _/usr/include/xkbcommon/xkbcommon-keysyms.h_.
## Commands
*spawn* _modifiers_ _keysym_ _command_
Launch a program.
*focus_next_window*, *focus_prev_window*
Focus the next/previous window in the stack.
*focus_next_output*, *focus_prev_output*
Focus the next/previous output.
*send_to_next_output*, *send_to_prev_output*
Send the focused window to the next/previous output.
*zoom*
Swap the focused window with the primary window.
*swap_next*, *swap_prev*
Swap the focused window with the next/previous in stack.
*toggle_float*
Float/unfloat the focused window.
*toggle_fullscreen*
Toggle fullscreen on the focused window.
*close_window*
Close the focused window.
*change_ratio* _modifiers_ _keysym_ _float_
Adjust the primary/stack ratio on the current output.
*increment_primary_count*, *decrement_primary_count*
Add/remove a window from the primary side.
*move_up*, *move_down*, *move_left*, *move_right* _modifiers_ _keysym_ _pixels_
Move a floating window by the given number of pixels.
*resize_width*, *resize_height* _modifiers_ _keysym_ _pixels_
Resize a floating window (negative to shrink).
*center_float*
Center the focused floating window on its output.
*set_output_tags* _modifiers_ _keysym_ _bitmask_
Set the tags on the focused output.
*set_window_tags* _modifiers_ _keysym_ _bitmask_
Set the tags on the focused window.
*toggle_output_tags* _modifiers_ _keysym_ _bitmask_
Toggle a tag on the focused output.
*toggle_window_tags* _modifiers_ _keysym_ _bitmask_
Toggle a tag on the focused window.
*reload_config*
Reload the config file.
*toggle_passthrough*
Toggle passthrough mode to disable keybinds.
## Tag Binds
The special *tag_bind* command generates bindings for keys 1-9 mapped to tags
1<<0 through 1<<8:
```
keybinds {
tag_bind Mod4 set_output_tags
tag_bind Mod4+Shift set_window_tags
tag_bind Mod4+Ctrl toggle_output_tags
tag_bind Mod4+Ctrl+Shift toggle_window_tags
}
```
# POINTER BINDS
Mouse button bindings are placed inside a *pointer_binds* block:
```
pointer_binds {
move_window Mod4 BTN_LEFT
resize_window Mod4 BTN_RIGHT
}
```
*move_window*
Drag to move the window.
*resize_window*
Drag to resize the window.
Button names: *BTN_LEFT* / *button1*, *BTN_RIGHT* / *button3*,
*BTN_MIDDLE* / *button2*. Numeric button codes are also accepted. Names are
case-insensitive.
# INPUT DEVICE CONFIGURATION
Input device settings are placed inside *input* blocks. A block without a
*name=* property applies to all devices. A block with *name=* applies only to
the matching device:
```
input {
accel_profile "flat"
}
input name="PIXA3854:00 093A:0274 Touchpad" {
accel_profile "adaptive"
natural_scroll "enabled"
tap "disabled"
}
```
All libinput configuration options supported by river are also supported by
*beansprout*. Available settings include:
- *accel_profile*
- *accel_speed*
- *click_method*
- *clickfinger_button_map*
- *drag*
- *drag_lock*
- *dwt*
- *dwtp*
- *left_handed*
- *middle_emulation*
- *natural_scroll*
- *rotation*
- *scroll_button*
- *scroll_button_lock*
- *scroll_method*
- *send_events*
- *tap*
- *tap_button_map*
- *three_finger_drag*
# SEE ALSO
*beansprout*(1), *river*(1)