lamco_rdp_input/
mouse.rs

1//! Mouse Event Handling
2//!
3//! Handles mouse movement, button presses, and scroll wheel events with
4//! coordinate transformation and button mapping.
5
6use crate::coordinates::CoordinateTransformer;
7use crate::error::Result;
8use std::time::Instant;
9use tracing::debug;
10
11/// Mouse button identifiers
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum MouseButton {
14    /// Left mouse button
15    Left,
16    /// Right mouse button
17    Right,
18    /// Middle mouse button
19    Middle,
20    /// Extra button 1 (side button)
21    Extra1,
22    /// Extra button 2 (side button)
23    Extra2,
24}
25
26impl MouseButton {
27    /// Convert to Linux button code
28    pub fn to_linux_button(&self) -> u32 {
29        match self {
30            MouseButton::Left => 0x110,   // BTN_LEFT
31            MouseButton::Right => 0x111,  // BTN_RIGHT
32            MouseButton::Middle => 0x112, // BTN_MIDDLE
33            MouseButton::Extra1 => 0x113, // BTN_SIDE
34            MouseButton::Extra2 => 0x114, // BTN_EXTRA
35        }
36    }
37
38    /// Convert from RDP button flags
39    pub fn from_rdp_button(button: u16) -> Option<Self> {
40        match button {
41            0x1000 => Some(MouseButton::Left),
42            0x2000 => Some(MouseButton::Right),
43            0x4000 => Some(MouseButton::Middle),
44            0x0080 => Some(MouseButton::Extra1),
45            0x0100 => Some(MouseButton::Extra2),
46            _ => None,
47        }
48    }
49}
50
51/// Mouse event types
52#[derive(Debug, Clone)]
53pub enum MouseEvent {
54    /// Mouse moved to absolute position
55    Move {
56        /// X coordinate
57        x: f64,
58        /// Y coordinate
59        y: f64,
60        /// Event timestamp
61        timestamp: Instant,
62    },
63
64    /// Mouse button pressed
65    ButtonDown {
66        /// Button that was pressed
67        button: MouseButton,
68        /// Event timestamp
69        timestamp: Instant,
70    },
71
72    /// Mouse button released
73    ButtonUp {
74        /// Button that was released
75        button: MouseButton,
76        /// Event timestamp
77        timestamp: Instant,
78    },
79
80    /// Mouse wheel scrolled
81    Scroll {
82        /// Horizontal scroll delta
83        delta_x: i32,
84        /// Vertical scroll delta
85        delta_y: i32,
86        /// Event timestamp
87        timestamp: Instant,
88    },
89}
90
91/// Mouse event handler
92pub struct MouseHandler {
93    /// Current mouse position (stream coordinates)
94    current_x: f64,
95    current_y: f64,
96
97    /// Button states
98    button_states: [bool; 5],
99
100    /// Last event timestamp
101    last_event_time: Option<Instant>,
102
103    /// Enable high-precision scrolling
104    high_precision_scroll: bool,
105
106    /// Scroll accumulator for high-precision scrolling
107    scroll_accum_x: f64,
108    scroll_accum_y: f64,
109}
110
111impl MouseHandler {
112    /// Create a new mouse handler
113    pub fn new() -> Self {
114        Self {
115            current_x: 0.0,
116            current_y: 0.0,
117            button_states: [false; 5],
118            last_event_time: None,
119            high_precision_scroll: true,
120            scroll_accum_x: 0.0,
121            scroll_accum_y: 0.0,
122        }
123    }
124
125    /// Process absolute mouse movement from RDP
126    pub fn handle_absolute_move(
127        &mut self,
128        rdp_x: u32,
129        rdp_y: u32,
130        transformer: &mut CoordinateTransformer,
131    ) -> Result<MouseEvent> {
132        let (stream_x, stream_y) = transformer.rdp_to_stream(rdp_x, rdp_y)?;
133
134        // Clamp to bounds
135        let (stream_x, stream_y) = transformer.clamp_to_bounds(stream_x, stream_y);
136
137        self.current_x = stream_x;
138        self.current_y = stream_y;
139
140        let timestamp = Instant::now();
141        self.last_event_time = Some(timestamp);
142
143        debug!(
144            "Mouse move: RDP({}, {}) -> Stream({:.2}, {:.2})",
145            rdp_x, rdp_y, stream_x, stream_y
146        );
147
148        Ok(MouseEvent::Move {
149            x: stream_x,
150            y: stream_y,
151            timestamp,
152        })
153    }
154
155    /// Process relative mouse movement from RDP
156    pub fn handle_relative_move(
157        &mut self,
158        delta_x: i32,
159        delta_y: i32,
160        transformer: &mut CoordinateTransformer,
161    ) -> Result<MouseEvent> {
162        let (stream_x, stream_y) = transformer.apply_relative_movement(delta_x, delta_y)?;
163
164        // Clamp to bounds
165        let (stream_x, stream_y) = transformer.clamp_to_bounds(stream_x, stream_y);
166
167        self.current_x = stream_x;
168        self.current_y = stream_y;
169
170        let timestamp = Instant::now();
171        self.last_event_time = Some(timestamp);
172
173        debug!(
174            "Mouse relative move: Delta({}, {}) -> Stream({:.2}, {:.2})",
175            delta_x, delta_y, stream_x, stream_y
176        );
177
178        Ok(MouseEvent::Move {
179            x: stream_x,
180            y: stream_y,
181            timestamp,
182        })
183    }
184
185    /// Process mouse button press
186    pub fn handle_button_down(&mut self, button: MouseButton) -> Result<MouseEvent> {
187        let button_index = Self::button_to_index(button);
188        self.button_states[button_index] = true;
189
190        let timestamp = Instant::now();
191        self.last_event_time = Some(timestamp);
192
193        debug!("Mouse button down: {:?}", button);
194
195        Ok(MouseEvent::ButtonDown { button, timestamp })
196    }
197
198    /// Process mouse button release
199    pub fn handle_button_up(&mut self, button: MouseButton) -> Result<MouseEvent> {
200        let button_index = Self::button_to_index(button);
201        self.button_states[button_index] = false;
202
203        let timestamp = Instant::now();
204        self.last_event_time = Some(timestamp);
205
206        debug!("Mouse button up: {:?}", button);
207
208        Ok(MouseEvent::ButtonUp { button, timestamp })
209    }
210
211    /// Process mouse wheel scroll
212    pub fn handle_scroll(&mut self, delta_x: i32, delta_y: i32) -> Result<MouseEvent> {
213        let timestamp = Instant::now();
214        self.last_event_time = Some(timestamp);
215
216        let (final_delta_x, final_delta_y) = if self.high_precision_scroll {
217            // Accumulate fractional scrolling
218            self.scroll_accum_x += delta_x as f64 / 120.0;
219            self.scroll_accum_y += delta_y as f64 / 120.0;
220
221            let x = self.scroll_accum_x.trunc() as i32;
222            let y = self.scroll_accum_y.trunc() as i32;
223
224            self.scroll_accum_x -= x as f64;
225            self.scroll_accum_y -= y as f64;
226
227            (x, y)
228        } else {
229            // Standard scrolling
230            (delta_x / 120, delta_y / 120)
231        };
232
233        debug!("Mouse scroll: ({}, {})", final_delta_x, final_delta_y);
234
235        Ok(MouseEvent::Scroll {
236            delta_x: final_delta_x,
237            delta_y: final_delta_y,
238            timestamp,
239        })
240    }
241
242    /// Get current mouse position
243    pub fn current_position(&self) -> (f64, f64) {
244        (self.current_x, self.current_y)
245    }
246
247    /// Check if button is currently pressed
248    pub fn is_button_pressed(&self, button: MouseButton) -> bool {
249        let index = Self::button_to_index(button);
250        self.button_states[index]
251    }
252
253    /// Get time since last event
254    pub fn time_since_last_event(&self) -> Option<std::time::Duration> {
255        self.last_event_time.map(|t| t.elapsed())
256    }
257
258    /// Set high-precision scrolling enabled
259    pub fn set_high_precision_scroll(&mut self, enabled: bool) {
260        self.high_precision_scroll = enabled;
261        if !enabled {
262            self.scroll_accum_x = 0.0;
263            self.scroll_accum_y = 0.0;
264        }
265    }
266
267    /// Convert button to array index
268    fn button_to_index(button: MouseButton) -> usize {
269        match button {
270            MouseButton::Left => 0,
271            MouseButton::Right => 1,
272            MouseButton::Middle => 2,
273            MouseButton::Extra1 => 3,
274            MouseButton::Extra2 => 4,
275        }
276    }
277
278    /// Reset mouse state
279    pub fn reset(&mut self) {
280        self.button_states = [false; 5];
281        self.scroll_accum_x = 0.0;
282        self.scroll_accum_y = 0.0;
283    }
284}
285
286impl Default for MouseHandler {
287    fn default() -> Self {
288        Self::new()
289    }
290}
291
292#[cfg(test)]
293mod tests {
294    use super::*;
295    use crate::coordinates::MonitorInfo;
296
297    fn create_test_transformer() -> CoordinateTransformer {
298        let monitor = MonitorInfo {
299            id: 1,
300            name: "Primary".to_string(),
301            x: 0,
302            y: 0,
303            width: 1920,
304            height: 1080,
305            dpi: 96.0,
306            scale_factor: 1.0,
307            stream_x: 0,
308            stream_y: 0,
309            stream_width: 1920,
310            stream_height: 1080,
311            is_primary: true,
312        };
313
314        CoordinateTransformer::new(vec![monitor]).unwrap()
315    }
316
317    #[test]
318    fn test_mouse_handler_creation() {
319        let handler = MouseHandler::new();
320        let (x, y) = handler.current_position();
321        assert_eq!(x, 0.0);
322        assert_eq!(y, 0.0);
323    }
324
325    #[test]
326    fn test_absolute_move() {
327        let mut handler = MouseHandler::new();
328        let mut transformer = create_test_transformer();
329
330        let event = handler.handle_absolute_move(960, 540, &mut transformer).unwrap();
331
332        match event {
333            MouseEvent::Move { x, y, .. } => {
334                assert!(x > 0.0);
335                assert!(y > 0.0);
336            }
337            _ => panic!("Expected Move event"),
338        }
339
340        let (x, y) = handler.current_position();
341        assert!(x > 0.0);
342        assert!(y > 0.0);
343    }
344
345    #[test]
346    fn test_relative_move() {
347        let mut handler = MouseHandler::new();
348        let mut transformer = create_test_transformer();
349
350        let event = handler.handle_relative_move(10, 10, &mut transformer).unwrap();
351
352        match event {
353            MouseEvent::Move { .. } => {}
354            _ => panic!("Expected Move event"),
355        }
356    }
357
358    #[test]
359    fn test_button_press_release() {
360        let mut handler = MouseHandler::new();
361
362        // Press left button
363        let event = handler.handle_button_down(MouseButton::Left).unwrap();
364        match event {
365            MouseEvent::ButtonDown { button, .. } => {
366                assert_eq!(button, MouseButton::Left);
367            }
368            _ => panic!("Expected ButtonDown event"),
369        }
370
371        assert!(handler.is_button_pressed(MouseButton::Left));
372
373        // Release left button
374        let event = handler.handle_button_up(MouseButton::Left).unwrap();
375        match event {
376            MouseEvent::ButtonUp { button, .. } => {
377                assert_eq!(button, MouseButton::Left);
378            }
379            _ => panic!("Expected ButtonUp event"),
380        }
381
382        assert!(!handler.is_button_pressed(MouseButton::Left));
383    }
384
385    #[test]
386    fn test_scroll_event() {
387        let mut handler = MouseHandler::new();
388
389        let event = handler.handle_scroll(0, 120).unwrap();
390
391        match event {
392            MouseEvent::Scroll { delta_y, .. } => {
393                assert_eq!(delta_y, 1);
394            }
395            _ => panic!("Expected Scroll event"),
396        }
397    }
398
399    #[test]
400    fn test_high_precision_scroll() {
401        let mut handler = MouseHandler::new();
402        handler.set_high_precision_scroll(true);
403
404        // Send small scroll increments
405        for _ in 0..10 {
406            let _ = handler.handle_scroll(0, 12); // 1/10 of a standard scroll unit
407        }
408
409        // Should accumulate to one full scroll unit
410        let event = handler.handle_scroll(0, 0).unwrap();
411        match event {
412            MouseEvent::Scroll { delta_y, .. } => {
413                assert_eq!(delta_y, 0); // Accumulated but not yet reached threshold
414            }
415            _ => panic!("Expected Scroll event"),
416        }
417    }
418
419    #[test]
420    fn test_mouse_button_to_linux() {
421        assert_eq!(MouseButton::Left.to_linux_button(), 0x110);
422        assert_eq!(MouseButton::Right.to_linux_button(), 0x111);
423        assert_eq!(MouseButton::Middle.to_linux_button(), 0x112);
424        assert_eq!(MouseButton::Extra1.to_linux_button(), 0x113);
425        assert_eq!(MouseButton::Extra2.to_linux_button(), 0x114);
426    }
427
428    #[test]
429    fn test_mouse_button_from_rdp() {
430        assert_eq!(MouseButton::from_rdp_button(0x1000), Some(MouseButton::Left));
431        assert_eq!(MouseButton::from_rdp_button(0x2000), Some(MouseButton::Right));
432        assert_eq!(MouseButton::from_rdp_button(0x4000), Some(MouseButton::Middle));
433        assert_eq!(MouseButton::from_rdp_button(0x0080), Some(MouseButton::Extra1));
434        assert_eq!(MouseButton::from_rdp_button(0x0100), Some(MouseButton::Extra2));
435        assert_eq!(MouseButton::from_rdp_button(0x9999), None);
436    }
437
438    #[test]
439    fn test_multiple_button_states() {
440        let mut handler = MouseHandler::new();
441
442        handler.handle_button_down(MouseButton::Left).unwrap();
443        handler.handle_button_down(MouseButton::Right).unwrap();
444
445        assert!(handler.is_button_pressed(MouseButton::Left));
446        assert!(handler.is_button_pressed(MouseButton::Right));
447        assert!(!handler.is_button_pressed(MouseButton::Middle));
448
449        handler.handle_button_up(MouseButton::Left).unwrap();
450
451        assert!(!handler.is_button_pressed(MouseButton::Left));
452        assert!(handler.is_button_pressed(MouseButton::Right));
453    }
454
455    #[test]
456    fn test_mouse_reset() {
457        let mut handler = MouseHandler::new();
458
459        handler.handle_button_down(MouseButton::Left).unwrap();
460        handler.handle_button_down(MouseButton::Right).unwrap();
461        handler.scroll_accum_x = 5.0;
462        handler.scroll_accum_y = 5.0;
463
464        handler.reset();
465
466        assert!(!handler.is_button_pressed(MouseButton::Left));
467        assert!(!handler.is_button_pressed(MouseButton::Right));
468        assert_eq!(handler.scroll_accum_x, 0.0);
469        assert_eq!(handler.scroll_accum_y, 0.0);
470    }
471
472    #[test]
473    fn test_time_since_last_event() {
474        let mut handler = MouseHandler::new();
475
476        assert!(handler.time_since_last_event().is_none());
477
478        handler.handle_button_down(MouseButton::Left).unwrap();
479
480        assert!(handler.time_since_last_event().is_some());
481        assert!(handler.time_since_last_event().unwrap().as_millis() < 100);
482    }
483}