No description
  • Zig 97%
  • Shell 3%
Find a file
2026-01-28 20:01:25 +01:00
common Migrate to Zig 0.14 2025-06-11 19:26:35 +02:00
doc doc: Update docs and build man page from README 2024-07-09 12:10:07 +02:00
protocol Remove dependency on status protocol 2024-07-09 19:08:46 +02:00
src better tiling 2026-01-28 20:01:25 +01:00
.gitignore Improve docs and comment user_config.zig 2025-06-11 12:49:35 +02:00
build.zig build: Release version 1.3.0 2025-06-11 19:44:01 +02:00
build.zig.zon Migrate to Zig 0.14 2025-06-11 19:26:35 +02:00
CHANGELOG.md build: Release version 1.3.0 2025-06-11 19:44:01 +02:00
CONTRIBUTING.md Document configuration, remove hardcoded configuration 2024-05-25 23:22:24 +02:00
COPYING COPYING: Permit later version of GPL 2021-12-02 14:02:54 +01:00
README.md build: Release version 1.3.0 2025-06-11 19:44:01 +02:00
release.sh release: Specify Zig version to use to build and test 2025-06-11 19:27:05 +02:00

river-ultitile

A layout generator for river. Features include:

  • configurable layouts employing nested tiles (no juggling with coordinates),
  • widescreen support by default,
  • default layouts, switchable at run time with a command or key binding:
    • dwm-like main/stack layout,
      • main on the left on normal screens,
      • main in the center and stacks on both sides on widescreens,
    • a vertical stack,
    • a horizontal stack, and
    • a monocle layout,
  • optional per-tag-per-output state.

Building

Requirements:

Download the sources with

git clone https://git.sr.ht/~midgard/river-ultitile -b v1.3.0

Build with e.g.

cd river-ultitile
zig build -Doptimize=ReleaseSafe --prefix ~/.local

And make sure ~/.local/bin is in your path.

Quick start

Integrate the snippet into your river init file and look at the key bindings it defines.

Usage and configuration

Configuration of layouts happens by modifying src/user_config.zig and recompiling (see Configuring layouts below). Layouts can use parameters that can be modified at runtime (see Modifying parameters below).

Any error messages will appear in the stderr of river, so if your configuration isn't working as expected, look there.

Example river init file snippet

This snippet can be integrated in your river init file (usually $XDG_CONFIG_HOME/river/init). (If you're just getting started with river, note that you will want to add more than this. Look at river's documentation and default init file for that.)

mod=Super
riverctl map normal $mod K focus-view previous
riverctl map normal $mod J focus-view next
riverctl map normal $mod Z zoom
# Rest of river configuration...

# Set the default layout generator to be river-ultitile and start it.
# River will send the process group of the init executable SIGTERM on exit.
riverctl default-layout river-ultitile
river-ultitile &

# These keybinds work with the default river-ultitile layouts
# Increase/decrease the main size
riverctl map normal $mod U send-layout-cmd river-ultitile "set integer main-size += 4"
riverctl map normal $mod I send-layout-cmd river-ultitile "set integer main-size -= 4"

# Decrease/increase the main size if it is in the center (on widescreens) and
# there are no views to the left or right
riverctl map normal $mod+Shift U send-layout-cmd river-ultitile "set integer main-size-if-only-centered-main += 4"
riverctl map normal $mod+Shift I send-layout-cmd river-ultitile "set integer main-size-if-only-centered-main -= 4"

# Decrease/increase the main count
riverctl map normal $mod N send-layout-cmd river-ultitile "set integer main-count += 1"
riverctl map normal $mod M send-layout-cmd river-ultitile "set integer main-count -= 1"

# Change layout
riverctl map normal $mod Up    send-layout-cmd river-ultitile "set string layout = vstack"
riverctl map normal $mod Right send-layout-cmd river-ultitile "set string layout = hstack"
riverctl map normal $mod Down  send-layout-cmd river-ultitile "set string layout = monocle"
riverctl map normal $mod Left  send-layout-cmd river-ultitile "set string layout = main"

