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

Add exclusive fullscreen mode #925

Merged
merged 36 commits into from Jul 29, 2019
Merged

Add exclusive fullscreen mode #925

merged 36 commits into from Jul 29, 2019

Conversation

ghost
Copy link

@ghost ghost commented Jun 15, 2019

Resolves #717. This uses CGConfigureDisplayWithDisplayMode on macOS for now, but I think what I'm going to end up doing is move to use enterFullScreenMode instead, since that seems to be the preferred way to do fullscreen on macOS. Implementing this on X11 should be fairly straight-forward. I'm not sure how to do this on Wayland however.

  • Tested on all platforms changed
    • Windows
    • macOS
    • X11
    • Wayland
    • iOS
  • 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

@elinorbgr
Copy link
Contributor

I'm not sure how to do this on Wayland however.

That'll be pretty simple actually: the wayland server will never give any app direct control over the monitor, hence "exclusive fullscreen" does not exist, only regular fullscreen.

@Osspial Osspial self-requested a review June 16, 2019 18:15
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.

Thanks for looking into this and implementing it!

I've tested this on my machine, and have left comments where I noticed bugs/potential bugs.

examples/fullscreen.rs Outdated Show resolved Hide resolved
src/platform_impl/windows/window.rs Outdated Show resolved Hide resolved
@@ -50,17 +50,16 @@ impl Iterator for AvailableMonitorsIter {
/// - [`MonitorHandle::video_modes`][monitor_get].
///
/// [monitor_get]: ../monitor/struct.MonitorHandle.html#method.video_modes
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Derivative)]
#[derivative(Clone, Debug = "transparent")]
pub struct VideoMode {
Copy link
Contributor

Choose a reason for hiding this comment

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

I realize this specific feedback would've been better-placed in the original PR, but it's only occurred to me now: is it possible to add a function to Monitor to retrieve the currently-active video mode? That way, applications can launch in fullscreen with the sensible default of whatever fullscreen settings the monitor currently has, instead of having to guess which mode to use.

Copy link
Author

Choose a reason for hiding this comment

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

Shouldn't be a problem, but I'm inclined to do this in a separate pull request since I will have to implement it for all four platforms. I think the borderless mode would suffice for a sensible default in any case.

src/platform_impl/windows/window_state.rs Show resolved Hide resolved
@ghost
Copy link
Author

ghost commented Jun 17, 2019

The macOS implementation is starting to look pretty good. The only remaining issue there is that upon exiting exclusive fullscreen and re-entering it, the video mode change seems to fail. Perhaps the list of video modes is invalidated upon capturing the display.

With these new fullscreen modes, I'm inclined to deprecate set_simple_fullscreen because that fullscreen behaviour seems to me very strange and unidiomatic, and most certainly not what the user is going to expect. I won't touch upon that in this pull request however, and will leave that open for discussion.

@ghost
Copy link
Author

ghost commented Jun 21, 2019

I've addressed most of your notes regarding the Windows implementation. I still have to fix the case where the video mode is changed while already in exclusive fullscreen mode. I realized that this hasn't been tested on the macOS implementation either, so I will have to create a test case for this. As for sorting the video modes, I've implemented this for Windows. It may be better to do this on the public API level (so that it's consistent across platforms and that the user can take advantage of the Ord trait implementation). I'll take a look at this tomorrow.

I've marked exclusive fullscreen unimplemented!() on iOS for now, as I can't seem to be able to detect external displays consistently with the simulator (and they report only a single video mode). I would have to test this on a physical device, which I would need to go out and buy some cables for..

Once I've worked out the remaining kinks, all that remains is the X11 implementation. I think I may have to end up doing a proper Linux install for this, as the virtual machine video driver only reports a single video mode. I'll also have to mark this unimplemented!() for Wayland. Or perhaps we want to modify the API so that set_fullscreen returns Result, with it always being an Err on Wayland?

While writing this, I was reminded that we could potentially have set_fullscreen called on a window when a different window may already be in fullscreen mode.. I think it might be good for us to track this and disallow set_fullscreen if there already is a fullscreen window. It'd be good to return an Err for this reason as well.

@goddessfreya goddessfreya self-requested a review June 23, 2019 08:16
@felixrabe
Copy link
Contributor

felixrabe commented Jun 23, 2019

Q project people: Could we merge this in a few smaller commits than just one mega-commit if accepted?

