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
4use alloc::rc::Rc;
5#[cfg(not(feature = "std"))]
6use alloc::{boxed::Box, string::String};
7use core::ffi::c_void;
8use i_slint_core::api::{
9 LogicalSize, PhysicalPosition, PhysicalSize, Window, WindowPosition, WindowSize,
10};
11use i_slint_core::graphics::euclid;
12use i_slint_core::graphics::IntSize;
13use i_slint_core::platform::{Clipboard, Platform, PlatformError};
14use i_slint_core::renderer::Renderer;
15use i_slint_core::window::ffi::WindowAdapterRcOpaque;
16use i_slint_core::window::{WindowAdapter, WindowProperties};
17use i_slint_core::SharedString;
18
19type WindowAdapterUserData = *mut c_void;
20
21// FIXME wrapper over &dyn Renderer
22#[repr(C)]
23pub struct RendererPtr {
24 _a: *const c_void,
25 _b: *const c_void,
26}
27
28pub struct CppWindowAdapter {
29 window: Window,
30 user_data: WindowAdapterUserData,
31 drop: unsafe extern "C" fn(WindowAdapterUserData),
32 /// Safety: the returned pointer must live for the lifetime of self
33 get_renderer_ref: unsafe extern "C" fn(WindowAdapterUserData) -> RendererPtr,
34 set_visible: unsafe extern "C" fn(WindowAdapterUserData, bool),
35 request_redraw: unsafe extern "C" fn(WindowAdapterUserData),
36 size: unsafe extern "C" fn(WindowAdapterUserData) -> IntSize,
37 set_size: unsafe extern "C" fn(WindowAdapterUserData, IntSize),
38 update_window_properties: unsafe extern "C" fn(WindowAdapterUserData, &WindowProperties),
39 position:
40 unsafe extern "C" fn(WindowAdapterUserData, &mut euclid::default::Point2D<i32>) -> bool,
41 set_position: unsafe extern "C" fn(WindowAdapterUserData, euclid::default::Point2D<i32>),
42}
43
44impl Drop for CppWindowAdapter {
45 fn drop(&mut self) {
46 unsafe { (self.drop)(self.user_data) };
47 }
48}
49
50impl WindowAdapter for CppWindowAdapter {
51 fn window(&self) -> &Window {
52 &self.window
53 }
54
55 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
56 unsafe { (self.set_visible)(self.user_data, visible) };
57 Ok(())
58 }
59
60 fn position(&self) -> Option<PhysicalPosition> {
61 let mut pos = euclid::default::Point2D::<i32>::default();
62 if unsafe { (self.position)(self.user_data, &mut pos) } {
63 Some(i_slint_core::graphics::ffi::physical_position_to_api(pos))
64 } else {
65 None
66 }
67 }
68
69 fn set_position(&self, position: WindowPosition) {
70 let physical_position = i_slint_core::graphics::ffi::physical_position_from_api(
71 position.to_physical(self.window.scale_factor()),
72 );
73 unsafe { (self.set_position)(self.user_data, physical_position) }
74 }
75
76 fn set_size(&self, size: WindowSize) {
77 let physical_size = i_slint_core::graphics::ffi::physical_size_from_api(
78 size.to_physical(self.window.scale_factor()),
79 );
80 unsafe { (self.set_size)(self.user_data, physical_size) }
81 }
82
83 fn size(&self) -> PhysicalSize {
84 let s = unsafe { (self.size)(self.user_data) };
85 PhysicalSize::new(s.width, s.height)
86 }
87
88 fn renderer(&self) -> &dyn Renderer {
89 unsafe { core::mem::transmute((self.get_renderer_ref)(self.user_data)) }
90 }
91
92 fn request_redraw(&self) {
93 unsafe { (self.request_redraw)(self.user_data) }
94 }
95
96 fn update_window_properties(&self, properties: WindowProperties<'_>) {
97 unsafe { (self.update_window_properties)(self.user_data, &properties) }
98 }
99}
100
101#[no_mangle]
102pub extern "C" fn slint_window_properties_get_title(wp: &WindowProperties, out: &mut SharedString) {
103 *out = wp.title();
104}
105
106#[no_mangle]
107pub extern "C" fn slint_window_properties_get_background(
108 wp: &WindowProperties,
109 out: &mut i_slint_core::Brush,
110) {
111 *out = wp.background();
112}
113
114#[no_mangle]
115pub extern "C" fn slint_window_properties_get_fullscreen(wp: &WindowProperties) -> bool {
116 wp.is_fullscreen()
117}
118
119#[no_mangle]
120pub extern "C" fn slint_window_properties_get_minimized(wp: &WindowProperties) -> bool {
121 wp.is_minimized()
122}
123
124#[no_mangle]
125pub extern "C" fn slint_window_properties_get_maximized(wp: &WindowProperties) -> bool {
126 wp.is_maximized()
127}
128
129#[repr(C)]
130#[derive(Clone, Copy)]
131/// a Repr(C) variant of slint::platform::LayoutConstraints
132pub struct LayoutConstraintsReprC {
133 pub min: i_slint_core::graphics::Size,
134 pub max: i_slint_core::graphics::Size,
135 pub preferred: i_slint_core::graphics::Size,
136 pub has_min: bool,
137 pub has_max: bool,
138}
139
140#[no_mangle]
141pub extern "C" fn slint_window_properties_get_layout_constraints(
142 wp: &WindowProperties,
143) -> LayoutConstraintsReprC {
144 let c: LayoutConstraints = wp.layout_constraints();
145 LayoutConstraintsReprC {
146 min: i_slint_core::lengths::logical_size_from_api(size:c.min.unwrap_or_default()).to_untyped(),
147 max: i_slint_coreSize2D::lengths::logical_size_from_api(
148 size:c.max.unwrap_or(default:LogicalSize { width: f32::MAX, height: f32::MAX }),
149 )
150 .to_untyped(),
151 preferred: i_slint_core::lengths::logical_size_from_api(size:c.preferred).to_untyped(),
152 has_min: c.min.is_some(),
153 has_max: c.max.is_some(),
154 }
155}
156
157#[no_mangle]
158pub unsafe extern "C" fn slint_window_adapter_new(
159 user_data: WindowAdapterUserData,
160 drop: unsafe extern "C" fn(WindowAdapterUserData),
161 get_renderer_ref: unsafe extern "C" fn(WindowAdapterUserData) -> RendererPtr,
162 set_visible: unsafe extern "C" fn(WindowAdapterUserData, bool),
163 request_redraw: unsafe extern "C" fn(WindowAdapterUserData),
164 size: unsafe extern "C" fn(WindowAdapterUserData) -> IntSize,
165 set_size: unsafe extern "C" fn(WindowAdapterUserData, IntSize),
166 update_window_properties: unsafe extern "C" fn(WindowAdapterUserData, &WindowProperties),
167 position: unsafe extern "C" fn(
168 WindowAdapterUserData,
169 &mut euclid::default::Point2D<i32>,
170 ) -> bool,
171 set_position: unsafe extern "C" fn(WindowAdapterUserData, euclid::default::Point2D<i32>),
172 target: *mut WindowAdapterRcOpaque,
173) {
174 let window: Rc = Rc::<CppWindowAdapter>::new_cyclic(|w: &Weak| CppWindowAdapter {
175 window: Window::new(window_adapter_weak:w.clone()),
176 user_data,
177 drop,
178 get_renderer_ref,
179 set_visible,
180 request_redraw,
181 size,
182 set_size,
183 update_window_properties,
184 position,
185 set_position,
186 });
187
188 core::ptr::write(dst:target as *mut Rc<dyn WindowAdapter>, src:window);
189}
190
191type PlatformUserData = *mut c_void;
192
193struct CppPlatform {
194 user_data: PlatformUserData,
195 drop: unsafe extern "C" fn(PlatformUserData),
196 window_factory: unsafe extern "C" fn(PlatformUserData, *mut WindowAdapterRcOpaque),
197 #[cfg(not(feature = "std"))]
198 duration_since_start: unsafe extern "C" fn(PlatformUserData) -> u64,
199 // silent the warning despite `Clipboard` is a `#[non_exhaustive]` enum from another crate.
200 #[allow(improper_ctypes_definitions)]
201 set_clipboard_text: unsafe extern "C" fn(PlatformUserData, &SharedString, Clipboard),
202 #[allow(improper_ctypes_definitions)]
203 clipboard_text: unsafe extern "C" fn(PlatformUserData, &mut SharedString, Clipboard) -> bool,
204 run_event_loop: unsafe extern "C" fn(PlatformUserData),
205 quit_event_loop: unsafe extern "C" fn(PlatformUserData),
206 invoke_from_event_loop: unsafe extern "C" fn(PlatformUserData, PlatformTaskOpaque),
207}
208
209impl Drop for CppPlatform {
210 fn drop(&mut self) {
211 unsafe { (self.drop)(self.user_data) };
212 }
213}
214
215impl Platform for CppPlatform {
216 fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
217 let mut uninit = core::mem::MaybeUninit::<Rc<dyn WindowAdapter>>::uninit();
218 unsafe {
219 (self.window_factory)(
220 self.user_data,
221 uninit.as_mut_ptr() as *mut WindowAdapterRcOpaque,
222 );
223 Ok(uninit.assume_init())
224 }
225 }
226
227 #[cfg(not(feature = "std"))]
228 fn duration_since_start(&self) -> core::time::Duration {
229 core::time::Duration::from_millis(unsafe { (self.duration_since_start)(self.user_data) })
230 }
231
232 fn run_event_loop(&self) -> Result<(), PlatformError> {
233 unsafe { (self.run_event_loop)(self.user_data) };
234 Ok(())
235 }
236
237 fn new_event_loop_proxy(&self) -> Option<Box<dyn i_slint_core::platform::EventLoopProxy>> {
238 Some(Box::new(CppEventLoopProxy {
239 user_data: self.user_data,
240 quit_event_loop: self.quit_event_loop,
241 invoke_from_event_loop: self.invoke_from_event_loop,
242 }))
243 }
244
245 fn set_clipboard_text(&self, text: &str, clipboard: Clipboard) {
246 let shared_text = SharedString::from(text);
247 unsafe { (self.set_clipboard_text)(self.user_data, &shared_text, clipboard) }
248 }
249
250 fn clipboard_text(&self, clipboard: Clipboard) -> Option<String> {
251 let mut out_text = SharedString::new();
252 let status = unsafe { (self.clipboard_text)(self.user_data, &mut out_text, clipboard) };
253 status.then(|| out_text.into())
254 }
255}
256
257struct CppEventLoopProxy {
258 user_data: PlatformUserData,
259 quit_event_loop: unsafe extern "C" fn(PlatformUserData),
260 invoke_from_event_loop: unsafe extern "C" fn(PlatformUserData, PlatformTaskOpaque),
261}
262
263impl i_slint_core::platform::EventLoopProxy for CppEventLoopProxy {
264 fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
265 unsafe { (self.quit_event_loop)(self.user_data) };
266 Ok(())
267 }
268
269 fn invoke_from_event_loop(
270 &self,
271 event: Box<dyn FnOnce() + Send>,
272 ) -> Result<(), i_slint_core::api::EventLoopError> {
273 unsafe {
274 (self.invoke_from_event_loop)(
275 self.user_data,
276 core::mem::transmute::<*mut dyn FnOnce(), PlatformTaskOpaque>(src:Box::into_raw(event)),
277 )
278 };
279 Ok(())
280 }
281}
282
283unsafe impl Send for CppEventLoopProxy {}
284unsafe impl Sync for CppEventLoopProxy {}
285
286// silent the warning depite `Clipboard` is a `#[non_exhaustive]` enum from another crate.
287#[allow(improper_ctypes_definitions)]
288#[no_mangle]
289pub unsafe extern "C" fn slint_platform_register(
290 user_data: PlatformUserData,
291 drop: unsafe extern "C" fn(PlatformUserData),
292 window_factory: unsafe extern "C" fn(PlatformUserData, *mut WindowAdapterRcOpaque),
293 #[allow(unused)] duration_since_start: unsafe extern "C" fn(PlatformUserData) -> u64,
294 set_clipboard_text: unsafe extern "C" fn(PlatformUserData, &SharedString, Clipboard),
295 clipboard_text: unsafe extern "C" fn(PlatformUserData, &mut SharedString, Clipboard) -> bool,
296 run_event_loop: unsafe extern "C" fn(PlatformUserData),
297 quit_event_loop: unsafe extern "C" fn(PlatformUserData),
298 invoke_from_event_loop: unsafe extern "C" fn(PlatformUserData, PlatformTaskOpaque),
299) {
300 let p: CppPlatform = CppPlatform {
301 user_data,
302 drop,
303 window_factory,
304 #[cfg(not(feature = "std"))]
305 duration_since_start,
306 set_clipboard_text,
307 clipboard_text,
308 run_event_loop,
309 quit_event_loop,
310 invoke_from_event_loop,
311 };
312 i_slint_core::platform::set_platform(Box::new(p)).unwrap();
313}
314
315#[no_mangle]
316pub unsafe extern "C" fn slint_windowrc_has_active_animations(
317 handle: *const WindowAdapterRcOpaque,
318) -> bool {
319 let window_adapter: &Rc = &*(handle as *const Rc<dyn WindowAdapter>);
320 window_adapter.window().has_active_animations()
321}
322
323#[no_mangle]
324pub extern "C" fn slint_platform_update_timers_and_animations() {
325 i_slint_core::platform::update_timers_and_animations()
326}
327
328/// Returns the duration in millisecond until the next timer or `u64::MAX` if there is no pending timers
329#[no_mangle]
330pub extern "C" fn slint_platform_duration_until_next_timer_update() -> u64 {
331 i_slint_core::platform::duration_until_next_timer_update()
332 .map_or(default:u64::MAX, |d: Duration| d.as_millis() as u64)
333}
334
335#[repr(C)]
336pub struct PlatformTaskOpaque(*const c_void, *const c_void);
337
338#[no_mangle]
339pub unsafe extern "C" fn slint_platform_task_drop(event: PlatformTaskOpaque) {
340 drop(Box::from_raw(core::mem::transmute::<PlatformTaskOpaque, *mut dyn FnOnce()>(src:event)));
341}
342
343#[no_mangle]
344pub unsafe extern "C" fn slint_platform_task_run(event: PlatformTaskOpaque) {
345 let f: Box = Box::from_raw(core::mem::transmute::<PlatformTaskOpaque, *mut dyn FnOnce()>(src:event));
346 f();
347}
348
349#[cfg(feature = "renderer-software")]
350mod software_renderer {
351 use super::*;
352 type SoftwareRendererOpaque = *const c_void;
353 use i_slint_core::graphics::{IntRect, Rgb8Pixel};
354 use i_slint_core::software_renderer::{RepaintBufferType, Rgb565Pixel, SoftwareRenderer};
355
356 #[no_mangle]
357 pub unsafe extern "C" fn slint_software_renderer_new(
358 buffer_age: u32,
359 ) -> SoftwareRendererOpaque {
360 let repaint_buffer_type = match buffer_age {
361 0 => RepaintBufferType::NewBuffer,
362 1 => RepaintBufferType::ReusedBuffer,
363 2 => RepaintBufferType::SwappedBuffers,
364 _ => unreachable!(),
365 };
366 Box::into_raw(Box::new(SoftwareRenderer::new_with_repaint_buffer_type(repaint_buffer_type)))
367 as SoftwareRendererOpaque
368 }
369
370 #[no_mangle]
371 pub unsafe extern "C" fn slint_software_renderer_drop(r: SoftwareRendererOpaque) {
372 drop(Box::from_raw(r as *mut SoftwareRenderer));
373 }
374
375 #[no_mangle]
376 pub unsafe extern "C" fn slint_software_renderer_render_rgb8(
377 r: SoftwareRendererOpaque,
378 buffer: *mut Rgb8Pixel,
379 buffer_len: usize,
380 pixel_stride: usize,
381 ) -> IntRect {
382 let buffer = core::slice::from_raw_parts_mut(buffer, buffer_len);
383 let renderer = &*(r as *const SoftwareRenderer);
384 let r = renderer.render(buffer, pixel_stride);
385 let (orig, size) = (r.bounding_box_origin(), r.bounding_box_size());
386 i_slint_core::graphics::euclid::rect(orig.x, orig.y, size.width as i32, size.height as i32)
387 }
388
389 #[no_mangle]
390 pub unsafe extern "C" fn slint_software_renderer_render_rgb565(
391 r: SoftwareRendererOpaque,
392 buffer: *mut u16,
393 buffer_len: usize,
394 pixel_stride: usize,
395 ) -> IntRect {
396 let buffer = core::slice::from_raw_parts_mut(buffer as *mut Rgb565Pixel, buffer_len);
397 let renderer = &*(r as *const SoftwareRenderer);
398 let r = renderer.render(buffer, pixel_stride);
399 let (orig, size) = (r.bounding_box_origin(), r.bounding_box_size());
400 i_slint_core::graphics::euclid::rect(orig.x, orig.y, size.width as i32, size.height as i32)
401 }
402
403 #[cfg(feature = "experimental")]
404 #[no_mangle]
405 pub unsafe extern "C" fn slint_software_renderer_set_rendering_rotation(
406 r: SoftwareRendererOpaque,
407 rotation: i32,
408 ) {
409 use i_slint_core::software_renderer::RenderingRotation;
410 let renderer = &*(r as *const SoftwareRenderer);
411 renderer.set_rendering_rotation(match rotation {
412 90 => RenderingRotation::Rotate90,
413 180 => RenderingRotation::Rotate180,
414 270 => RenderingRotation::Rotate270,
415 _ => RenderingRotation::NoRotation,
416 });
417 }
418
419 #[no_mangle]
420 pub unsafe extern "C" fn slint_software_renderer_handle(
421 r: SoftwareRendererOpaque,
422 ) -> RendererPtr {
423 let r = (r as *const SoftwareRenderer) as *const dyn Renderer;
424 core::mem::transmute(r)
425 }
426}
427
428#[cfg(all(feature = "i-slint-renderer-skia", feature = "raw-window-handle"))]
429pub mod skia {
430 use super::*;
431 use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
432
433 struct CppRawHandle(RawWindowHandle, RawDisplayHandle);
434
435 // the raw handle type are #[non_exhaustive], so they can't be initialize with the convenient syntax. Work that around.
436 macro_rules! init_raw {
437 ($ty:ty { $($var:ident),* }) => {
438 {
439 let mut h = <$ty>::empty();
440 $(h.$var = $var;)*
441 h
442 }
443 };
444 }
445
446 type CppRawHandleOpaque = *const c_void;
447
448 #[no_mangle]
449 pub unsafe extern "C" fn slint_new_raw_window_handle_win32(
450 hwnd: *mut c_void,
451 hinstance: *mut c_void,
452 ) -> CppRawHandleOpaque {
453 let handle = CppRawHandle(
454 RawWindowHandle::Win32(init_raw!(raw_window_handle::Win32WindowHandle {
455 hwnd,
456 hinstance
457 })),
458 RawDisplayHandle::Windows(raw_window_handle::WindowsDisplayHandle::empty()),
459 );
460 Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
461 }
462
463 #[no_mangle]
464 pub unsafe extern "C" fn slint_new_raw_window_handle_x11_xcb(
465 window: u32,
466 visual_id: u32,
467 connection: *mut c_void,
468 screen: core::ffi::c_int,
469 ) -> CppRawHandleOpaque {
470 use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle};
471 let handle = CppRawHandle(
472 RawWindowHandle::Xcb(init_raw!(XcbWindowHandle { window, visual_id })),
473 RawDisplayHandle::Xcb(init_raw!(XcbDisplayHandle { connection, screen })),
474 );
475 Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
476 }
477
478 #[no_mangle]
479 pub unsafe extern "C" fn slint_new_raw_window_handle_x11_xlib(
480 window: core::ffi::c_ulong,
481 visual_id: core::ffi::c_ulong,
482 display: *mut c_void,
483 screen: core::ffi::c_int,
484 ) -> CppRawHandleOpaque {
485 use raw_window_handle::{XlibDisplayHandle, XlibWindowHandle};
486 let handle = CppRawHandle(
487 RawWindowHandle::Xlib(init_raw!(XlibWindowHandle { window, visual_id })),
488 RawDisplayHandle::Xlib(init_raw!(XlibDisplayHandle { display, screen })),
489 );
490 Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
491 }
492
493 #[no_mangle]
494 pub unsafe extern "C" fn slint_new_raw_window_handle_wayland(
495 surface: *mut c_void,
496 display: *mut c_void,
497 ) -> CppRawHandleOpaque {
498 use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle};
499 let handle = CppRawHandle(
500 RawWindowHandle::Wayland(init_raw!(WaylandWindowHandle { surface })),
501 RawDisplayHandle::Wayland(init_raw!(WaylandDisplayHandle { display })),
502 );
503 Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
504 }
505
506 #[no_mangle]
507 pub unsafe extern "C" fn slint_new_raw_window_handle_appkit(
508 ns_view: *mut c_void,
509 ns_window: *mut c_void,
510 ) -> CppRawHandleOpaque {
511 use raw_window_handle::{AppKitDisplayHandle, AppKitWindowHandle};
512 let handle = CppRawHandle(
513 RawWindowHandle::AppKit(init_raw!(AppKitWindowHandle { ns_view, ns_window })),
514 RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()),
515 );
516 Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
517 }
518
519 #[no_mangle]
520 pub unsafe extern "C" fn slint_raw_window_handle_drop(handle: CppRawHandleOpaque) {
521 drop(Box::from_raw(handle as *mut CppRawHandle))
522 }
523
524 type SkiaRendererOpaque = *const c_void;
525 type SkiaRenderer = i_slint_renderer_skia::SkiaRenderer;
526
527 #[no_mangle]
528 pub unsafe extern "C" fn slint_skia_renderer_new(
529 handle_opaque: CppRawHandleOpaque,
530 size: IntSize,
531 ) -> SkiaRendererOpaque {
532 let handle = &*(handle_opaque as *const CppRawHandle);
533
534 // Safety: This is safe because the handle remains valid; the next rwh release provides `new()` without unsafe.
535 let active_handle = unsafe { raw_window_handle::ActiveHandle::new_unchecked() };
536
537 // Safety: the C++ code should ensure that the handle is valid
538 let (window_handle, display_handle) = unsafe {
539 (
540 raw_window_handle::WindowHandle::borrow_raw(handle.0, active_handle),
541 raw_window_handle::DisplayHandle::borrow_raw(handle.1),
542 )
543 };
544
545 let boxed_renderer: Box<SkiaRenderer> = Box::new(
546 SkiaRenderer::new(
547 window_handle,
548 display_handle,
549 PhysicalSize { width: size.width, height: size.height },
550 )
551 .unwrap(),
552 );
553 Box::into_raw(boxed_renderer) as SkiaRendererOpaque
554 }
555
556 #[no_mangle]
557 pub unsafe extern "C" fn slint_skia_renderer_drop(r: SkiaRendererOpaque) {
558 drop(Box::from_raw(r as *mut SkiaRenderer))
559 }
560
561 #[no_mangle]
562 pub unsafe extern "C" fn slint_skia_renderer_render(r: SkiaRendererOpaque) {
563 let r = &*(r as *const SkiaRenderer);
564 r.render().unwrap();
565 }
566
567 #[no_mangle]
568 pub unsafe extern "C" fn slint_skia_renderer_handle(r: SkiaRendererOpaque) -> RendererPtr {
569 let r = (r as *const SkiaRenderer) as *const dyn Renderer;
570 core::mem::transmute(r)
571 }
572}
573