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

Support listing available video modes for a monitor #896

Merged
merged 5 commits into from Jun 12, 2019
Merged

Support listing available video modes for a monitor #896

merged 5 commits into from Jun 12, 2019

Conversation

ghost
Copy link

@ghost ghost commented Jun 3, 2019

Resolves #877.

  • Tested on all platforms changed
    • Windows
    • macOS
    • X11
    • Wayland
    • iOS
    • Android
    • Emscripten
  • Added an entry to CHANGELOG.md if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created an example program if it would help users understand this functionality
  • Updated feature matrix, if new features were added or implemented

Adds a new method get_video_modes to the MonitorId struct in the public API, and implements it on the Windows platform. I'm going to look into implementing this on macOS as well..

@Osspial
Copy link
Contributor

Osspial commented Jun 3, 2019

Hey, thanks for the PR!

FYI before you get much further into implementing this on macOS - the vast majority of work is currently being done on the eventloop-2.0 branch, and we're expecting to merge that onto master Soon. I'd suggest reworking this PR against that branch. Fortunately, looking at these changes I wouldn't expect that to be too difficult to do.

@ghost
Copy link
Author

ghost commented Jun 3, 2019

Thanks for the heads up. I'll take a look.

src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct VideoMode {
dimensions: (u32, u32),
Copy link
Contributor

@goddessfreya goddessfreya Jun 3, 2019

Choose a reason for hiding this comment

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

Can this become PhysicalSize instead of (u32, u32)?

Copy link
Author

Choose a reason for hiding this comment

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

This needs to implement Hash which PhysicalSize does not (and can not, as it's essentially a tuple of f64). Hence, we're doing the conversion from (u32, u32) to PhysicalSize in the getter method.

Copy link
Contributor

Choose a reason for hiding this comment

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

PhysicalSize is stored as u32s and not f64s on eventloop-2.0, so this should be possible once rebased.

Copy link
Author

Choose a reason for hiding this comment

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

Looks like this would depend on the changes made in #895 being merged first.

src/lib.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@goddessfreya goddessfreya left a comment

Choose a reason for hiding this comment

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

As @Osspial mentioned, please rebase this off the other branch.

@ghost ghost changed the base branch from master to eventloop-2.0 June 4, 2019 07:24
@ghost ghost changed the title Add MonitorId::get_video_modes to list available modes Add MonitorId::video_modes to list available modes Jun 4, 2019
@ghost ghost changed the title Add MonitorId::video_modes to list available modes Support listing available video modes for a monitor Jun 4, 2019
@ghost
Copy link
Author

ghost commented Jun 4, 2019

I've reworked this against the eventloop-2.0 branch and added support for macOS. There's a bit of a quirk there, as we can't currently obtain the refresh rate of the monitor without some patches upstream against the core-video-sys crate for example.

I'm also not a fan of allocating an additional HashSet on macOS instead of just re-using the iterator we get from calling map on the Vec returned from CGDisplayMode::all_display_modes, but I couldn't come up with a clean way to do this.

How do we proceed with implementing this for the rest of the platforms? As for the Android and iOS platforms I think this comes down to just returning the screen size, and additionally obtaining the refresh rate and bit depth if applicable. I haven't looked into what Emscripten is doing.

src/monitor.rs Outdated Show resolved Hide resolved
@goddessfreya
Copy link
Contributor

I've reworked this against the eventloop-2.0 branch and added support for macOS. There's a bit of a quirk there, as we can't currently obtain the refresh rate of the monitor without some patches upstream against the core-video-sys crate for example.

Filled upstream already, yeah?

I'm also not a fan of allocating an additional HashSet on macOS instead of just re-using the iterator we get from calling map on the Vec returned from CGDisplayMode::all_display_modes, but I couldn't come up with a clean way to do this.

I guess you could use a vec, then sort it at the end (or always keep it sorted) and dedup it, however that sounds like a pain to maintain.

@ghost
Copy link
Author

ghost commented Jun 5, 2019

I've reworked this against the eventloop-2.0 branch and added support for macOS. There's a bit of a quirk there, as we can't currently obtain the refresh rate of the monitor without some patches upstream against the core-video-sys crate for example.

Filled upstream already, yeah?

I'll look into filing a pull request against core-video-sys for this today.

@ghost
Copy link
Author

ghost commented Jun 5, 2019

We're now reporting the display refresh rate correctly on macOS. I've filed LuoZijun/rust-core-video-sys#2 to expose the necessary functionality from the Core Video API.

@ghost
Copy link
Author

ghost commented Jun 6, 2019

LuoZijun/rust-core-video-sys#2 has been merged, so all that remains is implementing this for the remaining platforms. I suppose I could take a look at either iOS or Linux today, so that we can ship this as soon as possible.

@ghost
Copy link
Author

ghost commented Jun 6, 2019

I've finished the iOS implementation. I couldn't get the Emscripten or Android platforms to compile. It looks like their implementation in eventloop-2.0 hasn't been finished.

@ghost
Copy link
Author

ghost commented Jun 8, 2019

I've finished implementing this on Wayland, and found a cleaner way to return iterators from the video_modes method. I've refactored all platforms accordingly. I'm going to look into implementing this on X11 next.

@ghost ghost marked this pull request as ready for review June 8, 2019 12:28
@ghost
Copy link
Author

ghost commented Jun 8, 2019

X11 implementation is done. This is now implemented on all platforms except for Android and Emscripten that don't seem to be ported over to eventloop-2.0 yet. Should be ready for final review!

Copy link
Contributor

@goddessfreya goddessfreya left a comment

Choose a reason for hiding this comment

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

Quick skim of changes.

src/platform_impl/windows/monitor.rs Outdated Show resolved Hide resolved
src/platform_impl/ios/monitor.rs Show resolved Hide resolved
.filter(|x| output_modes.iter().any(|id| x.id == *id))
.map(|x| {
let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 {
x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain this math?

Copy link
Author

Choose a reason for hiding this comment

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

I don't know what's going on here, but this is the same line of code that SDL, GTK and GLFW use to calculate the display refresh rate. I couldn't find any documentation for this.

src/platform_impl/linux/x11/util/randr.rs Outdated Show resolved Hide resolved
.into_iter()
.map(|x| VideoMode {
dimensions: (x.dimensions.0 as u32, x.dimensions.1 as u32),
refresh_rate: (x.refresh_rate as f32 / 1000.0 + 0.5) as u16,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain this math?

Copy link
Author

@ghost ghost Jun 9, 2019

Choose a reason for hiding this comment

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

We're receiving the refresh rate from Wayland in millihertz, so we must divide it by a thousand to get it in hertz. We then effectively ceil round the float before casting it into an integer, but I suppose we could've used f32::ceil f32::round here also.

Copy link
Contributor

Choose a reason for hiding this comment

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

In all, I'm not sure it is the right approach to represent refresh rates as u16 in Hertz. There are displays whose refresh rate is not an integer in this unit, no?

Copy link
Author

Choose a reason for hiding this comment

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

The refresh rate is usually not an exact integer, but very close (e.g. 59.996 Hz). We could report the display refresh rate as a float, but Windows and iOS only report it as an integer, so it might be misleading to return a float on these platforms. I assume this is what you meant?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't get what would be misleading about using a float while some platform always return integer values, could you elaborate on that?

We already have precedent with the DPI factor values, which are always integer on Wayland for example.

Copy link
Author

Choose a reason for hiding this comment

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

To elaborate, I feel like that returning a float here implies that this is an exact refresh rate, when in fact it's truncated or rounded (on at least two platforms). Returning an integer type does not have this implication to me.

If we look at other windowing libraries, there's precedent of using integers to represent the refresh rate in SDL and GLFW. I don't feel particularly strongly about this either way, so I'm fine with changing this to return floats if we all agree on that.

Copy link
Contributor

Choose a reason for hiding this comment

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

I personally have the other expectation, imo an integer refresh rate suggests that it is exact...

Anyway, I don't know much about which uses an app would have for the refresh rate. If rounding it doe snot pose any functionality issue, then I'm fine with it.

Copy link
Author

Choose a reason for hiding this comment

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

Having slept on it, I think we should leave it as a rounded integer, so at least the behaviour is consistent across platforms. If the user requires an exact value, there's not much point in us returning an exact value on some platforms, and a rounded value on others. Furthermore, changing this to a floating point value would complicate the implementation (though slightly) for VideoMode as we would now have to implement Hash and Eq by hand. If there later appears an use-case where an exact value is required, we can revisit this. For now, perhaps we can document it in the function description that the returned value is an approximation?

src/platform_impl/macos/monitor.rs Outdated Show resolved Hide resolved
@aloucks
Copy link
Contributor

aloucks commented Jun 9, 2019

It makes it difficult to see what's going on when you force-push after comments have started. The commits will all get squashed in the end anyway.

@ghost
Copy link
Author

ghost commented Jun 9, 2019

Sorry about that. I didn't realize this repository squashes pull requests upon merge. While making changes to the Wayland platform, I noticed that I needed to restructure the other platforms as well, and I decided to squash it all into a single commit at that point. There shouldn't be any more large changes landing to this branch at this point.

Copy link
Contributor

@Osspial Osspial left a comment

Choose a reason for hiding this comment

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

I've got some relatively nitpicky changes, but overall this looks good. Thanks for looking into this!

FEATURES.md Outdated Show resolved Hide resolved
src/platform_impl/windows/monitor.rs Outdated Show resolved Hide resolved
@Osspial Osspial merged commit 47b5dfa into rust-windowing:eventloop-2.0 Jun 12, 2019
@felixrabe
Copy link
Contributor

felixrabe commented Jul 1, 2019

I just noticed on my old macOS 10.9 MBAir that the core-video-sys crate brings in metal as a dependency, which means I can't compile at all on that machine. (This is my hack-during-commute laptop.)

Is there some way to do this without depending on Metal?

@ghost
Copy link
Author

ghost commented Jul 1, 2019

We don't actually need Metal for this at all, but core-video-sys happens to pull that in for other reasons. I've opened LuoZijun/rust-core-video-sys#3 to make the Metal dependency optional. I will follow this up with a pull request to winit that then disables the "metal" feature for the core-video-sys dependency.

@ghost ghost mentioned this pull request Jul 2, 2019
6 tasks
kosyak pushed a commit to kosyak/winit that referenced this pull request Jul 10, 2019
* Support listing available video modes for a monitor

* Use derivative for Windows `MonitorHandle`

* Update FEATURES.md

* Fix multiline if statement

* Add documentation for `VideoMode` type
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.

6 participants