1use crate::error::{InputError, Result};
8use tracing::debug;
9
10#[derive(Debug, Clone)]
12pub struct MonitorInfo {
13 pub id: u32,
15
16 pub name: String,
18
19 pub x: i32,
21 pub y: i32,
22
23 pub width: u32,
25 pub height: u32,
26
27 pub dpi: f64,
29
30 pub scale_factor: f64,
32
33 pub stream_x: u32,
35 pub stream_y: u32,
36
37 pub stream_width: u32,
39 pub stream_height: u32,
40
41 pub is_primary: bool,
43}
44
45impl MonitorInfo {
46 pub fn contains_point(&self, x: f64, y: f64) -> bool {
48 x >= self.x as f64
49 && x < (self.x + self.width as i32) as f64
50 && y >= self.y as f64
51 && y < (self.y + self.height as i32) as f64
52 }
53
54 pub fn contains_stream_point(&self, x: f64, y: f64) -> bool {
56 let end_x = self.stream_x + self.stream_width;
57 let end_y = self.stream_y + self.stream_height;
58
59 x >= self.stream_x as f64 && x < end_x as f64 && y >= self.stream_y as f64 && y < end_y as f64
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct CoordinateSystem {
66 pub rdp_width: u32,
68 pub rdp_height: u32,
69
70 pub virtual_width: u32,
72 pub virtual_height: u32,
73 pub virtual_x_offset: i32,
74 pub virtual_y_offset: i32,
75
76 pub stream_width: u32,
78 pub stream_height: u32,
79
80 pub rdp_dpi: f64,
82 pub system_dpi: f64,
83}
84
85pub struct CoordinateTransformer {
87 coord_system: CoordinateSystem,
89
90 monitors: Vec<MonitorInfo>,
92
93 sub_pixel_x: f64,
95 sub_pixel_y: f64,
96
97 last_rdp_x: u32,
99 last_rdp_y: u32,
100
101 enable_acceleration: bool,
103
104 acceleration_factor: f64,
106
107 enable_sub_pixel: bool,
109}
110
111impl CoordinateTransformer {
112 pub fn new(monitors: Vec<MonitorInfo>) -> Result<Self> {
114 if monitors.is_empty() {
115 return Err(InputError::InvalidMonitorConfig("No monitors configured".to_string()));
116 }
117
118 let coord_system = Self::calculate_coordinate_system(&monitors);
119
120 Ok(Self {
121 coord_system,
122 monitors,
123 sub_pixel_x: 0.0,
124 sub_pixel_y: 0.0,
125 last_rdp_x: 0,
126 last_rdp_y: 0,
127 enable_acceleration: true,
128 acceleration_factor: 1.0,
129 enable_sub_pixel: true,
130 })
131 }
132
133 fn calculate_coordinate_system(monitors: &[MonitorInfo]) -> CoordinateSystem {
135 let mut min_x = i32::MAX;
137 let mut min_y = i32::MAX;
138 let mut max_x = i32::MIN;
139 let mut max_y = i32::MIN;
140
141 for monitor in monitors {
142 min_x = min_x.min(monitor.x);
143 min_y = min_y.min(monitor.y);
144 max_x = max_x.max(monitor.x + monitor.width as i32);
145 max_y = max_y.max(monitor.y + monitor.height as i32);
146 }
147
148 let virtual_width = (max_x - min_x) as u32;
149 let virtual_height = (max_y - min_y) as u32;
150
151 let stream_width = monitors.iter().map(|m| m.stream_width).max().unwrap_or(0);
153 let stream_height = monitors.iter().map(|m| m.stream_height).max().unwrap_or(0);
154
155 let primary = monitors.iter().find(|m| m.is_primary).unwrap_or(&monitors[0]);
157
158 CoordinateSystem {
159 rdp_width: primary.width,
160 rdp_height: primary.height,
161 virtual_width,
162 virtual_height,
163 virtual_x_offset: min_x,
164 virtual_y_offset: min_y,
165 stream_width,
166 stream_height,
167 rdp_dpi: primary.dpi,
168 system_dpi: 96.0, }
170 }
171
172 pub fn rdp_to_stream(&mut self, rdp_x: u32, rdp_y: u32) -> Result<(f64, f64)> {
174 let norm_x = rdp_x as f64 / self.coord_system.rdp_width as f64;
176 let norm_y = rdp_y as f64 / self.coord_system.rdp_height as f64;
177
178 let dpi_scale = self.coord_system.system_dpi / self.coord_system.rdp_dpi;
180 let scaled_x = norm_x * dpi_scale;
181 let scaled_y = norm_y * dpi_scale;
182
183 let virtual_x = scaled_x * self.coord_system.virtual_width as f64 + self.coord_system.virtual_x_offset as f64;
185 let virtual_y = scaled_y * self.coord_system.virtual_height as f64 + self.coord_system.virtual_y_offset as f64;
186
187 let monitor = self.find_monitor_at_point(virtual_x, virtual_y)?;
189
190 let local_x = virtual_x - monitor.x as f64;
192 let local_y = virtual_y - monitor.y as f64;
193
194 let monitor_scale_x = monitor.stream_width as f64 / monitor.width as f64;
196 let monitor_scale_y = monitor.stream_height as f64 / monitor.height as f64;
197
198 let stream_x = monitor.stream_x as f64 + (local_x * monitor_scale_x * monitor.scale_factor);
199 let stream_y = monitor.stream_y as f64 + (local_y * monitor_scale_y * monitor.scale_factor);
200
201 if self.enable_sub_pixel {
203 self.sub_pixel_x += stream_x - stream_x.floor();
204 self.sub_pixel_y += stream_y - stream_y.floor();
205
206 let final_x = stream_x.floor()
207 + if self.sub_pixel_x >= 1.0 {
208 self.sub_pixel_x -= 1.0;
209 1.0
210 } else {
211 0.0
212 };
213
214 let final_y = stream_y.floor()
215 + if self.sub_pixel_y >= 1.0 {
216 self.sub_pixel_y -= 1.0;
217 1.0
218 } else {
219 0.0
220 };
221
222 Ok((final_x, final_y))
223 } else {
224 Ok((stream_x, stream_y))
225 }
226 }
227
228 pub fn stream_to_rdp(&self, stream_x: f64, stream_y: f64) -> Result<(u32, u32)> {
230 let monitor = self.find_monitor_from_stream(stream_x, stream_y)?;
232
233 let local_stream_x = stream_x - monitor.stream_x as f64;
235 let local_stream_y = stream_y - monitor.stream_y as f64;
236
237 let monitor_scale_x = monitor.width as f64 / monitor.stream_width as f64;
239 let monitor_scale_y = monitor.height as f64 / monitor.stream_height as f64;
240
241 let local_x = local_stream_x * monitor_scale_x / monitor.scale_factor;
242 let local_y = local_stream_y * monitor_scale_y / monitor.scale_factor;
243
244 let virtual_x = monitor.x as f64 + local_x;
246 let virtual_y = monitor.y as f64 + local_y;
247
248 let norm_x = (virtual_x - self.coord_system.virtual_x_offset as f64) / self.coord_system.virtual_width as f64;
250 let norm_y = (virtual_y - self.coord_system.virtual_y_offset as f64) / self.coord_system.virtual_height as f64;
251
252 let dpi_scale = self.coord_system.rdp_dpi / self.coord_system.system_dpi;
254 let scaled_x = norm_x * dpi_scale;
255 let scaled_y = norm_y * dpi_scale;
256
257 let rdp_x = (scaled_x * self.coord_system.rdp_width as f64).round() as u32;
259 let rdp_y = (scaled_y * self.coord_system.rdp_height as f64).round() as u32;
260
261 let rdp_x = rdp_x.min(self.coord_system.rdp_width.saturating_sub(1));
263 let rdp_y = rdp_y.min(self.coord_system.rdp_height.saturating_sub(1));
264
265 Ok((rdp_x, rdp_y))
266 }
267
268 pub fn apply_relative_movement(&mut self, delta_x: i32, delta_y: i32) -> Result<(f64, f64)> {
270 let accel_x = if self.enable_acceleration {
272 delta_x as f64 * self.calculate_acceleration(delta_x.abs())
273 } else {
274 delta_x as f64
275 };
276
277 let accel_y = if self.enable_acceleration {
278 delta_y as f64 * self.calculate_acceleration(delta_y.abs())
279 } else {
280 delta_y as f64
281 };
282
283 let new_rdp_x = (self.last_rdp_x as i32 + accel_x as i32).max(0) as u32;
285 let new_rdp_y = (self.last_rdp_y as i32 + accel_y as i32).max(0) as u32;
286
287 let new_rdp_x = new_rdp_x.min(self.coord_system.rdp_width.saturating_sub(1));
289 let new_rdp_y = new_rdp_y.min(self.coord_system.rdp_height.saturating_sub(1));
290
291 self.last_rdp_x = new_rdp_x;
292 self.last_rdp_y = new_rdp_y;
293
294 self.rdp_to_stream(new_rdp_x, new_rdp_y)
296 }
297
298 fn calculate_acceleration(&self, speed: i32) -> f64 {
300 let base = self.acceleration_factor;
302 if speed < 2 {
303 base
304 } else if speed < 4 {
305 base * 1.5
306 } else if speed < 6 {
307 base * 2.0
308 } else if speed < 9 {
309 base * 2.5
310 } else if speed < 13 {
311 base * 3.0
312 } else {
313 base * 3.5
314 }
315 }
316
317 fn find_monitor_at_point(&self, x: f64, y: f64) -> Result<&MonitorInfo> {
319 for monitor in &self.monitors {
320 if monitor.contains_point(x, y) {
321 return Ok(monitor);
322 }
323 }
324
325 self.monitors
327 .iter()
328 .find(|m| m.is_primary)
329 .or_else(|| self.monitors.first())
330 .ok_or(InputError::InvalidCoordinate(x, y))
331 }
332
333 fn find_monitor_from_stream(&self, stream_x: f64, stream_y: f64) -> Result<&MonitorInfo> {
335 for monitor in &self.monitors {
336 if monitor.contains_stream_point(stream_x, stream_y) {
337 return Ok(monitor);
338 }
339 }
340
341 self.monitors
343 .first()
344 .ok_or(InputError::InvalidCoordinate(stream_x, stream_y))
345 }
346
347 pub fn clamp_to_bounds(&self, x: f64, y: f64) -> (f64, f64) {
349 let clamped_x = x.max(0.0).min(self.coord_system.stream_width as f64 - 1.0);
350 let clamped_y = y.max(0.0).min(self.coord_system.stream_height as f64 - 1.0);
351 (clamped_x, clamped_y)
352 }
353
354 pub fn update_monitors(&mut self, monitors: Vec<MonitorInfo>) -> Result<()> {
356 if monitors.is_empty() {
357 return Err(InputError::InvalidMonitorConfig("No monitors configured".to_string()));
358 }
359
360 self.coord_system = Self::calculate_coordinate_system(&monitors);
361 self.monitors = monitors;
362 self.sub_pixel_x = 0.0;
363 self.sub_pixel_y = 0.0;
364
365 debug!("Updated monitor configuration: {} monitors", self.monitors.len());
366 Ok(())
367 }
368
369 pub fn set_acceleration_enabled(&mut self, enabled: bool) {
371 self.enable_acceleration = enabled;
372 }
373
374 pub fn set_acceleration_factor(&mut self, factor: f64) {
376 self.acceleration_factor = factor;
377 }
378
379 pub fn set_sub_pixel_enabled(&mut self, enabled: bool) {
381 self.enable_sub_pixel = enabled;
382 }
383
384 pub fn monitor_count(&self) -> usize {
386 self.monitors.len()
387 }
388
389 pub fn get_monitor(&self, id: u32) -> Option<&MonitorInfo> {
391 self.monitors.iter().find(|m| m.id == id)
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 fn create_test_monitor() -> MonitorInfo {
400 MonitorInfo {
401 id: 1,
402 name: "Primary".to_string(),
403 x: 0,
404 y: 0,
405 width: 1920,
406 height: 1080,
407 dpi: 96.0,
408 scale_factor: 1.0,
409 stream_x: 0,
410 stream_y: 0,
411 stream_width: 1920,
412 stream_height: 1080,
413 is_primary: true,
414 }
415 }
416
417 #[test]
418 fn test_coordinate_transformer_creation() {
419 let monitor = create_test_monitor();
420 let transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
421
422 assert_eq!(transformer.monitor_count(), 1);
423 }
424
425 #[test]
426 fn test_rdp_to_stream_single_monitor() {
427 let monitor = create_test_monitor();
428 let mut transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
429
430 let (x, y) = transformer.rdp_to_stream(0, 0).unwrap();
432 assert!(x >= 0.0 && x <= 1.0);
433 assert!(y >= 0.0 && y <= 1.0);
434
435 let (x, y) = transformer.rdp_to_stream(1919, 1079).unwrap();
436 assert!(x <= 1920.0);
437 assert!(y <= 1080.0);
438
439 let (x, y) = transformer.rdp_to_stream(960, 540).unwrap();
441 assert!(x > 900.0 && x < 1000.0);
442 assert!(y > 500.0 && y < 600.0);
443 }
444
445 #[test]
446 fn test_stream_to_rdp_single_monitor() {
447 let monitor = create_test_monitor();
448 let transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
449
450 let (rdp_x, rdp_y) = transformer.stream_to_rdp(100.0, 100.0).unwrap();
452 assert!(rdp_x < 1920);
453 assert!(rdp_y < 1080);
454
455 let (rdp_x, rdp_y) = transformer.stream_to_rdp(1900.0, 1000.0).unwrap();
456 assert!(rdp_x < 1920);
457 assert!(rdp_y < 1080);
458 }
459
460 #[test]
461 fn test_round_trip_transformation() {
462 let monitor = create_test_monitor();
463 let mut transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
464 transformer.set_sub_pixel_enabled(false); let test_points = vec![(0, 0), (960, 540), (1919, 1079)];
468
469 for (orig_x, orig_y) in test_points {
470 let (stream_x, stream_y) = transformer.rdp_to_stream(orig_x, orig_y).unwrap();
471 let (rdp_x, rdp_y) = transformer.stream_to_rdp(stream_x, stream_y).unwrap();
472
473 assert!((rdp_x as i32 - orig_x as i32).abs() <= 1);
475 assert!((rdp_y as i32 - orig_y as i32).abs() <= 1);
476 }
477 }
478
479 #[test]
480 fn test_multi_monitor_configuration() {
481 let monitors = vec![
482 MonitorInfo {
483 id: 1,
484 name: "Left".to_string(),
485 x: 0,
486 y: 0,
487 width: 1920,
488 height: 1080,
489 dpi: 96.0,
490 scale_factor: 1.0,
491 stream_x: 0,
492 stream_y: 0,
493 stream_width: 1920,
494 stream_height: 1080,
495 is_primary: true,
496 },
497 MonitorInfo {
498 id: 2,
499 name: "Right".to_string(),
500 x: 1920,
501 y: 0,
502 width: 1920,
503 height: 1080,
504 dpi: 96.0,
505 scale_factor: 1.0,
506 stream_x: 1920,
507 stream_y: 0,
508 stream_width: 1920,
509 stream_height: 1080,
510 is_primary: false,
511 },
512 ];
513
514 let transformer = CoordinateTransformer::new(monitors).unwrap();
515 assert_eq!(transformer.monitor_count(), 2);
516 }
517
518 #[test]
519 fn test_relative_movement() {
520 let monitor = create_test_monitor();
521 let mut transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
522 transformer.last_rdp_x = 960;
523 transformer.last_rdp_y = 540;
524
525 let (x, y) = transformer.apply_relative_movement(10, 10).unwrap();
526 assert!(x > 960.0);
527 assert!(y > 540.0);
528 }
529
530 #[test]
531 fn test_mouse_acceleration() {
532 let monitor = create_test_monitor();
533 let mut transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
534 transformer.set_acceleration_enabled(true);
535 transformer.set_acceleration_factor(1.0);
536
537 let accel_small = transformer.calculate_acceleration(1);
539 assert_eq!(accel_small, 1.0);
540
541 let accel_large = transformer.calculate_acceleration(15);
543 assert!(accel_large > 1.0);
544 }
545
546 #[test]
547 fn test_clamp_to_bounds() {
548 let monitor = create_test_monitor();
549 let transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
550
551 let (x, y) = transformer.clamp_to_bounds(-10.0, -10.0);
553 assert_eq!(x, 0.0);
554 assert_eq!(y, 0.0);
555
556 let (x, y) = transformer.clamp_to_bounds(2000.0, 2000.0);
557 assert!(x < 1920.0);
558 assert!(y < 1080.0);
559 }
560
561 #[test]
562 fn test_monitor_contains_point() {
563 let monitor = create_test_monitor();
564
565 assert!(monitor.contains_point(100.0, 100.0));
566 assert!(monitor.contains_point(1919.0, 1079.0));
567 assert!(!monitor.contains_point(-1.0, 0.0));
568 assert!(!monitor.contains_point(1920.0, 0.0));
569 }
570
571 #[test]
572 fn test_update_monitors() {
573 let monitor = create_test_monitor();
574 let mut transformer = CoordinateTransformer::new(vec![monitor]).unwrap();
575
576 let new_monitors = vec![
577 create_test_monitor(),
578 MonitorInfo {
579 id: 2,
580 name: "Secondary".to_string(),
581 x: 1920,
582 y: 0,
583 width: 1920,
584 height: 1080,
585 dpi: 96.0,
586 scale_factor: 1.0,
587 stream_x: 1920,
588 stream_y: 0,
589 stream_width: 1920,
590 stream_height: 1080,
591 is_primary: false,
592 },
593 ];
594
595 transformer.update_monitors(new_monitors).unwrap();
596 assert_eq!(transformer.monitor_count(), 2);
597 }
598
599 #[test]
600 fn test_empty_monitor_list_error() {
601 let result = CoordinateTransformer::new(vec![]);
602 assert!(result.is_err());
603 }
604}