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

Is it possible to wrap an existing window? #1161

Open
LordBenjamin opened this issue Sep 13, 2019 · 29 comments
Open

Is it possible to wrap an existing window? #1161

LordBenjamin opened this issue Sep 13, 2019 · 29 comments
Labels
F - question There's no such thing as a stupid one

Comments

@LordBenjamin
Copy link

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 goddessfreya added the F - question There's no such thing as a stupid one label Sep 13, 2019
@goddessfreya
Copy link
Contributor

Afaik, no.

@LordBenjamin
Copy link
Author

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?

@LordBenjamin
Copy link
Author

Looking at the implementation a bit more, the init method

  • Creates a window using winuser::CreateWindowExW
  • Wraps the resulting HWND using WindowWrapper(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)

@goddessfreya
Copy link
Contributor

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
Copy link

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

@LordBenjamin
Copy link
Author

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
Copy link
Contributor

Osspial commented Sep 15, 2019

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
Copy link
Author

LordBenjamin commented Sep 15, 2019

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

@schell
Copy link

schell commented May 10, 2021

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
Copy link

schell commented Dec 18, 2021

Is there any path forward here?

@rdrpenguin04
Copy link

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
Copy link

schell commented Sep 30, 2022

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
Copy link
Member

notgull commented Nov 26, 2023

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
Copy link
Member

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

@rdrpenguin04
Copy link

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
Copy link
Member

@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
Copy link

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

@kchibisov
Copy link
Member

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
Copy link

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
Copy link
Member

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
Copy link

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
Copy link
Member

madsmtm commented Jan 10, 2024

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

@rdrpenguin04
Copy link

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
Copy link
Member

madsmtm commented Jan 11, 2024

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
Copy link
Member

@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
Copy link
Member

madsmtm commented Jan 11, 2024

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
Copy link

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.

@madsmtm
Copy link
Member

madsmtm commented Jan 12, 2024

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 #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
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
F - question There's no such thing as a stupid one
Development

No branches or pull requests

10 participants