Skip to main content

zng_view_angle/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! Helpers for enabling ANGLE support of the ZNG apps on Windows.
5//!
6//! The default ZNG view-process implementation will attempt to dynamically load ANGLE DLLs if requested,
7//! this crate helps signal the view-process and acquire the DLLs.
8//!
9//! The following is a minimal setup example:
10//!
11//! **First** add the crate dependency, ZNG only supports ANGLE on Windows so the dependency can be conditional.
12//!
13//! ```toml
14//! [target.'cfg(windows)'.dependencies]
15//! zng-view-angle = { version = "0.1.5", features = ["download"] }
16//! ```
17//!
18//! With the `"download"` feature enabled the crate will download the required ANGLE DLLs from [zng-ui/build-angle]
19//! and copy them to the output dir. Its only two files `libEGL.dll` and `libGLESv2.dll`, around 5MB total, you must
20//! ensure these files are packaged with the app installer.
21//!
22//! **Second** signal the view-process to use ANGLE:
23//!
24//! ```
25//! # macro_rules! demo { () => {
26//! use zng::prelude::*;
27//!
28//! fn main() {
29//!     zng::env::init!();
30//!
31//!     APP.defaults().run_window(async {
32//!         #[cfg(windows)]
33//!         {
34//!             zng_view_angle::register_license();
35//!             zng_view_angle::register_root_extender();
36//!         }
37//!
38//!         Window! {
39//!             // ...
40//!         }
41//!     });
42//! }
43//! # } }
44//! ```
45//!
46//! The [`register_root_extender`] will insert a node in all subsequent windows that signals the view-process to
47//! find and use the ANGLE DLLs when render mode uses the GPU. If the DLLs are not found the native OpenGL driver is used instead.
48//!
49//! The [`register_license`] simply adds the ANGLE BSD3-Clause license to the [`LICENSES`] service.
50//!
51//! [zng-ui/build-angle]: https://github.com/zng-ui/build-angle
52//!
53//! # Crate
54//!
55#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
56
57use zng_app::{
58    third_party::{LICENSES, License, LicenseUsed, User},
59    view_process::{VIEW_PROCESS, VIEW_PROCESS_INITED_EVENT},
60};
61use zng_ext_window::{WINDOW_Ext as _, WINDOWS_EXTENSIONS};
62use zng_view_api::api_extension::*;
63use zng_wgt::prelude::*;
64
65fn init(win_id: WindowId) {
66    const EXT_NAME: &str = "zng-view.prefer_angle";
67    let ext_id = VIEW_PROCESS
68        .extension_id(EXT_NAME)
69        .ok()
70        .flatten()
71        .unwrap_or(ApiExtensionId::INVALID);
72    if ext_id == ApiExtensionId::INVALID {
73        return tracing::error!("view-process does not support {EXT_NAME:?}");
74    }
75
76    WINDOWS_EXTENSIONS
77        .view_extensions_init(win_id, ext_id, ApiExtensionPayload::serialize(&true).unwrap())
78        .unwrap();
79}
80
81/// Signal the view-process to try and use EGL for this window when the ANGLE DLLs can be found and the render mode is not software.
82///
83/// Note that the `libEGL.dll` and `libGLESv2.dll` files must be distributed with the executable.
84///
85/// Note that this will enable ANGLE just for this window, use [`register_root_extender`] to enable angle for all windows.
86///
87/// This does nothing when app is not running on Windows.
88#[property(CONTEXT)]
89pub fn prefer_angle_egl(child: impl IntoUiNode, enable: impl IntoValue<bool>) -> UiNode {
90    if enable.into() && cfg!(windows) {
91        match_node(child, move |_c, op| {
92            if let UiNodeOp::Init = op {
93                let win_id = WINDOW.id();
94
95                let mut loading_handle = None;
96                if VIEW_PROCESS.is_connected() {
97                    init(win_id);
98                } else {
99                    loading_handle = WINDOW.loading_handle(5.secs(), "prefer_angle_egl");
100                }
101                let handle = VIEW_PROCESS_INITED_EVENT.hook(move |_| {
102                    let _ = loading_handle.take();
103                    init(win_id);
104                    // retain in case of respawn
105                    true
106                });
107                WIDGET.push_var_handle(handle);
108            }
109        })
110    } else {
111        child.into_node()
112    }
113}
114
115/// Enable ANGLE for all subsequent open windows.
116///
117/// This uses a [`WINDOWS_EXTENSIONS::register_root_extender`] to set [`prefer_angle_egl`] in all windows.
118///
119/// [`prefer_angle_egl`]: fn@prefer_angle_egl
120pub fn register_root_extender() {
121    WINDOWS_EXTENSIONS.register_root_extender(|a| prefer_angle_egl(a.root, true));
122}
123
124/// Register the ANGLE license with the [`LICENSES`] service.
125///
126/// The license is a [BSD3-Clause type license](https://github.com/google/angle/blob/fca8ca8a87c387515e0d2901916e4fae6b97e83f/LICENSE).
127pub fn register_license() {
128    fn l() -> Vec<LicenseUsed> {
129        vec![LicenseUsed {
130            license: License::new("BSD3-Clause", r#"BSD 3-Clause "Revised" License"#, LICENSE),
131            used_by: vec![User::new(
132                "ANGLE",
133                "fca8ca8",
134                "https://github.com/zng-ui/build-angle/releases/tag/2026-05-17",
135            )],
136        }]
137    }
138    LICENSES.register(l);
139}
140
141const LICENSE: &str = r#"
142// Copyright 2018 The ANGLE Project Authors.
143// All rights reserved.
144//
145// Redistribution and use in source and binary forms, with or without
146// modification, are permitted provided that the following conditions
147// are met:
148//
149//     Redistributions of source code must retain the above copyright
150//     notice, this list of conditions and the following disclaimer.
151//
152//     Redistributions in binary form must reproduce the above
153//     copyright notice, this list of conditions and the following
154//     disclaimer in the documentation and/or other materials provided
155//     with the distribution.
156//
157//     Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc.
158//     Ltd., nor the names of their contributors may be used to endorse
159//     or promote products derived from this software without specific
160//     prior written permission.
161//
162// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
163// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
164// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
165// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
166// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
167// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
168// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
169// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
170// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
171// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
172// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
173// POSSIBILITY OF SUCH DAMAGE.
174"#;