Simplify Skia Renderer

Allocate the renderer's surface type in the constructor.

This also changes the C++ API to match this, which complicates some code
a little bit.
This commit is contained in:
Simon Hausmann 2023-05-08 15:20:48 +02:00 committed by Simon Hausmann
parent 7c0f9e3b15
commit d0cdc462c7
6 changed files with 298 additions and 276 deletions

View file

@ -227,6 +227,53 @@ public:
}
};
/// An opaque, low-level window handle that internalizes everything necessary to exchange messages
/// with the windowing system. This includes the connection to the display server, if necessary.
///
/// Note that this class does not provide any kind of ownership. The caller is responsible for
/// ensuring that the pointers supplied to the constructor are valid throughout the lifetime of the
/// WindowHandle.
class WindowHandle
{
cbindgen_private::CppRawHandleOpaque inner;
public:
WindowHandle() = delete;
# if !defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)
WindowHandle(uint32_t /*xcb_window_t*/ window, uint32_t /*xcb_visualid_t*/ visual_id,
xcb_connection_t *connection, int screen)
{
cbindgen_private::slint_new_raw_window_handle_x11(window, visual_id, connection, screen,
&inner);
}
WindowHandle(wl_surface *surface, wl_display *display)
{
cbindgen_private::slint_new_raw_window_handle_wayland(surface, display, size, &inner);
}
# elif defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)
WindowHandle(void *nsview, void *nswindow)
{
cbindgen_private::slint_new_raw_window_handle_appkit(nsview, nswindow, &inner);
}
# elif !defined(__APPLE__) && (defined(_WIN32) || !defined(_WIN64))
/// Windows handle
WindowHandle(void *hwnd, void *hinstance)
{
cbindgen_private::slint_new_raw_window_handle_win32(hwnd, hinstance, &inner);
}
# endif
/// \private
cbindgen_private::CppRawHandleOpaque handle() const { return inner; }
};
/// Slint's Skia renderer.
///
/// To be used as a template parameter of the WindowAdapter.
@ -235,10 +282,11 @@ public:
/// of the homonymous functions
///
/// Use render to perform the rendering.
class SkiaRenderer
{
mutable cbindgen_private::SkiaRendererOpaque inner;
mutable cbindgen_private::SkiaRendererOpaque inner = nullptr;
WindowHandle window_handle;
PhysicalSize initial_size;
public:
virtual ~SkiaRenderer()
@ -249,7 +297,12 @@ public:
};
SkiaRenderer(const SkiaRenderer &) = delete;
SkiaRenderer &operator=(const SkiaRenderer &) = delete;
SkiaRenderer() = default;
/// Constructs a new Skia renderer for the given window - referenced by the provided
/// WindowHandle - and the specified initial size.
SkiaRenderer(WindowHandle window_handle, PhysicalSize initial_size)
: window_handle(window_handle), initial_size(initial_size)
{
}
/// \private
void init(const cbindgen_private::WindowAdapterRcOpaque *win) const
@ -257,7 +310,8 @@ public:
if (inner) {
cbindgen_private::slint_skia_renderer_drop(inner);
}
inner = cbindgen_private::slint_skia_renderer_new(win);
inner = cbindgen_private::slint_skia_renderer_new(win, window_handle.handle(),
initial_size);
}
/// \private
@ -278,34 +332,7 @@ public:
void hide() const { cbindgen_private::slint_skia_renderer_hide(inner); }
# if !defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)
void show(uint32_t /*xcb_window_t*/ window, uint32_t /*xcb_visualid_t*/ visual_id,
xcb_connection_t *connection, int screen, PhysicalSize size) const
{
cbindgen_private::slint_skia_renderer_show_x11(inner, window, visual_id, connection, screen,
size);
}
void show(wl_surface *surface, wl_display *display, PhysicalSize size) const
{
cbindgen_private::slint_skia_renderer_show_wayland(inner, surface, display, size);
}
# elif defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)
void show(void *nsview, void *nswindow, PhysicalSize size) const
{
cbindgen_private::slint_skia_renderer_show_appkit(inner, nsview, nswindow, size);
}
# elif !defined(__APPLE__) && (defined(_WIN32) || !defined(_WIN64))
/// Windows handle
void show(void *HWND, void *hinstance, PhysicalSize size) const
{
cbindgen_private::slint_skia_renderer_show_win32(inner, HWND, hinstance, size);
}
# endif
void show() const { cbindgen_private::slint_skia_renderer_show(inner); }
};
/// Call this function at each iteration of the event loop to call the timer handler and advance