# Cycle through layouts on all tags/outputs
riverctl map normal $mod E spawn "riverctl send-layout-cmd river-ultitile 'unset-all-local layout'; riverctl send-layout-cmd river-ultitile 'set global string layout @ main hstack vstack'"

Running river-ultitile

In your river init file, start river-ultitile as a background process.

Modifying parameters

To change the parameters that are used in the configuration, one sends commands to river-ultitile using riverctl send-layout-cmd river-ultitile "<command>", where <command> takes one of the following forms:

set [global] <variable_type> <variable_name> <operator> <value...>

Set the value for a parameter called <variable_name>.

[global]: By default, a parameter assignment will be bound to the current dominant tag (the least-significant bit) of the currently focused Wayland output (monitor). Adding global to a definition will instead apply to all tags of all outputs.

<variable_type>

  • integer (32-bit signed integers)
  • string
  • boolean (either true or false)

<operator>

  • = (set value, takes only one <value>)
  • @ (cycle between an arbitrary amount of unique values)
  • For integers only: +=, -= (add and subtract from current value)

unset-local <variable_name>

Unset local assignments for <variable_name> for the current tags of the currently focused Wayland output.

unset-all-local <variable_name>

Unset local assignments for <variable_name> for all tags of all Wayland outputs.

Configuring layouts

Modifying layouts is done by modifying src/user_config.zig and recompiling (see Building above).

In the src/user_config.zig file, the function layoutSpecification should return your layout expressed in terms of tiles which can be nested arbitrarily deep. Certain tiles should be designated to hold views by setting their max_views to null (for unlimited) or a non-zero number.

Tiles can have their contents arranged horizontally (typ=.hsplit), vertically (typ=.vsplit), or superimposed (typ=.overlay).

Views are assigned to tiles in an order determined by the order property of the tiles. The assignment of views starts with the lower orders, so order 0 gets the view on the top of the river view stack. To divide views evenly between tiles, give them the same order and use suborder to determine which tiles will be filled first. Having tiles with the same order and suborder and a non-zero max_views is not allowed.

+---left---+--center--+--right---+
|order=1   |order=0   |order=1   |
|suborder=1|          |suborder=0|
|          |maxviews=1|          |
+----------+----------+----------+

In this example, views are assigned in this order: center, right, left, right, left …

Tile properties are:

  • typ=<.hsplit|.vsplit|.overlay> (default .hsplit)
  • padding=<number|null> (default null; null means inherit)): space around the tile's contents, in pixels
  • margin=<number> (default 0): space around the tile, in pixels
  • stretch=<number> (default 100): a relative width* specifier. The stretches of a tile's subtiles are summed, and the subtiles get a width* proportional to their stretch divided by that sum. Views always have a stretch of 100.
  • order=<number> (default 0): see above
  • suborder=<number> (default 0): see above
  • max_views=<number|null> (default 0; null means unlimited): the maximum amount of views that will be assigned to this tile

*: or height for vsplit

Of course you can also use dynamic parameters in your custom layout. The variables object passed to the layoutSpecification function has methods getString, getInteger and getBoolean for that. Make sure to specify a default value in setDefaultVariables, or get… will return null.

If your layout is not behaving as expected, you can build river-ultitile in debug mode (with -Doptimize=Debug) which will cause river-ultitile to log each view it's proposing.

Contributing

See CONTRIBUTING.md

Thanks

Thanks to Isaac Freund and Leon Henrik Plickat for river obviously!

Thanks to Hugo Machet for rivercarro, from which river-ultitile is forked!

License

river-ultitile is licensed under the GNU General Public License v3.0 or later

Files in common/ and protocol/ directories are released under various licenses by various parties. You should refer to the copyright block of each files for the licensing information.

URLs

Project homepage: https://sr.ht/~midgard/river-ultitile/