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_EXTENDED_2) {
95 out.extend_from_slice(b"\x1b[>4;2m");
96 } else if on(mode::MODE_KEYS_EXTENDED) {
97 out.extend_from_slice(b"\x1b[>4;1m");
98 }
99
100 if cursor_style != 0 {
102 out.extend_from_slice(format!("\x1b[{cursor_style} q").as_bytes());
103 }
104}
105
106pub fn render_dec_modes_for_snapshot(mode_bits: u32, cursor_style: u32, out: &mut Vec<u8>) {
115 let on = |bit: u32| mode_bits & bit != 0;
116
117 out.extend_from_slice(if on(mode::MODE_WRAP) {
118 b"\x1b[?7h".as_slice()
119 } else {
120 b"\x1b[?7l".as_slice()
121 });
122 out.extend_from_slice(if on(mode::MODE_INSERT) {
123 b"\x1b[4h".as_slice()
124 } else {
125 b"\x1b[4l".as_slice()
126 });
127 out.extend_from_slice(if on(mode::MODE_KCURSOR) {
128 b"\x1b[?1h".as_slice()
129 } else {
130 b"\x1b[?1l".as_slice()
131 });
132 out.extend_from_slice(if on(mode::MODE_KKEYPAD) {
133 b"\x1b=".as_slice()
134 } else {
135 b"\x1b>".as_slice()
136 });
137 out.extend_from_slice(if on(mode::MODE_CRLF) {
138 b"\x1b[20h".as_slice()
139 } else {
140 b"\x1b[20l".as_slice()
141 });
142 out.extend_from_slice(if on(mode::MODE_FOCUSON) {
143 b"\x1b[?1004h".as_slice()
144 } else {
145 b"\x1b[?1004l".as_slice()
146 });
147 out.extend_from_slice(if on(mode::MODE_BRACKETPASTE) {
148 b"\x1b[?2004h".as_slice()
149 } else {
150 b"\x1b[?2004l".as_slice()
151 });
152 out.extend_from_slice(if on(mode::MODE_THEME_UPDATES) {
153 b"\x1b[?2031h".as_slice()
154 } else {
155 b"\x1b[?2031l".as_slice()
156 });
157
158 out.extend_from_slice(b"\x1b[?2026l");
159
160 out.extend_from_slice(b"\x1b[?1000l\x1b[?1002l\x1b[?1003l");
161 if on(mode::MODE_MOUSE_ALL) {
162 out.extend_from_slice(b"\x1b[?1003h");
163 } else if on(mode::MODE_MOUSE_BUTTON) {
164 out.extend_from_slice(b"\x1b[?1002h");
165 } else if on(mode::MODE_MOUSE_STANDARD) {
166 out.extend_from_slice(b"\x1b[?1000h");
167 }
168
169 out.extend_from_slice(b"\x1b[?1005l\x1b[?1006l");
170 if on(mode::MODE_MOUSE_SGR) {
171 out.extend_from_slice(b"\x1b[?1006h");
172 } else if on(mode::MODE_MOUSE_UTF8) {
173 out.extend_from_slice(b"\x1b[?1005h");
174 }
175
176 if on(mode::MODE_KEYS_EXTENDED_2) {
177 out.extend_from_slice(b"\x1b[>4;2m");
178 } else if on(mode::MODE_KEYS_EXTENDED) {
179 out.extend_from_slice(b"\x1b[>4;1m");
180 } else {
181 out.extend_from_slice(b"\x1b[>4;0m");
182 }
183
184 out.extend_from_slice(format!("\x1b[{cursor_style} q").as_bytes());
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 fn rendered(mode_bits: u32, cursor_style: u32) -> String {
192 let mut out = Vec::new();
193 render_dec_modes(mode_bits, cursor_style, &mut out);
194 String::from_utf8(out).expect("dec-mode sequences are ascii")
195 }
196
197 #[test]
198 fn post_reset_defaults_emit_nothing() {
199 assert_eq!(rendered(mode::MODE_CURSOR | mode::MODE_WRAP, 0), "");
201 }
202
203 #[test]
204 fn mouse_button_sgr_and_bracketed_paste_are_reasserted() {
205 let bits = mode::MODE_CURSOR
206 | mode::MODE_WRAP
207 | mode::MODE_MOUSE_BUTTON
208 | mode::MODE_MOUSE_SGR
209 | mode::MODE_BRACKETPASTE;
210 let out = rendered(bits, 0);
211 assert!(out.contains("\x1b[?1002h"), "{out:?}");
212 assert!(out.contains("\x1b[?1006h"), "{out:?}");
213 assert!(out.contains("\x1b[?2004h"), "{out:?}");
214 }
215
216 #[test]
217 fn highest_mouse_level_wins() {
218 let bits =
219 mode::MODE_CURSOR | mode::MODE_WRAP | mode::MODE_MOUSE_ALL | mode::MODE_MOUSE_BUTTON;
220 let out = rendered(bits, 0);
221 assert!(out.contains("\x1b[?1003h"), "{out:?}");
222 assert!(!out.contains("\x1b[?1002h"), "{out:?}");
223 }
224
225 #[test]
226 fn application_cursor_keys_and_keypad() {
227 let bits = mode::MODE_CURSOR | mode::MODE_WRAP | mode::MODE_KCURSOR | mode::MODE_KKEYPAD;
228 let out = rendered(bits, 0);
229 assert!(out.contains("\x1b[?1h"), "{out:?}");
230 assert!(out.contains("\x1b="), "{out:?}");
231 }
232
233 #[test]
234 fn cursor_hidden_when_mode_off() {
235 let out = rendered(mode::MODE_WRAP, 0); assert!(out.contains("\x1b[?25l"), "{out:?}");
237 }
238
239 #[test]
240 fn cursor_style_emitted_when_set() {
241 let out = rendered(mode::MODE_CURSOR | mode::MODE_WRAP, 4);
242 assert!(out.contains("\x1b[4 q"), "{out:?}");
243 }
244
245 #[test]
246 fn snapshot_mode_render_clears_stale_transient_modes() {
247 let mut out = Vec::new();
248 render_dec_modes_for_snapshot(mode::MODE_CURSOR | mode::MODE_WRAP, 0, &mut out);
249 let out = String::from_utf8(out).expect("snapshot modes are ascii");
250
251 assert!(out.contains("\x1b[?2026l"), "{out:?}");
252 assert!(out.contains("\x1b[?2004l"), "{out:?}");
253 assert!(out.contains("\x1b[?1006l"), "{out:?}");
254 assert!(out.contains("\x1b[>4;0m"), "{out:?}");
255 assert!(out.contains("\x1b[0 q"), "{out:?}");
256 assert!(!out.contains("\x1b[?2026h"), "{out:?}");
257 }
258
259 #[test]
260 fn snapshot_mode_render_sets_enabled_modes_after_clearing_family_state() {
261 let bits = mode::MODE_CURSOR
262 | mode::MODE_WRAP
263 | mode::MODE_MOUSE_BUTTON
264 | mode::MODE_MOUSE_SGR
265 | mode::MODE_BRACKETPASTE
266 | mode::MODE_KEYS_EXTENDED_2;
267 let mut out = Vec::new();
268 render_dec_modes_for_snapshot(bits, 4, &mut out);
269 let out = String::from_utf8(out).expect("snapshot modes are ascii");
270
271 assert!(out.contains("\x1b[?1002h"), "{out:?}");
272 assert!(out.contains("\x1b[?1006h"), "{out:?}");
273 assert!(out.contains("\x1b[?2004h"), "{out:?}");
274 assert!(out.contains("\x1b[>4;2m"), "{out:?}");
275 assert!(out.contains("\x1b[4 q"), "{out:?}");
276 }
277}