slint icon indicating copy to clipboard operation
slint copied to clipboard

expose window as RawWindowHandle

Open Be-ing opened this issue 3 years ago • 4 comments

RawWindowHandle is a cross platform API for window handles. Some libraries like rfd can make use of a RawWindowHandle (in rfd's case, to parent a popup dialog to the appropriate window).

Be-ing avatar Jan 26 '22 16:01 Be-ing

I pushed a WIP - see linked commit/branch. The Qt implementation is missing, which is a bit of work, but quite doable I think.

In C++ we can get the window handle for a QWidget/Window via QWindow::winId() and the display connection via QPlatformNativeInterface, which supports a per-window connection, for example for the xcb_connection_t. After that we should be able to construct the different variants of https://docs.rs/raw-window-handle/0.4.2/raw_window_handle/index.html .

tronical avatar Jan 26 '22 16:01 tronical

@tronical I couldn't find this code in the current Slint UI release. I'm guessing it was never finished?

levrik avatar May 05 '22 07:05 levrik

Right, this was not yet merged to the master branch.

ogoffart avatar May 05 '22 12:05 ogoffart

I had another look at this and this is not so easy as it looked originally:

  • QWindow::winId is not a native handle on all platform (for example not with wayland https://bugreports.qt.io/browse/QTBUG-76983 ) and getting the actual meaning of it can't seem to be done with public API
  • It's a bit difficult to uphold the unsafety rules. The implementation in the current wip/raw-window-handle branch is unsoud because the PlatformWindow trait could return an invalid window handle. It would be unfortunate to mark the PlatformWindow trait as unsafe just because of that. Also I don't exactly know what's the validity of the handle. For how long is it valid?
  • Would be nice if the RawWindowHandle had a variant that means "None". Right now, if I want to return that there is no handle, I need to make a null pointer for some random other platform.

ogoffart avatar May 05 '22 13:05 ogoffart

consumer API

From the consumer point of view, we have two option:

  1. impl HasRawWindowHandle for Window and impl HasRawDisplayHandle for Window . That would be my preference. But one the issue with this is that slint::Window can only be accessed as a reference from the consumer side (given a ComponentHandle), and some API that consume the RawWindowHandle take the ownership of it. But they can take a &Window as well. We'll have to see how this play with consumer API

  2. Add a getter: fn Window::raw_handle(&slef) -> impl RawDisplayHandle + RawWindowHandle

platform API

Then from the platform point of view, we need to extend the WindowAdapter trait in some way.

  • This would have to be optional to implement (with a default that would return an invalid handle)
  • This shouldn't make the WindowAdapter unsafe to implement.

I wonder if this should be just one function, or two functions for the window and display handle. The example bellow use one function with a tuple, but it would be changed. So one suggestions:

trait WindowAdapter {
   fn raw_handle(&self) -> (Option<&dyn HasRawWindowHandle>, Option<&dyn HasRawDisplayHandle>) 
   { ... }
}

With such API, one would typically implement Has*Handle for the struct that implements WindowAdapter and just return self. But that means that the platform need to be unsafe.

trait WindowAdapter {
   fn raw_handle(&self) -> (Option<Rc<dyn HasRawWindowHandle>>, Option<Rc<dyn HasRawDisplayHandle>>) 
   { ... }
}

That would work fine for the winit platform when we keep a Rcwinit::Window. But otherwise we have the risk that we'd need to make an allocation just to put the return value in the Rc. Also the returned struct must have a 'static lifetime.

trait WindowAdapter {
   type RawHandle<'a> : HasRawWindowHandle + HasRawDisplayHandle;
   fn raw_handle<'a>(&'a self) -> Self::RawHandle<'a>;
}

But since there is no default for associated types, we can't make it optional. (Looking forward for the return impl in trait)

  1. If we implement the trait on slint::Window, something that would work and would be the most flexible would be using a callback, but it's also a bit ugly
trait WindowAdapter {
   fn  raw_handle(callback: &mut dyn FnMut(&dyn HasRawWindowHandle, &dyn HasRawDisplayHandle)) {}
}

I think i'd go with 2 at first

ogoffart avatar Dec 17 '22 18:12 ogoffart

any progress?

ghost avatar Apr 14 '23 13:04 ghost

We're trying to sort out how to model the RWH api with the interior mutability of the winit window owner. Meanwhile https://github.com/slint-ui/slint/pull/2617 adds API to the i-slint-backend-winit crate to provide you with access to the winit window and consequently the RWH. Can you give it a try?

tronical avatar Apr 24 '23 18:04 tronical

For Qt, commit db9b6a5cf6f664bfdd4ca4cfc8207266e89392f2 in the Slint repo attempts to implement RHW support for the Qt backend. This works on macOS and Windows, but requires the use of private API. For Linux, the same could be done, but that kind of sucks because it means it can't be easily compiled against Linux distro package, which typically don't install private Qt headers.

One workaround would be to use QWindow::winId() and cast accordingly. By looking at the public QGuiApplication::platformName() we could see if we need to cast that to a wl_surface* or xcb_window*. However, I don't see a way to retrieve the wl_display* using public API.

So for now, I'll leave the Qt backend. If you need RWH support, the use of the winit backend is required.

tronical avatar Mar 22 '24 15:03 tronical