Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect scale_factor on Windows #176

Closed
caesay opened this issue Dec 15, 2024 · 6 comments
Closed

Incorrect scale_factor on Windows #176

caesay opened this issue Dec 15, 2024 · 6 comments

Comments

@caesay
Copy link

caesay commented Dec 15, 2024

If dpiAware is set in application manifest eg.

  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
    </windowsSettings>
  </application>

Then xcap will return scale_factor = 1.0 always, which is incorrect. The GetDpiForMonitor function should be used to determine the monitor scale.

@nashaofu
Copy link
Owner

nashaofu commented Dec 16, 2024

#177
Please review this to see if it meets your requirements. Note that I haven't conducted any actual testing, so you may need to help adjust the parameters accordingly.

features: https://learn.microsoft.com/zh-cn/windows/win32/api/shellscalingapi/ne-shellscalingapi-monitor_dpi_type

    let dpi_type = {
        cfg_if! {
            if #[cfg(feature = "windows_hi_dpi_effective")] {
                use windows::Win32::UI::HiDpi::MDT_EFFECTIVE_DPI;
                MDT_EFFECTIVE_DPI
            } else if #[cfg(feature = "windows_hi_dpi_angular")] {
                use windows::Win32::UI::HiDpi::MDT_ANGULAR_DPI;
                MDT_ANGULAR_DPI
            } else {
                use windows::Win32::UI::HiDpi::MDT_RAW_DPI;
                MDT_RAW_DPI
            }
        }
    };

@nashaofu
Copy link
Owner

@caesay Does this align with your expectations?

@caesay
Copy link
Author

caesay commented Dec 21, 2024

Hi, thanks for looking at this, and I think the changes you made will work fine for me, but the only valid flag for determining screen DPI is MDT_EFFECTIVE_DPI, so I'm not sure the cargo features are needed to gate this.

If I could provide a suggestion, ideally the implementation should just use GetDpiForMonitor automatically if Windows >= 8.1, since the current approach of GetDeviceCaps will give incorrect results on multi-monitor computers. If xcap is supporting older versions of windows than 8.1, then use libloading to access GetDpiForMonitor dynamically, instead of statically importing the function.

The current implementation in #177 means that I have to choose whether I target windows 7/8 or 8.1+ at compile time, since without the feature it will not work on 8.1+ and with the feature it will not work on 7.

@nashaofu
Copy link
Owner

The cargo features have been removed and replaced with dynamic linking. Additionally, it now detects whether the current process has DPI awareness enabled, and automatically switches the method for obtaining DPI accordingly.

use std::time::Instant;
use windows::Win32::UI::HiDpi::{
    SetProcessDpiAwareness, PROCESS_DPI_AWARENESS, PROCESS_PER_MONITOR_DPI_AWARE,
};
use xcap::Monitor;

fn change_windows_dpi_awarness(val: PROCESS_DPI_AWARENESS) {
    unsafe {
        SetProcessDpiAwareness(val).unwrap();
    }
}

fn main() {
    // Modify the program's DPI awareness. You can set the value to PROCESS_DPI_UNAWARE or PROCESS_PER_MONITOR_DPI_AWARE for testing.
    // 修改程序的 DPI 感知,你可以将值设置为 PROCESS_DPI_UNAWARE 或 PROCESS_PER_MONITOR_DPI_AWARE 进行测试
    change_windows_dpi_awarness(PROCESS_PER_MONITOR_DPI_AWARE);
    let start = Instant::now();
    let monitors = Monitor::all().unwrap();
    println!("Monitor::all() 运行耗时: {:?}", start.elapsed());

    for monitor in monitors {
        println!(
            "Monitor:\n id: {}\n name: {}\n position: {:?}\n size: {:?}\n state:{:?}\n",
            monitor.id(),
            monitor.name(),
            (monitor.x(), monitor.y()),
            (monitor.width(), monitor.height()),
            (
                monitor.rotation(),
                monitor.scale_factor(),
                monitor.frequency(),
                monitor.is_primary()
            )
        );
    }

    let monitor = Monitor::from_point(100, 100).unwrap();

    println!("Monitor::from_point(): {}", monitor.name());
    println!(
        "Monitor::from_point(100, 100) 运行耗时: {:?}",
        start.elapsed()
    );

    println!("运行耗时: {:?}", start.elapsed());
}

@nashaofu
Copy link
Owner

@nashaofu
Copy link
Owner

nashaofu commented Dec 23, 2024

This issue is now closed. If it is not resolved, please reopen it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants