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

Expose get_physical_extents for the MonitorId #442

Closed
wants to merge 11 commits into from

Conversation

fschutt
Copy link

@fschutt fschutt commented Apr 1, 2018

At least on Windows, X11, Wayland, macOS and iOS, this new function returns the physical size of the screen in millimeter. On Android, I can't implement it because there is no way for me to the access to the JavaVM. This is waiting on rust-mobile/android-rs-glue#116. I need this for two reasons:

  1. As a poor mans DPI value until proper DPI support is added - for rasterizing the font with freetype, I need the correct screen DPI - see: Font sizes don't respect the monitor DPI on Linux servo/webrender#2596
  2. I am working on a GIS application where correct scaling is important - i.e. I have to know how large the physical monitor is so I can calculate the map scale accordingly.

I haven't tested that the function actually returns the value in millimeter, though, just that it compiles.

@francesca64
Copy link
Member

Sorry for the wait.

  • This needs a CHANGELOG entry.
  • Looks good on X11, and returned values seem correct (though I must admit I didn't check with a ruler!)
  • Wayland implementation looks solid, though I've yet to test it.
  • I tested this on Windows 10, and it always returns None. On the first call, it fails at "reset to whatever DPI awareness was set before", and on all subsequent calls, it fails at "couldn't set to the required DPI awareness, reset and quit". On that note, I'm not really sure what the value is in returning None when it can't be reset, since we'd still have usable dimensions, and the process is still in an unexpected state. Also, is there a reason VERTSIZE/HORZSIZE aren't used?
  • The logic looks good on macOS (I'll try to test it later), though since the size values are 0 for an invalid display, would it make sense to return None in that case?
  • Is it possible for me to test the Android implementation if I use the android-rs-glue PR?
  • I have no idea about iOS.

@fschutt
Copy link
Author

fschutt commented Apr 15, 2018

@francesca64 I'm working on the windows thing.

  • Why the VERTSIZE isn't used: https://stackoverflow.com/questions/577736/how-to-obtain-the-correct-physical-size-of-the-monitor - VERTSIZE / HORZSIZE don't always work as you expect them to.

  • I returned "None" because in the case that the app is DPI-unaware, it is technically a failure to not reset it to what DPI awareness it had before. However, I could set it to PROCESS_SYSTEM_DPI_AWARE, meaning that the application asked at least once for the DPI (usually on startup). I'm not sure about this, have to debug this on Windows

  • For iOS, there is no DPI, you would have to guess it from the pixel dimensions (i.e. "700px1300px = iPhone 5 = XXX mmXXX mm"). At least to my knowledge, there's no other way of getting this. The current design returns a "scale" value, not sure how accurate it is.

  • Android: The current code will not work without fixing the imports. Other than that, it should compile and run (solution is based off of this stackoverflow post, but I wanted to leave it in in case the next guy comes along and wants to work on DPI awareness for Android. However, the code uses the jni crate, which is also used by the PR. So, the code will probably work, but you need to fix the imports. tomaka wanted to re-export jni before the PR is merged, see the discussion there.

I'll test X11 and Wayland on a VM, just to be sure that the implementation is correct. Working on a windows fix.

@fschutt
Copy link
Author

fschutt commented Apr 15, 2018

Also relevant: https://ofekshilon.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/

Windows "smartly" enlarges the monitors size in order to correct for the distance of the eye to the monitor. Which is - in my opinion - a complete shitpile of design. It may work, it may not work. Reading the EDID data from the windows registry seems to be the only proper way to do it, as ridicoulous as this may sound.

Note: this function is named get_physical_extents(), it doesn't have anything to do with the actual DPI itself.

@fschutt
Copy link
Author

fschutt commented Apr 15, 2018

It seems that this is GLFW's solution:

if (IsWindows8Point1OrGreater()) {
    widthMM  = GetDeviceCaps(dc, HORZSIZE);
    heightMM = GetDeviceCaps(dc, VERTSIZE);
} else {
    widthMM  = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
    heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
}

https://github.com/glfw/glfw/blob/8d98a6e37d122f45439263c96dd50ac2f16b7f5f/src/win32_monitor.c#L82-L91

@francesca64
Copy link
Member

I've verified that this works as expected on macOS. For Windows, I assume that's not finished yet.

@fschutt
Copy link
Author

fschutt commented May 8, 2018

Yeah, I haven't had much time to work on this... I'm basically trying to port the solution presented here: https://ofekshilon.com/2014/06/19/reading-specific-monitor-dimensions/ - this way it'll work on all versions on Windows and with multiple monitors, too.

@fschutt fschutt force-pushed the expose_dpi branch 2 times, most recently from 55299ce to 95e7c01 Compare May 9, 2018 18:53
Copy link
Contributor

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor nits ^^


#[inline]
pub fn get_physical_extents(&self) -> Option<(u64, u64)> {
match self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should match on *self

// Find the display_device with the correct monitor name
let display_device = get_display_device_from_monitor_name(monitor_name)?;
let device_id = wchar_as_string(&display_device.DeviceID[..]);
println!("device id - {:?}, monitor name: {:?}", device_id, monitor_name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be removed or replaced by logging.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for helping out, but this PR is WIP, so I don't think this is pertinent.

(Also, winit has no logging infrastructure whatsoever, so your proposed alternative would be something of an undertaking. If you want winit to have logging sooner, then I recommend opening an issue so everyone has a chance to argue about whether or not to use slog. It's something I've been meaning to do, but I've been busy.)

let app: id = msg_send![Class::get("UIApplication")?, sharedApplication];
let delegate: id = msg_send![app, delegate];
let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
let state = state as *mut DelegateState;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I've had a chance to work on iOS now. I don't believe this needs to be wrapped in setjmp, and you can just use self.delegate_state instead of re-retrieving everything.

let scale = (*state).scale;
let width = (*state).size.0;
let height = (*state).size.1;
Some(((width as f32 * scale) as u64, (height as f32 * scale) as u64))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and note that converting a float to an integer doesn't automatically round. Is this what you wanted?

@@ -86,6 +89,7 @@ pub fn get_available_monitors(x: &Arc<XConnection>) -> Vec<MonitorId> {
dimensions: ((*crtc).width as u32, (*crtc).height as u32),
position: ((*crtc).x as i32, (*crtc).y as i32),
primary: true,
extents_mm: (x_mm as u64, y_mm as u64),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fun fact: these can sometimes be 0. #543

@francesca64
Copy link
Member

I'm not sure if you've lost patience with us yet, but proper multi-plat DPI support will be landing soon so you'll be in for a bit of a rebase.

By "proper", I mean "proper except for Android", since it doesn't look like I can get access to the DPI factor without rust-mobile/android-rs-glue#116... if I'm wrong, please let me know.

@fschutt
Copy link
Author

fschutt commented Jun 3, 2018

Oh I haven't, it's just that the reason why I needed this in the first place, which is webrender, has "fixed itself" because I noticed that I mixed up pixels and points: servo/webrender#2596 (comment)

So for my use-case, I don't really need it anymore... I'll definitely wait until proper DPI is done, then maybe I can get some redundancy out.

@francesca64
Copy link
Member

Proper DPI is over here #548, so it will probably be out in a week or so (though I should know better than to say things like that).

@Osspial Osspial changed the base branch from master to winit-legacy June 13, 2019 05:36
@goddessfreya
Copy link
Contributor

This PR hasn't had any activity for over a year! If this is still relevant, please rebase this PR against master and reopen it. Thanks!

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

Successfully merging this pull request may close these issues.

4 participants