View file

@ -205,13 +205,92 @@ macro_rules! init_raw {
};
}
// FIXME: This may need different values for a 32-bit build
#[repr(C)]
pub struct CppRawHandleOpaque([usize; 7]);
impl CppRawHandleOpaque {
fn into_raw_handle(self) -> CppRawHandle {
// Safety: there should be no way to construct a CppRawHandleOpaque without it holding an actual CppRawHandle
unsafe { std::mem::transmute::<CppRawHandleOpaque, CppRawHandle>(self) }
}
}
/// Asserts that CppRawHandleOpaque is as large as CppRawHandle and has the same alignment, to make transmute safe.
const _: [(); std::mem::size_of::<CppRawHandleOpaque>()] =
[(); std::mem::size_of::<CppRawHandle>()];
#[no_mangle]
pub unsafe extern "C" fn slint_new_raw_window_handle_win32(
hwnd: *mut c_void,
hinstance: *mut c_void,
handle_opaque: *mut CppRawHandleOpaque,
) {
let handle = CppRawHandle(
RawWindowHandle::Win32(init_raw!(raw_window_handle::Win32WindowHandle { hwnd, hinstance })),
RawDisplayHandle::Windows(raw_window_handle::WindowsDisplayHandle::empty()),
);
core::ptr::write(handle_opaque as *mut CppRawHandle, handle);
}
#[no_mangle]
pub unsafe extern "C" fn slint_new_raw_window_handle_x11(
window: u32,
visual_id: u32,
connection: *mut c_void,
screen: core::ffi::c_int,
handle_opaque: *mut CppRawHandleOpaque,
) {
use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle};
let handle = CppRawHandle(
RawWindowHandle::Xcb(init_raw!(XcbWindowHandle { window, visual_id })),
RawDisplayHandle::Xcb(init_raw!(XcbDisplayHandle { connection, screen })),
);
core::ptr::write(handle_opaque as *mut CppRawHandle, handle);
}
#[no_mangle]
pub unsafe extern "C" fn slint_new_raw_window_handle_wayland(
surface: *mut c_void,
display: *mut c_void,
handle_opaque: *mut CppRawHandleOpaque,
) {
use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle};
let handle = CppRawHandle(
RawWindowHandle::Wayland(init_raw!(WaylandWindowHandle { surface })),
RawDisplayHandle::Wayland(init_raw!(WaylandDisplayHandle { display })),
);
core::ptr::write(handle_opaque as *mut CppRawHandle, handle);
}
#[no_mangle]
pub unsafe extern "C" fn slint_new_raw_window_handle_appkit(
ns_view: *mut c_void,
ns_window: *mut c_void,
handle_opaque: *mut CppRawHandleOpaque,
) {
use raw_window_handle::{AppKitDisplayHandle, AppKitWindowHandle};
let handle = CppRawHandle(
RawWindowHandle::AppKit(init_raw!(AppKitWindowHandle { ns_view, ns_window })),
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()),
);
core::ptr::write(handle_opaque as *mut CppRawHandle, handle);
}
#[no_mangle]
pub unsafe extern "C" fn slint_skia_renderer_new(
window: &WindowAdapterRcOpaque,
window_adapter: &WindowAdapterRcOpaque,
handle_opaque: CppRawHandleOpaque,
size: IntSize,
) -> SkiaRendererOpaque {
let window = core::mem::transmute::<&WindowAdapterRcOpaque, &Rc<dyn WindowAdapter>>(window);
let weak = Rc::downgrade(window);
Box::into_raw(Box::new(SkiaRenderer::new(weak))) as SkiaRendererOpaque
let window_adapter =
core::mem::transmute::<&WindowAdapterRcOpaque, &Rc<dyn WindowAdapter>>(window_adapter);
let weak = Rc::downgrade(window_adapter);
Box::into_raw(Box::new(SkiaRenderer::new(
weak,
handle_opaque.into_raw_handle(),
PhysicalSize { width: size.width, height: size.height },
))) as SkiaRendererOpaque
}
#[no_mangle]
@ -220,70 +299,9 @@ pub unsafe extern "C" fn slint_skia_renderer_drop(r: SkiaRendererOpaque) {
}
#[no_mangle]
pub unsafe extern "C" fn slint_skia_renderer_show_win32(
r: SkiaRendererOpaque,
hwnd: *mut c_void,
hinstance: *mut c_void,
size: IntSize,
) {
pub unsafe extern "C" fn slint_skia_renderer_show(r: SkiaRendererOpaque) {
let r = &*(r as *const SkiaRenderer);
let handle = CppRawHandle(
RawWindowHandle::Win32(init_raw!(raw_window_handle::Win32WindowHandle { hwnd, hinstance })),
RawDisplayHandle::Windows(raw_window_handle::WindowsDisplayHandle::empty()),
);
r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap()
}
#[no_mangle]
pub unsafe extern "C" fn slint_skia_renderer_show_x11(
r: SkiaRendererOpaque,
window: u32,
visual_id: u32,
connection: *mut c_void,
screen: core::ffi::c_int,
size: IntSize,
) {
use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle};
let r = &*(r as *const SkiaRenderer);
let handle = CppRawHandle(
RawWindowHandle::Xcb(init_raw!(XcbWindowHandle { window, visual_id })),
RawDisplayHandle::Xcb(init_raw!(XcbDisplayHandle { connection, screen })),
);
r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap();
}
#[no_mangle]
pub unsafe extern "C" fn slint_skia_renderer_show_wayland(
r: SkiaRendererOpaque,
surface: *mut c_void,
display: *mut c_void,
size: IntSize,
) {
use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle};
let r = &*(r as *const SkiaRenderer);
let handle = CppRawHandle(
RawWindowHandle::Wayland(init_raw!(WaylandWindowHandle { surface })),
RawDisplayHandle::Wayland(init_raw!(WaylandDisplayHandle { display })),
);
r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap();
}
#[no_mangle]
pub unsafe extern "C" fn slint_skia_renderer_show_appkit(
r: SkiaRendererOpaque,
ns_view: *mut c_void,
ns_window: *mut c_void,
size: IntSize,
) {
use raw_window_handle::{AppKitDisplayHandle, AppKitWindowHandle};
let r = &*(r as *const SkiaRenderer);
let handle = CppRawHandle(
RawWindowHandle::AppKit(init_raw!(AppKitWindowHandle { ns_view, ns_window })),
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()),
);
r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap();
r.show().unwrap()
}
#[no_mangle]

