@@ -6,6 +6,7 @@ use windows::Win32::Graphics::Gdi::{
66 GetDeviceCaps , GetDIBits , GetPixel , LOGPIXELSX , ReleaseDC , SelectObject , BITMAPINFO ,
77 BITMAPINFOHEADER , BI_RGB , CLR_INVALID , DIB_RGB_COLORS , HDC , SRCCOPY ,
88} ;
9+ use windows:: Win32 :: UI :: HiDpi :: { SetProcessDpiAwarenessContext , DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 } ;
910use windows:: Win32 :: UI :: WindowsAndMessaging :: GetCursorPos ;
1011
1112pub struct WindowsSampler {
@@ -16,6 +17,11 @@ pub struct WindowsSampler {
1617impl WindowsSampler {
1718 pub fn new ( ) -> Result < Self , String > {
1819 unsafe {
20+ // Set DPI awareness to per-monitor v2 so we can access physical pixels
21+ // This must be done before any GDI calls
22+ // Ignore errors - if it fails, we'll fall back to system DPI awareness
23+ let _ = SetProcessDpiAwarenessContext ( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ) ;
24+
1925 let hdc = GetDC ( None ) ;
2026
2127 if hdc. is_invalid ( ) {
@@ -27,6 +33,8 @@ impl WindowsSampler {
2733 // Standard DPI is 96, so scale = actual_dpi / 96
2834 let dpi = GetDeviceCaps ( hdc, LOGPIXELSX ) ;
2935 let dpi_scale = dpi as f64 / 96.0 ;
36+
37+ eprintln ! ( "[WindowsSampler] DPI scale factor: {}" , dpi_scale) ;
3038
3139 Ok ( WindowsSampler {
3240 hdc,
@@ -47,12 +55,13 @@ impl Drop for WindowsSampler {
4755impl PixelSampler for WindowsSampler {
4856 fn sample_pixel ( & mut self , x : i32 , y : i32 ) -> Result < Color , String > {
4957 unsafe {
50- // On Windows, for a DPI-unaware process (which this Rust subprocess is):
51- // - GetCursorPos returns VIRTUALIZED coordinates (e.g., 0-2559 at 200% on 5120 wide screen)
52- // - GetDC(None) returns a VIRTUALIZED DC that also uses virtual coordinates
53- // - GetPixel on that DC expects the SAME virtualized coordinates
54- // NO conversion needed - both APIs work in the same virtualized space
55- let color_ref = GetPixel ( self . hdc , x, y) ;
58+ // With DPI awareness enabled, follow the macOS pattern:
59+ // - x, y are logical coordinates (like CGWindowListCreateImage on macOS)
60+ // - Convert to physical coordinates internally for GDI
61+ let physical_x = ( x as f64 * self . dpi_scale ) as i32 ;
62+ let physical_y = ( y as f64 * self . dpi_scale ) as i32 ;
63+
64+ let color_ref = GetPixel ( self . hdc , physical_x, physical_y) ;
5665
5766 // Check for error (CLR_INVALID is returned on error)
5867 // COLORREF is a newtype wrapper around u32
@@ -79,14 +88,16 @@ impl PixelSampler for WindowsSampler {
7988 GetCursorPos ( & mut point)
8089 . map_err ( |e| format ! ( "Failed to get cursor position: {}" , e) ) ?;
8190
82- // Convert from virtual coordinates (returned by GetCursorPos) to physical coordinates
83- // Electron (per-monitor DPI aware) expects physical coordinates for window positioning
84- let physical_x = ( point. x as f64 * self . dpi_scale ) as i32 ;
85- let physical_y = ( point. y as f64 * self . dpi_scale ) as i32 ;
86-
91+ // With DPI awareness enabled, follow the macOS pattern:
92+ // - GetCursorPos returns physical coordinates
93+ // - Convert to logical coordinates (like macOS CGEventGetLocation)
94+ // - This matches Electron's coordinate system and main.rs expectations
95+ let logical_x = ( point. x as f64 / self . dpi_scale ) as i32 ;
96+ let logical_y = ( point. y as f64 / self . dpi_scale ) as i32 ;
97+
8798 Ok ( Point {
88- x : physical_x ,
89- y : physical_y ,
99+ x : logical_x ,
100+ y : logical_y ,
90101 } )
91102 }
92103 }
@@ -97,10 +108,14 @@ impl PixelSampler for WindowsSampler {
97108 unsafe {
98109 let half_size = ( grid_size / 2 ) as i32 ;
99110
100- // For a DPI-unaware process, all GDI operations use virtualized coordinates
101- // No conversion needed
102- let x_start = center_x - half_size;
103- let y_start = center_y - half_size;
111+ // With DPI awareness enabled, follow the macOS pattern:
112+ // - center_x, center_y are logical coordinates
113+ // - Convert to physical coordinates for GDI operations
114+ let physical_center_x = ( center_x as f64 * self . dpi_scale ) as i32 ;
115+ let physical_center_y = ( center_y as f64 * self . dpi_scale ) as i32 ;
116+
117+ let x_start = physical_center_x - half_size;
118+ let y_start = physical_center_y - half_size;
104119 let width = grid_size as i32 ;
105120 let height = grid_size as i32 ;
106121
@@ -220,13 +235,16 @@ impl WindowsSampler {
220235 let half_size = ( grid_size / 2 ) as i32 ;
221236 let mut grid = Vec :: with_capacity ( grid_size) ;
222237
223- // For DPI-unaware process, use coordinates directly
238+ // center_x, center_y are logical coordinates - convert to physical
239+ let physical_center_x = ( center_x as f64 * self . dpi_scale ) as i32 ;
240+ let physical_center_y = ( center_y as f64 * self . dpi_scale ) as i32 ;
241+
224242 for row in 0 ..grid_size {
225243 let mut row_pixels = Vec :: with_capacity ( grid_size) ;
226244 for col in 0 ..grid_size {
227- // Calculate pixel coordinates (no conversion needed)
228- let x = center_x + ( col as i32 - half_size) ;
229- let y = center_y + ( row as i32 - half_size) ;
245+ // Calculate physical pixel coordinates
246+ let x = physical_center_x + ( col as i32 - half_size) ;
247+ let y = physical_center_y + ( row as i32 - half_size) ;
230248
231249 let color_ref = GetPixel ( self . hdc , x, y) ;
232250
0 commit comments