winit
winit copied to clipboard
Is it possible to wrap an existing window?
Is it possible to use winit to get a Window
struct representing an existing window (not created by winit), given a RawWindowHandle
?
i.e. an equivalent to the .NET NativeWindow.FromHandle(IntPtr) method
Afaik, no.
Thanks for the response.
Are you able to provide any detail as to why this isn't supported?
- Outwith scope / ethos of winit
- Technically challenging / impossible
- Nobody has needed to do it yet
- Something else?
Looking at the implementation a bit more, the init
method
- Creates a window using
winuser::CreateWindowExW
- Wraps the resulting
HWND
usingWindowWrapper(handle)
- Creates an actual
Window
struct using
let win = Window {
window: real_window,
window_state,
thread_executor: event_loop.create_thread_executor(),
};
Since real_window
is a wrapper around the HWND
, this could presumably be substituted for the handle of an existing window (within the process boundary)?
window_state
looks like data that should be possible to retrieve from an existing window via the win32 API.
Not sure about thread_executor
/ the events loop and how this might map to an existing WindowProc
. However, if the window was created externally, do we need to manage the event loop at all? (messages will presumably be being pumped by the host application)
I don't think anyone's needed to do it before. What exactly would be the use case? Would the window be driven with winit's eventloop or be externally driven? I can't imagine this would be an easy task.
This would be useful for audio plugins (VSTs), since they can only use the raw window handle given to them by the host.
What exactly would be the use case?
In a word - interop. My interest just now is mostly in using a Rust library to draw 3D graphics in a non-Rust host application. @Azorlogh's example of VST plugins is another use case.
Would the window be driven with winit's eventloop or be externally driven?
Using the .NET NativeWindow
class as a reference, I'd suggest the event loop would be pumped by the host application (that created and owns the window), but that winit could subclass the window and listen to the loop, handling events on the Rust side as required.
AFAIK the main use for making Winit able to handle foreign windows is so that, when Winit eventually gets a child/embeddable window API, you can parent Winit windows to foreign windows in a sane way. Is that correct? If so, it would probably be easier to have that API take a &impl HasRawWindowHandle
than it would be to adapt Winit's logic to foreign-owned windows.
That sounds like another use case.
For me it's less about being able to parent a winit-owned window to a foreign window (or vice versa), but rather about being able to access data/events from a foreign window.
e.g.
- Create a window in C#
- Pass the window handle into a Rust library
- From the Rust library, query the window dimensions, react to the window paint events, etc for the same window that was passed in
I would also like to use winit for my VST work, but I can't switch away from SDL2 until I can create a window from a *mut c_void
:
pub fn build_from_window_ptr<'a, 'b>(
self,
parent: *mut c_void,
) -> Result<Engine<'a, 'b>, String> {
let sdl_context = sdl2::init()?;
let w: *mut sdl2_sys::SDL_Window =
unsafe { sdl2_sys::SDL_CreateWindowFrom(parent as *const c_void) };
let window: sdl2::video::Window = {
let video_subsystem = sdl_context.video()?;
unsafe { sdl2::video::Window::from_ll(video_subsystem, w) }
};
...
Ok(engine)
}
Is there any path forward here?
Ey, I'm coming here for the same reason as schell and Azorlogh: working on a VST plugin. Until this is done, it appears the best way to do this is to attempt to something with raw wgpu and re-implement various winit features, which is not ideal.
I'd be all for making winit more capable in this way.
This is something that we will have to provide ourselves. I think a good path forward would be to create a new crate foreign-raw-window-handle
which does the work of converting a *mut c_void
into RawWindowHandle
. I'm sure we could use the SDL2 code as a guide.
Of course, we could also write this as a patch into the raw-window-handle
crate but then we would be beholden to their acceptance and release schedule. It might be just as easy to release the crate, get immediate use and then merge it to another crate later (this one or raw-window-handle
).
This is somewhat difficult to do in real life. For something like X11, it is expected that there is only one event loop handler driving events. So you can't really trivially wrap some other X11 window with a winit
window. Some other system might be trying to poll events from that window. Missing an event is annoying at best and can cause undefined behavior at worst.
The only one where it's kind of possible is Windows, where the event handler is stored in the window itself. But it requires some finesse and would still be wildly unsafe.
For Web this is already possible through WindowBuilder::with_canvas()
.
This is somewhat difficult to do in real life. For something like X11, it is expected that there is only one event loop handler driving events. So you can't really trivially wrap some other X11 window with a
winit
window. Some other system might be trying to poll events from that window. Missing an event is annoying at best and can cause undefined behavior at worst.The only one where it's kind of possible is Windows, where the event handler is stored in the window itself. But it requires some finesse and would still be wildly unsafe.
The plugin use-case is that we are given a window handle that doesn't have an event loop on it yet. For other situations I agree that this would be a problem, but it would work in all of the situations it is expected to work (i.e. if something else already ran an event loop, we likely wouldn't be given a window pointer anyway).
@rdrpenguin04 if you're given with a window you're not the one handling events for it, so you basically have a RawWindowHandle
, not the actual window, because you're not owning that window in the first place.
Fair; that was a terminology mistake, as that was entirely what I meant.
Yeah, then use https://github.com/rust-windowing/raw-window-handle/ which is what is used for such things and what wgpu/glutin use, they don't use Winit
window, they use that handle.
Let me catch you up. Some plugin APIs, such as VST, pass the application a window handle in some platform-specific manner. If I want to use winit for a VST, I need to create a raw-window-handle
from that platform-specific form, and then the goal is to pass that handle to winit. That's what this issue report is about.
If you don't have event loop winit is useless. If the platform relies on hijacking global events, then winit can't help either and it's not crossplatform anyway. I'm not sure why you need winit in a first place.
The plugin will be passed a window handle, and it can attach an event loop to that. I would like to use winit specifically to manage the event loop and give useful input abstractions.
VST plugins are Windows specific, right? Or does it work on other platforms? If so, which ones?
VST3 is cross-platform, but even VST2 was ported to Linux IIRC. LV2 has a very similar setup, as does clap, and all of these are just audio plugin specifications I know about; I'm sure other types of plugins have similar setups as well.
And what kind of functionality do you want to access on the window?
I guess it would be technologically feasible in some cases to allow e.g. Window::inner_size
, but getting events for a window is an entirely different matter.
@madsmtm on Wayland you can't do anything like that at all, so it's just straight not working.
The only thing you can get is inner_size
, but not from winit, but because you define it and control, and not the server. On X11 you can do more for example, but it's not cross platform.
Yeah, I was mostly thinking if there is feasibly some sort of platform-specific extension that would make sense for this (like, some of this would be possible on macOS, even though I heavily dislike it), or if we should consider closing the issue as "wontfix"
Looking at the LV2 specification, the plugin will be given one of the following:
- On macOS (Cocoa),
NSView*
- On Windows,
HWND
- On Linux (X11),
Window
Wayland is not presently supported by LV2.
For the GUI extension to clap, the plugin will either be given one of the above or it will declare that it will create its own window, which is required on Wayland.
I'm not finding information on VST3's GUI system; I know that VST2 is the same as LV2, but without supporting macOS.
My proposal would be simply to accept a RawWindowHandle
on initializing a window; I don't know how much infrastructure that would break, but it seems like it would be theoretically okay. It would then be able to set up events and so forth, as those won't be set up on the window yet. If that needs to be an unsafe contract, so be it.
The problem, as you hinted at yourself, is that this requires a lot of infrastructure changes. Theoretically simple, yes, but the reality is quite different.
I can only really speak for the macOS backend here, but there, we have the concept of a root NSWindow
, and NSView
s are nested inside that. Winit currently lump these together as just Window
, but the things you can do and access from these two is quite different.
RawWindowHandle
contains an NSView
, so while we could add a child view to the NSView
that we own, we probably cannot control the NSWindow
nearly as much.
Which in turn means we're quite limited in the events we'd be able to set up. Depends on whether we're allowed to install a new NSWindowDelegate
or not?
For now though, the platforms you mentioned seems like the same ones baseview
support, perhaps that'll fulfil your requirements?
In the future, if we do the backend splitting as is discussed in https://github.com/rust-windowing/winit/issues/3367, it might become easier for specific platforms to support this, by exposing their internals? Unsure, and not sure I'd want to take on the maintenance burden of that (supporting windows in more states is always difficult).
Alright, baseview
does support what I need for now, so this isn't quite as pertinent of a request for winit
as I imagined. I wonder how much could be ported from baseview
to winit
to give some set of the features.