View file

@ -19,57 +19,36 @@ struct Geometry
uint32_t height = 0;
};
struct MyWindowAdapter : public slint_platform::WindowAdapter<slint_platform::SkiaRenderer>
struct NativeWindowHandle
{
HWND hwnd;
};
struct MyWindowAdapter : NativeWindowHandle,
public slint_platform::WindowAdapter<slint_platform::SkiaRenderer>
{
HWND parentWindow;
HINSTANCE hInstance = GetModuleHandleW(nullptr);
mutable std::optional<HWND> hwnd;
Geometry geometry = { 0, 0, 600, 300 };
MyWindowAdapter(HWND winId) : parentWindow(winId) { }
MyWindowAdapter(HWND winId)
: NativeWindowHandle { MyWindowAdapter::create_window(winId) },
slint_platform::WindowAdapter<slint_platform::SkiaRenderer>(
slint_platform::WindowHandle(hwnd, GetModuleHandleW(nullptr)),
slint::PhysicalSize({ 600, 300 }))
{
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this);
}
slint::PhysicalSize windowSize() const
{
RECT r;
GetWindowRect(*hwnd, &r);
GetWindowRect(hwnd, &r);
return slint::PhysicalSize({ uint32_t(r.right - r.left), uint32_t(r.bottom - r.top) });
}
void show() const override
{
if (hwnd)
return;
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = MyWindowAdapter::windowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
hwnd = CreateWindowEx(0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_CHILDWINDOW, // Window style
// Size and position
geometry.x, geometry.y, geometry.width, geometry.height,
parentWindow,
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
SetWindowLongPtr(*hwnd, GWLP_USERDATA, (LONG_PTR)this);
ShowWindow(*hwnd, SW_SHOWNORMAL);
renderer().show(*hwnd, hInstance, slint::PhysicalSize({ geometry.width, geometry.height }));
ShowWindow(hwnd, SW_SHOWNORMAL);
renderer().show();
}
void hide() const override
@ -78,12 +57,7 @@ struct MyWindowAdapter : public slint_platform::WindowAdapter<slint_platform::Sk
renderer().hide();
}
void request_redraw() const override
{
if (!hwnd)
return;
InvalidateRect(*hwnd, nullptr, false);
}
void request_redraw() const override { InvalidateRect(hwnd, nullptr, false); }
void render()
{
@ -101,11 +75,7 @@ struct MyWindowAdapter : public slint_platform::WindowAdapter<slint_platform::Sk
void setGeometry(int x, int y, int width, int height)
{
if (!hwnd) {
geometry = { x, y, uint32_t(width), uint32_t(height) };
return;
}
SetWindowPos(*hwnd, nullptr, x, y, width, height, 0);
SetWindowPos(hwnd, nullptr, x, y, width, height, 0);
}
std::optional<slint::cbindgen_private::MouseEvent> mouseEventForMessage(UINT uMsg,
@ -233,4 +203,39 @@ struct MyWindowAdapter : public slint_platform::WindowAdapter<slint_platform::Sk
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
private:
static HWND create_window(HWND parentWindow)
{
HINSTANCE hInstance = GetModuleHandleW(nullptr);
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = MyWindowAdapter::windowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_CHILDWINDOW, // Window style
// Size and position
0, 0, 600, 300,
parentWindow,
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
return hwnd;
}
};

View file

@ -25,11 +25,39 @@ slint::cbindgen_private::PointerEventButton convert_button(Qt::MouseButtons b)
}
}
static slint_platform::WindowHandle window_handle_for_qt_window(QWindow *window)
{
#ifdef __APPLE__
QPlatformNativeInterface *native = qApp->platformNativeInterface();
void *nsview = native->nativeResourceForWindow(QByteArray("nsview"), window);
void *nswindow = native->nativeResourceForWindow(QByteArray("nswindow"), window);
return slint_platform::WindowHandle(nsview, nswindow);
#elif defined Q_OS_WIN
auto wid = Qt::HANDLE(window->winId());
return slint_platform::WindowHandle(wid, GetModuleHandle(nullptr));
#else
auto wid = winId();
auto visual_id = 0; // FIXME
QPlatformNativeInterface *native = qApp->platformNativeInterface();
auto *connection = reinterpret_cast<xcb_connection_t *>(
native->nativeResourceForWindow(QByteArray("connection"), window));
auto screen = quintptr(native->nativeResourceForWindow(QByteArray("screen"), window));
return slint_platform::WindowHandle(wid, wid, connection, screen);
#endif
}
class MyWindow : public QWindow, public slint_platform::WindowAdapter<slint_platform::SkiaRenderer>
{
public:
MyWindow(QWindow *parentWindow = nullptr) : QWindow(parentWindow) { }
MyWindow(QWindow *parentWindow = nullptr)
: QWindow(parentWindow),
slint_platform::WindowAdapter<slint_platform::SkiaRenderer>(
window_handle_for_qt_window(this),
slint::PhysicalSize({ uint32_t(width()), uint32_t(height()) }))
{
}
/*void keyEvent(QKeyEvent *event) override
{
@ -62,29 +90,7 @@ public:
{
auto window = const_cast<QWindow *>(static_cast<const QWindow *>(this));
window->QWindow::show();
auto windowSize = slint::PhysicalSize({ uint32_t(width()), uint32_t(height()) });
#ifdef __APPLE__
QPlatformNativeInterface *native = qApp->platformNativeInterface();
void *nsview = native->nativeResourceForWindow(QByteArray("nsview"), window);
void *nswindow = native->nativeResourceForWindow(QByteArray("nswindow"), window);
renderer().show(nsview, nswindow, windowSize);
#elif defined Q_OS_WIN
auto wid = Qt::HANDLE(winId());
renderer().show(
wid, GetModuleHandle(nullptr),
slint_platform::WindowAdapter<slint_platform::SkiaRenderer>::window().size());
#else
auto wid = winId();
auto visual_id = 0; // FIXME
QPlatformNativeInterface *native = qApp->platformNativeInterface();
auto *connection = reinterpret_cast<xcb_connection_t *>(
native->nativeResourceForWindow(QByteArray("connection"), window));
auto screen = quintptr(native->nativeResourceForWindow(QByteArray("screen"), window));
renderer().show(
wid, wid, connection, screen,
slint_platform::WindowAdapter<slint_platform::SkiaRenderer>::window().size());
#endif
renderer().show();
}
void hide() const override
{
@ -92,10 +98,7 @@ public:
const_cast<MyWindow *>(this)->QWindow::hide();
}
void request_redraw() const override
{
const_cast<MyWindow *>(this)->requestUpdate();
}
void request_redraw() const override { const_cast<MyWindow *>(this)->requestUpdate(); }
void resizeEvent(QResizeEvent *ev) override
{