-
Notifications
You must be signed in to change notification settings - Fork 51
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 an invalid raw handle #104
Conversation
When we want to return an empty or invalid handle, it is possible to create an empty handle for an arbitrary platform, but if there is a dedicated enum value for it, then we don't have to pick an arbitrary platform.
In case of display handles empty is a correct thing actually, since if platform doesn't have any display any is valid. Like on macOS. Only a few has real display handle like Wayland. What you're trying to solve is already done via |
Thanks for your answer. The thing is that HasRawWindowHandle doesn't return a Result, so that can't be done with a result. Here is my situation: unsafe impl HasRawWindowHandle for MyWindow {
fn raw_window_handle(&self) -> RawWindowHandle {
match &self.backend {
// forward to the backend's raw_window_handle implementation
Some(b) => b.raw_window_handle(),
None => {
// What should i answer here? Can't be a Result.
//RawWindowHandle::Invalid
// This is a bit awkward. An explicit Invalid would be nicer.
RawWindowHandle::Haiku(HaikuDisplayHandle::empty())
}
}
}
} Do you suggest returning an invalid handle from an arbitrary enum? Or should MyWindow not implement HasRawWindowHandle?, instead, there could be a
I don't think so, because RawWindowHandle is |
Could you not implement it for Also, you just build and return you don't have to implement for haiku? You clearly know what type of RawWindowHandle your backend returns, so you can clearly use |
Well, Haiku was just an example, I could have returned anything in there. The point is that the platform is not Haiku (well, or maybe it is), we don't know what the platform is so i just return "something" at random.
No i don't know what my backend return. the backend is anything that implements
I don't want to panic in that case. I want to return something invalid that could be passed along to any other crate, and since they wouldn't know what to do with that handle, they would probably return an error.
I'm not sure what you mean by that. |
I agree with @kchibisov here, it seems like you should just panic in this case, since there really is nothing to do for the Alternatively, you could always do something like: None => {
#[cfg(target_os = "ios")]
RawWindowHandle::UIKit(UIKitDisplayHandle::empty())
#[cfg(target_os = "macos")]
RawWindowHandle::AppKit(AppKitDisplayHandle::empty())
#[cfg(target_os = "linux", ...)]
RawWindowHandle::X11(X11DisplayHandle::empty())
// ...
} (which is clearly still flawed, but may be better than only using one) |
The HasRawWindowHandle consuler can report the same error as when we give empty handle of any platform. |
The lib providing a handle should not panic, and the lib consuming the handle shouldn't panic either. The bin controlling both libs should probably not even panic, there's much more graceful ways to report an error to the user and close up shop than giving them a panic message. |
Hmm, let me reframe. I think I understand your use-case of wanting to swap-out the backend @ogoffart - but I believe that the desire to have a window that's "backed by no backend at all" is wrong, in the sense that such a situation should either be impossible at compile-time, or be considered a programmer error (hence why I think a panic here is acceptable). If not, could you elaborate on why it would be a user-error that no windowing backend exist at runtime? Or why you couldn't just do something like: struct MyWindow(Box<dyn RawWindowHandle>);
impl HasRawWindowHandle for MyWindow { ... }
struct MaybeMyWindow(Option<MyWindow>);
impl MaybeMyWindow {
fn get(&self) -> Option<&MyWindow>;
} |
Thank you all for taking the time to review this PR. To put a bit of context, the issue i'm trying to solve is slint-ui/slint#877: Exposing the raw window handle from Slint. Based on your feedback, it seems that you are recommending the use of a getter that returns an Option: impl Window {
pub fn raw_handles<'a>(&'a self) -> Option<impl HasRawWindowHandle + HasRawDisplayHandle + 'a> {
// return a wrapper around the backend if there is one.
}
} Note that I still believe it would be more practical to implement the traits directly on the |
Fwiw in my app I am also always fighting whether to pass a "window" around (opaque thing that implements HRWH) or the RWH directly, which is cheaply copyable and doesn't need to have a lifetime/borrow. That is also an advantage for the former, where liveliness of the window (for a RWH to stay valid / be retrievable) becomes an explicit part of your Rust API design. IIRC @kchibisov tried to remove the traits a while ago, but ended up not doing so because it'd break ABI. Furthermore, the trait isn't implemented for RWH either making it impossible to use any API that accepts the trait if you already have a RWH/RDH. Having that would make your proposed For that reason Ash for example takes the value type directly: ash-rs/ash#645, to not force/restrict the caller to rely on a trait-implementer. Perhaps we should ask ourselves again what value the trait is adding? |
Any API that accept a RWH directly would have to be unsafe, because the raw pointer inside the RWH could be dangling. |
You can't break something that doesn't exist. But yes, a lot of crates rely on taking
That's not true unless you model the lifetimes as well. Since you must ensure it yourself, so it's unsafe in the end. If you look at the |
For example rfd::FileDialog::set_parent is safe: I thought this was the whole point of this trait, allowing a safe way to pass the handle from a provider crate (eg, winit) to a consumer crate (eg, rfd) |
yes, that's the point of the trait. |
@ogoffart That's what I said. However, Ash being a low-level wrapper with no concept of lifetimes on raw Vulkan handles like @kchibisov removing public API is considered a breaking change. |
Continuing on this thread, maybe we should make The issue I could see is that we bleed the error into the @Lokathor how do you feel about such change to the trait? The |
Actually yeah, I would definitely prefer the error outside the enum, so people use |
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.
@ogoffart, if you still want to go forward on this PR, I'd suggest to refactor the traits to return the Result
instead.
I think it is important for such crate to not break semver compatibility otherwise it break the ecosystem. I think a Invalid handle is harmless. And while i'm still wondering how to have a safe interface that proxy the HasRaw*Handle, i think i'll just go with a null value for some random backend. |
We can break semver however we feel like until The |
As long as we don't do it too often it's reasonable to have breaking changes. If people need to have fallibility much more than was initially expected, a change in the API to use the proper error handling types is preferred, particularly in the long term. One week or even one month of small trouble isn't a big deal compared to hopefully using this library for the next many years. |
I just raised a related issue for Winit (rust-windowing/winit#2769) while I don't really like how the Android backend for Winit will currently simply panic if you try and query a raw-window-handle while the app is suspended, and I'd much rather return an I also think that Winit's Wayland backend could potentially benefit from being able to return In terms of invalid enum, vs A special enum could potentially make sense if it was useful for the consumer to be able to differentiate the "no backend" situation from the "currently unavailable" situation that could apply to existing backends (such as for Android, and potentially Wayland). |
But isn't |
Yeah, I suppose the "currently unavailable" case is basically an error condition that would make sense as a more descriptive |
This particular issue should be addressed by the the new So if you have something that may or may not be backend in a window handle, you can safely provide a method returning That said...
...this seems like a more compelling case. Panicking here doesn't sound great. Would this case on Android be the only instance in which Winit would return anything other than a valid window handle? |
A case could be made for returning an invalid handle on wayland before getting a configuration acknowledgement from the compositor, and allowing |
Would that require returning an invalid handle, though? |
Yeah, with Wayland only some protocols require a configure dance on the startup, and it doesn't make such surface invalid right away, you can use it to initialize your EGL platform, you just can't draw with it in that case, unless you got a configure. |
Closing in favor of #122. |
When we want to return an empty or invalid handle, it is possible to create an empty handle for an arbitrary platform, but if there is a dedicated enum value for it, then we don't have to pick an arbitrary platform.
The usecase here is to implement HasRawWindowHandle for a
Window
struct that can be backed by any backed decided at runtime (example winit, or no backend at all). And the question is what handle to return when we don't have an handle. AnInvalid
one seems a better choice thanHaiku(HaikuDisplayHandle::empty())
(just an arbitrary random pick)What do you think?