Q everyone: Maybe split this up into smaller chunks merged in separate PRs or something? (If it is not too much trouble.)

Why I suggest this: ... Edit: Moved longer off-topic comment to issue #970 to discuss future PR merge strategies.

@ghost
Copy link
Author

ghost commented Jun 23, 2019

The macOS and Windows implementations have had all issues addressed and should be ready for final review. I will still have to do the implementation for X11.

I don't think splitting this into multiple pull requests makes any sense. This just adds friction for no benefit, as they will all be merged in anyway. I could squash these commits into a single commit per backend, and we could merge those commits as is without squashing again, but I don't think that's hardly worth the effort. I would suggest squash-merging this as usual in a single commit. If you wish to only inspect a single backend in the commit, you can do so easily by ignoring files in the other folders.

@ghost ghost marked this pull request as ready for review June 25, 2019 11:26
@ghost
Copy link
Author

ghost commented Jun 25, 2019

It doesn't look like core-foundation-rs is actively maintained, so instead of waiting for a review for servo/core-foundation-rs/pull/318, I might have to pull this functionality into our ffi.rs for macOS.

With the exception of that, this should be all done! Implemented on all supported platforms (i.e. macOS, Windows and X11), with the exception of iOS, but I feel like it's quite a niche use-case there anyway.

@ghost
Copy link
Author

ghost commented Jun 27, 2019

At the start sometimes it landed on the wrong window. Not sure if this is caused by this PR or #978. :/

I haven't touched the window positioning code, so this is most likely the same issue.

@Osspial
Copy link
Contributor

Osspial commented Jun 27, 2019

I'm getting a protocol error upon running the fullscreen example on Weston in the master branch: zxdg_shell_v6@13: xdg_surface buffer does not match the configured state. It's not caused by the changes in this pull request.

My guess is that this should get fixed by #981.

@elinorbgr
Copy link
Contributor

This is pretty likely that #981 will solve the protocol error indeed.

@ghost
Copy link
Author

ghost commented Jul 2, 2019

I will be leaving on a two week long trip in a few days and will be unable to make any further changes for quite some time, so I would like to get this merged before I leave. Do we have a consensus on the remaining review notes, or do you feel strongly that these ought to be changed? I will change these if that's what it takes to get this merged, but I still feel like long-term maintainability would suffer from these changes.

@Osspial
Copy link
Contributor

Osspial commented Jul 3, 2019

I'm still running into the window in wrong position problem in my latest tests (see video here), so I suspect my initial diagnosis for that wasn't the correct one. If you're unable to reproduce this, I could look into it on my machine, then just push directly to the branch and merge it once I've figured out what's going on.

@ghost
Copy link
Author

ghost commented Jul 3, 2019

I'm not able to reproduce this on my system. I suspect that may have to do with the virtual layout in which your monitors are configured. Thank you for offering to look into this! It would be helpful. Feel free to commit directly to this branch. I also have an opportunity tomorrow to make changes, and maybe a few hours on Friday, but then I'll be unreachable until the end of July or so.

@Osspial
Copy link
Contributor

Osspial commented Jul 22, 2019

Alright, this turned out to be one hell of a bug to track down.

It looks like the underlying cause was that sometimes, the call to ChangeDisplaySettingsExW would hang for such a long time that Windows would erroneously think our program had frozen, taking away control over the Winit window*. When that function finally returned, we'd still lack control over our window, causing SetWindowPos to fail and the window not getting moved to the proper position.

If you're interested in reproducing this bug, just apply this patch, run the fullscreen example in exclusive mode, and furiously click on the window before it properly gets fullscreened:

diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs
index 756fc5ad..b8362e1f 100644
--- a/src/platform_impl/windows/window.rs
+++ b/src/platform_impl/windows/window.rs
@@ -480,6 +480,7 @@ impl Window {
                     let mut native_video_mode = video_mode.video_mode.native_video_mode.clone();

                     let res = unsafe {
+                        std::thread::sleep_ms(6000);
                         winuser::ChangeDisplaySettingsExW(
                             display_name.as_ptr(),
                             &mut native_video_mode,

The furious clicking is necessary to make Windows detect a freeze in our program. I'm not sure what steps you need to take to reproduce this without that patch, since this seems to be caused by various timing flukes outside of our control, but after inspecting my ETW logs I'm confident that this is the correct diagnosis.

Fortunately, the solution is pretty simple - just insert a call to PeekMessageW after ChangeDisplaySettingsExW to inform Windows that our program is still running properly. I've pushed that change to this branch.

* This is the same infrastructure that causes the window to gray out and (Not Responding) to appear in the title bar. Funnily enough, windbg disables that behavior in debugged applications, making this bug impossible to reproduce when running in a debugger. :/

@ghost
Copy link
Author

ghost commented Jul 23, 2019

Thank you for tracking this down! I just got back home from my trip last evening. It's interesting that ChangeDisplaySettingsExW would take such a long time to complete. This didn't occur on my system. I wonder if that is dependent on the display and drivers being used.

@Osspial
Copy link
Contributor

Osspial commented Jul 23, 2019

It's interesting that ChangeDisplaySettingsExW would take such a long time to complete. This didn't occur on my system. I wonder if that is dependent on the display and drivers being used.

@aleksijuvani I did a bit more investigation, and I don't think I was entirely correct with my original assessment! ChangeDisplaySettingsExW takes some time to execute, but that only makes the problem more visible instead of directly causing it - the root cause is that we ask for the monitor between creating the EventLoop and creating the Window. If the user takes a while to enter their desired monitor configuration, the program freeze handler kicks in and the SetWindowPos call doesn't work. Incidentally, that also means it's possible to reproduce this bug on master without exclusive fullscreen - just wait five or more seconds before entering the desired monitor configuration

That's why I was able to reproduce it, but you weren't - whenever the display selection interaction happened, I took a good amount of time to select the correct display and configuration, which took long enough that Windows thought the program had frozen. I'm assuming you didn't do that, so you never saw the bug.

@ghost
Copy link
Author

ghost commented Jul 24, 2019

I'm still not able to reproduce this even after waiting quite a bit before applying the desired display configuration. Which Windows version are you running on? I'm on Windows 10 version 1809. It seems odd to me that Windows would outright ignore API calls if it thinks that the window is frozen. Surely it should continue to allow the window to be manipulated?

@ghost
Copy link
Author

ghost commented Jul 26, 2019

I neglected to do this before, but I've just now also tested reproducing this by inserting the std::thread::sleep_ms call, and this does allow me to reproduce the issue. Either way, introducing the PeekMessageW call seems to fix the issue. Shall we go ahead with merging this?

@Osspial Osspial merged commit 5bc3cf1 into rust-windowing:master Jul 29, 2019
@Osspial
Copy link
Contributor

Osspial commented Jul 29, 2019

@aleksijuvani I've merged this now. Thank you so much for implementing this everywhere!

cheako pushed a commit to cheako/winit that referenced this pull request Oct 2, 2019
* Add exclusive fullscreen mode

* Add `WindowExtMacOS::set_fullscreen_presentation_options`

* Capture display for exclusive fullscreen on macOS

* Fix applying video mode on macOS after a fullscreen cycle

* Fix compilation on iOS

* Set monitor appropriately for fullscreen on macOS

* Fix exclusive to borderless fullscreen transitions on macOS

* Fix borderless to exclusive fullscreen transition on macOS

* Sort video modes on Windows

* Fix fullscreen issues on Windows

* Fix video mode changes during exclusive fullscreen on Windows

* Add video mode sorting for macOS and iOS

* Fix monitor `ns_screen` returning `None` after video mode change

* Fix "multithreaded" example on macOS

* Restore video mode upon closing an exclusive fullscreen window

* Fix "multithreaded" example closing multiple windows at once

* Fix compilation on Linux

* Update FEATURES.md

* Don't care about logical monitor groups on X11

* Add exclusive fullscreen for X11

* Update FEATURES.md

* Fix transitions between exclusive and borderless fullscreen on X11

* Update CHANGELOG.md

* Document that Wayland doesn't support exclusive fullscreen

* Replace core-graphics display mode bindings on macOS

* Use `panic!()` instead of `unreachable!()` in "fullscreen" example

* Fix fullscreen "always on top" flag on Windows

* Track current monitor for fullscreen in "multithreaded" example

* Fix exclusive fullscreen sometimes not positioning window properly

* Format

* More formatting and fix CI issues

* Fix formatting

* Fix changelog formatting
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.

Windows: Fullscreen mode is borderless window, not exclusive fullscreen
5 participants