use crate::coordinates::CoordinateTransformer;
use crate::error::Result;
use std::time::Instant;
use tracing::debug;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MouseButton {
Left,
Right,
Middle,
Extra1,
Extra2,
}
impl MouseButton {
pub fn to_linux_button(&self) -> u32 {
match self {
MouseButton::Left => 0x110, MouseButton::Right => 0x111, MouseButton::Middle => 0x112, MouseButton::Extra1 => 0x113, MouseButton::Extra2 => 0x114, }
}
pub fn from_rdp_button(button: u16) -> Option<Self> {
match button {
0x1000 => Some(MouseButton::Left),
0x2000 => Some(MouseButton::Right),
0x4000 => Some(MouseButton::Middle),
0x0080 => Some(MouseButton::Extra1),
0x0100 => Some(MouseButton::Extra2),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub enum MouseEvent {
Move {
x: f64,
y: f64,
timestamp: Instant,
},
ButtonDown {
button: MouseButton,
timestamp: Instant,
},
ButtonUp {
button: MouseButton,
timestamp: Instant,
},
Scroll {
delta_x: i32,
delta_y: i32,
timestamp: Instant,
},
}
pub struct MouseHandler {
current_x: f64,
current_y: f64,
button_states: [bool; 5],
last_event_time: Option<Instant>,
high_precision_scroll: bool,
scroll_accum_x: f64,
scroll_accum_y: f64,
}
impl MouseHandler {
pub fn new() -> Self {
Self {
current_x: 0.0,
current_y: 0.0,
button_states: [false; 5],
last_event_time: None,
high_precision_scroll: true,
scroll_accum_x: 0.0,
scroll_accum_y: 0.0,
}
}
pub fn handle_absolute_move(
&mut self,
rdp_x: u32,
rdp_y: u32,
transformer: &mut CoordinateTransformer,
) -> Result<MouseEvent> {
let (stream_x, stream_y) = transformer.rdp_to_stream(rdp_x, rdp_y)?;
let (stream_x, stream_y) = transformer.clamp_to_bounds(stream_x, stream_y);
self.current_x = stream_x;
self.current_y = stream_y;
let timestamp = Instant::now();
self.last_event_time = Some(timestamp);
debug!(
"Mouse move: RDP({}, {}) -> Stream({:.2}, {:.2})",
rdp_x, rdp_y, stream_x, stream_y
);
Ok(MouseEvent::Move {
x: stream_x,
y: stream_y,
timestamp,
})
}
pub fn handle_relative_move(
&mut self,
delta_x: i32,
delta_y: i32,
transformer: &mut CoordinateTransformer,
) -> Result<MouseEvent> {
let (stream_x, stream_y) = transformer.apply_relative_movement(delta_x, delta_y)?;
let (stream_x, stream_y) = transformer.clamp_to_bounds(stream_x, stream_y);
self.current_x = stream_x;
self.current_y = stream_y;
let timestamp = Instant::now();
self.last_event_time = Some(timestamp);
debug!(
"Mouse relative move: Delta({}, {}) -> Stream({:.2}, {:.2})",
delta_x, delta_y, stream_x, stream_y
);
Ok(MouseEvent::Move {
x: stream_x,
y: stream_y,
timestamp,
})
}
pub fn handle_button_down(&mut self, button: MouseButton) -> Result<MouseEvent> {
let button_index = Self::button_to_index(button);
self.button_states[button_index] = true;
let timestamp = Instant::now();
self.last_event_time = Some(timestamp);
debug!("Mouse button down: {:?}", button);
Ok(MouseEvent::ButtonDown { button, timestamp })
}
pub fn handle_button_up(&mut self, button: MouseButton) -> Result<MouseEvent> {
let button_index = Self::button_to_index(button);
self.button_states[button_index] = false;
let timestamp = Instant::now();
self.last_event_time = Some(timestamp);
debug!("Mouse button up: {:?}", button);
Ok(MouseEvent::ButtonUp { button, timestamp })
}
pub fn handle_scroll(&mut self, delta_x: i32, delta_y: i32) -> Result<MouseEvent> {
let timestamp = Instant::now();
self.last_event_time = Some(timestamp);
let (final_delta_x, final_delta_y) = if self.high_precision_scroll {
self.scroll_accum_x += delta_x as f64 / 120.0;
self.scroll_accum_y += delta_y as f64 / 120.0;
let x = self.scroll_accum_x.trunc() as i32;
let y = self.scroll_accum_y.trunc() as i32;
self.scroll_accum_x -= x as f64;
self.scroll_accum_y -= y as f64;
(x, y)
} else {
(delta_x / 120, delta_y / 120)
};
debug!("Mouse scroll: ({}, {})", final_delta_x, final_delta_y);
Ok(MouseEvent::Scroll {
delta_x: final_delta_x,
delta_y: final_delta_y,
timestamp,
})
}
pub fn current_position(&self) -> (f64, f64) {
(self.current_x, self.current_y)
}
pub fn is_button_pressed(&self, button: MouseButton) -> bool {
let index = Self::button_to_index(button);
self.button_states[index]
}
pub fn time_since_last_event(&self) -> Option<std::time::Duration> {
self.last_event_time.map(|t| t.elapsed())
}
pub fn set_high_precision_scroll(&mut self, enabled: bool) {
self.high_precision_scroll = enabled;
if !enabled {
self.scroll_accum_x = 0.0;
self.scroll_accum_y = 0.0;
}
}
fn button_to_index(button: MouseButton) -> usize {
match button {
MouseButton::Left => 0,
MouseButton::Right => 1,
MouseButton::Middle => 2,
MouseButton::Extra1 => 3,
MouseButton::Extra2 => 4,
}
}
pub fn reset(&mut self) {
self.button_states = [false; 5];
self.scroll_accum_x = 0.0;
self.scroll_accum_y = 0.0;
}
}
impl Default for MouseHandler {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::coordinates::MonitorInfo;
fn create_test_transformer() -> CoordinateTransformer {
let monitor = MonitorInfo {
id: 1,
name: "Primary".to_string(),
x: 0,
y: 0,
width: 1920,
height: 1080,
dpi: 96.0,
scale_factor: 1.0,
stream_x: 0,
stream_y: 0,
stream_width: 1920,
stream_height: 1080,
is_primary: true,
};
CoordinateTransformer::new(vec![monitor]).unwrap()
}
#[test]
fn test_mouse_handler_creation() {
let handler = MouseHandler::new();
let (x, y) = handler.current_position();
assert_eq!(x, 0.0);
assert_eq!(y, 0.0);
}
#[test]
fn test_absolute_move() {
let mut handler = MouseHandler::new();
let mut transformer = create_test_transformer();
let event = handler.handle_absolute_move(960, 540, &mut transformer).unwrap();
match event {
MouseEvent::Move { x, y, .. } => {
assert!(x > 0.0);
assert!(y > 0.0);
}
_ => panic!("Expected Move event"),
}
let (x, y) = handler.current_position();
assert!(x > 0.0);
assert!(y > 0.0);
}
#[test]
fn test_relative_move() {
let mut handler = MouseHandler::new();
let mut transformer = create_test_transformer();
let event = handler.handle_relative_move(10, 10, &mut transformer).unwrap();
match event {
MouseEvent::Move { .. } => {}
_ => panic!("Expected Move event"),
}
}
#[test]
fn test_button_press_release() {
let mut handler = MouseHandler::new();
let event = handler.handle_button_down(MouseButton::Left).unwrap();
match event {
MouseEvent::ButtonDown { button, .. } => {
assert_eq!(button, MouseButton::Left);
}
_ => panic!("Expected ButtonDown event"),
}
assert!(handler.is_button_pressed(MouseButton::Left));
let event = handler.handle_button_up(MouseButton::Left).unwrap();
match event {
MouseEvent::ButtonUp { button, .. } => {
assert_eq!(button, MouseButton::Left);
}
_ => panic!("Expected ButtonUp event"),
}
assert!(!handler.is_button_pressed(MouseButton::Left));
}
#[test]
fn test_scroll_event() {
let mut handler = MouseHandler::new();
let event = handler.handle_scroll(0, 120).unwrap();
match event {
MouseEvent::Scroll { delta_y, .. } => {
assert_eq!(delta_y, 1);
}
_ => panic!("Expected Scroll event"),
}
}
#[test]
fn test_high_precision_scroll() {
let mut handler = MouseHandler::new();
handler.set_high_precision_scroll(true);
for _ in 0..10 {
let _ = handler.handle_scroll(0, 12); }
let event = handler.handle_scroll(0, 0).unwrap();
match event {
MouseEvent::Scroll { delta_y, .. } => {
assert_eq!(delta_y, 0); }
_ => panic!("Expected Scroll event"),
}
}
#[test]
fn test_mouse_button_to_linux() {
assert_eq!(MouseButton::Left.to_linux_button(), 0x110);
assert_eq!(MouseButton::Right.to_linux_button(), 0x111);
assert_eq!(MouseButton::Middle.to_linux_button(), 0x112);
assert_eq!(MouseButton::Extra1.to_linux_button(), 0x113);
assert_eq!(MouseButton::Extra2.to_linux_button(), 0x114);
}
#[test]
fn test_mouse_button_from_rdp() {
assert_eq!(MouseButton::from_rdp_button(0x1000), Some(MouseButton::Left));
assert_eq!(MouseButton::from_rdp_button(0x2000), Some(MouseButton::Right));
assert_eq!(MouseButton::from_rdp_button(0x4000), Some(MouseButton::Middle));
assert_eq!(MouseButton::from_rdp_button(0x0080), Some(MouseButton::Extra1));
assert_eq!(MouseButton::from_rdp_button(0x0100), Some(MouseButton::Extra2));
assert_eq!(MouseButton::from_rdp_button(0x9999), None);
}
#[test]
fn test_multiple_button_states() {
let mut handler = MouseHandler::new();
handler.handle_button_down(MouseButton::Left).unwrap();
handler.handle_button_down(MouseButton::Right).unwrap();
assert!(handler.is_button_pressed(MouseButton::Left));
assert!(handler.is_button_pressed(MouseButton::Right));
assert!(!handler.is_button_pressed(MouseButton::Middle));
handler.handle_button_up(MouseButton::Left).unwrap();
assert!(!handler.is_button_pressed(MouseButton::Left));
assert!(handler.is_button_pressed(MouseButton::Right));
}
#[test]
fn test_mouse_reset() {
let mut handler = MouseHandler::new();
handler.handle_button_down(MouseButton::Left).unwrap();
handler.handle_button_down(MouseButton::Right).unwrap();
handler.scroll_accum_x = 5.0;
handler.scroll_accum_y = 5.0;
handler.reset();
assert!(!handler.is_button_pressed(MouseButton::Left));
assert!(!handler.is_button_pressed(MouseButton::Right));
assert_eq!(handler.scroll_accum_x, 0.0);
assert_eq!(handler.scroll_accum_y, 0.0);
}
#[test]
fn test_time_since_last_event() {
let mut handler = MouseHandler::new();
assert!(handler.time_since_last_event().is_none());
handler.handle_button_down(MouseButton::Left).unwrap();
assert!(handler.time_since_last_event().is_some());
assert!(handler.time_since_last_event().unwrap().as_millis() < 100);
}
}