1use super::Event;
37use super::key::KeyModifiers;
38use crate::ansi::params::Params;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub enum MouseButton {
47 Left,
49 Middle,
51 Right,
53 WheelUp,
55 WheelDown,
57 WheelLeft,
59 WheelRight,
61 Button4,
63 Button5,
65 Button6,
67 Button7,
69 Button8,
71 Button9,
73 Button10,
75 Button11,
77 None,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92pub struct Mouse {
93 pub x: u16,
98 pub y: u16,
103 pub button: MouseButton,
105 pub modifiers: KeyModifiers,
107}
108
109impl Mouse {
110 pub fn new(x: u16, y: u16, button: MouseButton, modifiers: KeyModifiers) -> Self {
116 Self {
117 x,
118 y,
119 button,
120 modifiers,
121 }
122 }
123}
124
125pub fn mouse_pixel_to_cell(
136 m: Mouse,
137 pixel_width: u16,
138 pixel_height: u16,
139 cols: u16,
140 rows: u16,
141) -> Mouse {
142 let x = if pixel_width > 0 {
143 (m.x as u32 * cols as u32 / pixel_width as u32) as u16
144 } else {
145 0
146 };
147 let y = if pixel_height > 0 {
148 (m.y as u32 * rows as u32 / pixel_height as u32) as u16
149 } else {
150 0
151 };
152 Mouse::new(x, y, m.button, m.modifiers)
153}
154
155pub(crate) fn decode_sgr_mouse(params: Params<'_>, is_release: bool) -> Option<Event> {
162 if params.len() < 3 {
163 return None;
164 }
165
166 let cb = params.get_or(0, 0) as u16;
167 let cx = (params.get_or(1, 0).saturating_sub(1)) as u16;
168 let cy = (params.get_or(2, 0).saturating_sub(1)) as u16;
169
170 Some(build_mouse_event(cb, cx, cy, Some(is_release)))
171}
172
173pub(crate) fn decode_x10_mouse(cb: u8, cx: u8, cy: u8) -> Option<Event> {
177 let cb = cb.wrapping_sub(32) as u16;
178 let cx = cx.wrapping_sub(33) as u16;
179 let cy = cy.wrapping_sub(33) as u16;
180 Some(build_mouse_event(cb, cx, cy, None))
181}
182
183pub(crate) fn decode_urxvt_mouse(params: Params<'_>) -> Option<Event> {
188 if params.len() < 3 {
189 return None;
190 }
191 let cb = (params.get_or(0, 0).wrapping_sub(32)) as u16;
192 let cx = (params.get_or(1, 0).saturating_sub(1)) as u16;
193 let cy = (params.get_or(2, 0).saturating_sub(1)) as u16;
194 Some(build_mouse_event(cb, cx, cy, None))
195}
196
197pub(crate) fn decode_utf8_mouse(data: &[u8]) -> Option<(Event, usize)> {
203 let mut idx = 0;
205 let (cb, n) = decode_utf8_codepoint(&data[idx..])?;
206 idx += n;
207 let (cx, n) = decode_utf8_codepoint(&data[idx..])?;
208 idx += n;
209 let (cy, n) = decode_utf8_codepoint(&data[idx..])?;
210 idx += n;
211
212 let cb = cb.wrapping_sub(32);
213 let cx = cx.wrapping_sub(33);
214 let cy = cy.wrapping_sub(33);
215 Some((build_mouse_event(cb, cx, cy, None), idx))
216}
217
218fn decode_utf8_codepoint(data: &[u8]) -> Option<(u16, usize)> {
219 if data.is_empty() {
220 return None;
221 }
222 let b0 = data[0];
223 let (cp, len) = if b0 < 0x80 {
224 (b0 as u32, 1)
225 } else if b0 < 0xc0 {
226 return None;
227 } else if b0 < 0xe0 {
228 if data.len() < 2 {
229 return None;
230 }
231 (((b0 as u32 & 0x1f) << 6) | (data[1] as u32 & 0x3f), 2)
232 } else if b0 < 0xf0 {
233 if data.len() < 3 {
234 return None;
235 }
236 (
237 ((b0 as u32 & 0x0f) << 12) | ((data[1] as u32 & 0x3f) << 6) | (data[2] as u32 & 0x3f),
238 3,
239 )
240 } else {
241 return None;
242 };
243 Some((cp as u16, len))
244}
245
246fn build_mouse_event(cb: u16, cx: u16, cy: u16, is_release: Option<bool>) -> Event {
250 let modifiers = decode_mouse_modifiers(cb);
251 let is_move = cb & 32 != 0;
252 let is_wheel = cb & 64 != 0;
253 let is_extra = cb & 128 != 0;
254
255 let button = if is_wheel {
256 match cb & 3 {
257 0 => MouseButton::WheelUp,
258 1 => MouseButton::WheelDown,
259 2 => MouseButton::WheelLeft,
260 3 => MouseButton::WheelRight,
261 _ => MouseButton::None,
262 }
263 } else if is_extra {
264 match cb & 3 {
266 0 => MouseButton::Button8,
267 1 => MouseButton::Button9,
268 2 => MouseButton::Button10,
269 3 => MouseButton::Button11,
270 _ => MouseButton::None,
271 }
272 } else {
273 match cb & 3 {
274 0 => MouseButton::Left,
275 1 => MouseButton::Middle,
276 2 => MouseButton::Right,
277 3 => MouseButton::None, _ => MouseButton::None,
279 }
280 };
281
282 let mouse = Mouse::new(cx, cy, button, modifiers);
283
284 let release = is_release.unwrap_or(cb & 3 == 3 && !is_wheel && !is_extra);
285 if is_wheel {
286 Event::MouseWheel(mouse)
287 } else if release {
288 Event::MouseRelease(mouse)
289 } else if is_move {
290 Event::MouseMove(mouse)
291 } else {
292 Event::MouseClick(mouse)
293 }
294}
295
296#[allow(dead_code)]
300pub(crate) fn encode_sgr_mouse(event: &Event) -> Option<(u16, u16, u16, bool)> {
301 let m = match event {
302 Event::MouseClick(m)
303 | Event::MouseRelease(m)
304 | Event::MouseWheel(m)
305 | Event::MouseMove(m) => m,
306 _ => return None,
307 };
308 let mut cb: u16 = match m.button {
309 MouseButton::Left => 0,
310 MouseButton::Middle => 1,
311 MouseButton::Right => 2,
312 MouseButton::None => 3,
313 MouseButton::WheelUp => 64,
314 MouseButton::WheelDown => 65,
315 MouseButton::WheelLeft => 66,
316 MouseButton::WheelRight => 67,
317 MouseButton::Button4 => 128,
318 MouseButton::Button5 => 129,
319 MouseButton::Button6 => 130,
320 MouseButton::Button7 => 131,
321 MouseButton::Button8 => 256,
322 MouseButton::Button9 => 257,
323 MouseButton::Button10 => 258,
324 MouseButton::Button11 => 259,
325 };
326
327 if m.modifiers.contains(KeyModifiers::SHIFT) {
328 cb |= 4;
329 }
330 if m.modifiers.contains(KeyModifiers::ALT) {
331 cb |= 8;
332 }
333 if m.modifiers.contains(KeyModifiers::CTRL) {
334 cb |= 16;
335 }
336
337 if matches!(event, Event::MouseMove(_)) {
338 cb |= 32;
339 }
340
341 let is_release = matches!(event, Event::MouseRelease(_));
342 Some((cb, m.x + 1, m.y + 1, is_release))
343}
344
345#[allow(dead_code)]
348pub(crate) fn write_sgr_mouse<W: std::io::Write>(w: &mut W, event: &Event) -> std::io::Result<()> {
349 let Some((cb, cx, cy, is_release)) = encode_sgr_mouse(event) else {
350 return Ok(());
351 };
352 let final_char = if is_release { 'm' } else { 'M' };
353 write!(w, "\x1b[<{cb};{cx};{cy}{final_char}")
354}
355
356fn decode_mouse_modifiers(cb: u16) -> KeyModifiers {
357 let mut mods = KeyModifiers::empty();
358 if cb & 4 != 0 {
359 mods |= KeyModifiers::SHIFT;
360 }
361 if cb & 8 != 0 {
362 mods |= KeyModifiers::ALT;
363 }
364 if cb & 16 != 0 {
365 mods |= KeyModifiers::CTRL;
366 }
367 mods
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
375 fn test_decode_sgr_left_click() {
376 let event = decode_sgr_mouse(Params::from_raw(b"0;10;20"), false).unwrap();
377 match event {
378 Event::MouseClick(m) => {
379 assert_eq!(m.button, MouseButton::Left);
380 assert_eq!(m.x, 9);
381 assert_eq!(m.y, 19);
382 }
383 _ => panic!("Expected MouseClick"),
384 }
385 }
386
387 #[test]
388 fn test_decode_sgr_release() {
389 let event = decode_sgr_mouse(Params::from_raw(b"0;5;5"), true).unwrap();
390 assert!(matches!(event, Event::MouseRelease(_)));
391 }
392
393 #[test]
394 fn test_decode_sgr_wheel() {
395 let event = decode_sgr_mouse(Params::from_raw(b"64;1;1"), false).unwrap();
396 match event {
397 Event::MouseWheel(m) => {
398 assert_eq!(m.button, MouseButton::WheelUp);
399 }
400 _ => panic!("Expected MouseWheel"),
401 }
402 }
403
404 #[test]
405 fn test_mouse_pixel_to_cell() {
406 let m = Mouse::new(100, 200, MouseButton::Left, KeyModifiers::empty());
408 let c = mouse_pixel_to_cell(m, 800, 600, 80, 24);
409 assert_eq!(c.x, 10);
410 assert_eq!(c.y, 8);
411 }
412
413 #[test]
414 fn test_decode_urxvt_click() {
415 let event = decode_urxvt_mouse(Params::from_raw(b"32;11;21")).unwrap();
417 match event {
418 Event::MouseClick(m) => {
419 assert_eq!(m.button, MouseButton::Left);
420 assert_eq!(m.x, 10);
421 assert_eq!(m.y, 20);
422 }
423 _ => panic!("Expected MouseClick"),
424 }
425 }
426
427 #[test]
428 fn test_decode_utf8_mouse() {
429 let (event, n) = decode_utf8_mouse(b" !!").unwrap();
431 assert_eq!(n, 3);
432 match event {
433 Event::MouseClick(m) => {
434 assert_eq!(m.x, 0);
435 assert_eq!(m.y, 0);
436 }
437 _ => panic!("Expected MouseClick"),
438 }
439 }
440
441 #[test]
442 fn test_sgr_roundtrip() {
443 let original =
444 Event::MouseClick(Mouse::new(10, 20, MouseButton::Left, KeyModifiers::empty()));
445
446 let mut buf = Vec::new();
447 write_sgr_mouse(&mut buf, &original).unwrap();
448 assert_eq!(buf, b"\x1b[<0;11;21M");
449 }
450}