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}:
23let
24 cfg = config.services.buffyboard;
25 ini = pkgs.formats.ini { };
26in
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}