From 8e93395360f056d91018d5beb899b10022ff4173 Mon Sep 17 00:00:00 2001 From: Ben Buhse Date: Wed, 11 Feb 2026 20:22:41 -0600 Subject: [PATCH] Update README.md, add docs/CONFIGURATION.md I've finally added a somewhat usable README and also explained configuration in docs/CONFIGURATION.md --- README.md | 76 +++++++++++- REUSE.toml | 2 +- docs/CONFIGURATION.md | 270 ++++++++++++++++++++++++++++++++++++++++++ docs/TODO.md | 7 +- examples/config.kdl | 2 +- 5 files changed, 348 insertions(+), 9 deletions(-) create mode 100644 docs/CONFIGURATION.md diff --git a/README.md b/README.md index 50b291e..74823bb 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,78 @@ -# beansprout wm +# beansprout -A tiling window manager for the [river](https://codeberg.org/river/river/) Wayland compositor. + +## Overview + +A tiling window manager for the [river](https://codeberg.org/river/river/) Wayland compositor written in Zig. +The window manager communicates using the [river-window-management-v1](protocol/river-window-management-v1.xml) +protocol, as well as some of River's additional Wayland protocols. + +Beansprout uses a primary/stack tiling layout inspired by dwm with a customizable ratio and primary count. +Similarly, beansprout has a 32-bit tag system, rather than workspaces. Each output has its own tags and own +primary count/ratio. + +## Building + +Requires [Zig](https://ziglang.org/) 0.15.1 or later. + +### Dependencies + +To compile beansprout, you'll need the dependencies found below. I've listed the package names for Gentoo and +Debian/Ubuntu, but they likely exist on all major distros. + +Needed at both build-time and runtime: + +| Dependency | Gentoo Name | Debian/Ubuntu Name | +|--------------------|-----------------------------|---------------------| +| wayland-client | `dev-libs/wayland` | `libwayland-dev` | +| pixman | `x11-libs/pixman` | `libpixman-1-dev` | +| xkbcommon | `x11-libs/libxkbcommon` | `libxkbcommon-dev` | + +Only needed at build-time: + +| Dependency | Gentoo Name | Debian/Ubuntu Name | +|--------------------|------------------------------|---------------------| +| wayland-protocols | `dev-libs/wayland-protocols` | `wayland-protocols` | + +#### Note for Gentoo Users + +Beansprout is available in my personal [ebuild repo](https://codeberg.org/bwbuhse/beansprout). +Right now, it's just a live ebuild, but I will add versioned ebuilds once v0.1.0 is released. + +### Build + +Build and install with: + +``` +zig build -Doptimize=ReleaseSafe --prefix ~/.local install +``` + +Run `zig build -h` to see a list of all options. ## Usage -More to come. +You can either add `beansprout` to the river `init` file or directly run `river -c beansprout`. -## License +If you need to exit river, for example, if `beansprout` crashes, you can use river's hardcoded +`Ctrl+Alt+Delete` keybind. -GPL-3.0-only +## Configuration + +Beansprout is configured with a [KDL](https://kdl.dev) file at +`$XDG_CONFIG_HOME/beansprout/config.kdl`. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) +for a full reference and [examples/config.kdl](examples/config.kdl) for an example config. + +## Licensing + +This project follows the [REUSE Specification](https://reuse.software/spec-3.3/), +all files have SPDX copyright and license information and/or are listed under +`REUSE.toml`. + +In overview: + +- beansprout's source code is released under the GPL-3.0-only license. +- beansprout's documentation is released under the CC-BY-4.0 license. +- beansprout's examples are released under the CC0-1.0 license. + +All licenses can be found under `LICENSES` diff --git a/REUSE.toml b/REUSE.toml index bd33631..9e41b16 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -1,7 +1,7 @@ version = 1 [[annotations]] -path = ["README.md", "docs/TODO.md"] +path = ["README.md", "docs/**"] SPDX-FileCopyrightText = "2026 Ben Buhse " SPDX-License-Identifier = "CC-BY-4.0" diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 0000000..920c8b2 --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,270 @@ +# Configuration + +Beansprout is configured with a [KDL](https://kdl.dev) file located at: + +``` +$XDG_CONFIG_HOME/beansprout/config.kdl +``` + +If `$XDG_CONFIG_HOME` is not set, this defaults to `~/.config/beansprout/config.kdl`. + +An example config can be found at [examples/config.kdl](../examples/config.kdl). + +If the config file is missing, beansprout falls back to its built-in defaults (which +is missing keybinds!). Similarly, if an individual node or block is invalid, it will +try to ignore the error and continue on. + +All configuration is applied top down, so later options will overwrite earlier ones. + +## General Settings + +```kdl +// Whether new windows should go to the top or bottom of the window stack +attach_mode "top" + +// Whether mousing over a new window should move focus +focus_follows_pointer #true + +// Whether the focus should warp to the center of newly-focused windows +pointer_warp_on_focus_change #true + +// Path to image to use as wallpaper +// The same image is displayed on all outputs, but scaled separately +// If this config is missing, then the background is blank black screen +wallpaper_image_path "~/Pictures/wallpaper.png" +``` + +| Setting | Type | Default | Description | +|------------------------------|--------|---------|-----------------------------------------------------| +| `attach_mode` | enum | `top` | Where new windows go in the stack (`top` or `bottom`) | +| `focus_follows_pointer` | bool | `#true` | Focus follows the pointer between windows | +| `pointer_warp_on_focus_change` | bool | `#true` | Warp pointer to center of newly-focused windows | +| `wallpaper_image_path` | string | none | Path to wallpaper image | + +Boolean values can be written as `#true`/`#false` or `true`/`false`. + +## Borders + +Border settings are placed inside a `borders` block: + +```kdl +borders { + width 2 + color_focused "0x89b4fa" + color_unfocused "0x1e1e2e" +} +``` + +| Setting | Type | Default | Description | +|-------------------|--------|--------------|------------------------------------| +| `width` | u8 | `2` | Border width in pixels | +| `color_focused` | color | `0x89b4fa` | Border color of the focused window | +| `color_unfocused` | color | `0x1e1e2e` | Border color of unfocused windows | + +Colors are specified in `0xRRGGBB` or `0xRRGGBBAA` hex format. + +## 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. + +| Modifier | Description | +|------------------|--------------------| +| `None` | No modifier, e.g. for media keys | +| `Mod4` or `Super` | Super/Windows key | +| `Shift` | Shift key | +| `Ctrl` | Control key | +| `Mod1` or `Alt` | Alt key | +| `Mod3` | Mod3 key | +| `Mod5` | Mod5 key | + +Example: `Mod4+Shift`, `Ctrl+Alt`, `None` + +### Keysyms + +Keysym names follow the XKB naming convention (case-insensitive). Most keys +are simply the character that is typed, e.g. `T`, or `f`, while others have +names such as `Return` for the "enter" or "return" key, `Space`, or +`XF86AudioRaiseVolume`. + +A full list of the key names can typically be found at +`/usr/include/xkbcommon/xkbcommon-keysyms.h`. + +### Commands + +```kdl +keybinds { + // Launch a program. The third argument is the command to run. + spawn Mod4 T "foot" + + // Focus the next/previous window in the stack + focus_next_window Mod4 J + focus_prev_window Mod4 K + +} +``` + +Full command reference: + +| Command | Arguments | Description | +|--------------------------|------------------|---------------------------------------------------| +| `spawn` | command (string) | Launch a program | +| `focus_next_window` | | Focus next window in stack | +| `focus_prev_window` | | Focus previous window in stack | +| `focus_next_output` | | Focus next output | +| `focus_prev_output` | | Focus previous output | +| `send_to_next_output` | | Send focused window to next output | +| `send_to_prev_output` | | Send focused window to previous output | +| `zoom` | | Swap focused window with the primary window | +| `swap_next` | | Swap focused window with the next in stack | +| `swap_prev` | | Swap focused window with the previous in stack | +| `toggle_float` | | Float/unfloat the focused window | +| `toggle_fullscreen` | | Toggle fullscreen on focused window | +| `close_window` | | Close the focused window | +| `change_ratio` | float | Adjust primary/stack ratio on current output | +| `increment_primary_count`| | Add a window to the primary side | +| `decrement_primary_count`| | Remove a window from the primary side | +| `move_up` | pixels | Move floating window up | +| `move_down` | pixels | Move floating window down | +| `move_left` | pixels | Move floating window left | +| `move_right` | pixels | Move floating window right | +| `resize_width` | pixels | Resize floating window width (negative to shrink) | +| `resize_height` | pixels | Resize floating window height (negative to shrink)| +| `set_output_tags` | tags (u32 bitmask) | Set the tags on the focused output | +| `set_window_tags` | tags (u32 bitmask) | Set the tags on the focused window | +| `toggle_output_tags` | tags (u32 bitmask) | Toggle a tag on the focused output | +| `toggle_window_tags` | tags (u32 bitmask) | Toggle a tag on the focused window | +| `reload_config` | | Reload the config file | + +### Tag Binds + +Tags use a 32-bit bitfield. Instead of binding each tag key individually, +you can also use the special `tag_bind` command to automatically generates +bindings for keys `1` through `9`, mapped to tags `1<<0` through `1<<8`: + +```kdl +keybinds { + // Mod4+1 switches to tag 1, Mod4+2 to tag 2, etc. + tag_bind Mod4 set_output_tags + // Mod4+Shift+1 moves the focused window to tag 1, etc. + tag_bind Mod4+Shift set_window_tags + // Mod4+Ctrl+1 toggles tag 1 visibility, etc. + tag_bind Mod4+Ctrl toggle_output_tags + // Mod4+Ctrl+Shift+1 toggles tag 1 on the focused window, etc. + tag_bind Mod4+Ctrl+Shift toggle_window_tags +} +``` + +As mentioned in the table above, you can also bind specific tag values directly +using the regular keybind syntax with the `set_output_tags`, `set_window_tags`, +`toggle_output_tags`, and `toggle_window_tags` commands. The argument is a +32-bit integer representing the tag bitfield, e.g. `0x0001` is tag `1`, `0x0003` +is tags 1 and 2. Any format supported by Zig's `parseInt()` is accepted. This +can be useful if you don't want to use keys 1-9 for your binds or if you want +more than 10 tags in use, for example, for a "scratchpad". + +## Pointer Binds + +Mouse button bindings are placed inside a `pointer_binds` block: + +```kdl +pointer_binds { + // Mod4 + left click to move floating windows + move_window Mod4 BTN_LEFT + // Mod4 + right click to resize floating windows + resize_window Mod4 BTN_RIGHT +} +``` + +| Action | Description | +|-----------------|----------------------------------| +| `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 (decimal or hex) are also +accepted. Button names are case-insensitive. + +To find button codes, you can usually look in `/usr/include/linux/input-event-codes.h`. + +## 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: + +```kdl +// Default settings for all devices +input { + accel_profile "flat" +} + +// Settings for a specific device (overrides the defaults) +input name="PIXA3854:00 093A:0274 Touchpad" { + accel_profile "adaptive" + click_method "clickfinger" + natural_scroll "enabled" + tap "disabled" +} +``` + +### Input Settings + +All libinput configuration options supported by river should also be supported by beansprout. + +| Setting | Type | Values | +|------------------------|--------|------------------------------------------------------| +| `accel_profile` | enum | `none`, `flat`, `adaptive`, `custom` | +| `accel_speed` | float | | +| `click_method` | enum | `none`, `button_areas`, `clickfinger` | +| `clickfinger_button_map` | enum | `lrm`, `lmr` | +| `drag` | enum | `disabled`, `enabled` | +| `drag_lock` | enum | `disabled`, `enabled_timeout`, `enabled_sticky` | +| `dwt` | enum | `disabled`, `enabled` | +| `dwtp` | enum | `disabled`, `enabled` | +| `left_handed` | enum | `disabled`, `enabled` | +| `middle_emulation` | enum | `disabled`, `enabled` | +| `natural_scroll` | enum | `disabled`, `enabled` | +| `rotation` | u32 | Rotation angle in degrees | +| `scroll_button` | button | Button name or code (see Pointer Binds) | +| `scroll_button_lock` | enum | `disabled`, `enabled` | +| `scroll_method` | enum | `no_scroll`, `two_finger`, `edge`, `on_button_down` | +| `send_events` | enum | `enabled`, `disabled`, `disabled_on_external_mouse` | +| `tap` | enum | `disabled`, `enabled` | +| `tap_button_map` | enum | `lrm`, `lmr` | +| `three_finger_drag` | enum | `disabled`, `enabled_3fg`, `enabled_4fg` | + +**dwt** = disable while typing. **dwtp** = disable while trackpointing. + +## Per-Host Configuration + +Any node can also have a `host=` property to restrict it to a specific +hostname. This is useful for sharing a config file across machines: + +```kdl +// Per-host wallpaper +wallpaper_image_path "~/Pictures/desktop.png" host="desktop" +wallpaper_image_path "~/Pictures/laptop.png" host="laptop" + +// Per-host border width inside a borders block +borders { + width 4 host="desktop" + width 2 host="laptop" +} + +// Per-host keybind +keybinds { + spawn Mod4 T "foot" host="laptop" + spawn Mod4 T "alacritty" host="desktop" +} +``` + +Nodes without a `host=` property apply to all hosts. diff --git a/docs/TODO.md b/docs/TODO.md index 37a62dd..d849542 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -2,10 +2,13 @@ These are in rough order of my priority, though no promises I do them in this order. -- [ ] Implement an optional clock bar +- [ ] Implement primary count/ratio per tagmask +- [ ] Add primary_count and primary_ratio to Config - [ ] Implement a river-tag-overlay clone -- [ ] Support overriding config location +- [ ] Implement an optional clock bar - [ ] Support window rules (float/tags/SSD by app-id/title) +- [ ] Support overriding config location +- [ ] Support configuring primary vs secondary stack side - [ ] Support switch handling (e.g. lid close) - [ ] Support keybind modes (e.g. passthrough) - [ ] Support solid `background-color` fallback (no wallpaper) diff --git a/examples/config.kdl b/examples/config.kdl index b43ec9e..af79e0c 100644 --- a/examples/config.kdl +++ b/examples/config.kdl @@ -66,7 +66,7 @@ keybinds { // Brightness keys (no modifier) spawn None XF86MonBrightnessUp "~/.config/river/brightness-up.sh" spawn None XF86MonBrightnessDown "~/.config/river/brightness-down.sh" - // Special command to generate keybinds for keys 1-9 and tags 1<<0 through 1<<9 + // Special command to generate keybinds for keys 1-9 and tags 1<<0 through 1<<8 tag_bind Mod4 set_output_tags tag_bind Mod4+Shift set_window_tags tag_bind Mod4+Ctrl toggle_output_tags