Skip to content

Commit 2655342

Browse files
uninsanemisuzu
andcommitted
nixos/bonsaid: init
Co-authored-by: misuzu <[email protected]>
1 parent e7e64b5 commit 2655342

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

nixos/doc/manual/release-notes/rl-2505.section.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
- [Bazecor](https://github.com/Dygmalab/Bazecor), the graphical configurator for Dygma Products.
2222

23+
- [Bonsai](https://git.sr.ht/~stacyharper/bonsai), a general-purpose event mapper/state machine primarily used to create complex key shortcuts, and as part of the [SXMO](https://sxmo.org/) desktop environment. Available as [services.bonsaid](#opt-services.bonsaid.enable).
24+
2325
- [scanservjs](https://github.com/sbs20/scanservjs/), a web UI for SANE scanners. Available at [services.scanservjs](#opt-services.scanservjs.enable).
2426

2527
- [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](options.html#opt-services.kimai).

nixos/modules/module-list.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@
509509
./services/desktops/ayatana-indicators.nix
510510
./services/desktops/bamf.nix
511511
./services/desktops/blueman.nix
512+
./services/desktops/bonsaid.nix
512513
./services/desktops/cpupower-gui.nix
513514
./services/desktops/deepin/deepin-anything.nix
514515
./services/desktops/deepin/dde-api.nix
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
7+
let
8+
json = pkgs.formats.json { };
9+
transitionType = lib.types.submodule {
10+
freeformType = json.type;
11+
options.type = lib.mkOption {
12+
type = lib.types.enum [
13+
"delay"
14+
"event"
15+
"exec"
16+
];
17+
description = ''
18+
Type of transition. Determines how bonsaid interprets the other options in this transition.
19+
'';
20+
};
21+
options.command = lib.mkOption {
22+
type = lib.types.nullOr (lib.types.listOf lib.types.str);
23+
default = null;
24+
description = ''
25+
Command to run when this transition is taken.
26+
This is executed inline by `bonsaid` and blocks handling of any other events until completion.
27+
To perform the command asynchronously, specify it like `[ "setsid" "-f" "my-command" ]`.
28+
29+
Only effects transitions with `type = "exec"`.
30+
'';
31+
};
32+
options.delay_duration = lib.mkOption {
33+
type = lib.types.nullOr lib.types.int;
34+
default = null;
35+
description = ''
36+
Nanoseconds to wait after the previous state change before performing this transition.
37+
This can be placed at the same level as a `type = "event"` transition to achieve a
38+
timeout mechanism.
39+
40+
Only effects transitions with `type = "delay"`.
41+
'';
42+
};
43+
options.event_name = lib.mkOption {
44+
type = lib.types.nullOr lib.types.str;
45+
default = null;
46+
description = ''
47+
Name of the event which should trigger this transition when received by `bonsaid`.
48+
Events are sent to `bonsaid` by running `bonsaictl -e <event_name>`.
49+
50+
Only effects transitions with `type = "event"`.
51+
'';
52+
};
53+
options.transitions = lib.mkOption {
54+
type = lib.types.listOf transitionType;
55+
default = [ ];
56+
description = ''
57+
List of transitions out of this state.
58+
If left empty, then this state is considered a terminal state and entering it will
59+
trigger an immediate transition back to the root state (after processing side effects).
60+
'';
61+
visible = "shallow";
62+
};
63+
};
64+
cfg = config.services.bonsaid;
65+
in
66+
{
67+
meta.maintainers = [ lib.maintainers.colinsane ];
68+
69+
options.services.bonsaid = {
70+
enable = lib.mkEnableOption "bonsaid";
71+
package = lib.mkPackageOption pkgs "bonsai" { };
72+
extraFlags = lib.mkOption {
73+
type = lib.types.listOf lib.types.str;
74+
default = [ ];
75+
description = ''
76+
Extra flags to pass to `bonsaid`, such as `[ "-v" ]` to enable verbose logging.
77+
'';
78+
};
79+
settings = lib.mkOption {
80+
type = lib.types.listOf transitionType;
81+
description = ''
82+
State transition definitions. See the upstream [README](https://git.sr.ht/~stacyharper/bonsai)
83+
for extended documentation and a more complete example.
84+
'';
85+
example = [
86+
{
87+
type = "event";
88+
event_name = "power_button_pressed";
89+
transitions = [
90+
{
91+
# Hold power button for 600ms to trigger a command
92+
type = "delay";
93+
delay_duration = 600000000;
94+
transitions = [
95+
{
96+
type = "exec";
97+
command = [
98+
"swaymsg"
99+
"--"
100+
"output"
101+
"*"
102+
"power"
103+
"off"
104+
];
105+
# `transitions = []` marks this as a terminal state,
106+
# so bonsai will return to the root state immediately after executing the above command.
107+
transitions = [ ];
108+
}
109+
];
110+
}
111+
{
112+
# If the power button is released before the 600ms elapses, return to the root state.
113+
type = "event";
114+
event_name = "power_button_released";
115+
transitions = [ ];
116+
}
117+
];
118+
}
119+
];
120+
};
121+
configFile = lib.mkOption {
122+
type = lib.types.path;
123+
description = ''
124+
Path to a .json file specifying the state transitions.
125+
You don't need to set this unless you prefer to provide the json file
126+
yourself instead of using the `settings` option.
127+
'';
128+
};
129+
};
130+
131+
config = lib.mkIf cfg.enable {
132+
services.bonsaid.configFile =
133+
let
134+
filterNulls =
135+
v:
136+
if lib.isAttrs v then
137+
lib.mapAttrs (_: filterNulls) (lib.filterAttrs (_: a: a != null) v)
138+
else if lib.isList v then
139+
lib.map filterNulls (lib.filter (a: a != null) v)
140+
else
141+
v;
142+
in
143+
lib.mkDefault (json.generate "bonsai_tree.json" (filterNulls cfg.settings));
144+
145+
# bonsaid is controlled by bonsaictl, so place the latter in the environment by default.
146+
# bonsaictl is typically invoked by scripts or a DE so this isn't strictly necesssary,
147+
# but it's helpful while administering the service generally.
148+
environment.systemPackages = [ cfg.package ];
149+
150+
systemd.user.services.bonsaid = {
151+
description = "Bonsai Finite State Machine daemon";
152+
documentation = [ "https://git.sr.ht/~stacyharper/bonsai" ];
153+
wantedBy = [ "multi-user.target" ];
154+
serviceConfig = {
155+
ExecStart = lib.escapeShellArgs (
156+
[
157+
(lib.getExe' cfg.package "bonsaid")
158+
"-t"
159+
cfg.configFile
160+
]
161+
++ cfg.extraFlags
162+
);
163+
Restart = "on-failure";
164+
RestartSec = "5s";
165+
};
166+
};
167+
};
168+
}

0 commit comments

Comments
 (0)