rust-windowing / raw-window-handle

A common windowing interoperability library for Rust
Apache License 2.0
312 stars 49 forks source link

Would it make sense to have some kind of "view" or "renderable" handle? #123

Open madsmtm opened 1 year ago

madsmtm commented 1 year ago

I'm only really familiar with macOS and iOS, but on those platforms it would make sense to split the window and the view handles in two.

That way, users could make some sort of interface in the native toolkit, and just provide the NSView handle to wgpu to render into. With the current design, we're assuming that the whole window's content is what should be rendered into.

I think this is roughly how it already works on web, e.g. you can lay out the page in a certain way, and then just create a canvas that you pass on to the renderer to do its thing. (Though arguably winit shouldn't be creating a canvas, perhaps instead it should provide a div that the graphics library itself can insert the canvas into?)

Would this sort of thing work on other platforms?

MarijnS95 commented 1 year ago

I think this is roughly how it already works on web

And on android, where NativeWindow is just a silly name for the native wrapper around a Surface and could be any part of the view layout, not necessarily the whole "window surface".

https://github.com/MarijnS95/AndroidNativeSurface

madsmtm commented 1 year ago

Basically, I want better cooperation with the existing layout systems (if they exist).

I'm especially unsure what the best approach is for building a "native" user interface on Linux? GTK? Or would it be best to do it "raw", and provide something like struct XcbViewHandle { visual_id: u32, position: (u32, u32), size: (u32, u32) }?

kchibisov commented 1 year ago

Wayland's wl_surface can be used for anything as well, but that's how the handle is done.

I wonder whether on macOS you don't need to have a reference to a window to perform some requests on it? Also, the user could simply omit the window, fields are not required to be set, so setting just NSView is absolutely fine.

and provide something like struct XcbViewHandle { visual_id: u32, position: (u32, u32), size: (u32, u32) }?

visual has nothing to do with windows on X11, it's a pixel format. Everything is a XID, which is window as it's said in the structs.

madsmtm commented 1 year ago

The window field is entirely redundant on macOS, it can always be gotten from the view.

So maybe it would actually make sense to rename the [Raw]WindowHandle to [Raw]ViewHandle (or similar)?

MarijnS95 commented 1 year ago

to [Raw]ViewHandle (or similar)?

The common name for this thing on e.g. Android and in graphics APIs like (E)GL and Vulkan is a "surface", I'd name it that.

OTOH it's not that in Vulkan/GL yet, you still have to create a Surface from a Surface which may sound weird?

madsmtm commented 2 weeks ago

I've been thinking about this again, and I think my problem is that we have conflated distinct concepts in raw-window-handle: A "widget" and a "surface", and both are named "window" (I don't care much for the naming here, I just want to make clear that these are distinct things, and all useful in each their own way).

To start with, the task of this library was to pass a render-able surface from Winit/SDL/similar to Glutin/Wgpu/similar. I can only really speak for macOS/iOS, but the correct way to do this correct is to pass a CALayer. That said, other use-cases for this library have appeared (see rfd's usage, window-shadows, muda, window-vibrancy, I can imagine a bunch more), and these need access to the system's widget tree. This is the reason why we are currently passing UIView/NSView on UIKit/AppKit these platforms.

I'm currently undecided myself whether we really ought to provide CALayer on UIKit/AppKit instead, and make it very clear that the handle is actually a handle to a surface, if we should have both a widget and a surface handle, or whether the status-quo is good enough?

In any case, this matters if we want to provide proper subsurface support in Winit. With the current design of raw-window-handle, this would require us to model subsurfaces as NSView/UIView, which is inefficient if you don't need the event handling that provides.


An idea for how this may look is as follows:

enum RawSurfaceHandle {
    AndroidNdk(ANativeWindow),
    CoreGraphics(CALayer), // Both AppKit and UIKit
    Drm(plane), // Probably
    Gbm(device), // Probably
    Haiku(BDirectWindow), // Probably
    OhosNdk(OHNativeWindow),
    Wayland(wl_surface),
    WebCanvas(HtmlCanvasElement),
    WebOffscreenCanvas(OffscreenCanvas),
    Win32(HWND, GWLP_HINSTANCE), // Unsure
    WinRt(CoreWindow), // Maybe?
    Xcb(xcb_window_t, xcb_visualid_t), // Unsure
    Xlib(Window, visual ID), // Unsure
}

// May not be available on all platforms, a higher-level concept in windowing libraries, not related to drawing
enum RawWidgetHandle {
    AndroidNdk(View), // Maybe?
    AppKit(NSView),
    Haiku(BWindow), // Probably
    OhosNDK(XComponent),
    Orbital(orbclient::Window),
    UIKit(UIView),
    Web(HTMLElement),
    Win32(HWND), // Maybe?
}

I think you can usually go from RawWidgetHandle to RawSurfaceHandle, but this needs access to platform APIs (and raw-window-handle is intentionally dependency free), and may also need a transform.

Unsure how we should model this trait-wise.

MarijnS95 commented 2 weeks ago

You're correct that it would be a View object on Android. Around that, applications still wrap this in a Window which contains a "root surface" that the view hierarchy renders into.

Unrelated to this, we could also provide an Option<SurfaceControl> next to NativeWindow (even though the user is able to query/create it themselves from NativeWindow). And we could even have the whole NativeWindow optional (or have two independent variants).