rust-windowing / winit

Window handling library in pure Rust
https://docs.rs/winit/
Apache License 2.0
4.7k stars 887 forks source link

Is it possible to wrap an existing window? #1161

Open LordBenjamin opened 4 years ago

LordBenjamin commented 4 years ago

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

goddessfreya commented 4 years ago

Afaik, no.

LordBenjamin commented 4 years ago

Thanks for the response.

Are you able to provide any detail as to why this isn't supported?

LordBenjamin commented 4 years ago

Looking at the implementation a bit more, the init method

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)

goddessfreya commented 4 years ago

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.

Azorlogh commented 4 years ago

This would be useful for audio plugins (VSTs), since they can only use the raw window handle given to them by the host.

LordBenjamin commented 4 years ago

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.

Osspial commented 4 years ago

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.

LordBenjamin commented 4 years ago

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.

schell commented 3 years ago

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)
    }
schell commented 2 years ago

Is there any path forward here?

rdrpenguin04 commented 1 year ago

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.

schell commented 1 year ago

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).

notgull commented 9 months ago

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.

daxpedda commented 9 months ago

For Web this is already possible through WindowBuilder::with_canvas().

rdrpenguin04 commented 8 months ago

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).

kchibisov commented 8 months ago

@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.

rdrpenguin04 commented 8 months ago

Fair; that was a terminology mistake, as that was entirely what I meant.

kchibisov commented 8 months ago

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.

rdrpenguin04 commented 8 months ago

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.

kchibisov commented 8 months ago

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.

rdrpenguin04 commented 8 months ago

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.

madsmtm commented 8 months ago

VST plugins are Windows specific, right? Or does it work on other platforms? If so, which ones?

rdrpenguin04 commented 8 months ago

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.

madsmtm commented 8 months ago

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.

kchibisov commented 8 months ago

@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.

madsmtm commented 8 months ago

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"

rdrpenguin04 commented 8 months ago

Looking at the LV2 specification, the plugin will be given one of the following:

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.

madsmtm commented 7 months ago

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 NSViews 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).

rdrpenguin04 commented 7 months ago

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.