-
Notifications
You must be signed in to change notification settings - Fork 891
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 safe area and document coordinate systems #3890
base: master
Are you sure you want to change the base?
Conversation
7899334
to
67113a7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can macOS backend provide safe area when the transparent decorations are used? The ones where the buttons are over the main surface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
positions are points, outer_size
is window_size
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outer_size
is not window_size
currently, and I'm not sure I want it to be? It's already defined on Window
, so it seems a bit excessive to repeat "window". If anything, I'd rather call it just size
, but that might lead people to use that over surface_size
.
What do you mean by "positions are points"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outer_size is not window_size currently, and I'm not sure I want it to be? It's already defined on Window, so it seems a bit excessive to repeat "window". If anything, I'd rather call it just size, but that might lead people to use that over surface_size.
it's a size with all the decorations, which is what we have and that's what we're reporting, so it's effectively a window size.
If you think of window as a part without decorations, probably then not, but in the current state it's window_size from what I can say.
What do you mean by "positions are points"?
I mean, you show them with lines as in they are areas, but you should just draw them as a point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, you show them with lines as in they are areas, but you should just draw them as a point.
Ah, yeah, but I wasn't sure how to represent what the point was relative to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe color coding, so different coordinate spaces will be differently colored, though, you already do so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't even know we had I was kinda hoping I could define |
You just need to |
Added `Window::safe_area`, which describes the area of the surface that is unobstructed by notches, bezels etc. The drawing code in the examples have been updated to draw a star inside the safe area, and the plain background outside of it. Also renamed `Window::inner_position` to `Window::surface_position`, and changed it to from screen coordinates to window coordinates, to better align how these coordinate systems work together. Finally, added some SVG images and documentation to describe how all of this works.
67113a7
to
98a9914
Compare
Dunno, I can imagine that it will happen on some platforms
I have ensured that |
ed83b5f
to
bc12e13
Compare
Very bare-bones, probably not correct
/// | ||
/// - **Android / Orbital / Wayland / Web / Windows / X11:** Unimplemented, returns `((0, 0), | ||
/// surface_size)`. | ||
fn safe_area(&self) -> (PhysicalPosition<u32>, PhysicalSize<u32>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I considered letting this be safe_area_insets
, i.e. return { left, right, top, bottom }
instead, since I think that's generally a nicer API for users. I decided against it for now, since we'd have to define yet another type, and then I'd rather re-use our existing types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The position and size is generally better, since it's easier to forward stuff like that into the e.g. glViewport
stuff, since you generally want an offset and not left/right
.
let mut buffer = self.surface.buffer_mut()?; | ||
|
||
// Fill the whole surface with a plain background | ||
buffer.fill(match self.theme { | ||
Theme::Light => 0xffffffff, // White | ||
Theme::Dark => 0xff181818, // Dark gray | ||
}); | ||
|
||
// Draw a star (without anti-aliasing) inside the safe area | ||
let surface_size = self.window.surface_size(); | ||
let (origin, size) = self.window.safe_area(); | ||
let in_star = |x, y| -> bool { | ||
// Shamelessly adapted from https://stackoverflow.com/a/2049593. | ||
let sign = |p1: (i32, i32), p2: (i32, i32), p3: (i32, i32)| -> i32 { | ||
(p1.0 - p3.0) * (p2.1 - p3.1) - (p2.0 - p3.0) * (p1.1 - p3.1) | ||
}; | ||
|
||
let pt = (x as i32, y as i32); | ||
let v1 = (0, size.height as i32 / 2); | ||
let v2 = (size.width as i32 / 2, 0); | ||
let v3 = (size.width as i32, size.height as i32 / 2); | ||
let v4 = (size.width as i32 / 2, size.height as i32); | ||
|
||
let d1 = sign(pt, v1, v2); | ||
let d2 = sign(pt, v2, v3); | ||
let d3 = sign(pt, v3, v4); | ||
let d4 = sign(pt, v4, v1); | ||
|
||
let has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0) || (d4 < 0); | ||
let has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0) || (d4 > 0); | ||
|
||
let color = match self.theme { | ||
Theme::Light => WHITE, | ||
Theme::Dark => DARK_GRAY, | ||
!(has_neg && has_pos) | ||
}; | ||
for y in 0..size.height { | ||
for x in 0..size.width { | ||
if in_star(x, y) { | ||
let index = (origin.y + y) * surface_size.width + (origin.x + x); | ||
buffer[index as usize] = match self.theme { | ||
Theme::Light => 0xffe8e8e8, // Light gray | ||
Theme::Dark => 0xff525252, // Medium gray | ||
}; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That looks a bit complicated, maybe just different coloring for something inside the unsafe area should be fine?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The nice thing about the star is that then you can very clearly see whether it lines up with the edges or not.
For example on Mac Catalyst, the surface is actually obscured by the title bar, but I couldn't see this just by colouring the unsafe part differently.
@@ -338,6 +337,11 @@ impl CoreWindow for Window { | |||
super::logical_to_physical_rounded(window_state.outer_size(), scale_factor) | |||
} | |||
|
|||
fn safe_area(&self) -> (PhysicalPosition<u32>, PhysicalSize<u32>) { | |||
// FIXME: Include CSDs drawn by Winit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop this thing, they don't draw into the surface like you have on macOS, so it's all irrelevant for them, since they can not physically overlap, since they are on a completely different surface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, should we then update surface_position
to include the CSDs drawn by Winit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Wayland, I think it won't change anything since CSDs are in negative part of the window, so the surface is still at (0,0), it's just CSDs are in (-border, -header), so don't have to do anything, since position is at (0,0).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, that's wild! How does the compositor allow you to create and draw surfaces outside the bounds that you are otherwise restricted to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not restricted to anything on Wayland and negative geometry is how on protocol level was defined for this exact reason I guess, so you don't change the way normal stuff works.
So, everything we're doing is how it's done by everyone else and nothing more to it.
And in the end of the day, user has full control over drawing, and they can draw things as big as they want, compositor may decide cropping what they draw, but it's not an error by any means.
Just thinking more about it and that you should generally always call So |
Hmm, if you set the terminal viewport to only be the safe area, what does Alacritty intend to draw in the unsafe area, then? |
just padding with background color, it's just the safe area changes the size of the content, which we should forward down the line for us? It's not really an issue for regular GUIs, I guess. |
To support on Alacritty
Okay, so if it's something that only affects Alacritty, then I'd argue that we don't need the event, since Alacritty only works on desktop anyhow, and I'm fine for now with guaranteeing for that case that on macOS, a change to the safe area will always correspond with a I'm not in general against an event for it, but it's kinda cumbersome to set up, and I'd like to see a better use-case for it - and besides, it can be added after this PR. |
I don't think that the issue is specific to alacritty though, it's specific to any application that computes layout and then decides whether to render or not. Re-running your layout calculation each time during rendering is not that great, unless you're egui or something, but for retained UIs they probably want to react to state a bit earlier. I'd also not mention alacritty in the source, since it makes it sound like its problem, where in reality any program that forwards sizes to some external process will work the same, like vnc, etc. In general though, I'd assume that on mobile it's not really an issue since you have rotation event, when it'll change the safe area and you can just call it once, since it doesn't change over time. The thing about safe area though, is that it can not really change for the given monitor, because it's a hardware property, what changes is the state of winit decorations and the state where this safe area should be honored, so if talking about event, you don't have to hook into the actual change to the bits you're quering for safe area, but to send this event from places where system wants you to honor/stop honoring the safe area. In case of macOS, it'll be when you enter simple fullscreen mode, and in case of ios/android you can just always send it once you open your application or rotation happens (if rotation changes coordinate system). So I don't see it being cumbersome to setup, since the area itself can not change. |
I'm mostly against such an event because if we want it to be useful, we also have to emit it every time we do Again, I believe it does not have to be handled in this PR, so I've opened #3911, let's discuss it there instead. |
It doesn't have to be synced though, you should emit it only when the area changes, resize doesn't mean that it changed, because it may not apply at all. I won't block on event, since it doesn't really matter for the majority of the clients though. |
Resolves #2308, by adding
Window::safe_area
, which describes the area of the surface that is unobstructed by notches, bezels etc. The drawing code in the examples have been updated to draw a star inside the safe area, and the plain background outside of it.Also renamed
Window::inner_position
(introduced in #430) toWindow::surface_position
, and changed it to from screen coordinates to window coordinates, to better align how these coordinate systems work together.Finally, I've added some SVG images and documentation to describe how these coordinate systems work together. The images should be auto-adjusting to the page theme when viewed on docs.rs. See also #3891, whether we use window coordinates or surface coordinates is currently a bit confusing.
Completing implementation on all platforms is tracked in #3910
Fixes #1122
Fixes #2308
Fixes #3742
Fixes #2066
Fixes #2347
Fixes #2235