Skip to main content

usehid_core/
mouse.rs

1//! Virtual Mouse implementation
2
3use crate::error::Result;
4use crate::hid::MouseReport;
5use crate::platform::HidBackend;
6use crate::Device;
7use bitflags::bitflags;
8use serde::{Deserialize, Serialize};
9
10bitflags! {
11    /// Mouse button flags
12    #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13    pub struct MouseButton: u8 {
14        const LEFT = 0x01;
15        const RIGHT = 0x02;
16        const MIDDLE = 0x04;
17        const BUTTON4 = 0x08;
18        const BUTTON5 = 0x10;
19    }
20}
21
22/// Virtual mouse device
23pub struct Mouse {
24    backend: Option<Box<dyn HidBackend>>,
25    report: MouseReport,
26    name: String,
27}
28
29impl Mouse {
30    /// Create a new virtual mouse
31    pub fn new() -> Self {
32        Self::with_name("useHID Virtual Mouse")
33    }
34    
35    /// Create a new virtual mouse with custom name
36    pub fn with_name(name: &str) -> Self {
37        Self {
38            backend: None,
39            report: MouseReport::default(),
40            name: name.to_string(),
41        }
42    }
43    
44    /// Move mouse by relative offset
45    pub fn move_by(&mut self, dx: i32, dy: i32) -> Result<()> {
46        // Clamp to i8 range, may need multiple reports for large movements
47        let steps_x = (dx.abs() + 126) / 127;
48        let steps_y = (dy.abs() + 126) / 127;
49        let steps = steps_x.max(steps_y).max(1);
50        
51        for i in 0..steps {
52            let x = if i < steps_x {
53                (dx.signum() * (dx.abs() / steps).min(127)) as i8
54            } else {
55                0
56            };
57            let y = if i < steps_y {
58                (dy.signum() * (dy.abs() / steps).min(127)) as i8
59            } else {
60                0
61            };
62            
63            self.report.x = x;
64            self.report.y = y;
65            self.send_report()?;
66        }
67        
68        self.report.x = 0;
69        self.report.y = 0;
70        Ok(())
71    }
72    
73    /// Press mouse button(s)
74    pub fn press(&mut self, button: MouseButton) -> Result<()> {
75        self.report.buttons |= button.bits();
76        self.send_report()
77    }
78    
79    /// Release mouse button(s)
80    pub fn release(&mut self, button: MouseButton) -> Result<()> {
81        self.report.buttons &= !button.bits();
82        self.send_report()
83    }
84    
85    /// Click (press and release) mouse button
86    pub fn click(&mut self, button: MouseButton) -> Result<()> {
87        self.press(button)?;
88        std::thread::sleep(std::time::Duration::from_millis(10));
89        self.release(button)
90    }
91    
92    /// Double click
93    pub fn double_click(&mut self, button: MouseButton) -> Result<()> {
94        self.click(button)?;
95        std::thread::sleep(std::time::Duration::from_millis(50));
96        self.click(button)
97    }
98    
99    /// Scroll wheel
100    pub fn scroll(&mut self, delta: i8) -> Result<()> {
101        self.report.wheel = delta;
102        self.send_report()?;
103        self.report.wheel = 0;
104        Ok(())
105    }
106    
107    fn send_report(&self) -> Result<()> {
108        if let Some(backend) = &self.backend {
109            backend.send_report(self.report.as_bytes())
110        } else {
111            Err(crate::error::Error::DeviceNotCreated)
112        }
113    }
114}
115
116impl Default for Mouse {
117    fn default() -> Self {
118        Self::new()
119    }
120}
121
122impl Device for Mouse {
123    fn create(&mut self) -> Result<()> {
124        if self.backend.is_some() {
125            return Err(crate::error::Error::DeviceAlreadyExists);
126        }
127        
128        let backend = crate::platform::create_mouse_backend(&self.name)?;
129        self.backend = Some(backend);
130        Ok(())
131    }
132    
133    fn destroy(&mut self) -> Result<()> {
134        if let Some(backend) = self.backend.take() {
135            backend.destroy()
136        } else {
137            Err(crate::error::Error::DeviceNotCreated)
138        }
139    }
140    
141    fn is_created(&self) -> bool {
142        self.backend.is_some()
143    }
144}