// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 #pragma once #include "slint.h" #include #include #include #include struct xcb_connection_t; struct wl_surface; struct wl_display; #if defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64) # ifdef __OBJC__ @class NSView; @class NSWindow; # else typedef struct objc_object NSView; typedef struct objc_object NSWindow; # endif #endif namespace slint { /// Use the types in this namespace when implementing a custom Slint platform. /// /// Slint comes with built-in support for different windowing systems, called backends. A backend /// is a module that implements the Platform interface in this namespace, interacts with a /// windowing system, and uses one of Slint's renderers to display a scene to the windowing system. /// A typical Slint application uses one of the built-in backends. Implement your own Platform if /// you're using Slint in an environment without a windowing system, such as with microcontrollers, /// or you're embedding a Slint UI as plugin in other applications. /// /// Examples of custom platform implementation can be found in the Slint repository: /// - https://github.com/slint-ui/slint/tree/master/examples/cpp/platform_native /// - https://github.com/slint-ui/slint/tree/master/examples/cpp/platform_qt /// - https://github.com/slint-ui/slint/blob/master/api/cpp/esp-idf/slint/src/slint-esp.cpp /// /// The entry point to re-implement a platform is the Platform class. Derive /// from slint::platform::Platform, and call slint::platform::set_platform /// to set it as the Slint platform. /// /// Another important class to subclass is the WindowAdapter. namespace platform { /// Internal interface for a renderer for use with the WindowAdapter. /// /// This class is not intended to be re-implemented. In places where this class is required, use /// one of the existing implementations such as SoftwareRenderer or SkiaRenderer. class AbstractRenderer { private: virtual ~AbstractRenderer() { } AbstractRenderer(const AbstractRenderer &) = delete; AbstractRenderer &operator=(const AbstractRenderer &) = delete; AbstractRenderer() = default; /// \private virtual cbindgen_private::RendererPtr renderer_handle() const = 0; friend class WindowAdapter; friend class SoftwareRenderer; friend class SkiaRenderer; }; /// Base class for the layer between a slint::Window and the windowing system specific window type, /// such as a Win32 `HWND` handle or a `wayland_surface_t`. /// /// Re-implement this class to establish the link between the two, and pass messages in both /// directions: /// /// - When receiving messages from the windowing system about state changes, such as the window /// being resized, the user requested the window to be closed, input being received, etc. you /// need to call the corresponding event functions on the Window, such as /// Window::dispatch_resize_event(), Window::dispatch_mouse_press_event(), or /// Window::dispatch_close_requested_event(). /// /// - Slint sends requests to change visibility, position, size, etc. via virtual functions such as /// set_visible(), set_size(), set_position(), or update_window_properties(). /// Re-implement these functions and delegate the requests to the windowing system. /// /// If the implementation of this bi-directional message passing protocol is incomplete, the user /// may experience unexpected behavior, or the intention of the developer calling functions on the /// Window API may not be fulfilled. /// /// Your WindowAdapter subclass must hold a renderer (either a SoftwareRenderer or a SkiaRenderer). /// In the renderer() method, you must return a reference to it. /// /// # Example /// ```cpp /// class MyWindowAdapter : public slint::platform::WindowAdapter { /// slint::platform::SoftwareRenderer m_renderer; /// NativeHandle m_native_window; // a handle to the native window /// public: /// void request_redraw() override { m_native_window.refresh(); } /// slint::PhysicalSize size() const override { /// return slint::PhysicalSize({m_native_window.width, m_native_window.height}); /// } /// slint::platform::AbstractRenderer &renderer() override { return m_renderer; } /// void set_visible(bool v) override { /// if (v) { /// m_native_window.show(); /// } else { /// m_native_window.hide(); /// } /// } /// // ... /// void repaint_callback(); /// } /// ``` /// /// Rendering is typically asynchronous, and your windowing system or event loop would invoke /// a callback when it is time to render. /// ```cpp /// void MyWindowAdapter::repaint_callback() /// { /// slint::platform::update_timers_and_animations(); /// m_renderer.render(m_native_window.buffer(), m_native_window.width); /// // if animations are running, schedule the next frame /// if (window().has_active_animations()) m_native_window.refresh(); /// } /// ``` class WindowAdapter { // This is a pointer to the rust window that own us. // Note that we do not have ownership (there is no reference increase for this) // because it would otherwise be a reference loop cbindgen_private::WindowAdapterRcOpaque self {}; // Whether this WindowAdapter was already given to the slint runtime bool was_initialized = false; cbindgen_private::WindowAdapterRcOpaque initialize() { cbindgen_private::slint_window_adapter_new( this, [](void *wa) { delete reinterpret_cast(wa); }, [](void *wa) { return reinterpret_cast(wa)->renderer().renderer_handle(); }, [](void *wa, bool visible) { reinterpret_cast(wa)->set_visible(visible); }, [](void *wa) { reinterpret_cast(wa)->request_redraw(); }, [](void *wa) -> cbindgen_private::IntSize { return reinterpret_cast(wa)->size(); }, [](void *wa, cbindgen_private::IntSize size) { reinterpret_cast(wa)->set_size( slint::PhysicalSize({ size.width, size.height })); }, [](void *wa, const cbindgen_private::WindowProperties *p) { reinterpret_cast(wa)->update_window_properties( *reinterpret_cast(p)); }, [](void *wa, cbindgen_private::Point2D *point) -> bool { if (auto pos = reinterpret_cast(wa)->position()) { *point = *pos; return true; } else { return false; } }, [](void *wa, cbindgen_private::Point2D point) { reinterpret_cast(wa)->set_position( slint::PhysicalPosition({ point.x, point.y })); }, &self); was_initialized = true; return self; } friend inline void set_platform(std::unique_ptr platform); public: /// Construct a WindowAdapter explicit WindowAdapter() { } virtual ~WindowAdapter() = default; /// This function is called by Slint when the slint window is shown or hidden. /// /// Re-implement this function to forward the call to show/hide the native window /// /// When the window becomes visible, this is a good time to call /// slint::Window::dispatch_scale_factor_change_event to initialise the scale factor. virtual void set_visible(bool) { } /// This function is called when Slint detects that the window need to be repainted. /// /// Reimplement this function to forward the call to the window manager. /// /// You should not render the window in the implementation of this call. Instead you should /// do that in the next iteration of the event loop, or in a callback from the window manager. virtual void request_redraw() { } /// Request a new size for the window to the specified size on the screen, in physical or /// logical pixels and excluding a window frame (if present). /// /// This is called from slint::Window::set_size(). /// /// The default implementation does nothing /// /// This function should send the size to the Windowing system. If the window size actually /// changes, you should call slint::Window::dispatch_resize_event to propagate the new size /// to the slint view. virtual void set_size(slint::PhysicalSize) { } /// Returns the actual physical size of the window virtual slint::PhysicalSize size() = 0; /// Sets the position of the window on the screen, in physical screen coordinates and including /// a window frame (if present). /// /// The default implementation does nothing /// /// Called from slint::Window::set_position(). virtual void set_position(slint::PhysicalPosition) { } /// Returns the position of the window on the screen, in physical screen coordinates and /// including a window frame (if present). /// /// The default implementation returns std::nullopt. /// /// Called from slint::Window::position(). virtual std::optional position() { return std::nullopt; } /// This struct contains getters that provide access to properties of the Window /// element, and is used with WindowAdapter::update_window_properties(). struct WindowProperties { /// Returns the title of the window. SharedString title() const { SharedString out; cbindgen_private::slint_window_properties_get_title(inner(), &out); return out; } /// Returns the background brush of the window. Brush background() const { Brush out; cbindgen_private::slint_window_properties_get_background(inner(), &out); return out; } /// \deprecated Use is_fullscreen() instead [[deprecated("Renamed is_fullscreen()")]] bool fullscreen() const { return is_fullscreen(); } /// Returns true if the window should be shown fullscreen; false otherwise. bool is_fullscreen() const { return cbindgen_private::slint_window_properties_get_fullscreen(inner()); } /// Returns true if the window should be minimized; false otherwise bool is_minimized() const { return cbindgen_private::slint_window_properties_get_minimized(inner()); } /// Returns true if the window should be maximized; false otherwise bool is_maximized() const { return cbindgen_private::slint_window_properties_get_maximized(inner()); } /// This struct describes the layout constraints of a window. /// /// It is the return value of WindowProperties::layout_constraints(). struct LayoutConstraints { /// This represents the minimum size the window can be. If this is set, the window /// should not be able to be resized smaller than this size. If it is left unset, there /// is no minimum size. std::optional min; /// This represents the maximum size the window can be. If this is set, the window /// should not be able to be resized larger than this size. If it is left unset, there /// is no maximum size. std::optional max; /// This represents the preferred size of the window. This is the size of the window /// should have by default LogicalSize preferred; }; /// Returns the layout constraints of the window LayoutConstraints layout_constraints() const { auto lc = cbindgen_private::slint_window_properties_get_layout_constraints(inner()); return LayoutConstraints { .min = lc.has_min ? std::optional(LogicalSize(lc.min)) : std::nullopt, .max = lc.has_max ? std::optional(LogicalSize(lc.max)) : std::nullopt, .preferred = LogicalSize(lc.preferred) }; } private: /// This struct is opaque and cannot be constructed by C++ WindowProperties() = delete; ~WindowProperties() = delete; WindowProperties(const WindowProperties &) = delete; WindowProperties &operator=(const WindowProperties &) = delete; const cbindgen_private::WindowProperties *inner() const { return reinterpret_cast(this); } }; /// Re-implement this function to update the properties such as window title or layout /// constraints. /// /// This function is called before `set_visible(true)`, and will be called again when the /// properties that were queried on the last call are changed. If you do not query any /// properties, it may not be called again. virtual void update_window_properties(const WindowProperties &) { } /// Re-implement this function to provide a reference to the renderer for use with the window /// adapter. /// /// Your re-implementation should contain a renderer such as SoftwareRenderer or SkiaRenderer /// and you must return a reference to it. virtual AbstractRenderer &renderer() = 0; /// Return the slint::Window associated with this window. /// /// Note that this function can only be called if the window was initialized, which is only /// the case after it has been returned from a call to Platform::create_window_adapter const Window &window() const { if (!was_initialized) std::abort(); // This works because cbindgen_private::WindowAdapterRcOpaque and Window have the same // layout return *reinterpret_cast(&self); } /// Overload Window &window() { if (!was_initialized) std::abort(); // This works because cbindgen_private::WindowAdapterRcOpaque and Window have the same // layout return *reinterpret_cast(&self); } }; /// The platform acts as a factory to create WindowAdapter instances. /// /// Call slint::platform::set_platform() before creating any other Slint handles. Any subsequently /// created Slint windows will use the WindowAdapter provided by the create_window_adapter function. class Platform { public: virtual ~Platform() = default; Platform(const Platform &) = delete; Platform &operator=(const Platform &) = delete; Platform() = default; /// Returns a new WindowAdapter virtual std::unique_ptr create_window_adapter() = 0; #if defined(SLINT_FEATURE_FREESTANDING) || defined(DOXYGEN) /// Returns the amount of milliseconds since start of the application. /// /// This function should only be implemented if the runtime is compiled with /// SLINT_FEATURE_FREESTANDING virtual std::chrono::milliseconds duration_since_start() = 0; #endif /// The type of clipboard used in Platform::clipboard_text and PLatform::set_clipboard_text. enum class Clipboard { /// This is the default clipboard used for text action for Ctrl+V, Ctrl+C. /// Corresponds to the secondary selection on X11. DefaultClipboard = static_cast(cbindgen_private::Clipboard::DefaultClipboard), /// This is the clipboard that is used when text is selected /// Corresponds to the primary selection on X11. /// The Platform implementation should do nothing if copy on select is not supported on that /// platform. SelectionClipboard = static_cast(cbindgen_private::Clipboard::SelectionClipboard), }; /// Sends the given text into the system clipboard. /// /// If the platform doesn't support the specified clipboard, this function should do nothing virtual void set_clipboard_text(const SharedString &, Clipboard) { } /// Returns a copy of text stored in the system clipboard, if any. /// /// If the platform doesn't support the specified clipboard, the function should return nullopt virtual std::optional clipboard_text(Clipboard) { return {}; } /// Spins an event loop and renders the visible windows. virtual void run_event_loop() { } /// Exits the event loop. /// /// This is what is called by slint::quit_event_loop() and can be called from a different thread /// or re-enter from the event loop virtual void quit_event_loop() { } /// A task that is passed to the Platform::run_in_event_loop function and needs to be /// run in the event loop and not in any other thread. class Task { cbindgen_private::PlatformTaskOpaque inner { nullptr, nullptr }; friend inline void set_platform(std::unique_ptr platform); explicit Task(cbindgen_private::PlatformTaskOpaque inner) : inner(inner) { } public: ~Task() { if (inner._0) { cbindgen_private::slint_platform_task_drop( std::exchange(inner, { nullptr, nullptr })); } } Task(const Task &) = delete; Task &operator=(const Task &) = delete; /// Move constructor. A moved from Task can no longer be run. Task(Task &&other) : inner(other.inner) { other.inner = { nullptr, nullptr }; } /// Move operator. Task &operator=(Task &&other) { std::swap(other.inner, inner); return *this; } /// Run the task. /// /// Can only be invoked once and should only be called from the event loop. void run() && { private_api::assert_main_thread(); assert(inner._0 && "calling invoke form a moved-from Task"); if (inner._0) { cbindgen_private::slint_platform_task_run( std::exchange(inner, { nullptr, nullptr })); } }; }; /// Run a task from the event loop. /// /// This function is called by slint::invoke_from_event_loop(). /// It can be called from any thread, but the passed function must only be called /// from the event loop. /// Reimplements this function and moves the event to the event loop before calling /// Task::run() virtual void run_in_event_loop(Task) { } }; /// Registers the platform with Slint. Must be called before Slint windows are created. /// Can only be called once in an application. inline void set_platform(std::unique_ptr platform) { cbindgen_private::slint_platform_register( platform.release(), [](void *p) { delete reinterpret_cast(p); }, [](void *p, cbindgen_private::WindowAdapterRcOpaque *out) { auto w = reinterpret_cast(p)->create_window_adapter(); *out = w->initialize(); (void)w.release(); }, []([[maybe_unused]] void *p) -> uint64_t { #ifndef SLINT_FEATURE_FREESTANDING return 0; #else return reinterpret_cast(p)->duration_since_start().count(); #endif }, [](void *p, const SharedString *text, cbindgen_private::Clipboard clipboard) { reinterpret_cast(p)->set_clipboard_text( *text, static_cast(clipboard)); }, [](void *p, SharedString *out_text, cbindgen_private::Clipboard clipboard) -> bool { auto maybe_clipboard = reinterpret_cast(p)->clipboard_text( static_cast(clipboard)); bool status = maybe_clipboard.has_value(); if (status) *out_text = *maybe_clipboard; return status; }, [](void *p) { return reinterpret_cast(p)->run_event_loop(); }, [](void *p) { return reinterpret_cast(p)->quit_event_loop(); }, [](void *p, cbindgen_private::PlatformTaskOpaque event) { return reinterpret_cast(p)->run_in_event_loop(Platform::Task(event)); }); } #ifdef SLINT_FEATURE_RENDERER_SOFTWARE /// A 16bit pixel that has 5 red bits, 6 green bits and 5 blue bits struct Rgb565Pixel { /// The blue component, encoded in 5 bits. uint16_t b : 5; /// The green component, encoded in 6 bits. uint16_t g : 6; /// The red component, encoded in 5 bits. uint16_t r : 5; /// Default constructor. constexpr Rgb565Pixel() : b(0), g(0), r(0) { } /// \brief Constructor that constructs from an Rgb8Pixel. explicit constexpr Rgb565Pixel(const Rgb8Pixel &pixel) : b(pixel.b >> 3), g(pixel.g >> 2), r(pixel.r >> 3) { } /// \brief Get the red component as an 8-bit value. /// /// The bits are shifted so that the result is between 0 and 255. /// \return The red component as an 8-bit value. constexpr uint8_t red() const { return (r << 3) | (r >> 2); } /// \brief Get the green component as an 8-bit value. /// /// The bits are shifted so that the result is between 0 and 255. /// \return The green component as an 8-bit value. constexpr uint8_t green() const { return (g << 2) | (g >> 4); } /// \brief Get the blue component as an 8-bit value. /// /// The bits are shifted so that the result is between 0 and 255. /// \return The blue component as an 8-bit value. constexpr uint8_t blue() const { return (b << 3) | (b >> 2); } /// \brief Convert to Rgb8Pixel. constexpr operator Rgb8Pixel() const { return { red(), green(), blue() }; } /// Returns true if \a lhs \a rhs are pixels with identical colors. friend bool operator==(const Rgb565Pixel &lhs, const Rgb565Pixel &rhs) = default; }; /// Slint's software renderer. /// /// To be used as a template parameter of the WindowAdapter. /// /// Use the render() function to render in a buffer class SoftwareRenderer : public AbstractRenderer { mutable cbindgen_private::SoftwareRendererOpaque inner; /// \private cbindgen_private::RendererPtr renderer_handle() const override { return cbindgen_private::slint_software_renderer_handle(inner); } public: /// Represents a region on the screen, used for partial rendering. /// /// The region may be composed of multiple sub-regions. struct PhysicalRegion { /// Returns the size of the bounding box of this region. PhysicalSize bounding_box_size() const { if (inner.count == 0) { return PhysicalSize(); } auto origin = bounding_box_origin(); PhysicalSize size({ .width = uint32_t(inner.rectangles[0].max.x - origin.x), .height = uint32_t(inner.rectangles[0].max.y - origin.y) }); for (size_t i = 1; i < inner.count; ++i) { size.width = std::max(size.width, uint32_t(inner.rectangles[i].max.x - origin.x)); size.height = std::max(size.height, uint32_t(inner.rectangles[i].max.y - origin.y)); } return size; } /// Returns the origin of the bounding box of this region. PhysicalPosition bounding_box_origin() const { if (inner.count == 0) { return PhysicalPosition(); } PhysicalPosition origin( { .x = inner.rectangles[0].min.x, .y = inner.rectangles[0].min.y }); for (size_t i = 1; i < inner.count; ++i) { origin.x = std::min(origin.x, inner.rectangles[i].min.x); origin.y = std::min(origin.y, inner.rectangles[i].min.y); } return origin; } /// Returns a view on all the rectangles in this region. /// The rectangles do not overlap. /// The returned type is a C++ view over PhysicalRegion::Rect structs. /// /// It can be used like so: /// ```cpp /// for (auto [origin, size] : region.rectangles()) { /// // Do something with the rect /// } /// ``` auto rectangles() const { SharedVector rectangles; slint_software_renderer_region_to_rects(&inner, &rectangles); # if __cpp_lib_ranges >= 202110L // DR20 P2415R2 using std::ranges::owning_view; # else struct owning_view : std::ranges::view_interface { SharedVector rectangles; owning_view(SharedVector &&rectangles) : rectangles(rectangles) { } auto begin() const { return rectangles.begin(); } auto end() const { return rectangles.end(); } }; # endif return owning_view(std::move(rectangles)) | std::views::transform([](const auto &r) { return Rect { .origin = PhysicalPosition({ .x = r.x, .y = r.y }), .size = PhysicalSize({ .width = uint32_t(r.width), .height = uint32_t(r.height) }) }; }); } /// A Rectangle defined with an origin and a size. /// The PhysicalRegion::rectangles() function returns a view over them struct Rect { /// The origin of the rectangle. PhysicalPosition origin; /// The size of the rectangle. PhysicalSize size; }; private: cbindgen_private::PhysicalRegion inner; friend class SoftwareRenderer; PhysicalRegion(cbindgen_private::PhysicalRegion inner) : inner(std::move(inner)) { } }; /// This enum describes which parts of the buffer passed to the SoftwareRenderer may be /// re-used to speed up painting. enum class RepaintBufferType : uint32_t { /// The full window is always redrawn. No attempt at partial rendering will be made. NewBuffer = 0, /// Only redraw the parts that have changed since the previous call to render(). /// /// This variant assumes that the same buffer is passed on every call to render() and /// that it still contains the previously rendered frame. ReusedBuffer = 1, /// Redraw the part that have changed since the last two frames were drawn. /// /// This is used when using double buffering and swapping of the buffers. SwappedBuffers = 2, }; # ifdef SLINT_FEATURE_EXPERIMENTAL /// Representation of a texture to blend in the destination buffer. // (FIXME: this is currently opaque, but should be exposed) using DrawTextureArgs = cbindgen_private::DrawTextureArgs; /// Arguments for draw_rectagle using DrawRectangleArgs = cbindgen_private::DrawRectangleArgs; /// Abstract base class for a target pixel buffer where certain drawing operations can be /// delegated. Use this to implement support for hardware accelerators such as DMA2D, PPA, or /// PXP on Microcontrollers. /// /// **Note**: This class is still experimental - its API is subject to changes and not /// stabilized yet. To use the class, you must enable the `SLINT_FEATURE_EXPERIMENTAL=ON` CMake /// option. template struct TargetPixelBuffer { virtual ~TargetPixelBuffer() { } /// Returns a span of pixels for the specified line number. virtual std::span line_slice(std::size_t line_number) = 0; /// Returns the number of lines in the buffer. This is the height of the buffer in pixels. virtual std::size_t num_lines() = 0; /// Draw a portion of provided texture to the specified pixel coordinates. /// Each pixel of the texture is to be blended with the given colorize color as well as the /// alpha value. // FIXME: Texture is currently opaque, but should be exposed virtual bool draw_texture(const DrawTextureArgs &texture, const PhysicalRegion &clip) = 0; /// Fill the background of the buffer with the given brush. virtual bool fill_background(const Brush &brush, const PhysicalRegion &clip) = 0; /// Draw a rectangle specified by the DrawRectangleArgs. That rectangle must be clipped to /// the given region. virtual bool draw_rectangle(const DrawRectangleArgs &args, const PhysicalRegion &clip) = 0; private: friend class SoftwareRenderer; cbindgen_private::CppTargetPixelBuffer wrap() { return cbindgen_private::CppTargetPixelBuffer { .user_data = this, .line_slice = [](void *self, uintptr_t line_number, PixelType **slice_ptr, uintptr_t *slice_len) { auto *buffer = reinterpret_cast *>(self); auto slice = buffer->line_slice(line_number); *slice_ptr = slice.data(); *slice_len = slice.size(); }, .num_lines = [](void *self) { auto *buffer = reinterpret_cast *>(self); return buffer->num_lines(); }, .fill_background = [](void *self, const Brush *brush, const cbindgen_private::PhysicalRegion *clip) { auto *buffer = reinterpret_cast *>(self); auto clip_region = PhysicalRegion { *clip }; return buffer->fill_background(*brush, clip_region); }, .draw_rectangle = [](void *self, const cbindgen_private::DrawRectangleArgs *args, const cbindgen_private::PhysicalRegion *clip) { auto *buffer = reinterpret_cast *>(self); auto clip_region = PhysicalRegion { *clip }; return buffer->draw_rectangle(*args, clip_region); }, .draw_texture = [](void *self, const cbindgen_private::DrawTextureArgs *texture, const cbindgen_private::PhysicalRegion *clip) { auto *buffer = reinterpret_cast *>(self); auto clip_region = PhysicalRegion { *clip }; return buffer->draw_texture(*texture, clip_region); } }; } }; # endif virtual ~SoftwareRenderer() { cbindgen_private::slint_software_renderer_drop(inner); }; SoftwareRenderer(const SoftwareRenderer &) = delete; SoftwareRenderer &operator=(const SoftwareRenderer &) = delete; /// Constructs a new SoftwareRenderer with the \a buffer_type as strategy for handling the /// differences between rendering buffers. explicit SoftwareRenderer(RepaintBufferType buffer_type) { inner = cbindgen_private::slint_software_renderer_new(uint32_t(buffer_type)); } /// Render the window scene into a pixel buffer /// /// The buffer must be at least as large as the associated slint::Window /// /// The stride is the amount of pixels between two lines in the buffer. /// It is must be at least as large as the width of the window. PhysicalRegion render(std::span buffer, std::size_t pixel_stride) const { auto r = cbindgen_private::slint_software_renderer_render_rgb8(inner, buffer.data(), buffer.size(), pixel_stride); return PhysicalRegion { r }; } /// Render the window scene into an RGB 565 encoded pixel buffer /// /// The buffer must be at least as large as the associated slint::Window /// /// The stride is the amount of pixels between two lines in the buffer. /// It is must be at least as large as the width of the window. PhysicalRegion render(std::span buffer, std::size_t pixel_stride) const { auto r = cbindgen_private::slint_software_renderer_render_rgb565( inner, reinterpret_cast(buffer.data()), buffer.size(), pixel_stride); return PhysicalRegion { r }; } /// Render the window scene, line by line. The provided Callback will be invoked for each line /// that needs to rendered. /// /// The renderer uses a cache internally and will only render the part of the window /// which are dirty. /// /// This function returns the physical region that was rendered considering the rotation. /// /// The callback must be an invocable with the signature (size_t line, size_t begin, size_t end, /// auto render_fn). It is invoked with the line number as first parameter, and the start x and /// end x coordinates of the line as second and third parameter. The implementation must provide /// a line buffer (as std::span) and invoke the provided fourth parameter (render_fn) with it, /// to fill it with pixels. After the line buffer is filled with pixels, your implementation is /// free to flush that line to the screen for display. /// /// The first template parameter (PixelType) must be specified and can be either Rgb565Pixel or /// Rgb8Pixel. template requires requires(Callback callback) { callback(size_t(0), size_t(0), size_t(0), [&callback](std::span) { }); } PhysicalRegion render_by_line(Callback process_line_callback) const { auto process_line_fn = [](void *process_line_callback_ptr, uintptr_t line, uintptr_t line_start, uintptr_t line_end, void (*render_fn)(const void *, PixelType *, std::size_t), const void *render_fn_data) { (*reinterpret_cast(process_line_callback_ptr))( std::size_t(line), std::size_t(line_start), std::size_t(line_end), [render_fn, render_fn_data](std::span line_span) { render_fn(render_fn_data, line_span.data(), line_span.size()); }); }; if constexpr (std::is_same_v) { return PhysicalRegion { cbindgen_private::slint_software_renderer_render_by_line_rgb565( inner, process_line_fn, &process_line_callback) }; } else if constexpr (std::is_same_v) { return PhysicalRegion { cbindgen_private::slint_software_renderer_render_by_line_rgb8( inner, process_line_fn, &process_line_callback) }; } else { static_assert(std::is_same_v || std::is_same_v, "Unsupported PixelType. It must be either Rgba8Pixel or Rgb565Pixel"); } } # ifdef SLINT_FEATURE_EXPERIMENTAL /// Renders into the given TargetPixelBuffer. /// /// **Note**: This class is still experimental - its API is subject to changes and not /// stabilized yet. To use the class, you must enable the `SLINT_FEATURE_EXPERIMENTAL=ON` CMake /// option. PhysicalRegion render(TargetPixelBuffer *buffer) const { auto wrapper = buffer->wrap(); auto r = cbindgen_private::slint_software_renderer_render_accel_rgb8(inner, &wrapper); return PhysicalRegion { r }; } /// Renders into the given TargetPixelBuffer. /// /// **Note**: This class is still experimental - its API is subject to changes and not /// stabilized yet. To use the class, you must enable the `SLINT_FEATURE_EXPERIMENTAL=ON` CMake /// option. PhysicalRegion render(TargetPixelBuffer *buffer) const { auto wrapper = buffer->wrap(); auto r = cbindgen_private::slint_software_renderer_render_accel_rgb565(inner, &wrapper); return PhysicalRegion { r }; } # endif /// This enum describes the rotation that is applied to the buffer when rendering. /// To be used in set_rendering_rotation() enum class RenderingRotation { /// No rotation NoRotation = 0, /// Rotate 90° to the left Rotate90 = 90, /// 180° rotation (upside-down) Rotate180 = 180, /// Rotate 90° to the right Rotate270 = 270, }; /// Set how the window needs to be rotated in the buffer. /// /// This is typically used to implement screen rotation in software void set_rendering_rotation(RenderingRotation rotation) { cbindgen_private::slint_software_renderer_set_rendering_rotation( inner, static_cast(rotation)); } }; #endif #ifdef SLINT_FEATURE_RENDERER_SKIA /// 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 /// NativeWindowHandle. class NativeWindowHandle { cbindgen_private::CppRawHandleOpaque inner; friend class SkiaRenderer; NativeWindowHandle(cbindgen_private::CppRawHandleOpaque inner) : inner(inner) { } public: NativeWindowHandle() = delete; NativeWindowHandle(const NativeWindowHandle &) = delete; NativeWindowHandle &operator=(const NativeWindowHandle &) = delete; /// Creates a new NativeWindowHandle by moving the handle data from \a other into this /// NativeWindowHandle. NativeWindowHandle(NativeWindowHandle &&other) { inner = std::exchange(other.inner, nullptr); } /// Creates a new NativeWindowHandle by moving the handle data from \a other into this /// NativeWindowHandle. NativeWindowHandle &operator=(NativeWindowHandle &&other) { if (this == &other) { return *this; } if (inner) { cbindgen_private::slint_raw_window_handle_drop(inner); } inner = std::exchange(other.inner, nullptr); return *this; } # if (!defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)) || defined(DOXYGEN) /// Creates a new NativeWindowHandle from the given xcb_window_t \a window, /// xcb_visualid_t \a visual_id, XCB \a connection, and \a screen number. static NativeWindowHandle from_x11_xcb(uint32_t /*xcb_window_t*/ window, uint32_t /*xcb_visualid_t*/ visual_id, xcb_connection_t *connection, int screen) { return { cbindgen_private::slint_new_raw_window_handle_x11_xcb(window, visual_id, connection, screen) }; } /// Creates a new NativeWindowHandle from the given XLib \a window, /// VisualID \a visual_id, Display \a display, and \a screen number. static NativeWindowHandle from_x11_xlib(uint32_t /*Window*/ window, unsigned long /*VisualID*/ visual_id, void /*Display*/ *display, int screen) { return { cbindgen_private::slint_new_raw_window_handle_x11_xlib(window, visual_id, display, screen) }; } /// Creates a new NativeWindowHandle from the given wayland \a surface, /// and \a display. static NativeWindowHandle from_wayland(wl_surface *surface, wl_display *display) { return { cbindgen_private::slint_new_raw_window_handle_wayland(surface, display) }; } # endif # if (defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)) || defined(DOXYGEN) /// Creates a new NativeWindowHandle from the given \a nsview, and \a nswindow. static NativeWindowHandle from_appkit(NSView *nsview, NSWindow *nswindow) { return { cbindgen_private::slint_new_raw_window_handle_appkit(nsview, nswindow) }; } # endif # if (!defined(__APPLE__) && (defined(_WIN32) || !defined(_WIN64))) || defined(DOXYGEN) /// Creates a new NativeWindowHandle from the given HWND \a hwnd, and HINSTANCE \a hinstance. static NativeWindowHandle from_win32(void *hwnd, void *hinstance) { return { cbindgen_private::slint_new_raw_window_handle_win32(hwnd, hinstance) }; } # endif /// Destroys the NativeWindowHandle. ~NativeWindowHandle() { if (inner) { cbindgen_private::slint_raw_window_handle_drop(inner); } } }; /// Slint's Skia renderer. /// /// Create the renderer when you have created a native window with a non-zero size. /// Call the render() function to render the scene into the window. class SkiaRenderer : public AbstractRenderer { mutable cbindgen_private::SkiaRendererOpaque inner; /// \private cbindgen_private::RendererPtr renderer_handle() const override { return cbindgen_private::slint_skia_renderer_handle(inner); } public: virtual ~SkiaRenderer() { cbindgen_private::slint_skia_renderer_drop(inner); } SkiaRenderer(const SkiaRenderer &) = delete; SkiaRenderer &operator=(const SkiaRenderer &) = delete; /// Constructs a new Skia renderer for the given window - referenced by the provided /// WindowHandle - and the specified initial size. explicit SkiaRenderer(const NativeWindowHandle &window_handle, PhysicalSize initial_size) { inner = cbindgen_private::slint_skia_renderer_new(window_handle.inner, initial_size); } /// Renders the scene into the window provided to the SkiaRenderer's constructor. void render() const { cbindgen_private::slint_skia_renderer_render(inner); } }; #endif /// Call this function at each iteration of the event loop to call the timer handler and advance /// the animations. This should be called before the rendering or processing input events inline void update_timers_and_animations() { cbindgen_private::slint_platform_update_timers_and_animations(); } /// Returns the duration until the next timer if there are pending timers inline std::optional duration_until_next_timer_update() { uint64_t val = cbindgen_private::slint_platform_duration_until_next_timer_update(); if (val == std::numeric_limits::max()) { return std::nullopt; } else if (val >= uint64_t(std::chrono::milliseconds::max().count())) { return std::chrono::milliseconds::max(); } else { return std::chrono::milliseconds(val); } } } }