5.1 KiB
niri-tag
tag-based window management for the niri compositor
niri-tag allows you to use a simple tagging-based system to manage your windows.
how tags work
tag based management is relatively intuitive - sets of windows are assigned numeric tags.
these tags are then de/activated to raise and lower the windows.
only a single workspace is ever in active use per output.
usage
typical unix
first,
clone the repo.
clone a niri source tree into a subdirectory called niri - make sure it is the same release you are running.
build and install with cargo as per usual for rust projects on your platform.
next,
set up niri-tag as a user-level service.
for systemd users, something like the following should suffice:
# /etc/systemd/user/niri-tag.service
[Unit]
Description=Niri Tag Manager
PartOf=graphical-session.target
[Service]
ExecStart=/usr/bin/niri-tag
PrivateTmp=true
Restart=always
Type=notify
[Install]
WantedBy=graphical-session.target
niri's exec-once
should also be okay, but the use of a service manager is highly recommended.
finally,
set up niri binds using tagctl
to control windows and tags as you see fit.
nixos (flakes)
first,
add the following to your flake inputs:
niri-tag = {
url = "git+https://git.atagen.co/atagen/niri-tag";
inputs.niri-flake.follows = "niri-flake";
inputs.nixpkgs.follows = "nixpkgs";
}
it is assumed you use niri-flake, or else will use the stable
package output; this is important for the niri IPC definitions.
next,
- add
inputs.niri-tag.nixosModules.niri-tag
to your module imports - add
services.niri-tag.enable = true;
somewhere in your config - if you wish to use a stable niri instead of unstable from niri-flake (default), set
services.niri-tag.package = inputs.niri-tag.packages.${pkgs.system}.stable;
finally,
add binds to your niri configuration.
you will need the path of tagctl
, which you can get with something like the following:
let
niri-tag = inputs.niri-tag.packages.${pkgs.system}.unstable; # or stable
tagctl = lib.getExe' niri-tag "tagctl";
in
...
bindings
first,
it is recommended that you unset all workspace related binds, as
switching workspaces or moving windows between them
while using tag management can cause unexpected behaviours.
next,
set up:
Mod+Shift+$number
as a spawn bind fortagctl toggle $number
Mod+$number
astagctl toggle-tag $number
.
alternatively,
you may bind as you see fit the tagctl commands add
remove
toggle
for window tagging,
and enable-tag
disable-tag
toggle-tag
exclusive-tag
for managing tags.
all commands (except remove
) take a tag number from 1-255 after their command.
finally,
you may now use the aforementioned binds to assign tags to windows and toggle the tags on and off to hide/reveal them.
config
configs are expected to be located at $XDG_CONFIG_DIR/niri-tag/config.toml
.
they are simple, headerless toml files.
the current config options (and their defaults) are:
# prepopulates a certain number of tags, from 1-255.
# this is useful for getting the full number of tags you intend to use
# premade and sent in any initial event stream connection.
prepopulate = 3
# strict workspaces
# this prevents you from switching away from the workspaces that were on each monitor
# when niri-tag was started, ensuring you don't accidentally break the window management
# paradigm, or get your desktop stuck in a weird state.
strict = true
ipc
command socket
tagctl
is a simple wrapper over a one-shot socket connection to the niri-tag ipc, a socket which should be available at $XDG_RUNTIME_DIR/niri-tag.sock
.
this socket accepts all the tagctl
commands, as formatted in PascalCase rather than kebab-case, and sent as a single JSON object per connection (for now) followed by a line ending.
as an example,
{"EnableTag":4}
event stream socket
there is a stream of events available via $XDG_RUNTIME_DIR/niri-tag-events.sock
.
on initial connection, you will be sent your first and only TagFullState
event, a JSON object summarising the entire state of the tag system, sent in similar fashion to the command socket.
following that, a stream of tag events with one JSON object per line will be sent for every niri-tag operation, until you close the connection.
events
here is a summary of possible events:
{"TagEmpty": int} // tag no longer contains any windows
{"TagOccupied": int} // tag now contains window/s
{"TagUrgent": int} // tag has window signaling urgency
{"TagEnabled": int} // tag now enabled
{"TagDisabled": int} // tag now disabled
{"TagExclusive": int} // tag has been enabled to exclusion of all others
{"TagFullState": {
"1": {
"enabled": bool,
"occupied": bool,
"urgent": bool
},
... // more tags detailed in similar fashion
}
} // a full account of the tag state, sent only once on connection
acknowledgements
- YaLTeR - for creating niri and blessing it with excellent IPC
- sodiboo - for niri-flake
- outfoxxed - for advice on XDG, IPC, and other TLA
- me - for writing this excellent program
enjoy !