1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial |
3 | |
4 | #pragma once |
5 | |
6 | #include "slint.h" |
7 | |
8 | #include <cassert> |
9 | #include <utility> |
10 | |
11 | struct xcb_connection_t; |
12 | struct wl_surface; |
13 | struct wl_display; |
14 | |
15 | #if defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64) |
16 | # ifdef __OBJC__ |
17 | @class NSView; |
18 | @class NSWindow; |
19 | # else |
20 | typedef struct objc_object NSView; |
21 | typedef struct objc_object NSWindow; |
22 | # endif |
23 | #endif |
24 | |
25 | namespace slint { |
26 | |
27 | /// Use the types in this namespace when implementing a custom Slint platform. |
28 | /// |
29 | /// Slint comes with built-in support for different windowing systems, called backends. A backend |
30 | /// is a module that implements the Platform interface in this namespace, interacts with a |
31 | /// windowing system, and uses one of Slint's renderers to display a scene to the windowing system. |
32 | /// A typical Slint application uses one of the built-in backends. Implement your own Platform if |
33 | /// you're using Slint in an environment without a windowing system, such as with microcontrollers, |
34 | /// or you're embedding a Slint UI as plugin in other applications. |
35 | /// |
36 | /// Examples of custom platform implementation can be found in the Slint repository: |
37 | /// - https://github.com/slint-ui/slint/tree/master/examples/cpp/platform_native |
38 | /// - https://github.com/slint-ui/slint/tree/master/examples/cpp/platform_qt |
39 | /// - https://github.com/slint-ui/slint/blob/master/api/cpp/esp-idf/slint/src/slint-esp.cpp |
40 | /// |
41 | /// The entry point to re-implement a platform is the Platform class. Derive |
42 | /// from slint::platform::Platform, and call slint::platform::set_platform |
43 | /// to set it as the Slint platform. |
44 | /// |
45 | /// Another important class to subclass is the WindowAdapter. |
46 | namespace platform { |
47 | |
48 | /// Internal interface for a renderer for use with the WindowAdapter. |
49 | /// |
50 | /// This class is not intended to be re-implemented. In places where this class is required, use |
51 | /// of one the existing implementations such as SoftwareRenderer or SkiaRenderer. |
52 | class AbstractRenderer |
53 | { |
54 | private: |
55 | virtual ~AbstractRenderer() { } |
56 | AbstractRenderer(const AbstractRenderer &) = delete; |
57 | AbstractRenderer &operator=(const AbstractRenderer &) = delete; |
58 | AbstractRenderer() = default; |
59 | |
60 | /// \private |
61 | virtual cbindgen_private::RendererPtr renderer_handle() const = 0; |
62 | friend class WindowAdapter; |
63 | friend class SoftwareRenderer; |
64 | friend class SkiaRenderer; |
65 | }; |
66 | |
67 | /// Base class for the layer between a slint::Window and the windowing system specific window type, |
68 | /// such as a Win32 `HWND` handle or a `wayland_surface_t`. |
69 | /// |
70 | /// Re-implement this class to establish the link between the two, and pass messages in both |
71 | /// directions: |
72 | /// |
73 | /// - When receiving messages from the windowing system about state changes, such as the window |
74 | /// being resized, the user requested the window to be closed, input being received, etc. you |
75 | /// need to call the corresponding event functions on the Window, such as |
76 | /// Window::dispatch_resize_event(), Window::dispatch_mouse_press_event(), or |
77 | /// Window::dispatch_close_requested_event(). |
78 | /// |
79 | /// - Slint sends requests to change visibility, position, size, etc. via virtual functions such as |
80 | /// set_visible(), set_size(), set_position(), or update_window_properties(). |
81 | /// Re-implement these functions and delegate the requests to the windowing system. |
82 | /// |
83 | /// If the implementation of this bi-directional message passing protocol is incomplete, the user |
84 | /// may experience unexpected behavior, or the intention of the developer calling functions on the |
85 | /// Window API may not be fulfilled. |
86 | /// |
87 | /// Your WindowAdapter subclass must hold a renderer (either a SoftwareRenderer or a SkiaRenderer). |
88 | /// In the renderer() method, you must return a reference to it. |
89 | /// |
90 | /// # Example |
91 | /// ```cpp |
92 | /// class MyWindowAdapter : public slint::platform::WindowAdapter { |
93 | /// slint::platform::SoftwareRenderer m_renderer; |
94 | /// NativeHandle m_native_window; // a handle to the native window |
95 | /// public: |
96 | /// void request_redraw() override { m_native_window.refresh(); } |
97 | /// slint::PhysicalSize size() const override { |
98 | /// return slint::PhysicalSize({m_native_window.width, m_native_window.height}); |
99 | /// } |
100 | /// slint::platform::AbstractRenderer &renderer() override { return m_renderer; } |
101 | /// void set_visible(bool v) override { |
102 | /// if (v) { |
103 | /// m_native_window.show(); |
104 | /// } else { |
105 | /// m_native_window.hide(); |
106 | /// } |
107 | /// } |
108 | /// // ... |
109 | /// void repaint_callback(); |
110 | /// } |
111 | /// ``` |
112 | /// |
113 | /// Rendering is typically asynchronous, and your windowing system or event loop would invoke |
114 | /// a callback when it is time to render. |
115 | /// ```cpp |
116 | /// void MyWindowAdapter::repaint_callback() |
117 | /// { |
118 | /// slint::platform::update_timers_and_animations(); |
119 | /// m_renderer.render(m_native_window.buffer(), m_native_window.width); |
120 | /// // if animations are running, schedule the next frame |
121 | /// if (window().has_active_animations()) m_native_window.refresh(); |
122 | /// } |
123 | /// ``` |
124 | class WindowAdapter |
125 | { |
126 | // This is a pointer to the rust window that own us. |
127 | // Note that we do not have ownership (there is no reference increase for this) |
128 | // because it would otherwise be a reference loop |
129 | cbindgen_private::WindowAdapterRcOpaque self {}; |
130 | // Whether this WindowAdapter was already given to the slint runtime |
131 | bool was_initialized = false; |
132 | |
133 | cbindgen_private::WindowAdapterRcOpaque initialize() |
134 | { |
135 | cbindgen_private::slint_window_adapter_new( |
136 | user_data: this, drop: [](void *wa) { delete reinterpret_cast<WindowAdapter *>(wa); }, |
137 | get_renderer_ref: [](void *wa) { |
138 | return reinterpret_cast<WindowAdapter *>(wa)->renderer().renderer_handle(); |
139 | }, |
140 | set_visible: [](void *wa, bool visible) { |
141 | reinterpret_cast<WindowAdapter *>(wa)->set_visible(visible); |
142 | }, |
143 | request_redraw: [](void *wa) { reinterpret_cast<WindowAdapter *>(wa)->request_redraw(); }, |
144 | size: [](void *wa) -> cbindgen_private::IntSize { |
145 | return reinterpret_cast<WindowAdapter *>(wa)->size(); |
146 | }, |
147 | set_size: [](void *wa, cbindgen_private::IntSize size) { |
148 | reinterpret_cast<WindowAdapter *>(wa)->set_size( |
149 | slint::PhysicalSize({ .width: size.width, .height: size.height })); |
150 | }, |
151 | update_window_properties: [](void *wa, const cbindgen_private::WindowProperties *p) { |
152 | reinterpret_cast<WindowAdapter *>(wa)->update_window_properties( |
153 | *reinterpret_cast<const WindowProperties *>(p)); |
154 | }, |
155 | position: [](void *wa, cbindgen_private::Point2D<int32_t> *point) -> bool { |
156 | if (auto pos = reinterpret_cast<WindowAdapter *>(wa)->position()) { |
157 | *point = *pos; |
158 | return true; |
159 | } else { |
160 | return false; |
161 | } |
162 | }, |
163 | set_position: [](void *wa, cbindgen_private::Point2D<int32_t> point) { |
164 | reinterpret_cast<WindowAdapter *>(wa)->set_position( |
165 | slint::PhysicalPosition({ .x: point.x, .y: point.y })); |
166 | }, |
167 | target: &self); |
168 | was_initialized = true; |
169 | return self; |
170 | } |
171 | |
172 | friend inline void set_platform(std::unique_ptr<class Platform> platform); |
173 | |
174 | public: |
175 | /// Construct a WindowAdapter |
176 | explicit WindowAdapter() { } |
177 | virtual ~WindowAdapter() = default; |
178 | |
179 | /// This function is called by Slint when the slint window is shown or hidden. |
180 | /// |
181 | /// Re-implement this function to forward the call to show/hide the native window |
182 | /// |
183 | /// When the window becomes visible, this is a good time to call |
184 | /// slint::Window::dispatch_scale_factor_change_event to initialise the scale factor. |
185 | virtual void set_visible(bool) { } |
186 | |
187 | /// This function is called when Slint detects that the window need to be repainted. |
188 | /// |
189 | /// Reimplement this function to forward the call to the window manager. |
190 | /// |
191 | /// You should not render the window in the implementation of this call. Instead you should |
192 | /// do that in the next iteration of the event loop, or in a callback from the window manager. |
193 | virtual void request_redraw() { } |
194 | |
195 | /// Request a new size for the window to the specified size on the screen, in physical or |
196 | /// logical pixels and excluding a window frame (if present). |
197 | /// |
198 | /// This is called from slint::Window::set_size(). |
199 | /// |
200 | /// The default implementation does nothing |
201 | /// |
202 | /// This function should sent the size to the Windowing system. If the window size actually |
203 | /// changes, you should call slint::Window::dispatch_resize_event to propagate the new size |
204 | /// to the slint view. |
205 | virtual void set_size(slint::PhysicalSize) { } |
206 | |
207 | /// Returns the actual physical size of the window |
208 | virtual slint::PhysicalSize size() = 0; |
209 | |
210 | /// Sets the position of the window on the screen, in physical screen coordinates and including |
211 | /// a window frame (if present). |
212 | /// |
213 | /// The default implementation does nothing |
214 | /// |
215 | /// Called from slint::Window::set_position(). |
216 | virtual void set_position(slint::PhysicalPosition) { } |
217 | |
218 | /// Returns the position of the window on the screen, in physical screen coordinates and |
219 | /// including a window frame (if present). |
220 | /// |
221 | /// The default implementation returns std::nullopt. |
222 | /// |
223 | /// Called from slint::Window::position(). |
224 | virtual std::optional<slint::PhysicalPosition> position() { return std::nullopt; } |
225 | |
226 | /// This struct contains getters that provide access to properties of the Window |
227 | /// element, and is used with WindowAdapter::update_window_properties(). |
228 | struct WindowProperties |
229 | { |
230 | /// Returns the title of the window. |
231 | SharedString title() const |
232 | { |
233 | SharedString out; |
234 | cbindgen_private::slint_window_properties_get_title(wp: inner(), out: &out); |
235 | return out; |
236 | } |
237 | |
238 | /// Returns the background brush of the window. |
239 | Brush background() const |
240 | { |
241 | Brush out; |
242 | cbindgen_private::slint_window_properties_get_background(wp: inner(), out: &out); |
243 | return out; |
244 | } |
245 | |
246 | /// \deprecated Use is_fullscreen() instead |
247 | [[deprecated("Renamed is_fullscreen()" )]] bool fullscreen() const |
248 | { |
249 | return is_fullscreen(); |
250 | } |
251 | |
252 | /// Returns true if the window should be shown fullscreen; false otherwise. |
253 | bool is_fullscreen() const |
254 | { |
255 | return cbindgen_private::slint_window_properties_get_fullscreen(wp: inner()); |
256 | } |
257 | |
258 | /// Returns true if the window should be minimized; false otherwise |
259 | bool is_minimized() const |
260 | { |
261 | return cbindgen_private::slint_window_properties_get_minimized(wp: inner()); |
262 | } |
263 | |
264 | /// Returns true if the window should be maximized; false otherwise |
265 | bool is_maximized() const |
266 | { |
267 | return cbindgen_private::slint_window_properties_get_maximized(wp: inner()); |
268 | } |
269 | |
270 | /// This struct describes the layout constraints of a window. |
271 | /// |
272 | /// It is the return value of WindowProperties::layout_constraints(). |
273 | struct LayoutConstraints |
274 | { |
275 | /// This represents the minimum size the window can be. If this is set, the window |
276 | /// should not be able to be resized smaller than this size. If it is left unset, there |
277 | /// is no minimum size. |
278 | std::optional<LogicalSize> min; |
279 | /// This represents the maximum size the window can be. If this is set, the window |
280 | /// should not be able to be resized larger than this size. If it is left unset, there |
281 | /// is no maximum size. |
282 | std::optional<LogicalSize> max; |
283 | /// This represents the preferred size of the window. This is the size the window |
284 | /// should have by default |
285 | LogicalSize preferred; |
286 | }; |
287 | |
288 | /// Returns the layout constraints of the window |
289 | LayoutConstraints layout_constraints() const |
290 | { |
291 | auto lc = cbindgen_private::slint_window_properties_get_layout_constraints(wp: inner()); |
292 | return LayoutConstraints { |
293 | .min = lc.has_min ? std::optional(LogicalSize(lc.min)) : std::nullopt, |
294 | .max = lc.has_max ? std::optional(LogicalSize(lc.max)) : std::nullopt, |
295 | .preferred = LogicalSize(lc.preferred) |
296 | }; |
297 | } |
298 | |
299 | private: |
300 | /// This struct is opaque and cannot be constructed by C++ |
301 | WindowProperties() = delete; |
302 | ~WindowProperties() = delete; |
303 | WindowProperties(const WindowProperties &) = delete; |
304 | WindowProperties &operator=(const WindowProperties &) = delete; |
305 | const cbindgen_private::WindowProperties *inner() const |
306 | { |
307 | return reinterpret_cast<const cbindgen_private::WindowProperties *>(this); |
308 | } |
309 | }; |
310 | |
311 | /// Re-implement this function to update the properties such as window title or layout |
312 | /// constraints. |
313 | /// |
314 | /// This function is called before `set_visible(true)`, and will be called again when the |
315 | /// properties that were queried on the last call are changed. If you do not query any |
316 | /// properties, it may not be called again. |
317 | virtual void update_window_properties(const WindowProperties &) { } |
318 | |
319 | /// Re-implement this function to provide a reference to the renderer for use with the window |
320 | /// adapter. |
321 | /// |
322 | /// Your re-implementation should contain a renderer such as SoftwareRenderer or SkiaRenderer |
323 | /// and you must return a reference to it. |
324 | virtual AbstractRenderer &renderer() = 0; |
325 | |
326 | /// Return the slint::Window associated with this window. |
327 | /// |
328 | /// Note that this function can only be called if the window was initialized, which is only |
329 | /// the case after it has been returned from a call to Platform::create_window_adapter |
330 | const Window &window() const |
331 | { |
332 | if (!was_initialized) |
333 | std::abort(); |
334 | // This works because cbindgen_private::WindowAdapterRcOpaque and Window have the same |
335 | // layout |
336 | return *reinterpret_cast<const Window *>(&self); |
337 | } |
338 | |
339 | /// Overload |
340 | Window &window() |
341 | { |
342 | if (!was_initialized) |
343 | std::abort(); |
344 | // This works because cbindgen_private::WindowAdapterRcOpaque and Window have the same |
345 | // layout |
346 | return *reinterpret_cast<Window *>(&self); |
347 | } |
348 | }; |
349 | |
350 | /// The platform acts as a factory to create WindowAdapter instances. |
351 | /// |
352 | /// Call slint::platform::set_platform() before creating any other Slint handles. Any subsequently |
353 | /// created Slint windows will use the WindowAdapter provided by the create_window_adapter function. |
354 | class Platform |
355 | { |
356 | public: |
357 | virtual ~Platform() = default; |
358 | Platform(const Platform &) = delete; |
359 | Platform &operator=(const Platform &) = delete; |
360 | Platform() = default; |
361 | |
362 | /// Returns a new WindowAdapter |
363 | virtual std::unique_ptr<WindowAdapter> create_window_adapter() = 0; |
364 | |
365 | #if defined(SLINT_FEATURE_FREESTANDING) || defined(DOXYGEN) |
366 | /// Returns the amount of milliseconds since start of the application. |
367 | /// |
368 | /// This function should only be implemented if the runtime is compiled with |
369 | /// SLINT_FEATURE_FREESTANDING |
370 | virtual std::chrono::milliseconds duration_since_start() = 0; |
371 | #endif |
372 | |
373 | /// The type of clipboard used in Platform::clipboard_text and PLatform::set_clipboard_text. |
374 | enum class Clipboard { |
375 | /// This is the default clipboard used for text action for Ctrl+V, Ctrl+C. |
376 | /// Corresponds to the secondary selection on X11. |
377 | DefaultClipboard = static_cast<uint8_t>(cbindgen_private::Clipboard::DefaultClipboard), |
378 | /// This is the clipboard that is used when text is selected |
379 | /// Corresponds to the primary selection on X11. |
380 | /// The Platform implementation should do nothing if copy on select is not supported on that |
381 | /// platform. |
382 | SelectionClipboard = static_cast<uint8_t>(cbindgen_private::Clipboard::SelectionClipboard), |
383 | }; |
384 | |
385 | /// Sends the given text into the system clipboard. |
386 | /// |
387 | /// If the platform doesn't support the specified clipboard, this function should do nothing |
388 | virtual void set_clipboard_text(const SharedString &, Clipboard) { } |
389 | |
390 | /// Returns a copy of text stored in the system clipboard, if any. |
391 | /// |
392 | /// If the platform doesn't support the specified clipboard, the function should return nullopt |
393 | virtual std::optional<SharedString> clipboard_text(Clipboard) |
394 | { |
395 | return {}; |
396 | } |
397 | |
398 | /// Spins an event loop and renders the visible windows. |
399 | virtual void run_event_loop() { } |
400 | |
401 | /// Exits the event loop. |
402 | /// |
403 | /// This is what is called by slint::quit_event_loop() and can be called from a different thread |
404 | /// or re-enter from the event loop |
405 | virtual void quit_event_loop() { } |
406 | |
407 | /// An task that is passed to the Platform::run_in_event_loop function and needs to be |
408 | /// run in the event loop and not in any other thread. |
409 | class Task |
410 | { |
411 | cbindgen_private::PlatformTaskOpaque inner { ._0: nullptr, ._1: nullptr }; |
412 | friend inline void set_platform(std::unique_ptr<Platform> platform); |
413 | explicit Task(cbindgen_private::PlatformTaskOpaque inner) : inner(inner) { } |
414 | |
415 | public: |
416 | ~Task() |
417 | { |
418 | if (inner._0) { |
419 | cbindgen_private::slint_platform_task_drop( |
420 | event: std::exchange(obj&: inner, new_val: { ._0: nullptr, ._1: nullptr })); |
421 | } |
422 | } |
423 | Task(const Task &) = delete; |
424 | Task &operator=(const Task &) = delete; |
425 | /// Move constructor. A moved from Task can no longer be run. |
426 | Task(Task &&other) : inner(other.inner) { other.inner = { ._0: nullptr, ._1: nullptr }; } |
427 | /// Move operator. |
428 | Task &operator=(Task &&other) |
429 | { |
430 | std::swap(a&: other.inner, b&: inner); |
431 | return *this; |
432 | } |
433 | |
434 | /// Run the task. |
435 | /// |
436 | /// Can only be invoked once and should only be called from the event loop. |
437 | void run() && |
438 | { |
439 | private_api::assert_main_thread(); |
440 | assert(inner._0 && "calling invoke form a moved-from Task" ); |
441 | if (inner._0) { |
442 | cbindgen_private::slint_platform_task_run( |
443 | event: std::exchange(obj&: inner, new_val: { ._0: nullptr, ._1: nullptr })); |
444 | } |
445 | }; |
446 | }; |
447 | |
448 | /// Run a task from the event loop. |
449 | /// |
450 | /// This function is called by slint::invoke_from_event_loop(). |
451 | /// It can be called from any thread, but the passed function must only be called |
452 | /// from the event loop. |
453 | /// Reimplements this function and move the event to the event loop before calling |
454 | /// Task::run() |
455 | virtual void run_in_event_loop(Task) { } |
456 | }; |
457 | |
458 | /// Registers the platform with Slint. Must be called before Slint windows are created. |
459 | /// Can only be called once in an application. |
460 | inline void set_platform(std::unique_ptr<Platform> platform) |
461 | { |
462 | cbindgen_private::slint_platform_register( |
463 | user_data: platform.release(), drop: [](void *p) { delete reinterpret_cast<const Platform *>(p); }, |
464 | window_factory: [](void *p, cbindgen_private::WindowAdapterRcOpaque *out) { |
465 | auto w = reinterpret_cast<Platform *>(p)->create_window_adapter(); |
466 | *out = w->initialize(); |
467 | (void)w.release(); |
468 | }, |
469 | duration_since_start: []([[maybe_unused]] void *p) -> uint64_t { |
470 | #ifndef SLINT_FEATURE_FREESTANDING |
471 | return 0; |
472 | #else |
473 | return reinterpret_cast<Platform *>(p)->duration_since_start().count(); |
474 | #endif |
475 | }, |
476 | set_clipboard_text: [](void *p, const SharedString *text, cbindgen_private::Clipboard clipboard) { |
477 | reinterpret_cast<Platform *>(p)->set_clipboard_text( |
478 | *text, static_cast<Platform::Clipboard>(clipboard)); |
479 | }, |
480 | clipboard_text: [](void *p, SharedString *out_text, cbindgen_private::Clipboard clipboard) -> bool { |
481 | auto maybe_clipboard = reinterpret_cast<Platform *>(p)->clipboard_text( |
482 | static_cast<Platform::Clipboard>(clipboard)); |
483 | |
484 | bool status = maybe_clipboard.has_value(); |
485 | if (status) |
486 | *out_text = *maybe_clipboard; |
487 | return status; |
488 | }, |
489 | run_event_loop: [](void *p) { return reinterpret_cast<Platform *>(p)->run_event_loop(); }, |
490 | quit_event_loop: [](void *p) { return reinterpret_cast<Platform *>(p)->quit_event_loop(); }, |
491 | invoke_from_event_loop: [](void *p, cbindgen_private::PlatformTaskOpaque event) { |
492 | return reinterpret_cast<Platform *>(p)->run_in_event_loop(Platform::Task(event)); |
493 | }); |
494 | } |
495 | |
496 | #ifdef SLINT_FEATURE_RENDERER_SOFTWARE |
497 | |
498 | /// A 16bit pixel that has 5 red bits, 6 green bits and 5 blue bits |
499 | struct Rgb565Pixel |
500 | { |
501 | /// The blue component, encoded in 5 bits. |
502 | uint16_t b : 5; |
503 | /// The green component, encoded in 6 bits. |
504 | uint16_t g : 6; |
505 | /// The red component, encoded in 5 bits. |
506 | uint16_t r : 5; |
507 | |
508 | /// Default constructor. |
509 | constexpr Rgb565Pixel() : b(0), g(0), r(0) { } |
510 | |
511 | /// \brief Constructor that constructs from an Rgb8Pixel. |
512 | explicit constexpr Rgb565Pixel(const Rgb8Pixel &pixel) |
513 | : b(pixel.b >> 3), g(pixel.g >> 2), r(pixel.r >> 3) |
514 | { |
515 | } |
516 | |
517 | /// \brief Get the red component as an 8-bit value. |
518 | /// |
519 | /// The bits are shifted so that the result is between 0 and 255. |
520 | /// \return The red component as an 8-bit value. |
521 | constexpr uint8_t red() const { return (r << 3) | (r >> 2); } |
522 | |
523 | /// \brief Get the green component as an 8-bit value. |
524 | /// |
525 | /// The bits are shifted so that the result is between 0 and 255. |
526 | /// \return The green component as an 8-bit value. |
527 | constexpr uint8_t green() const { return (g << 2) | (g >> 4); } |
528 | |
529 | /// \brief Get the blue component as an 8-bit value. |
530 | /// |
531 | /// The bits are shifted so that the result is between 0 and 255. |
532 | /// \return The blue component as an 8-bit value. |
533 | constexpr uint8_t blue() const { return (b << 3) | (b >> 2); } |
534 | |
535 | /// \brief Convert to Rgb8Pixel. |
536 | constexpr operator Rgb8Pixel() const { return { .r: red(), .g: green(), .b: blue() }; } |
537 | |
538 | /// Returns true if \a lhs \a rhs are pixels with identical colors. |
539 | friend bool operator==(const Rgb565Pixel &lhs, const Rgb565Pixel &rhs) = default; |
540 | }; |
541 | |
542 | /// Slint's software renderer. |
543 | /// |
544 | /// To be used as a template parameter of the WindowAdapter. |
545 | /// |
546 | /// Use the render() function to render in a buffer |
547 | class SoftwareRenderer : public AbstractRenderer |
548 | { |
549 | mutable cbindgen_private::SoftwareRendererOpaque inner; |
550 | |
551 | /// \private |
552 | cbindgen_private::RendererPtr renderer_handle() const override |
553 | { |
554 | return cbindgen_private::slint_software_renderer_handle(r: inner); |
555 | } |
556 | |
557 | public: |
558 | /// Represents a region on the screen, used for partial rendering. |
559 | /// |
560 | /// The region may be composed of multiple sub-regions. |
561 | struct PhysicalRegion |
562 | { |
563 | /// Returns the size of the bounding box of this region. |
564 | PhysicalSize bounding_box_size() const |
565 | { |
566 | return PhysicalSize({ .width: uint32_t(inner.width), .height: uint32_t(inner.height) }); |
567 | } |
568 | /// Returns the origin of the bounding box of this region. |
569 | PhysicalPosition bounding_box_origin() const |
570 | { |
571 | return PhysicalPosition({ .x: inner.x, .y: inner.y }); |
572 | } |
573 | |
574 | private: |
575 | cbindgen_private::types::IntRect inner; |
576 | friend class SoftwareRenderer; |
577 | PhysicalRegion(cbindgen_private::types::IntRect inner) : inner(inner) { } |
578 | }; |
579 | |
580 | /// This enum describes which parts of the buffer passed to the SoftwareRenderer may be |
581 | /// re-used to speed up painting. |
582 | enum class RepaintBufferType : uint32_t { |
583 | /// The full window is always redrawn. No attempt at partial rendering will be made. |
584 | NewBuffer = 0, |
585 | /// Only redraw the parts that have changed since the previous call to render(). |
586 | /// |
587 | /// This variant assumes that the same buffer is passed on every call to render() and |
588 | /// that it still contains the previously rendered frame. |
589 | ReusedBuffer = 1, |
590 | |
591 | /// Redraw the part that have changed since the last two frames were drawn. |
592 | /// |
593 | /// This is used when using double buffering and swapping of the buffers. |
594 | SwappedBuffers = 2, |
595 | }; |
596 | |
597 | virtual ~SoftwareRenderer() { cbindgen_private::slint_software_renderer_drop(r: inner); }; |
598 | SoftwareRenderer(const SoftwareRenderer &) = delete; |
599 | SoftwareRenderer &operator=(const SoftwareRenderer &) = delete; |
600 | /// Constructs a new SoftwareRenderer with the \a buffer_type as strategy for handling the |
601 | /// differences between rendering buffers. |
602 | explicit SoftwareRenderer(RepaintBufferType buffer_type) |
603 | { |
604 | inner = cbindgen_private::slint_software_renderer_new(buffer_age: uint32_t(buffer_type)); |
605 | } |
606 | |
607 | /// Render the window scene into a pixel buffer |
608 | /// |
609 | /// The buffer must be at least as large as the associated slint::Window |
610 | /// |
611 | /// The stride is the amount of pixels between two lines in the buffer. |
612 | /// It is must be at least as large as the width of the window. |
613 | PhysicalRegion render(std::span<slint::Rgb8Pixel> buffer, std::size_t pixel_stride) const |
614 | { |
615 | auto r = cbindgen_private::slint_software_renderer_render_rgb8(r: inner, buffer: buffer.data(), |
616 | buffer_len: buffer.size(), pixel_stride); |
617 | return PhysicalRegion { r }; |
618 | } |
619 | |
620 | /// Render the window scene into an RGB 565 encoded pixel buffer |
621 | /// |
622 | /// The buffer must be at least as large as the associated slint::Window |
623 | /// |
624 | /// The stride is the amount of pixels between two lines in the buffer. |
625 | /// It is must be at least as large as the width of the window. |
626 | PhysicalRegion render(std::span<Rgb565Pixel> buffer, std::size_t pixel_stride) const |
627 | { |
628 | auto r = cbindgen_private::slint_software_renderer_render_rgb565( |
629 | r: inner, buffer: reinterpret_cast<uint16_t *>(buffer.data()), buffer_len: buffer.size(), pixel_stride); |
630 | return PhysicalRegion { r }; |
631 | } |
632 | |
633 | # ifdef SLINT_FEATURE_EXPERIMENTAL |
634 | /// This enum describes the rotation that is applied to the buffer when rendering. |
635 | /// To be used in set_rendering_rotation() |
636 | enum class RenderingRotation { |
637 | /// No rotation |
638 | NoRotation = 0, |
639 | /// Rotate 90° to the left |
640 | Rotate90 = 90, |
641 | /// 180° rotation (upside-down) |
642 | Rotate180 = 180, |
643 | /// Rotate 90° to the right |
644 | Rotate270 = 270, |
645 | }; |
646 | |
647 | /// Set how the window need to be rotated in the buffer. |
648 | /// |
649 | /// This is typically used to implement screen rotation in software |
650 | void set_rendering_rotation(RenderingRotation rotation) |
651 | { |
652 | cbindgen_private::slint_software_renderer_set_rendering_rotation( |
653 | r: inner, rotation: static_cast<int>(rotation)); |
654 | } |
655 | # endif |
656 | }; |
657 | #endif |
658 | |
659 | #ifdef SLINT_FEATURE_RENDERER_SKIA |
660 | /// An opaque, low-level window handle that internalizes everything necessary to exchange messages |
661 | /// with the windowing system. This includes the connection to the display server, if necessary. |
662 | /// |
663 | /// Note that this class does not provide any kind of ownership. The caller is responsible for |
664 | /// ensuring that the pointers supplied to the constructor are valid throughout the lifetime of the |
665 | /// NativeWindowHandle. |
666 | class NativeWindowHandle |
667 | { |
668 | cbindgen_private::CppRawHandleOpaque inner; |
669 | friend class SkiaRenderer; |
670 | |
671 | NativeWindowHandle(cbindgen_private::CppRawHandleOpaque inner) : inner(inner) { } |
672 | |
673 | public: |
674 | NativeWindowHandle() = delete; |
675 | NativeWindowHandle(const NativeWindowHandle &) = delete; |
676 | NativeWindowHandle &operator=(const NativeWindowHandle &) = delete; |
677 | /// Creates a new NativeWindowHandle by moving the handle data from \a other into this |
678 | /// NativeWindowHandle. |
679 | NativeWindowHandle(NativeWindowHandle &&other) { inner = std::exchange(obj&: other.inner, new_val: nullptr); } |
680 | /// Creates a new NativeWindowHandle by moving the handle data from \a other into this |
681 | /// NativeWindowHandle. |
682 | NativeWindowHandle &operator=(NativeWindowHandle &&other) |
683 | { |
684 | if (this == &other) { |
685 | return *this; |
686 | } |
687 | if (inner) { |
688 | cbindgen_private::slint_raw_window_handle_drop(handle: inner); |
689 | } |
690 | inner = std::exchange(obj&: other.inner, new_val: nullptr); |
691 | return *this; |
692 | } |
693 | |
694 | # if (!defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)) || defined(DOXYGEN) |
695 | /// Creates a new NativeWindowHandle from the given xcb_window_t \a window, |
696 | /// xcb_visualid_t \a visual_id, XCB \a connection, and \a screen number. |
697 | static NativeWindowHandle from_x11_xcb(uint32_t /*xcb_window_t*/ window, |
698 | uint32_t /*xcb_visualid_t*/ visual_id, |
699 | xcb_connection_t *connection, int screen) |
700 | { |
701 | |
702 | return { cbindgen_private::slint_new_raw_window_handle_x11_xcb(window, visual_id, |
703 | connection, screen) }; |
704 | } |
705 | |
706 | /// Creates a new NativeWindowHandle from the given XLib \a window, |
707 | /// VisualID \a visual_id, Display \a display, and \a screen number. |
708 | static NativeWindowHandle from_x11_xlib(uint32_t /*Window*/ window, |
709 | unsigned long /*VisualID*/ visual_id, |
710 | void /*Display*/ *display, int screen) |
711 | { |
712 | |
713 | return { cbindgen_private::slint_new_raw_window_handle_x11_xlib(window, visual_id, display, |
714 | screen) }; |
715 | } |
716 | |
717 | /// Creates a new NativeWindowHandle from the given wayland \a surface, |
718 | /// and \a display. |
719 | static NativeWindowHandle from_wayland(wl_surface *surface, wl_display *display) |
720 | { |
721 | |
722 | return { cbindgen_private::slint_new_raw_window_handle_wayland(surface, display) }; |
723 | } |
724 | |
725 | # endif |
726 | # if (defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)) || defined(DOXYGEN) |
727 | |
728 | /// Creates a new NativeWindowHandle from the given \a nsview, and \a nswindow. |
729 | static NativeWindowHandle from_appkit(NSView *nsview, NSWindow *nswindow) |
730 | { |
731 | |
732 | return { cbindgen_private::slint_new_raw_window_handle_appkit(nsview, nswindow) }; |
733 | } |
734 | |
735 | # endif |
736 | # if (!defined(__APPLE__) && (defined(_WIN32) || !defined(_WIN64))) || defined(DOXYGEN) |
737 | |
738 | /// Creates a new NativeWindowHandle from the given HWND \a hwnd, and HINSTANCE \a hinstance. |
739 | static NativeWindowHandle from_win32(void *hwnd, void *hinstance) |
740 | { |
741 | return { cbindgen_private::slint_new_raw_window_handle_win32(hwnd, hinstance) }; |
742 | } |
743 | # endif |
744 | /// Destroys the NativeWindowHandle. |
745 | ~NativeWindowHandle() |
746 | { |
747 | if (inner) { |
748 | cbindgen_private::slint_raw_window_handle_drop(handle: inner); |
749 | } |
750 | } |
751 | }; |
752 | |
753 | /// Slint's Skia renderer. |
754 | /// |
755 | /// Create the renderer when you have created a native window with a non-zero size. |
756 | /// Call the render() function to render the scene into the window. |
757 | class SkiaRenderer : public AbstractRenderer |
758 | { |
759 | mutable cbindgen_private::SkiaRendererOpaque inner; |
760 | |
761 | /// \private |
762 | cbindgen_private::RendererPtr renderer_handle() const override |
763 | { |
764 | return cbindgen_private::slint_skia_renderer_handle(r: inner); |
765 | } |
766 | |
767 | public: |
768 | virtual ~SkiaRenderer() { cbindgen_private::slint_skia_renderer_drop(r: inner); } |
769 | SkiaRenderer(const SkiaRenderer &) = delete; |
770 | SkiaRenderer &operator=(const SkiaRenderer &) = delete; |
771 | /// Constructs a new Skia renderer for the given window - referenced by the provided |
772 | /// WindowHandle - and the specified initial size. |
773 | explicit SkiaRenderer(const NativeWindowHandle &window_handle, PhysicalSize initial_size) |
774 | { |
775 | inner = cbindgen_private::slint_skia_renderer_new(handle_opaque: window_handle.inner, size: initial_size); |
776 | } |
777 | |
778 | /// Renders the scene into the window provided to the SkiaRenderer's constructor. |
779 | void render() const { cbindgen_private::slint_skia_renderer_render(r: inner); } |
780 | }; |
781 | #endif |
782 | |
783 | /// Call this function at each iteration of the event loop to call the timer handler and advance |
784 | /// the animations. This should be called before the rendering or processing input events |
785 | inline void update_timers_and_animations() |
786 | { |
787 | cbindgen_private::slint_platform_update_timers_and_animations(); |
788 | } |
789 | |
790 | /// Returns the duration until the next timer if there are pending timers |
791 | inline std::optional<std::chrono::milliseconds> duration_until_next_timer_update() |
792 | { |
793 | uint64_t val = cbindgen_private::slint_platform_duration_until_next_timer_update(); |
794 | if (val == std::numeric_limits<uint64_t>::max()) { |
795 | return std::nullopt; |
796 | } else if (val >= uint64_t(std::chrono::milliseconds::max().count())) { |
797 | return std::chrono::milliseconds::max(); |
798 | } else { |
799 | return std::chrono::milliseconds(val); |
800 | } |
801 | } |
802 | } |
803 | } |
804 | |