···2425- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).
260027<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
2829## Backward Incompatibilities {#sec-release-25.05-incompatibilities}
···2425- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).
2627+- [Buffyboard](https://gitlab.postmarketos.org/postmarketOS/buffybox/-/tree/master/buffyboard), a framebuffer on-screen keyboard. Available as [services.buffyboard](option.html#opt-services.buffyboard).
28+29<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
3031## Backward Incompatibilities {#sec-release-25.05-incompatibilities}
···1+# INTEGRATION NOTES:
2+# Buffyboard integrates as a virtual device in /dev/input
3+# which reads touch or pointer events from other input devices
4+# and generates events based on where those map to the keys it renders to the framebuffer.
5+#
6+# Buffyboard generates these events whether or not its onscreen keyboard is actually visible.
7+# Hence special care is needed if running anything which claims ownership of the display (such as a desktop environment),
8+# to avoid unwanted input events being triggered during normal desktop operation.
9+#
10+# Desktop users are recommended to either:
11+# 1. Stop buffyboard once your DE is started.
12+# e.g. `services.buffyboard.unitConfig.Conflicts = [ "my-de.service" ];`
13+# 2. Configure your DE to ignore input events from buffyboard (product-id=25209; vendor-id=26214; name=rd)
14+# e.g. `echo 'input "26214:25209:rd" events disabled' > ~/.config/sway/config`
15+16+{
17+ config,
18+ lib,
19+ pkgs,
20+ utils,
21+ ...
22+}:
23+let
24+ cfg = config.services.buffyboard;
25+ ini = pkgs.formats.ini { };
26+in
27+{
28+ meta.maintainers = with lib.maintainers; [ colinsane ];
29+30+ options = {
31+ services.buffyboard = with lib; {
32+ enable = mkEnableOption "buffyboard framebuffer keyboard (on-screen keyboard)";
33+ package = mkPackageOption pkgs "buffybox" { };
34+35+ extraFlags = mkOption {
36+ type = types.listOf types.str;
37+ default = [ ];
38+ description = ''
39+ Extra CLI arguments to pass to buffyboard.
40+ '';
41+ example = [
42+ "--geometry=1920x1080@640,0"
43+ "--dpi=192"
44+ "--rotate=2"
45+ "--verbose"
46+ ];
47+ };
48+49+ configFile = mkOption {
50+ type = lib.types.path;
51+ default = ini.generate "buffyboard.conf" (lib.filterAttrsRecursive (_: v: v != null) cfg.settings);
52+ defaultText = lib.literalExpression ''ini.generate "buffyboard.conf" cfg.settings'';
53+ description = ''
54+ Path to an INI format configuration file to provide Buffyboard.
55+ By default, this is generated from whatever you've set in `settings`.
56+ If specified manually, then `settings` is ignored.
57+58+ For an example config file see [here](https://gitlab.postmarketos.org/postmarketOS/buffybox/-/blob/master/buffyboard/buffyboard.conf)
59+ '';
60+ };
61+62+ settings = mkOption {
63+ description = ''
64+ Settings to include in /etc/buffyboard.conf.
65+ Every option here is strictly optional:
66+ Buffyboard will use its own baked-in defaults for those options left unset.
67+ '';
68+ type = types.submodule {
69+ freeformType = ini.type;
70+71+ options.input.pointer = mkOption {
72+ type = types.nullOr types.bool;
73+ default = null;
74+ description = ''
75+ Enable or disable the use of a hardware mouse or other pointing device.
76+ '';
77+ };
78+ options.input.touchscreen = mkOption {
79+ type = types.nullOr types.bool;
80+ default = null;
81+ description = ''
82+ Enable or disable the use of the touchscreen.
83+ '';
84+ };
85+86+ options.theme.default = mkOption {
87+ type = types.either types.str (
88+ types.enum [
89+ null
90+ "adwaita-dark"
91+ "breezy-dark"
92+ "breezy-light"
93+ "nord-dark"
94+ "nord-light"
95+ "pmos-dark"
96+ "pmos-light"
97+ ]
98+ );
99+ default = null;
100+ description = ''
101+ Selects the default theme on boot. Can be changed at runtime to the alternative theme.
102+ '';
103+ };
104+ options.quirks.fbdev_force_refresh = mkOption {
105+ type = types.nullOr types.bool;
106+ default = null;
107+ description = ''
108+ If true and using the framebuffer backend, this triggers a display refresh after every draw operation.
109+ This has a negative performance impact.
110+ '';
111+ };
112+ };
113+ default = { };
114+ };
115+ };
116+ };
117+118+ config = lib.mkIf cfg.enable {
119+ systemd.packages = [ cfg.package ];
120+ systemd.services.buffyboard = {
121+ # upstream provides the service (including systemd hardening): we just configure it to start by default
122+ # and override ExecStart so as to optionally pass extra arguments
123+ serviceConfig.ExecStart = [
124+ "" # clear default ExecStart
125+ (utils.escapeSystemdExecArgs (
126+ [
127+ (lib.getExe' cfg.package "buffyboard")
128+ "--config-override"
129+ cfg.configFile
130+ ]
131+ ++ cfg.extraFlags
132+ ))
133+ ];
134+ wantedBy = [ "getty.target" ];
135+ before = [ "getty.target" ];
136+ };
137+ };
138+}