mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-19 23:07:21 +00:00

Some checks are pending
autofix.ci / format_fix (push) Waiting to run
autofix.ci / lint_typecheck (push) Waiting to run
CI / node_test (macos-14) (push) Blocked by required conditions
CI / files-changed (push) Waiting to run
CI / build_and_test (--exclude bevy-example, ubuntu-22.04, 1.85) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, --exclude bevy-example, windows-2022, 1.85) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, beta) (push) Blocked by required conditions
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, stable) (push) Blocked by required conditions
CI / build_and_test (ubuntu-22.04, nightly) (push) Blocked by required conditions
CI / node_test (ubuntu-22.04) (push) Blocked by required conditions
CI / node_test (windows-2022) (push) Blocked by required conditions
CI / python_test (macos-14) (push) Blocked by required conditions
CI / python_test (ubuntu-22.04) (push) Blocked by required conditions
CI / python_test (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (ubuntu-22.04, stable) (push) Blocked by required conditions
CI / cpp_test_driver (macos-14) (push) Blocked by required conditions
CI / cpp_test_driver (ubuntu-22.04) (push) Blocked by required conditions
CI / cpp_test_driver (windows-2022) (push) Blocked by required conditions
CI / cpp_cmake (macos-14, 1.85) (push) Blocked by required conditions
CI / cpp_cmake (windows-2022, nightly) (push) Blocked by required conditions
CI / cpp_package_test (push) Blocked by required conditions
CI / vsce_build_test (push) Blocked by required conditions
CI / mcu (pico-st7789, thumbv6m-none-eabi) (push) Blocked by required conditions
CI / mcu (pico2-st7789, thumbv8m.main-none-eabihf) (push) Blocked by required conditions
CI / mcu (stm32h735g, thumbv7em-none-eabihf) (push) Blocked by required conditions
CI / mcu-embassy (push) Blocked by required conditions
CI / ffi_32bit_build (push) Blocked by required conditions
CI / docs (push) Blocked by required conditions
CI / wasm (push) Blocked by required conditions
CI / wasm_demo (push) Blocked by required conditions
CI / tree-sitter (push) Blocked by required conditions
CI / updater_test (0.3.0) (push) Blocked by required conditions
CI / fmt_test (push) Blocked by required conditions
CI / esp-idf-quick (push) Blocked by required conditions
CI / android (push) Blocked by required conditions
CI / miri (push) Blocked by required conditions
CI / test-figma-inspector (push) Blocked by required conditions
CI / material-components (push) Blocked by required conditions
* Parlay init * Start on femtovg * Cargo fmt * Decimate fonts.rs * Use fill_glyphs * [autofix.ci] apply automated fixes * Use positioned_glyphs instead * Clean up a little * Format * [autofix.ci] apply automated fixes * Few fixes * [autofix.ci] apply automated fixes * More small changes * Clean up * [autofix.ci] apply automated fixes * Display text cursor * Handle text_input_cursor_rect_for_byte_offset * stoke glyphs * Handle text selections * Stroke selection as well * Fix wierd cargo.toml padding * Move selection and stroking to brush settings * Removed commented out code * [autofix.ci] apply automated fixes * Cursor sizing * [autofix.ci] apply automated fixes * Mark unused variables * _scale -> scale * Handle a lot more layout options * Use the parley cursor * Removed unused PhysicalPoint * Start combining stuff WIP * Move things into i_slint_core::textlayout::sharedparley * [autofix.ci] apply automated fixes * Go back to splitting paragraphs correctly * Handle font_metrics via ttf_parser * Move (lack of) overflow handling to sharedparley * [autofix.ci] apply automated fixes * impl Deref for Layout * Be more explit about the width passed to layout being physical * Cargo fmt, rename fonts to font_cache * Use a thread local for layout context * Use parley selection * fix femtovg wgpu * Switch femtovg branch * max_physical_width -> max_width * Box contexts * [autofix.ci] apply automated fixes * Fallback to GenericFamily::SansSerif if no font is set * Add paint.set_font_size * Use max_physical_height * Fix text_size to return correct logical sizes * Use femtovg from crates.io * Fix C++ build The `sharedparley` module declares a public `Layout` struct, which clashes with the `Layout` struct we use in the C++ API. The former however is entirely internal to Rust, so we can instruct cbindgen to ignore the module. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev>
259 lines
9.6 KiB
Rust
259 lines
9.6 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
use std::{cell::RefCell, num::NonZeroU32, rc::Rc};
|
|
|
|
use i_slint_core::api::PlatformError;
|
|
|
|
use crate::{FemtoVGRenderer, GraphicsBackend, WindowSurface};
|
|
|
|
/// This trait describes the interface GPU accelerated renderers in Slint require to render with OpenGL.
|
|
///
|
|
/// It serves the purpose to ensure that the OpenGL context is current before running any OpenGL
|
|
/// commands, as well as providing access to the OpenGL implementation by function pointers.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// This trait is unsafe because an implementation of get_proc_address could return dangling
|
|
/// pointers. In practice an implementation of this trait should just forward to the EGL/WGL/CGL
|
|
/// C library that implements EGL/CGL/WGL.
|
|
#[allow(unsafe_code)]
|
|
pub unsafe trait OpenGLInterface {
|
|
/// Ensures that the OpenGL context is current when returning from this function.
|
|
fn ensure_current(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
|
/// This function is called by the renderers when all OpenGL commands have been issued and
|
|
/// the back buffer is reading for on-screen presentation. Typically implementations forward
|
|
/// this to platform specific APIs such as eglSwapBuffers.
|
|
fn swap_buffers(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
|
/// This function is called by the renderers when the surface needs to be resized, typically
|
|
/// in response to the windowing system notifying of a change in the window system.
|
|
/// For most implementations this is a no-op, with the exception for wayland for example.
|
|
fn resize(
|
|
&self,
|
|
width: NonZeroU32,
|
|
height: NonZeroU32,
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
|
/// Returns the address of the OpenGL function specified by name, or a null pointer if the
|
|
/// function does not exist.
|
|
fn get_proc_address(&self, name: &std::ffi::CStr) -> *const std::ffi::c_void;
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
struct WebGLNeedsNoCurrentContext;
|
|
#[cfg(target_arch = "wasm32")]
|
|
unsafe impl OpenGLInterface for WebGLNeedsNoCurrentContext {
|
|
fn ensure_current(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
Ok(())
|
|
}
|
|
|
|
fn swap_buffers(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
Ok(())
|
|
}
|
|
|
|
fn resize(
|
|
&self,
|
|
_width: NonZeroU32,
|
|
_height: NonZeroU32,
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
Ok(())
|
|
}
|
|
|
|
fn get_proc_address(&self, _: &std::ffi::CStr) -> *const std::ffi::c_void {
|
|
unreachable!()
|
|
}
|
|
}
|
|
|
|
struct SuspendedRenderer {}
|
|
|
|
unsafe impl OpenGLInterface for SuspendedRenderer {
|
|
fn ensure_current(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
Err("ensure current called on suspended renderer".to_string().into())
|
|
}
|
|
|
|
fn swap_buffers(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
Err("swap_buffers called on suspended renderer".to_string().into())
|
|
}
|
|
|
|
fn resize(
|
|
&self,
|
|
_: NonZeroU32,
|
|
_: NonZeroU32,
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
Ok(())
|
|
}
|
|
|
|
fn get_proc_address(&self, _: &std::ffi::CStr) -> *const std::ffi::c_void {
|
|
panic!("get_proc_address called on suspended renderer")
|
|
}
|
|
}
|
|
|
|
pub struct OpenGLBackend {
|
|
opengl_context: RefCell<Box<dyn OpenGLInterface>>,
|
|
#[cfg(target_family = "wasm")]
|
|
html_canvas: RefCell<Option<web_sys::HtmlCanvasElement>>,
|
|
}
|
|
|
|
impl OpenGLBackend {
|
|
pub fn set_opengl_context(
|
|
&self,
|
|
renderer: &FemtoVGRenderer<Self>,
|
|
#[cfg(not(target_arch = "wasm32"))] opengl_context: impl OpenGLInterface + 'static,
|
|
#[cfg(target_arch = "wasm32")] html_canvas: web_sys::HtmlCanvasElement,
|
|
) -> Result<(), i_slint_core::platform::PlatformError> {
|
|
#[cfg(target_arch = "wasm32")]
|
|
let opengl_context = WebGLNeedsNoCurrentContext {};
|
|
|
|
let opengl_context = Box::new(opengl_context);
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
let gl_renderer = unsafe {
|
|
femtovg::renderer::OpenGl::new_from_function_cstr(|name| {
|
|
opengl_context.get_proc_address(name)
|
|
})
|
|
.unwrap()
|
|
};
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
let gl_renderer = match femtovg::renderer::OpenGl::new_from_html_canvas(&html_canvas) {
|
|
Ok(gl_renderer) => gl_renderer,
|
|
Err(_) => {
|
|
use wasm_bindgen::JsCast;
|
|
|
|
// I don't believe that there's a way of disabling the 2D canvas.
|
|
let context_2d = html_canvas
|
|
.get_context("2d")
|
|
.unwrap()
|
|
.unwrap()
|
|
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
|
.unwrap();
|
|
context_2d.set_font("20px serif");
|
|
// We don't know if we're rendering on dark or white background, so choose a "color" in the middle for the text.
|
|
context_2d.set_fill_style_str("red");
|
|
context_2d
|
|
.fill_text("Slint requires WebGL to be enabled in your browser", 0., 30.)
|
|
.unwrap();
|
|
panic!("Cannot proceed without WebGL - aborting")
|
|
}
|
|
};
|
|
|
|
let femtovg_canvas = femtovg::Canvas::new_with_text_context(
|
|
gl_renderer,
|
|
crate::font_cache::FONT_CACHE.with(|cache| cache.borrow().text_context.clone()),
|
|
)
|
|
.unwrap();
|
|
|
|
*self.opengl_context.borrow_mut() = opengl_context;
|
|
#[cfg(target_family = "wasm")]
|
|
{
|
|
*self.html_canvas.borrow_mut() = Some(html_canvas);
|
|
}
|
|
|
|
let canvas = Rc::new(RefCell::new(femtovg_canvas));
|
|
renderer.reset_canvas(canvas);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub struct GLWindowSurface {}
|
|
|
|
impl WindowSurface<femtovg::renderer::OpenGl> for GLWindowSurface {
|
|
fn render_surface(&self) -> &<femtovg::renderer::OpenGl as femtovg::Renderer>::Surface {
|
|
&()
|
|
}
|
|
}
|
|
|
|
impl GraphicsBackend for OpenGLBackend {
|
|
type Renderer = femtovg::renderer::OpenGl;
|
|
type WindowSurface = GLWindowSurface;
|
|
const NAME: &'static str = "OpenGL";
|
|
|
|
fn new_suspended() -> Self {
|
|
Self {
|
|
opengl_context: RefCell::new(Box::new(SuspendedRenderer {})),
|
|
#[cfg(target_family = "wasm")]
|
|
html_canvas: RefCell::new(None),
|
|
}
|
|
}
|
|
|
|
fn clear_graphics_context(&self) {
|
|
*self.opengl_context.borrow_mut() = Box::new(SuspendedRenderer {});
|
|
}
|
|
|
|
/// Ensures that the OpenGL context is current when returning from this function.
|
|
fn begin_surface_rendering(
|
|
&self,
|
|
) -> Result<GLWindowSurface, Box<dyn std::error::Error + Send + Sync>> {
|
|
self.opengl_context.borrow().ensure_current()?;
|
|
Ok(GLWindowSurface {})
|
|
}
|
|
|
|
fn submit_commands(&self, _commands: <Self::Renderer as femtovg::Renderer>::CommandBuffer) {}
|
|
|
|
/// This function is called by the renderers when all OpenGL commands have been issued and
|
|
/// the back buffer is reading for on-screen presentation. Typically implementations forward
|
|
/// this to platform specific APIs such as eglSwapBuffers.
|
|
fn present_surface(
|
|
&self,
|
|
_surface: GLWindowSurface,
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
self.opengl_context.borrow().swap_buffers()
|
|
}
|
|
|
|
#[cfg(not(target_family = "wasm"))]
|
|
fn with_graphics_api<R>(
|
|
&self,
|
|
callback: impl FnOnce(Option<i_slint_core::api::GraphicsAPI<'_>>) -> R,
|
|
) -> Result<R, i_slint_core::platform::PlatformError> {
|
|
use i_slint_core::api::GraphicsAPI;
|
|
|
|
self.opengl_context.borrow().ensure_current()?;
|
|
let api = GraphicsAPI::NativeOpenGL {
|
|
get_proc_address: &|name| self.opengl_context.borrow().get_proc_address(name),
|
|
};
|
|
Ok(callback(Some(api)))
|
|
}
|
|
|
|
#[cfg(target_family = "wasm")]
|
|
fn with_graphics_api<R>(
|
|
&self,
|
|
callback: impl FnOnce(Option<i_slint_core::api::GraphicsAPI<'_>>) -> R,
|
|
) -> Result<R, i_slint_core::platform::PlatformError> {
|
|
use i_slint_core::api::GraphicsAPI;
|
|
|
|
let id =
|
|
self.html_canvas.borrow().as_ref().map_or_else(|| String::new(), |canvas| canvas.id());
|
|
|
|
let api = GraphicsAPI::WebGL { canvas_element_id: &id, context_type: "webgl2" };
|
|
Ok(callback(Some(api)))
|
|
}
|
|
|
|
/// This function is called by the renderers when the surface needs to be resized, typically
|
|
/// in response to the windowing system notifying of a change in the window system.
|
|
/// For most implementations this is a no-op, with the exception for wayland for example.
|
|
fn resize(
|
|
&self,
|
|
width: NonZeroU32,
|
|
height: NonZeroU32,
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
self.opengl_context.borrow().resize(width, height)
|
|
}
|
|
}
|
|
|
|
impl FemtoVGRenderer<OpenGLBackend> {
|
|
/// Creates a new renderer that renders using OpenGL. An implementation of the OpenGLInterface
|
|
/// trait needs to supplied.
|
|
pub fn new(
|
|
#[cfg(not(target_arch = "wasm32"))] opengl_context: impl OpenGLInterface + 'static,
|
|
#[cfg(target_arch = "wasm32")] html_canvas: web_sys::HtmlCanvasElement,
|
|
) -> Result<Self, PlatformError> {
|
|
use super::FemtoVGRendererExt;
|
|
let this = Self::new_suspended();
|
|
this.graphics_backend.set_opengl_context(
|
|
&this,
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
opengl_context,
|
|
#[cfg(target_arch = "wasm32")]
|
|
html_canvas,
|
|
)?;
|
|
Ok(this)
|
|
}
|
|
}
|