mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
winit: initial minibrowser (#29976)
* winit: add minibrowser feature that depends on egui{,-winit} * winit: carve out some space at the top of headed windows * winit: minimal toolbar and egui/winit integration (but no painting) * winit: try to paint with egui_glow (doesn’t work yet) * winit: add comment about toolbar size * Add framebuffer object, set it as glow's target * compositing: clear only the viewport, not the whole framebuffer * plumb the actual size of the egui toolbar to webrender * fix formatting * winit: fix crash when fbo is zero * winit: don’t bother binding the framebuffer object * winit: remove unsafe and get toolbar_height * winit: location field should reflect the current top-level url * [NFC] winit: move Minibrowser out of App::run * winit: clean up toolbar height code * winit: make App own the Minibrowser if any * winit: make the go button work * winit:make the location field reflect the current top-level url * winit: allow enabling minibrowser from command line * winit: tell compositor to repaint WR and flush when we repaint * winit: fix bug where location field edits would get overridden * winit: borrow the minibrowser once in App::handle_events * winit: address todo about viewport origin coordinates * winit: fix some minor problems with comments and errors * winit: update location field once per HistoryChanged event * winit: rename Window::set_toolbar_size to set_toolbar_height * winit: take toolbar height into account in hit testing * winit: pass egui only relevant CursorMoved events * winit: scratch that, coalesce minibrowser updates instead * ensure both minibrowser and WR are repainted on every frame * compositing: only skip framebuffer clear in external present mode * winit: destroy egui glow Painter when shutting down * winit: clean up and fix license lint * fix duplicate versions lint by downgrading bytemuck_derive was egui_glow ^0.22.0 (0.22.0) → egui/bytemuck ^0.22.0 (0.22.0) → epaint/bytemuck ^0.22.0 (0.22.0) → bytemuck ^1.7.2 (1.13.1) → bytemuck_derive ^1.4 (1.4.1) → syn ^2.0.1 (2.0.28) now lock has bytemuck_derive 1.4.0 → syn ^1.0.99 (1.0.103) * fix duplicate versions lint by disabling egui-winit/links (we don’t need support for hyperlinks in our use of egui) * squelch duplicate versions lint by excluding clipboard-win * winit: fix compile warnings * winit: make gleam an optional dependency under /minibrowser * winit: remove cargo feature, since it’s not really optional * winit: extract Minibrowser and related code to separate module * winit: remove unnecessary trailing comma * winit: simplify the ServoUrl serialisation optimisation --------- Co-authored-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
parent
a7bd9f0d43
commit
2778beeb7a
16 changed files with 734 additions and 58 deletions
224
Cargo.lock
generated
224
Cargo.lock
generated
|
@ -48,6 +48,17 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
|
@ -145,6 +156,24 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arboard"
|
||||||
|
version = "3.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854"
|
||||||
|
dependencies = [
|
||||||
|
"clipboard-win 4.5.0",
|
||||||
|
"log",
|
||||||
|
"objc",
|
||||||
|
"objc-foundation",
|
||||||
|
"objc_id",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot 0.12.0",
|
||||||
|
"thiserror",
|
||||||
|
"winapi",
|
||||||
|
"x11rb",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "array-init"
|
name = "array-init"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -555,6 +584,20 @@ name = "bytemuck"
|
||||||
version = "1.13.1"
|
version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck_derive"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
|
@ -784,7 +827,7 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7"
|
checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clipboard-win",
|
"clipboard-win 2.2.0",
|
||||||
"objc",
|
"objc",
|
||||||
"objc-foundation",
|
"objc-foundation",
|
||||||
"objc_id",
|
"objc_id",
|
||||||
|
@ -800,6 +843,17 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clipboard-win"
|
||||||
|
version = "4.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
|
||||||
|
dependencies = [
|
||||||
|
"error-code",
|
||||||
|
"str-buf",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmake"
|
name = "cmake"
|
||||||
version = "0.1.50"
|
version = "0.1.50"
|
||||||
|
@ -1453,12 +1507,73 @@ dependencies = [
|
||||||
"wio",
|
"wio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecolor"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e479a7fa3f23d4e794f8b2f8b3568dd4e47886ad1b12c9c095e141cb591eb63"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3aef8ec3ae1b772f340170c65bf27d5b8c28f543a0116c844d2ac08d01123e7"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"epaint",
|
||||||
|
"log",
|
||||||
|
"nohash-hasher",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui-winit"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a49155fd4a0a4fb21224407a91de0030847972ef90fc64edb63621caea61cb2"
|
||||||
|
dependencies = [
|
||||||
|
"arboard",
|
||||||
|
"egui",
|
||||||
|
"instant",
|
||||||
|
"log",
|
||||||
|
"raw-window-handle 0.5.0",
|
||||||
|
"smithay-clipboard",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_glow"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f8c2752cdf1b0ef5fcda59a898cacabad974d4f5880e92a420b2c917022da64"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"egui",
|
||||||
|
"egui-winit",
|
||||||
|
"glow",
|
||||||
|
"log",
|
||||||
|
"memoffset",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emath"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3857d743a6e0741cdd60b622a74c7a36ea75f5f8f11b793b41d905d2c9721a4b"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedder_traits"
|
name = "embedder_traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
@ -1537,6 +1652,23 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "epaint"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09333964d4d57f40a85338ba3ca5ed4716070ab184dcfed966b35491c5c64f3b"
|
||||||
|
dependencies = [
|
||||||
|
"ab_glyph",
|
||||||
|
"ahash",
|
||||||
|
"atomic_refcell",
|
||||||
|
"bytemuck",
|
||||||
|
"ecolor",
|
||||||
|
"emath",
|
||||||
|
"log",
|
||||||
|
"nohash-hasher",
|
||||||
|
"parking_lot 0.12.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -1558,6 +1690,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "error-code"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"str-buf",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "euclid"
|
name = "euclid"
|
||||||
version = "0.22.7"
|
version = "0.22.7"
|
||||||
|
@ -1892,6 +2034,16 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gethostname"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getopts"
|
name = "getopts"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -2201,6 +2353,18 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glow"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca0fe580e4b60a8ab24a868bc08e2f03cbcb20d3d676601fa909386713333728"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"slotmap",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glslopt"
|
name = "glslopt"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
@ -3958,6 +4122,12 @@ version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
@ -5284,8 +5454,13 @@ dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"cc",
|
"cc",
|
||||||
"clipboard",
|
"clipboard",
|
||||||
|
"egui",
|
||||||
|
"egui-winit",
|
||||||
|
"egui_glow",
|
||||||
"euclid",
|
"euclid",
|
||||||
"getopts",
|
"getopts",
|
||||||
|
"gleam",
|
||||||
|
"glow",
|
||||||
"image 0.24.6",
|
"image 0.24.6",
|
||||||
"keyboard-types",
|
"keyboard-types",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -5790,6 +5965,16 @@ dependencies = [
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smithay-clipboard"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8"
|
||||||
|
dependencies = [
|
||||||
|
"smithay-client-toolkit",
|
||||||
|
"wayland-client",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -5873,6 +6058,12 @@ dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "str-buf"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strict-num"
|
name = "strict-num"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -7155,6 +7346,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-wsapoll"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -7336,6 +7536,28 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11rb"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507"
|
||||||
|
dependencies = [
|
||||||
|
"gethostname",
|
||||||
|
"nix 0.24.3",
|
||||||
|
"winapi",
|
||||||
|
"winapi-wsapoll",
|
||||||
|
"x11rb-protocol",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11rb-protocol"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67"
|
||||||
|
dependencies = [
|
||||||
|
"nix 0.24.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xcb"
|
name = "xcb"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|
|
@ -230,6 +230,13 @@ pub struct IOCompositor<Window: WindowMethods + ?Sized> {
|
||||||
/// taken before the render is complete will not reflect the
|
/// taken before the render is complete will not reflect the
|
||||||
/// most up to date rendering.
|
/// most up to date rendering.
|
||||||
waiting_on_pending_frame: bool,
|
waiting_on_pending_frame: bool,
|
||||||
|
|
||||||
|
/// Whether to send a ReadyToPresent message to the constellation after rendering a new frame,
|
||||||
|
/// allowing external code to draw to the framebuffer and decide when to present the frame.
|
||||||
|
external_present: bool,
|
||||||
|
|
||||||
|
/// Waiting for external code to call present.
|
||||||
|
waiting_on_present: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -382,6 +389,8 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
exit_after_load,
|
exit_after_load,
|
||||||
convert_mouse_to_touch,
|
convert_mouse_to_touch,
|
||||||
waiting_on_pending_frame: false,
|
waiting_on_pending_frame: false,
|
||||||
|
external_present: false,
|
||||||
|
waiting_on_present: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1525,6 +1534,12 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
target: CompositeTarget,
|
target: CompositeTarget,
|
||||||
rect: Option<Rect<f32, CSSPixel>>,
|
rect: Option<Rect<f32, CSSPixel>>,
|
||||||
) -> Result<Option<Image>, UnableToComposite> {
|
) -> Result<Option<Image>, UnableToComposite> {
|
||||||
|
if self.waiting_on_present {
|
||||||
|
return Err(UnableToComposite::NotReadyToPaintImage(
|
||||||
|
NotReadyToPaint::WaitingOnConstellation,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let size = self.embedder_coordinates.framebuffer.to_u32();
|
let size = self.embedder_coordinates.framebuffer.to_u32();
|
||||||
|
|
||||||
if let Err(err) = self.webrender_surfman.make_gl_context_current() {
|
if let Err(err) = self.webrender_surfman.make_gl_context_current() {
|
||||||
|
@ -1703,8 +1718,15 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Perform the page flip. This will likely block for a while.
|
// Perform the page flip. This will likely block for a while.
|
||||||
if let Err(err) = self.webrender_surfman.present() {
|
if self.external_present {
|
||||||
warn!("Failed to present surface: {:?}", err);
|
self.waiting_on_present = true;
|
||||||
|
let msg =
|
||||||
|
ConstellationMsg::ReadyToPresent(self.root_pipeline.top_level_browsing_context_id);
|
||||||
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
|
warn!("Sending event to constellation failed ({:?}).", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.composition_request = CompositionRequest::NoCompositingNecessary;
|
self.composition_request = CompositionRequest::NoCompositingNecessary;
|
||||||
|
@ -1715,6 +1737,13 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
Ok(rv)
|
Ok(rv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn present(&mut self) {
|
||||||
|
if let Err(err) = self.webrender_surfman.present() {
|
||||||
|
warn!("Failed to present surface: {:?}", err);
|
||||||
|
}
|
||||||
|
self.waiting_on_present = false;
|
||||||
|
}
|
||||||
|
|
||||||
fn composite_if_necessary(&mut self, reason: CompositingReason) {
|
fn composite_if_necessary(&mut self, reason: CompositingReason) {
|
||||||
if self.composition_request == CompositionRequest::NoCompositingNecessary {
|
if self.composition_request == CompositionRequest::NoCompositingNecessary {
|
||||||
if self.is_running_problem_test {
|
if self.is_running_problem_test {
|
||||||
|
@ -1733,10 +1762,12 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
let gl = &self.webrender_gl;
|
let gl = &self.webrender_gl;
|
||||||
self.assert_gl_framebuffer_complete();
|
self.assert_gl_framebuffer_complete();
|
||||||
|
|
||||||
// Make framebuffer fully transparent.
|
if !self.external_present {
|
||||||
gl.clear_color(0.0, 0.0, 0.0, 0.0);
|
// Make framebuffer fully transparent.
|
||||||
gl.clear(gleam::gl::COLOR_BUFFER_BIT);
|
gl.clear_color(0.0, 0.0, 0.0, 0.0);
|
||||||
self.assert_gl_framebuffer_complete();
|
gl.clear(gleam::gl::COLOR_BUFFER_BIT);
|
||||||
|
self.assert_gl_framebuffer_complete();
|
||||||
|
}
|
||||||
|
|
||||||
// Make the viewport white.
|
// Make the viewport white.
|
||||||
let viewport = self.embedder_coordinates.get_flipped_viewport();
|
let viewport = self.embedder_coordinates.get_flipped_viewport();
|
||||||
|
@ -1919,6 +1950,10 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
None => eprintln!("Unable to locate path to save captures"),
|
None => eprintln!("Unable to locate path to save captures"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_external_present(&mut self, value: bool) {
|
||||||
|
self.external_present = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Why we performed a composite. This is used for debugging.
|
/// Why we performed a composite. This is used for debugging.
|
||||||
|
|
|
@ -109,6 +109,8 @@ pub enum ConstellationMsg {
|
||||||
ChangeBrowserVisibility(TopLevelBrowsingContextId, bool),
|
ChangeBrowserVisibility(TopLevelBrowsingContextId, bool),
|
||||||
/// Virtual keyboard was dismissed
|
/// Virtual keyboard was dismissed
|
||||||
IMEDismissed,
|
IMEDismissed,
|
||||||
|
/// Compositing done, but external code needs to present.
|
||||||
|
ReadyToPresent(TopLevelBrowsingContextId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ConstellationMsg {
|
impl fmt::Debug for ConstellationMsg {
|
||||||
|
@ -142,6 +144,7 @@ impl fmt::Debug for ConstellationMsg {
|
||||||
ChangeBrowserVisibility(..) => "ChangeBrowserVisibility",
|
ChangeBrowserVisibility(..) => "ChangeBrowserVisibility",
|
||||||
IMEDismissed => "IMEDismissed",
|
IMEDismissed => "IMEDismissed",
|
||||||
ClearCache => "ClearCache",
|
ClearCache => "ClearCache",
|
||||||
|
ReadyToPresent(..) => "ReadyToPresent",
|
||||||
};
|
};
|
||||||
write!(formatter, "ConstellationMsg::{}", variant)
|
write!(formatter, "ConstellationMsg::{}", variant)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1551,6 +1551,12 @@ where
|
||||||
FromCompositorMsg::ChangeBrowserVisibility(top_level_browsing_context_id, visible) => {
|
FromCompositorMsg::ChangeBrowserVisibility(top_level_browsing_context_id, visible) => {
|
||||||
self.handle_change_browser_visibility(top_level_browsing_context_id, visible);
|
self.handle_change_browser_visibility(top_level_browsing_context_id, visible);
|
||||||
},
|
},
|
||||||
|
FromCompositorMsg::ReadyToPresent(top_level_browsing_context_id) => {
|
||||||
|
self.embedder_proxy.send((
|
||||||
|
Some(top_level_browsing_context_id),
|
||||||
|
EmbedderMsg::ReadyToPresent,
|
||||||
|
));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,8 @@ pub enum EmbedderMsg {
|
||||||
MediaSessionEvent(MediaSessionEvent),
|
MediaSessionEvent(MediaSessionEvent),
|
||||||
/// Report the status of Devtools Server with a token that can be used to bypass the permission prompt.
|
/// Report the status of Devtools Server with a token that can be used to bypass the permission prompt.
|
||||||
OnDevtoolsStarted(Result<u16, ()>, String),
|
OnDevtoolsStarted(Result<u16, ()>, String),
|
||||||
|
/// Compositing done, but external code needs to present.
|
||||||
|
ReadyToPresent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for EmbedderMsg {
|
impl Debug for EmbedderMsg {
|
||||||
|
@ -248,6 +250,7 @@ impl Debug for EmbedderMsg {
|
||||||
EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"),
|
EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"),
|
||||||
EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"),
|
EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"),
|
||||||
EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"),
|
EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"),
|
||||||
|
EmbedderMsg::ReadyToPresent => write!(f, "ReadyToPresent"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -801,6 +801,14 @@ where
|
||||||
pub fn deinit(self) {
|
pub fn deinit(self) {
|
||||||
self.compositor.deinit();
|
self.compositor.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_external_present(&mut self, value: bool) {
|
||||||
|
self.compositor.set_external_present(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn present(&mut self) {
|
||||||
|
self.compositor.present();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_embedder_channel(
|
fn create_embedder_channel(
|
||||||
|
|
|
@ -48,8 +48,13 @@ xr-profile = ["libservo/xr-profile"]
|
||||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||||
backtrace = { workspace = true }
|
backtrace = { workspace = true }
|
||||||
clipboard = "0.5"
|
clipboard = "0.5"
|
||||||
|
egui = "0.22.0"
|
||||||
|
egui_glow = { version = "0.22.0", features = ["winit"] }
|
||||||
|
egui-winit = { version = "0.22.0", default-features = false, features = ["clipboard", "wayland"] }
|
||||||
euclid = { workspace = true }
|
euclid = { workspace = true }
|
||||||
getopts = { workspace = true }
|
getopts = { workspace = true }
|
||||||
|
gleam = "0.12"
|
||||||
|
glow = "0.12.2"
|
||||||
keyboard-types = { workspace = true }
|
keyboard-types = { workspace = true }
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
use crate::browser::Browser;
|
use crate::browser::Browser;
|
||||||
use crate::embedder::EmbedderCallbacks;
|
use crate::embedder::EmbedderCallbacks;
|
||||||
use crate::events_loop::{EventsLoop, WakerEvent};
|
use crate::events_loop::{EventsLoop, WakerEvent};
|
||||||
|
use crate::minibrowser::Minibrowser;
|
||||||
use crate::window_trait::WindowPortsMethods;
|
use crate::window_trait::WindowPortsMethods;
|
||||||
use crate::{headed_window, headless_window};
|
use crate::{headed_window, headless_window};
|
||||||
|
use gleam::gl;
|
||||||
use winit::window::WindowId;
|
use winit::window::WindowId;
|
||||||
use winit::event_loop::EventLoopWindowTarget;
|
use winit::event_loop::EventLoopWindowTarget;
|
||||||
use servo::compositing::windowing::EmbedderEvent;
|
use servo::compositing::windowing::EmbedderEvent;
|
||||||
|
@ -16,11 +18,12 @@ use servo::config::opts::{self, parse_url_or_filename};
|
||||||
use servo::servo_config::pref;
|
use servo::servo_config::pref;
|
||||||
use servo::servo_url::ServoUrl;
|
use servo::servo_url::ServoUrl;
|
||||||
use servo::Servo;
|
use servo::Servo;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell, RefMut};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use surfman::GLApi;
|
||||||
use webxr::glwindow::GlWindowDiscovery;
|
use webxr::glwindow::GlWindowDiscovery;
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
|
@ -29,6 +32,13 @@ pub struct App {
|
||||||
event_queue: RefCell<Vec<EmbedderEvent>>,
|
event_queue: RefCell<Vec<EmbedderEvent>>,
|
||||||
suspended: Cell<bool>,
|
suspended: Cell<bool>,
|
||||||
windows: HashMap<WindowId, Rc<dyn WindowPortsMethods>>,
|
windows: HashMap<WindowId, Rc<dyn WindowPortsMethods>>,
|
||||||
|
minibrowser: Option<RefCell<Minibrowser>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Action to be taken by the caller of [`App::handle_events`].
|
||||||
|
enum PumpResult {
|
||||||
|
Shutdown,
|
||||||
|
Present,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -60,51 +70,96 @@ impl App {
|
||||||
servo: None,
|
servo: None,
|
||||||
suspended: Cell::new(false),
|
suspended: Cell::new(false),
|
||||||
windows: HashMap::new(),
|
windows: HashMap::new(),
|
||||||
|
minibrowser: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if opts::get().minibrowser && window.winit_window().is_some() {
|
||||||
|
// Make sure the gl context is made current.
|
||||||
|
let webrender_surfman = window.webrender_surfman();
|
||||||
|
let webrender_gl = match webrender_surfman.connection().gl_api() {
|
||||||
|
GLApi::GL => unsafe { gl::GlFns::load_with(|s| webrender_surfman.get_proc_address(s)) },
|
||||||
|
GLApi::GLES => unsafe {
|
||||||
|
gl::GlesFns::load_with(|s| webrender_surfman.get_proc_address(s))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
webrender_surfman.make_gl_context_current().unwrap();
|
||||||
|
debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR);
|
||||||
|
|
||||||
|
// Set up egui context for minibrowser ui
|
||||||
|
// Adapted from https://github.com/emilk/egui/blob/9478e50d012c5138551c38cbee16b07bc1fcf283/crates/egui_glow/examples/pure_glow.rs
|
||||||
|
app.minibrowser = Some(Minibrowser::new(&webrender_surfman, &events_loop).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut minibrowser) = app.minibrowser() {
|
||||||
|
minibrowser.update(window.winit_window().unwrap());
|
||||||
|
window.set_toolbar_height(minibrowser.toolbar_height.get());
|
||||||
|
}
|
||||||
|
|
||||||
let ev_waker = events_loop.create_event_loop_waker();
|
let ev_waker = events_loop.create_event_loop_waker();
|
||||||
events_loop.run_forever(move |e, w, control_flow| {
|
events_loop.run_forever(move |e, w, control_flow| {
|
||||||
if let winit::event::Event::NewEvents(winit::event::StartCause::Init) = e {
|
match e {
|
||||||
let surfman = window.webrender_surfman();
|
winit::event::Event::NewEvents(winit::event::StartCause::Init) => {
|
||||||
|
let surfman = window.webrender_surfman();
|
||||||
|
|
||||||
let xr_discovery = if pref!(dom.webxr.glwindow.enabled) && ! opts::get().headless {
|
let xr_discovery = if pref!(dom.webxr.glwindow.enabled) && ! opts::get().headless {
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
// This should be safe because run_forever does, in fact,
|
// This should be safe because run_forever does, in fact,
|
||||||
// run forever. The event loop window target doesn't get
|
// run forever. The event loop window target doesn't get
|
||||||
// moved, and does outlast this closure, and we won't
|
// moved, and does outlast this closure, and we won't
|
||||||
// ever try to make use of it once shutdown begins and
|
// ever try to make use of it once shutdown begins and
|
||||||
// it stops being valid.
|
// it stops being valid.
|
||||||
let w = unsafe {
|
let w = unsafe {
|
||||||
std::mem::transmute::<
|
std::mem::transmute::<
|
||||||
&EventLoopWindowTarget<WakerEvent>,
|
&EventLoopWindowTarget<WakerEvent>,
|
||||||
&'static EventLoopWindowTarget<WakerEvent>
|
&'static EventLoopWindowTarget<WakerEvent>
|
||||||
>(w.unwrap())
|
>(w.unwrap())
|
||||||
|
};
|
||||||
|
let factory = Box::new(move || Ok(window.new_glwindow(w)));
|
||||||
|
Some(GlWindowDiscovery::new(
|
||||||
|
surfman.connection(),
|
||||||
|
surfman.adapter(),
|
||||||
|
surfman.context_attributes(),
|
||||||
|
factory,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
let factory = Box::new(move || Ok(window.new_glwindow(w)));
|
|
||||||
Some(GlWindowDiscovery::new(
|
|
||||||
surfman.connection(),
|
|
||||||
surfman.adapter(),
|
|
||||||
surfman.context_attributes(),
|
|
||||||
factory,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
// Implements embedder methods, used by libservo and constellation.
|
// Implements embedder methods, used by libservo and constellation.
|
||||||
let embedder = Box::new(EmbedderCallbacks::new(
|
let embedder = Box::new(EmbedderCallbacks::new(
|
||||||
ev_waker.clone(),
|
ev_waker.clone(),
|
||||||
xr_discovery,
|
xr_discovery,
|
||||||
));
|
));
|
||||||
|
|
||||||
let servo_data = Servo::new(embedder, window.clone(), user_agent.clone());
|
let servo_data = Servo::new(embedder, window.clone(), user_agent.clone());
|
||||||
let mut servo = servo_data.servo;
|
let mut servo = servo_data.servo;
|
||||||
servo.handle_events(vec![EmbedderEvent::NewBrowser(get_default_url(), servo_data.browser_id)]);
|
|
||||||
servo.setup_logging();
|
|
||||||
|
|
||||||
app.windows.insert(window.id(), window.clone());
|
// If we have a minibrowser, ask the compositor to notify us when a new frame
|
||||||
app.servo = Some(servo);
|
// is ready to present, so that we can paint the minibrowser then present.
|
||||||
|
servo.set_external_present(app.minibrowser.is_some());
|
||||||
|
|
||||||
|
servo.handle_events(vec![EmbedderEvent::NewBrowser(get_default_url(), servo_data.browser_id)]);
|
||||||
|
servo.setup_logging();
|
||||||
|
|
||||||
|
app.windows.insert(window.id(), window.clone());
|
||||||
|
app.servo = Some(servo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO does windows still need this workaround?
|
||||||
|
// https://github.com/emilk/egui/blob/9478e50d012c5138551c38cbee16b07bc1fcf283/crates/egui_glow/examples/pure_glow.rs#L203
|
||||||
|
// winit::event::Event::RedrawEventsCleared => todo!(),
|
||||||
|
winit::event::Event::RedrawRequested(_) => {
|
||||||
|
if let Some(mut minibrowser) = app.minibrowser() {
|
||||||
|
minibrowser.update(window.winit_window().unwrap());
|
||||||
|
|
||||||
|
// Tell Servo to repaint, which will in turn allow us to repaint the
|
||||||
|
// minibrowser and present a complete frame without partial updates.
|
||||||
|
app.event_queue.borrow_mut().push(EmbedderEvent::Refresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If self.servo is None here, it means that we're in the process of shutting down,
|
// If self.servo is None here, it means that we're in the process of shutting down,
|
||||||
|
@ -114,7 +169,23 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the event
|
// Handle the event
|
||||||
app.queue_embedder_events_for_winit_event(e);
|
let mut consumed = false;
|
||||||
|
if let Some(mut minibrowser) = app.minibrowser() {
|
||||||
|
if let winit::event::Event::WindowEvent { ref event, .. } = e {
|
||||||
|
let response = minibrowser.context.on_event(&event);
|
||||||
|
if response.repaint {
|
||||||
|
// Request a redraw event that will in turn trigger a minibrowser update.
|
||||||
|
// This allows us to coalesce minibrowser updates across multiple events.
|
||||||
|
window.winit_window().unwrap().request_redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO how do we handle the tab key? (see doc for consumed)
|
||||||
|
consumed = response.consumed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !consumed {
|
||||||
|
app.queue_embedder_events_for_winit_event(e);
|
||||||
|
}
|
||||||
|
|
||||||
let animating = app.is_animating();
|
let animating = app.is_animating();
|
||||||
|
|
||||||
|
@ -125,10 +196,33 @@ impl App {
|
||||||
*control_flow = winit::event_loop::ControlFlow::Poll;
|
*control_flow = winit::event_loop::ControlFlow::Poll;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stop = app.handle_events();
|
// Consume and handle any events from the Minibrowser.
|
||||||
if stop {
|
if let Some(mut minibrowser) = app.minibrowser() {
|
||||||
*control_flow = winit::event_loop::ControlFlow::Exit;
|
let browser = &mut app.browser.borrow_mut();
|
||||||
app.servo.take().unwrap().deinit();
|
let app_event_queue = &mut app.event_queue.borrow_mut();
|
||||||
|
minibrowser.queue_embedder_events_for_minibrowser_events(browser, app_event_queue);
|
||||||
|
if minibrowser.update_location_in_toolbar(browser) {
|
||||||
|
// Update the minibrowser immediately. While we could update by requesting a
|
||||||
|
// redraw, doing so would delay the location update by two frames.
|
||||||
|
minibrowser.update(window.winit_window().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match app.handle_events() {
|
||||||
|
Some(PumpResult::Shutdown) => {
|
||||||
|
*control_flow = winit::event_loop::ControlFlow::Exit;
|
||||||
|
app.servo.take().unwrap().deinit();
|
||||||
|
if let Some(mut minibrowser) = app.minibrowser() {
|
||||||
|
minibrowser.context.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(PumpResult::Present) => {
|
||||||
|
if let Some(mut minibrowser) = app.minibrowser() {
|
||||||
|
minibrowser.paint(window.winit_window().unwrap());
|
||||||
|
}
|
||||||
|
app.servo.as_mut().unwrap().present();
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -190,7 +284,7 @@ impl App {
|
||||||
/// Window queues to the Browser queue, and from the Browser queue to Servo. We receive and
|
/// Window queues to the Browser queue, and from the Browser queue to Servo. We receive and
|
||||||
/// collect embedder messages from the various Servo components, then take them out of the
|
/// collect embedder messages from the various Servo components, then take them out of the
|
||||||
/// Servo interface so that the Browser can handle them.
|
/// Servo interface so that the Browser can handle them.
|
||||||
fn handle_events(&mut self) -> bool {
|
fn handle_events(&mut self) -> Option<PumpResult> {
|
||||||
let mut browser = self.browser.borrow_mut();
|
let mut browser = self.browser.borrow_mut();
|
||||||
|
|
||||||
// FIXME:
|
// FIXME:
|
||||||
|
@ -212,16 +306,17 @@ impl App {
|
||||||
// Take any new embedder messages from Servo itself.
|
// Take any new embedder messages from Servo itself.
|
||||||
let mut embedder_messages = self.servo.as_mut().unwrap().get_events();
|
let mut embedder_messages = self.servo.as_mut().unwrap().get_events();
|
||||||
let mut need_resize = false;
|
let mut need_resize = false;
|
||||||
|
let mut need_present = false;
|
||||||
loop {
|
loop {
|
||||||
// Consume and handle those embedder messages.
|
// Consume and handle those embedder messages.
|
||||||
browser.handle_servo_events(embedder_messages);
|
need_present |= browser.handle_servo_events(embedder_messages);
|
||||||
|
|
||||||
// Route embedder events from the Browser to the relevant Servo components,
|
// Route embedder events from the Browser to the relevant Servo components,
|
||||||
// receives and collects embedder messages from various Servo components,
|
// receives and collects embedder messages from various Servo components,
|
||||||
// and runs the compositor.
|
// and runs the compositor.
|
||||||
need_resize |= self.servo.as_mut().unwrap().handle_events(browser.get_events());
|
need_resize |= self.servo.as_mut().unwrap().handle_events(browser.get_events());
|
||||||
if browser.shutdown_requested() {
|
if browser.shutdown_requested() {
|
||||||
return true;
|
return Some(PumpResult::Shutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take any new embedder messages from Servo itself.
|
// Take any new embedder messages from Servo itself.
|
||||||
|
@ -233,8 +328,17 @@ impl App {
|
||||||
|
|
||||||
if need_resize {
|
if need_resize {
|
||||||
self.servo.as_mut().unwrap().repaint_synchronously();
|
self.servo.as_mut().unwrap().repaint_synchronously();
|
||||||
|
need_present = true;
|
||||||
}
|
}
|
||||||
false
|
if need_present {
|
||||||
|
Some(PumpResult::Present)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn minibrowser(&self) -> Option<RefMut<Minibrowser>> {
|
||||||
|
self.minibrowser.as_ref().map(|x| x.borrow_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ use tinyfiledialogs::{self, MessageBoxIcon, OkCancel, YesNo};
|
||||||
|
|
||||||
pub struct Browser<Window: WindowPortsMethods + ?Sized> {
|
pub struct Browser<Window: WindowPortsMethods + ?Sized> {
|
||||||
current_url: Option<ServoUrl>,
|
current_url: Option<ServoUrl>,
|
||||||
|
current_url_string: Option<String>,
|
||||||
|
|
||||||
/// id of the top level browsing context. It is unique as tabs
|
/// id of the top level browsing context. It is unique as tabs
|
||||||
/// are not supported yet. None until created.
|
/// are not supported yet. None until created.
|
||||||
browser_id: Option<BrowserId>,
|
browser_id: Option<BrowserId>,
|
||||||
|
@ -57,6 +59,7 @@ where
|
||||||
Browser {
|
Browser {
|
||||||
title: None,
|
title: None,
|
||||||
current_url: None,
|
current_url: None,
|
||||||
|
current_url_string: None,
|
||||||
browser_id: None,
|
browser_id: None,
|
||||||
browsers: Vec::new(),
|
browsers: Vec::new(),
|
||||||
window,
|
window,
|
||||||
|
@ -72,6 +75,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn browser_id(&self) -> Option<BrowserId> {
|
||||||
|
self.browser_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_url_string(&self) -> Option<&str> {
|
||||||
|
self.current_url_string.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_events(&mut self) -> Vec<EmbedderEvent> {
|
pub fn get_events(&mut self) -> Vec<EmbedderEvent> {
|
||||||
std::mem::take(&mut self.event_queue)
|
std::mem::take(&mut self.event_queue)
|
||||||
}
|
}
|
||||||
|
@ -264,7 +275,9 @@ where
|
||||||
self.event_queue.push(event);
|
self.event_queue.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_servo_events(&mut self, events: Vec<(Option<BrowserId>, EmbedderMsg)>) {
|
/// Returns true iff the caller needs to manually present a new frame.
|
||||||
|
pub fn handle_servo_events(&mut self, events: Vec<(Option<BrowserId>, EmbedderMsg)>) -> bool {
|
||||||
|
let mut need_present = false;
|
||||||
for (browser_id, msg) in events {
|
for (browser_id, msg) in events {
|
||||||
match msg {
|
match msg {
|
||||||
EmbedderMsg::Status(_status) => {
|
EmbedderMsg::Status(_status) => {
|
||||||
|
@ -437,6 +450,7 @@ where
|
||||||
},
|
},
|
||||||
EmbedderMsg::HistoryChanged(urls, current) => {
|
EmbedderMsg::HistoryChanged(urls, current) => {
|
||||||
self.current_url = Some(urls[current].clone());
|
self.current_url = Some(urls[current].clone());
|
||||||
|
self.current_url_string = Some(urls[current].clone().into_string());
|
||||||
},
|
},
|
||||||
EmbedderMsg::SetFullscreenState(state) => {
|
EmbedderMsg::SetFullscreenState(state) => {
|
||||||
self.window.set_fullscreen(state);
|
self.window.set_fullscreen(state);
|
||||||
|
@ -511,8 +525,13 @@ where
|
||||||
EmbedderMsg::ShowContextMenu(sender, ..) => {
|
EmbedderMsg::ShowContextMenu(sender, ..) => {
|
||||||
let _ = sender.send(ContextMenuResult::Ignored);
|
let _ = sender.send(ContextMenuResult::Ignored);
|
||||||
},
|
},
|
||||||
|
EmbedderMsg::ReadyToPresent => {
|
||||||
|
need_present = true;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
need_present
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
129
ports/winit/egui_glue.rs
Normal file
129
ports/winit/egui_glue.rs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! A modified version of EguiGlow [from egui_glow 0.22.0][0] that retains its shapes,
|
||||||
|
//! allowing [`EguiGlow::paint`] to be called multiple times.
|
||||||
|
//!
|
||||||
|
//! [0]: https://github.com/emilk/egui/blob/0.22.0/crates/egui_glow/src/winit.rs
|
||||||
|
|
||||||
|
// Copyright (c) 2018-2021 Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any
|
||||||
|
// person obtaining a copy of this software and associated
|
||||||
|
// documentation files (the "Software"), to deal in the
|
||||||
|
// Software without restriction, including without
|
||||||
|
// limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software
|
||||||
|
// is furnished to do so, subject to the following
|
||||||
|
// conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
use egui_glow::ShaderVersion;
|
||||||
|
pub use egui_winit;
|
||||||
|
use egui_winit::winit;
|
||||||
|
pub use egui_winit::EventResponse;
|
||||||
|
|
||||||
|
/// Use [`egui`] from a [`glow`] app based on [`winit`].
|
||||||
|
pub struct EguiGlow {
|
||||||
|
pub egui_ctx: egui::Context,
|
||||||
|
pub egui_winit: egui_winit::State,
|
||||||
|
pub painter: egui_glow::Painter,
|
||||||
|
|
||||||
|
shapes: Vec<egui::epaint::ClippedShape>,
|
||||||
|
textures_delta: egui::TexturesDelta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EguiGlow {
|
||||||
|
/// For automatic shader version detection set `shader_version` to `None`.
|
||||||
|
pub fn new<E>(
|
||||||
|
event_loop: &winit::event_loop::EventLoopWindowTarget<E>,
|
||||||
|
gl: std::sync::Arc<glow::Context>,
|
||||||
|
shader_version: Option<ShaderVersion>,
|
||||||
|
) -> Self {
|
||||||
|
let painter = egui_glow::Painter::new(gl, "", shader_version)
|
||||||
|
.map_err(|err| {
|
||||||
|
log::error!("error occurred in initializing painter:\n{err}");
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
egui_ctx: Default::default(),
|
||||||
|
egui_winit: egui_winit::State::new(event_loop),
|
||||||
|
painter,
|
||||||
|
shapes: Default::default(),
|
||||||
|
textures_delta: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_event(&mut self, event: &winit::event::WindowEvent<'_>) -> EventResponse {
|
||||||
|
self.egui_winit.on_event(&self.egui_ctx, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `Duration` of the timeout after which egui should be repainted even if there's no new events.
|
||||||
|
///
|
||||||
|
/// Call [`Self::paint`] later to paint.
|
||||||
|
pub fn run(
|
||||||
|
&mut self,
|
||||||
|
window: &winit::window::Window,
|
||||||
|
run_ui: impl FnMut(&egui::Context),
|
||||||
|
) -> std::time::Duration {
|
||||||
|
let raw_input = self.egui_winit.take_egui_input(window);
|
||||||
|
let egui::FullOutput {
|
||||||
|
platform_output,
|
||||||
|
repaint_after,
|
||||||
|
textures_delta,
|
||||||
|
shapes,
|
||||||
|
} = self.egui_ctx.run(raw_input, run_ui);
|
||||||
|
|
||||||
|
self.egui_winit
|
||||||
|
.handle_platform_output(window, &self.egui_ctx, platform_output);
|
||||||
|
|
||||||
|
self.shapes = shapes;
|
||||||
|
self.textures_delta.append(textures_delta);
|
||||||
|
repaint_after
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paint the results of the last call to [`Self::run`].
|
||||||
|
pub fn paint(&mut self, window: &winit::window::Window) {
|
||||||
|
/////// let shapes = std::mem::take(&mut self.shapes);
|
||||||
|
let shapes = &self.shapes;
|
||||||
|
let mut textures_delta = std::mem::take(&mut self.textures_delta);
|
||||||
|
|
||||||
|
for (id, image_delta) in textures_delta.set {
|
||||||
|
self.painter.set_texture(id, &image_delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////// let clipped_primitives = self.egui_ctx.tessellate(shapes);
|
||||||
|
let clipped_primitives = self.egui_ctx.tessellate(shapes.clone());
|
||||||
|
let dimensions: [u32; 2] = window.inner_size().into();
|
||||||
|
self.painter.paint_primitives(
|
||||||
|
dimensions,
|
||||||
|
self.egui_ctx.pixels_per_point(),
|
||||||
|
&clipped_primitives,
|
||||||
|
);
|
||||||
|
|
||||||
|
for id in textures_delta.free.drain(..) {
|
||||||
|
self.painter.free_texture(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call to release the allocated graphics resources.
|
||||||
|
pub fn destroy(&mut self) {
|
||||||
|
self.painter.destroy();
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,6 +51,7 @@ pub struct Window {
|
||||||
webrender_surfman: WebrenderSurfman,
|
webrender_surfman: WebrenderSurfman,
|
||||||
screen_size: Size2D<u32, DeviceIndependentPixel>,
|
screen_size: Size2D<u32, DeviceIndependentPixel>,
|
||||||
inner_size: Cell<Size2D<u32, DeviceIndependentPixel>>,
|
inner_size: Cell<Size2D<u32, DeviceIndependentPixel>>,
|
||||||
|
toolbar_height: Cell<f32>,
|
||||||
mouse_down_button: Cell<Option<winit::event::MouseButton>>,
|
mouse_down_button: Cell<Option<winit::event::MouseButton>>,
|
||||||
mouse_down_point: Cell<Point2D<i32, DevicePixel>>,
|
mouse_down_point: Cell<Point2D<i32, DevicePixel>>,
|
||||||
primary_monitor: winit::monitor::MonitorHandle,
|
primary_monitor: winit::monitor::MonitorHandle,
|
||||||
|
@ -156,6 +157,7 @@ impl Window {
|
||||||
device_pixels_per_px,
|
device_pixels_per_px,
|
||||||
xr_window_poses: RefCell::new(vec![]),
|
xr_window_poses: RefCell::new(vec![]),
|
||||||
modifiers_state: Cell::new(ModifiersState::empty()),
|
modifiers_state: Cell::new(ModifiersState::empty()),
|
||||||
|
toolbar_height: Cell::new(0.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,8 +407,9 @@ impl WindowPortsMethods for Window {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
winit::event::WindowEvent::CursorMoved { position, .. } => {
|
winit::event::WindowEvent::CursorMoved { position, .. } => {
|
||||||
let (x, y): (i32, i32) = position.into();
|
let (x, y): (f64, f64) = position.into();
|
||||||
self.mouse_pos.set(Point2D::new(x, y));
|
let y = y - f64::from(self.toolbar_height.get());
|
||||||
|
self.mouse_pos.set(Point2D::new(x, y).to_i32());
|
||||||
self.event_queue
|
self.event_queue
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.push(EmbedderEvent::MouseWindowMoveEventClass(Point2D::new(
|
.push(EmbedderEvent::MouseWindowMoveEventClass(Point2D::new(
|
||||||
|
@ -504,6 +507,14 @@ impl WindowPortsMethods for Window {
|
||||||
self.xr_window_poses.borrow_mut().push(pose.clone());
|
self.xr_window_poses.borrow_mut().push(pose.clone());
|
||||||
Box::new(XRWindow { winit_window, pose })
|
Box::new(XRWindow { winit_window, pose })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn winit_window(&self) -> Option<&winit::window::Window> {
|
||||||
|
Some(&self.winit_window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toolbar_height(&self, height: f32) {
|
||||||
|
self.toolbar_height.set(height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowMethods for Window {
|
impl WindowMethods for Window {
|
||||||
|
@ -525,8 +536,14 @@ impl WindowMethods for Window {
|
||||||
let PhysicalSize { width, height } = self
|
let PhysicalSize { width, height } = self
|
||||||
.winit_window
|
.winit_window
|
||||||
.inner_size();
|
.inner_size();
|
||||||
let inner_size = (Size2D::new(width as f32, height as f32) * dpr).to_i32();
|
|
||||||
let viewport = DeviceIntRect::new(Point2D::zero(), inner_size);
|
// Subtract the minibrowser toolbar height if any
|
||||||
|
let toolbar_height = self.toolbar_height.get();
|
||||||
|
let inner_size = Size2D::new(width as f32, height as f32) * dpr;
|
||||||
|
let viewport_size = inner_size - Size2D::new(0f32, toolbar_height);
|
||||||
|
let viewport_origin = DeviceIntPoint::zero(); // bottom left
|
||||||
|
let viewport = DeviceIntRect::new(viewport_origin, viewport_size.to_i32());
|
||||||
|
|
||||||
let framebuffer = DeviceIntSize::from_untyped(viewport.size.to_untyped());
|
let framebuffer = DeviceIntSize::from_untyped(viewport.size.to_untyped());
|
||||||
EmbedderCoordinates {
|
EmbedderCoordinates {
|
||||||
viewport,
|
viewport,
|
||||||
|
|
|
@ -108,6 +108,14 @@ impl WindowPortsMethods for Window {
|
||||||
) -> Box<dyn webxr::glwindow::GlWindow> {
|
) -> Box<dyn webxr::glwindow::GlWindow> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn winit_window(&self) -> Option<&winit::window::Window> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_toolbar_height(&self, _height: f32) {
|
||||||
|
unimplemented!("headless Window only")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowMethods for Window {
|
impl WindowMethods for Window {
|
||||||
|
|
|
@ -14,11 +14,13 @@ mod app;
|
||||||
mod backtrace;
|
mod backtrace;
|
||||||
mod browser;
|
mod browser;
|
||||||
mod crash_handler;
|
mod crash_handler;
|
||||||
|
mod egui_glue;
|
||||||
mod embedder;
|
mod embedder;
|
||||||
mod events_loop;
|
mod events_loop;
|
||||||
mod headed_window;
|
mod headed_window;
|
||||||
mod headless_window;
|
mod headless_window;
|
||||||
mod keyutils;
|
mod keyutils;
|
||||||
|
mod minibrowser;
|
||||||
mod prefs;
|
mod prefs;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod window_trait;
|
mod window_trait;
|
||||||
|
|
110
ports/winit/minibrowser.rs
Normal file
110
ports/winit/minibrowser.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::{cell::{RefCell, Cell}, sync::Arc};
|
||||||
|
|
||||||
|
use egui::TopBottomPanel;
|
||||||
|
use servo::{servo_url::ServoUrl, compositing::windowing::EmbedderEvent};
|
||||||
|
use servo::webrender_surfman::WebrenderSurfman;
|
||||||
|
|
||||||
|
use crate::{egui_glue::EguiGlow, events_loop::EventsLoop, browser::Browser, window_trait::WindowPortsMethods};
|
||||||
|
|
||||||
|
pub struct Minibrowser {
|
||||||
|
pub context: EguiGlow,
|
||||||
|
pub event_queue: RefCell<Vec<MinibrowserEvent>>,
|
||||||
|
pub toolbar_height: Cell<f32>,
|
||||||
|
location: RefCell<String>,
|
||||||
|
|
||||||
|
/// Whether the location has been edited by the user without clicking Go.
|
||||||
|
location_dirty: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MinibrowserEvent {
|
||||||
|
/// Go button clicked.
|
||||||
|
Go,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Minibrowser {
|
||||||
|
pub fn new(webrender_surfman: &WebrenderSurfman, events_loop: &EventsLoop) -> Self {
|
||||||
|
let gl = unsafe {
|
||||||
|
glow::Context::from_loader_function(|s| {
|
||||||
|
webrender_surfman.get_proc_address(s)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
context: EguiGlow::new(events_loop.as_winit(), Arc::new(gl), None),
|
||||||
|
event_queue: RefCell::new(vec![]),
|
||||||
|
toolbar_height: 0f32.into(),
|
||||||
|
location: RefCell::new(String::default()),
|
||||||
|
location_dirty: false.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the minibrowser, but don’t paint.
|
||||||
|
pub fn update(&mut self, window: &winit::window::Window) {
|
||||||
|
let Self { context, event_queue, location, location_dirty, toolbar_height } = self;
|
||||||
|
let _duration = context.run(window, |ctx| {
|
||||||
|
TopBottomPanel::top("toolbar").show(ctx, |ui| {
|
||||||
|
ui.allocate_ui_with_layout(
|
||||||
|
ui.available_size(),
|
||||||
|
egui::Layout::right_to_left(egui::Align::Center),
|
||||||
|
|ui| {
|
||||||
|
if ui.button("go").clicked() {
|
||||||
|
event_queue.borrow_mut().push(MinibrowserEvent::Go);
|
||||||
|
location_dirty.set(false);
|
||||||
|
}
|
||||||
|
if ui.add_sized(
|
||||||
|
ui.available_size(),
|
||||||
|
egui::TextEdit::singleline(&mut *location.borrow_mut()),
|
||||||
|
).changed() {
|
||||||
|
location_dirty.set(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
toolbar_height.set(ctx.used_rect().height());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paint the minibrowser, as of the last update.
|
||||||
|
pub fn paint(&mut self, window: &winit::window::Window) {
|
||||||
|
self.context.paint(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes any outstanding events from the [Minibrowser], converting them to [EmbedderEvent] and
|
||||||
|
/// routing those to the App event queue.
|
||||||
|
pub fn queue_embedder_events_for_minibrowser_events(
|
||||||
|
&self, browser: &Browser<dyn WindowPortsMethods>,
|
||||||
|
app_event_queue: &mut Vec<EmbedderEvent>,
|
||||||
|
) {
|
||||||
|
for event in self.event_queue.borrow_mut().drain(..) {
|
||||||
|
match event {
|
||||||
|
MinibrowserEvent::Go => {
|
||||||
|
let browser_id = browser.browser_id().unwrap();
|
||||||
|
let location = self.location.borrow();
|
||||||
|
let Ok(url) = ServoUrl::parse(&location) else {
|
||||||
|
warn!("failed to parse location");
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
app_event_queue.push(EmbedderEvent::LoadUrl(browser_id, url));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the location field when the [Browser] says it has changed, unless the user has
|
||||||
|
/// started editing it without clicking Go.
|
||||||
|
pub fn update_location_in_toolbar(&mut self, browser: &mut Browser<dyn WindowPortsMethods>) -> bool {
|
||||||
|
if !self.location_dirty.get() {
|
||||||
|
if let Some(location) = browser.current_url_string() {
|
||||||
|
self.location = RefCell::new(location.to_owned());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,4 +31,6 @@ pub trait WindowPortsMethods: WindowMethods {
|
||||||
&self,
|
&self,
|
||||||
events_loop: &winit::event_loop::EventLoopWindowTarget<WakerEvent>
|
events_loop: &winit::event_loop::EventLoopWindowTarget<WakerEvent>
|
||||||
) -> Box<dyn webxr::glwindow::GlWindow>;
|
) -> Box<dyn webxr::glwindow::GlWindow>;
|
||||||
|
fn winit_window(&self) -> Option<&winit::window::Window>;
|
||||||
|
fn set_toolbar_height(&self, height: f32);
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,9 @@ packages = [
|
||||||
|
|
||||||
# Temporarily duplicated until gleam can be upgrded.
|
# Temporarily duplicated until gleam can be upgrded.
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
||||||
|
# winit port minibrowser (servo/servo#30049)
|
||||||
|
"clipboard-win",
|
||||||
]
|
]
|
||||||
# Files that are ignored for all tidy and lint checks.
|
# Files that are ignored for all tidy and lint checks.
|
||||||
files = [
|
files = [
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue