1use crate::input::mode;
20
21pub fn render_dec_modes(mode_bits: u32, cursor_style: u32, out: &mut Vec<u8>) {
37 let on = |bit: u32| mode_bits & bit != 0;
38
39 if !on(mode::MODE_CURSOR) {
41 out.extend_from_slice(b"\x1b[?25l");
42 }
43 if !on(mode::MODE_WRAP) {
44 out.extend_from_slice(b"\x1b[?7l");
45 }
46
47 if on(mode::MODE_INSERT) {
49 out.extend_from_slice(b"\x1b[4h");
50 }
51 if on(mode::MODE_KCURSOR) {
52 out.extend_from_slice(b"\x1b[?1h");
54 }
55 if on(mode::MODE_KKEYPAD) {
56 out.extend_from_slice(b"\x1b=");
58 }
59 if on(mode::MODE_CRLF) {
60 out.extend_from_slice(b"\x1b[20h");
62 }
63 if on(mode::MODE_FOCUSON) {
64 out.extend_from_slice(b"\x1b[?1004h");
65 }
66 if on(mode::MODE_BRACKETPASTE) {
67 out.extend_from_slice(b"\x1b[?2004h");
68 }
69 if on(mode::MODE_THEME_UPDATES) {
70 out.extend_from_slice(b"\x1b[?2031h");
71 }
72 if on(mode::MODE_MOUSE_ALL) {
80 out.extend_from_slice(b"\x1b[?1003h");
81 } else if on(mode::MODE_MOUSE_BUTTON) {
82 out.extend_from_slice(b"\x1b[?1002h");
83 } else if on(mode::MODE_MOUSE_STANDARD) {
84 out.extend_from_slice(b"\x1b[?1000h");
85 }
86 if on(mode::MODE_MOUSE_SGR) {
88 out.extend_from_slice(b"\x1b[?1006h");
89 } else if on(mode::MODE_MOUSE_UTF8) {
90 out.extend_from_slice(b"\x1b[?1005h");
91 }
92
93 if on(mode::MODE_KEYS_KITTY) {
95 out.extend_from_slice(b"\x1b[>1u");
96 } else if on(mode::MODE_KEYS_EXTENDED_2) {
97 out.extend_from_slice(b"\x1b[>4;2m");
98 } else if on(mode::MODE_KEYS_EXTENDED) {
99 out.extend_from_slice(b"\x1b[>4;1m");
100 }
101
102 if cursor_style != 0 {
104 out.extend_from_slice(format!("\x1b[{cursor_style} q").as_bytes());
105 }
106}
107
108pub fn render_dec_modes_for_snapshot(mode_bits: u32, cursor_style: u32, out: &mut Vec<u8>) {
117 let on = |bit: u32| mode_bits & bit != 0;
118
119 out.extend_from_slice(if on(mode::MODE_WRAP) {
120 b"\x1b[?7h".as_slice()
121 } else {
122 b"\x1b[?7l".as_slice()
123 });
124 out.extend_from_slice(if on(mode::MODE_INSERT) {
125 b"\x1b[4h".as_slice()
126 } else {
127 b"\x1b[4l".as_slice()
128 });
129 out.extend_from_slice(if on(mode::MODE_KCURSOR) {
130 b"\x1b[?1h".as_slice()
131 } else {
132 b"\x1b[?1l".as_slice()
133 });
134 out.extend_from_slice(if on(mode::MODE_KKEYPAD) {
135 b"\x1b=".as_slice()
136 } else {
137 b"\x1b>".as_slice()
138 });
139 out.extend_from_slice(if on(mode::MODE_CRLF) {
140 b"\x1b[20h".as_slice()
141 } else {
142 b"\x1b[20l".as_slice()
143 });
144 out.extend_from_slice(if on(mode::MODE_FOCUSON) {
145 b"\x1b[?1004h".as_slice()
146 } else {
147 b"\x1b[?1004l".as_slice()
148 });
149 out.extend_from_slice(if on(mode::MODE_BRACKETPASTE) {
150 b"\x1b[?2004h".as_slice()
151 } else {
152 b"\x1b[?2004l".as_slice()
153 });
154 out.extend_from_slice(if on(mode::MODE_THEME_UPDATES) {
155 b"\x1b[?2031h".as_slice()
156 } else {
157 b"\x1b[?2031l".as_slice()
158 });
159
160 out.extend_from_slice(b"\x1b[?2026l");
161
162 out.extend_from_slice(b"\x1b[?1000l\x1b[?1002l\x1b[?1003l");
163 if on(mode::MODE_MOUSE_ALL) {
164 out.extend_from_slice(b"\x1b[?1003h");
165 } else if on(mode::MODE_MOUSE_BUTTON) {
166 out.extend_from_slice(b"\x1b[?1002h");
167 } else if on(mode::MODE_MOUSE_STANDARD) {
168 out.extend_from_slice(b"\x1b[?1000h");
169 }
170
171 out.extend_from_slice(b"\x1b[?1005l\x1b[?1006l");
172 if on(mode::MODE_MOUSE_SGR) {
173 out.extend_from_slice(b"\x1b[?1006h");
174 } else if on(mode::MODE_MOUSE_UTF8) {
175 out.extend_from_slice(b"\x1b[?1005h");
176 }
177
178 if on(mode::MODE_KEYS_KITTY) {
179 out.extend_from_slice(b"\x1b[>1u");
180 } else if on(mode::MODE_KEYS_EXTENDED_2) {
181 out.extend_from_slice(b"\x1b[>4;2m");
182 } else if on(mode::MODE_KEYS_EXTENDED) {
183 out.extend_from_slice(b"\x1b[>4;1m");
184 } else {
185 out.extend_from_slice(b"\x1b[<u");
186 out.extend_from_slice(b"\x1b[>4;0m");
187 }
188
189 out.extend_from_slice(format!("\x1b[{cursor_style} q").as_bytes());
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 fn rendered(mode_bits: u32, cursor_style: u32) -> String {
197 let mut out = Vec::new();
198 render_dec_modes(mode_bits, cursor_style, &mut out);
199 String::from_utf8(out).expect("dec-mode sequences are ascii")
200 }
201
202 #[test]
203 fn post_reset_defaults_emit_nothing() {
204 assert_eq!(rendered(mode::MODE_CURSOR | mode::MODE_WRAP, 0), "");
206 }
207
208 #[test]
209 fn mouse_button_sgr_and_bracketed_paste_are_reasserted() {
210 let bits = mode::MODE_CURSOR
211 | mode::MODE_WRAP
212 | mode::MODE_MOUSE_BUTTON
213 | mode::MODE_MOUSE_SGR
214 | mode::MODE_BRACKETPASTE;
215 let out = rendered(bits, 0);
216 assert!(out.contains("\x1b[?1002h"), "{out:?}");
217 assert!(out.contains("\x1b[?1006h"), "{out:?}");
218 assert!(out.contains("\x1b[?2004h"), "{out:?}");
219 }
220
221 #[test]
222 fn highest_mouse_level_wins() {
223 let bits =
224 mode::MODE_CURSOR | mode::MODE_WRAP | mode::MODE_MOUSE_ALL | mode::MODE_MOUSE_BUTTON;
225 let out = rendered(bits, 0);
226 assert!(out.contains("\x1b[?1003h"), "{out:?}");
227 assert!(!out.contains("\x1b[?1002h"), "{out:?}");
228 }
229
230 #[test]
231 fn application_cursor_keys_and_keypad() {
232 let bits = mode::MODE_CURSOR | mode::MODE_WRAP | mode::MODE_KCURSOR | mode::MODE_KKEYPAD;
233 let out = rendered(bits, 0);
234 assert!(out.contains("\x1b[?1h"), "{out:?}");
235 assert!(out.contains("\x1b="), "{out:?}");
236 }
237
238 #[test]
239 fn cursor_hidden_when_mode_off() {
240 let out = rendered(mode::MODE_WRAP, 0); assert!(out.contains("\x1b[?25l"), "{out:?}");
242 }
243
244 #[test]
245 fn cursor_style_emitted_when_set() {
246 let out = rendered(mode::MODE_CURSOR | mode::MODE_WRAP, 4);
247 assert!(out.contains("\x1b[4 q"), "{out:?}");
248 }
249
250 #[test]
251 fn kitty_keyboard_mode_is_reasserted_as_csi_u() {
252 let bits = mode::MODE_CURSOR
253 | mode::MODE_WRAP
254 | mode::MODE_KEYS_EXTENDED_2
255 | mode::MODE_KEYS_KITTY;
256 let out = rendered(bits, 0);
257 assert!(out.contains("\x1b[>1u"), "{out:?}");
258 assert!(!out.contains("\x1b[>4;2m"), "{out:?}");
259 }
260
261 #[test]
262 fn snapshot_mode_render_clears_stale_transient_modes() {
263 let mut out = Vec::new();
264 render_dec_modes_for_snapshot(mode::MODE_CURSOR | mode::MODE_WRAP, 0, &mut out);
265 let out = String::from_utf8(out).expect("snapshot modes are ascii");
266
267 assert!(out.contains("\x1b[?2026l"), "{out:?}");
268 assert!(out.contains("\x1b[?2004l"), "{out:?}");
269 assert!(out.contains("\x1b[?1006l"), "{out:?}");
270 assert!(out.contains("\x1b[<u"), "{out:?}");
271 assert!(out.contains("\x1b[>4;0m"), "{out:?}");
272 assert!(out.contains("\x1b[0 q"), "{out:?}");
273 assert!(!out.contains("\x1b[?2026h"), "{out:?}");
274 }
275
276 #[test]
277 fn snapshot_mode_render_sets_enabled_modes_after_clearing_family_state() {
278 let bits = mode::MODE_CURSOR
279 | mode::MODE_WRAP
280 | mode::MODE_MOUSE_BUTTON
281 | mode::MODE_MOUSE_SGR
282 | mode::MODE_BRACKETPASTE
283 | mode::MODE_KEYS_EXTENDED_2;
284 let mut out = Vec::new();
285 render_dec_modes_for_snapshot(bits, 4, &mut out);
286 let out = String::from_utf8(out).expect("snapshot modes are ascii");
287
288 assert!(out.contains("\x1b[?1002h"), "{out:?}");
289 assert!(out.contains("\x1b[?1006h"), "{out:?}");
290 assert!(out.contains("\x1b[?2004h"), "{out:?}");
291 assert!(out.contains("\x1b[>4;2m"), "{out:?}");
292 assert!(out.contains("\x1b[4 q"), "{out:?}");
293 }
294
295 #[test]
296 fn snapshot_mode_render_prefers_kitty_keyboard_over_xterm_modifier_mode() {
297 let bits = mode::MODE_CURSOR
298 | mode::MODE_WRAP
299 | mode::MODE_KEYS_EXTENDED_2
300 | mode::MODE_KEYS_KITTY;
301 let mut out = Vec::new();
302 render_dec_modes_for_snapshot(bits, 0, &mut out);
303 let out = String::from_utf8(out).expect("snapshot modes are ascii");
304
305 assert!(out.contains("\x1b[>1u"), "{out:?}");
306 assert!(!out.contains("\x1b[>4;2m"), "{out:?}");
307 }
308}