1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7
8let
9 inherit (lib)
10 mkOption
11 mkPackageOption
12 mkIf
13 types
14 optionalString
15 ;
16
17 cfg = config.programs.tmux;
18
19 defaultKeyMode = "emacs";
20 defaultResize = 5;
21 defaultShortcut = "b";
22 defaultTerminal = "screen";
23
24 boolToStr = value: if value then "on" else "off";
25
26 tmuxConf = ''
27 set -g default-terminal "${cfg.terminal}"
28 set -g base-index ${toString cfg.baseIndex}
29 setw -g pane-base-index ${toString cfg.baseIndex}
30 set -g history-limit ${toString cfg.historyLimit}
31
32 ${optionalString cfg.newSession "new-session"}
33
34 ${optionalString cfg.reverseSplit ''
35 bind v split-window -h
36 bind s split-window -v
37 ''}
38
39 set -g status-keys ${cfg.keyMode}
40 set -g mode-keys ${cfg.keyMode}
41
42 ${optionalString (cfg.keyMode == "vi" && cfg.customPaneNavigationAndResize) ''
43 bind h select-pane -L
44 bind j select-pane -D
45 bind k select-pane -U
46 bind l select-pane -R
47
48 bind -r H resize-pane -L ${toString cfg.resizeAmount}
49 bind -r J resize-pane -D ${toString cfg.resizeAmount}
50 bind -r K resize-pane -U ${toString cfg.resizeAmount}
51 bind -r L resize-pane -R ${toString cfg.resizeAmount}
52 ''}
53
54 ${optionalString (cfg.shortcut != defaultShortcut) ''
55 # rebind main key: C-${cfg.shortcut}
56 unbind C-${defaultShortcut}
57 set -g prefix C-${cfg.shortcut}
58 bind ${cfg.shortcut} send-prefix
59 bind C-${cfg.shortcut} last-window
60 ''}
61
62 setw -g aggressive-resize ${boolToStr cfg.aggressiveResize}
63 setw -g clock-mode-style ${if cfg.clock24 then "24" else "12"}
64 set -s escape-time ${toString cfg.escapeTime}
65
66 ${cfg.extraConfigBeforePlugins}
67
68 ${lib.optionalString (cfg.plugins != [ ]) ''
69 # Run plugins
70 ${lib.concatMapStringsSep "\n" (x: "run-shell ${x.rtp}") cfg.plugins}
71
72 ''}
73
74 ${cfg.extraConfig}
75 '';
76
77in
78{
79 ###### interface
80
81 options = {
82 programs.tmux = {
83
84 enable = mkOption {
85 type = types.bool;
86 default = false;
87 description = "Whenever to configure {command}`tmux` system-wide.";
88 relatedPackages = [ "tmux" ];
89 };
90
91 package = mkPackageOption pkgs "tmux" { };
92
93 aggressiveResize = mkOption {
94 default = false;
95 type = types.bool;
96 description = ''
97 Resize the window to the size of the smallest session for which it is the current window.
98 '';
99 };
100
101 baseIndex = mkOption {
102 default = 0;
103 example = 1;
104 type = types.int;
105 description = "Base index for windows and panes.";
106 };
107
108 clock24 = mkOption {
109 default = false;
110 type = types.bool;
111 description = "Use 24 hour clock.";
112 };
113
114 customPaneNavigationAndResize = mkOption {
115 default = false;
116 type = types.bool;
117 description = "Override the hjkl and HJKL bindings for pane navigation and resizing in VI mode.";
118 };
119
120 escapeTime = mkOption {
121 default = 500;
122 example = 0;
123 type = types.int;
124 description = "Time in milliseconds for which tmux waits after an escape is input.";
125 };
126
127 extraConfigBeforePlugins = mkOption {
128 default = "";
129 description = ''
130 Additional contents of /etc/tmux.conf, to be run before sourcing plugins.
131 '';
132 type = types.lines;
133 };
134
135 extraConfig = mkOption {
136 default = "";
137 description = ''
138 Additional contents of /etc/tmux.conf, to be run after sourcing plugins.
139 '';
140 type = types.lines;
141 };
142
143 historyLimit = mkOption {
144 default = 2000;
145 example = 5000;
146 type = types.int;
147 description = "Maximum number of lines held in window history.";
148 };
149
150 keyMode = mkOption {
151 default = defaultKeyMode;
152 example = "vi";
153 type = types.enum [
154 "emacs"
155 "vi"
156 ];
157 description = "VI or Emacs style shortcuts.";
158 };
159
160 newSession = mkOption {
161 default = false;
162 type = types.bool;
163 description = "Automatically spawn a session if trying to attach and none are running.";
164 };
165
166 reverseSplit = mkOption {
167 default = false;
168 type = types.bool;
169 description = "Reverse the window split shortcuts.";
170 };
171
172 resizeAmount = mkOption {
173 default = defaultResize;
174 example = 10;
175 type = types.int;
176 description = "Number of lines/columns when resizing.";
177 };
178
179 shortcut = mkOption {
180 default = defaultShortcut;
181 example = "a";
182 type = types.str;
183 description = "Ctrl following by this key is used as the main shortcut.";
184 };
185
186 terminal = mkOption {
187 default = defaultTerminal;
188 example = "screen-256color";
189 type = types.str;
190 description = ''
191 Set the $TERM variable. Use tmux-direct if italics or 24bit true color
192 support is needed.
193 '';
194 };
195
196 secureSocket = mkOption {
197 default = true;
198 type = types.bool;
199 description = ''
200 Store tmux socket under /run, which is more secure than /tmp, but as a
201 downside it doesn't survive user logout.
202 '';
203 };
204
205 plugins = mkOption {
206 default = [ ];
207 type = types.listOf types.package;
208 description = "List of plugins to install.";
209 example = lib.literalExpression "[ pkgs.tmuxPlugins.nord ]";
210 };
211
212 withUtempter = mkOption {
213 description = ''
214 Whether to enable libutempter for tmux.
215 This is required so that tmux can write to /var/run/utmp (which can be queried with `who` to display currently connected user sessions).
216 Note, this will add a guid wrapper for the group utmp!
217 '';
218 default = true;
219 type = types.bool;
220 };
221 };
222 };
223
224 ###### implementation
225
226 config = mkIf cfg.enable {
227 environment = {
228 etc."tmux.conf".text = tmuxConf;
229
230 systemPackages = [ cfg.package ] ++ cfg.plugins;
231
232 variables = {
233 TMUX_TMPDIR = lib.optional cfg.secureSocket ''''${XDG_RUNTIME_DIR:-"/run/user/$(id -u)"}'';
234 };
235 };
236 security.wrappers = mkIf cfg.withUtempter {
237 utempter = {
238 source = "${pkgs.libutempter}/lib/utempter/utempter";
239 owner = "root";
240 group = "utmp";
241 setuid = false;
242 setgid = true;
243 };
244 };
245 };
246
247 imports = [
248 (lib.mkRenamedOptionModule
249 [ "programs" "tmux" "extraTmuxConf" ]
250 [ "programs" "tmux" "extraConfig" ]
251 )
252 ];
253
254 meta.maintainers = with lib.maintainers; [ hxtmdev ];
255}