Rework to single thread

This commit is contained in:
Wu Wayne
2023-11-27 15:47:01 +09:00
parent 1baff40683
commit 0cd8ff8bdd
6 changed files with 167 additions and 192 deletions

View File

@@ -55,6 +55,7 @@ servo-media = { git = "https://github.com/servo/media" }
crossbeam-channel = "0.5"
getopts = "0.2.17"
surfman = { version = "0.8", features = ["chains", "sm-angle", "sm-angle-default", "sm-x11", "sm-raw-window-handle"] }
winit = { version = "0.29", features = ["rwh_05"] }
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
javascriptcore-rs = { version = "=1.1", features = ["v2_28"], optional = true }
@@ -114,7 +115,6 @@ http-range = "0.1.5"
pollster = "0.3.0"
# tao = { git = "https://github.com/tauri-apps/tao", default-features = false, features = ["rwh_05"] }
wgpu = "0.18.0"
winit = { version = "0.29", features = ["rwh_05"] }
[patch.crates-io]
bindgen = { git = "https://github.com/sagudev/rust-bindgen", branch = "f16" }

View File

@@ -8,7 +8,7 @@ use winit::{
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
use wry::WebViewBuilder;
use wry::{WebViewBuilder, WebViewBuilderExtServo};
fn main() -> wry::Result<()> {
// #[cfg(any(
@@ -41,7 +41,7 @@ fn main() -> wry::Result<()> {
.unwrap();
#[allow(unused_mut)]
let mut builder = WebViewBuilder::new(&window);
let mut builder = WebViewBuilder::new_servo(window, event_loop.create_proxy());
let _webview = builder.with_url("https://tauri.app")?.build()?;
event_loop

View File

@@ -192,13 +192,18 @@ pub mod prelude {
pub use android::JniHandle;
#[cfg(target_os = "android")]
use android::*;
use winit::event_loop::EventLoopProxy;
#[cfg(servo)]
pub(crate) mod servo;
#[cfg(gtk)]
pub(crate) mod webkitgtk;
#[cfg(servo)]
pub use crate::servo::WebViewBuilderExtServo;
#[cfg(servo)]
use crate::servo::*;
#[cfg(servo)]
use winit::window::Window;
#[cfg(gtk)]
pub(crate) mod webkitgtk;
/// Re-exported [raw-window-handle](https://docs.rs/raw-window-handle/latest/raw_window_handle/) crate.
pub use raw_window_handle;
use raw_window_handle::HasRawWindowHandle;
@@ -517,6 +522,8 @@ pub struct WebViewBuilder<'a> {
web_context: Option<&'a mut WebContext>,
#[cfg(gtk)]
gtk_widget: Option<&'a gtk::Container>,
#[cfg(servo)]
winit: Option<(Window, EventLoopProxy<()>)>,
}
impl<'a> WebViewBuilder<'a> {
@@ -546,6 +553,8 @@ impl<'a> WebViewBuilder<'a> {
web_context: None,
#[cfg(gtk)]
gtk_widget: None,
#[cfg(servo)]
winit: None,
}
}
@@ -578,6 +587,8 @@ impl<'a> WebViewBuilder<'a> {
web_context: None,
#[cfg(gtk)]
gtk_widget: None,
#[cfg(servo)]
winit: None,
}
}
@@ -966,7 +977,20 @@ impl<'a> WebViewBuilder<'a> {
unreachable!()
}
#[cfg(not(gtk))]
#[cfg(servo)]
if let Some(window) = self.winit {
InnerWebView::new_servo(
window.0,
window.1,
self.attrs,
self.platform_specific,
self.web_context,
)?
} else {
unreachable!()
}
#[cfg(not(any(gtk, servo)))]
unreachable!()
};

View File

@@ -1,11 +1,5 @@
use std::{
rc::Rc,
thread::{self, JoinHandle},
};
use std::rc::Rc;
use crossbeam_channel::{unbounded, Receiver, Sender};
use once_cell::sync::Lazy;
use raw_window_handle::RawWindowHandle;
use servo::{
compositing::windowing::{EmbedderEvent, EmbedderMethods},
embedder_traits::{EmbedderMsg, EventLoopWaker},
@@ -13,141 +7,90 @@ use servo::{
servo_url::ServoUrl,
Servo,
};
use winit::event_loop::EventLoopProxy;
use crate::Rect;
use super::window::WebView;
/// Servo embedder thread. See [`Embedder`] static for more information.
pub static SERVO: Lazy<Embedder> = Lazy::new(|| {
let (embedder_tx, embedder_rx) = unbounded();
let callback_tx = embedder_tx.clone();
let _thread = thread::spawn(move || {
let mut servo = ServoThread::default();
while let Ok(event) = embedder_rx.recv() {
if servo.servo.is_none() {
servo.init(event, callback_tx.clone());
} else {
servo.handle_embedder_event(event);
}
}
});
Embedder {
_thread,
embedder_tx,
}
});
#[derive(Default)]
struct ServoThread {
servo: Option<Servo<WebView>>,
webview: Option<(TopLevelBrowsingContextId, Rc<WebView>)>,
}
impl ServoThread {
fn init(&mut self, event: ServoEvent, callback: Sender<ServoEvent>) {
if let ServoEvent::NewWebView(webview) = event {
let webview = Rc::new(WebView::new(webview));
let mut init_servo = Servo::new(
Box::new(EmbedderWaker(callback)),
webview.clone(),
Some(String::from(
"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0",
)),
);
init_servo
.servo
.handle_events(vec![EmbedderEvent::NewBrowser(
ServoUrl::parse("https://servo.org").unwrap(),
init_servo.browser_id,
)]);
init_servo.servo.setup_logging();
self.servo.replace(init_servo.servo);
self.webview.replace((init_servo.browser_id, webview));
} else {
log::warn!("Servo embedder received event while servo hasn't initialized yet: {event:?}");
}
}
/// Handle embedder event from embedder channel. These events are came from users and servo instance.
fn handle_embedder_event(&mut self, event: ServoEvent) {
log::trace!("Servo embedder is handling event: {event:?}");
match event {
ServoEvent::NewWebView(_) => {
log::warn!("Servo embedder got new webview request while servo is already initialized. Servo hasn't support multiwebview yet.");
}
ServoEvent::ResizeWebView(rect) => {
if let Some((_, webview)) = &self.webview {
webview.set_bounds(rect);
self.servo().handle_events(vec![EmbedderEvent::Resize]);
}
}
ServoEvent::Wake => {
self.handle_servo_message();
}
}
}
fn handle_servo_message(&mut self) {
let servo = self.servo();
let mut embedder_events = vec![];
servo.get_events().into_iter().for_each(|(w, m)| {
log::trace!("Servo embedder is handling servo message: {m:?} with browser id: {w:?}");
match m {
EmbedderMsg::BrowserCreated(w) => {
embedder_events.push(EmbedderEvent::SelectBrowser(w));
}
EmbedderMsg::ReadyToPresent => {
servo.recomposite();
servo.present();
}
e => {
log::warn!("Servo embedder hasn't supported handling this message yet: {e:?}")
}
}
});
embedder_events.push(EmbedderEvent::Idle);
let need_resize = servo.handle_events(embedder_events);
}
fn servo(&mut self) -> &mut Servo<WebView> {
self.servo.as_mut().unwrap()
}
}
#[derive(Debug)]
pub enum ServoEvent {
NewWebView(RawWindowHandle), //TODO url, useragent
ResizeWebView(Rect),
Wake,
}
unsafe impl Send for ServoEvent {}
unsafe impl Sync for ServoEvent {}
/// Servo embedder is an instance to work with other webview types and threads.
/// This creates its own event loop in another thread and using crossbean channel to communicate.
pub struct Embedder {
_thread: JoinHandle<()>,
embedder_tx: Sender<ServoEvent>,
servo: Servo<WebView>,
webview: (TopLevelBrowsingContextId, Rc<WebView>),
}
impl Embedder {
/// The sender to send event for servo thread to handle.
pub fn sender(&self) -> Sender<ServoEvent> {
self.embedder_tx.clone()
pub fn new(webview: WebView, callback: EmbedderWaker) -> Self {
let webview = Rc::new(webview);
let mut init_servo = Servo::new(
Box::new(callback),
webview.clone(),
Some(String::from(
"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0",
)),
);
init_servo
.servo
.handle_events(vec![EmbedderEvent::NewBrowser(
ServoUrl::parse("https://servo.org").unwrap(),
init_servo.browser_id,
)]);
init_servo.servo.setup_logging();
Embedder {
servo: init_servo.servo,
webview: (init_servo.browser_id, webview),
}
}
// /// The receiver to get event from servo thread.
// pub fn receiver(&self) -> Receiver<ServoEvent> {
// self.user_rx.clone()
// /// Handle embedder event from embedder channel. These events are came from users and servo instance.
// fn handle_embedder_event(&mut self, event: ServoEvent) {
// log::trace!("Servo embedder is handling event: {event:?}");
// match event {
// ServoEvent::NewWebView(_) => {
// log::warn!("Servo embedder got new webview request while servo is already initialized. Servo hasn't support multiwebview yet.");
// }
// ServoEvent::ResizeWebView(rect) => {
// self.webview.1.set_bounds(rect);
// self.servo.handle_events(vec![EmbedderEvent::Resize]);
// }
// ServoEvent::Wake => {
// self.handle_servo_message();
// }
// }
// }
// fn handle_servo_message(&mut self) {
// let mut embedder_events = vec![];
// let mut need_present = false;
// self.servo.get_events().into_iter().for_each(|(w, m)| {
// log::trace!("Servo embedder is handling servo message: {m:?} with browser id: {w:?}");
// match m {
// EmbedderMsg::BrowserCreated(w) => {
// embedder_events.push(EmbedderEvent::SelectBrowser(w));
// embedder_events.push(EmbedderEvent::Idle);
// }
// // EmbedderMsg::ResizeTo(_) |
// EmbedderMsg::ReadyToPresent => {
// need_present = true;
// }
// e => {
// log::warn!("Servo embedder hasn't supported handling this message yet: {e:?}")
// }
// }
// });
// let need_resize = self.servo.handle_events(embedder_events);
// // if need_resize {
// // servo.repaint_synchronously();
// // servo.present();
// if need_present {
// self.servo.recomposite();
// self.servo.present();
// }
// }
}
#[derive(Debug, Clone)]
pub struct EmbedderWaker(pub Sender<ServoEvent>);
pub struct EmbedderWaker(pub EventLoopProxy<()>);
impl EmbedderMethods for EmbedderWaker {
fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker> {
@@ -161,8 +104,8 @@ impl EventLoopWaker for EmbedderWaker {
}
fn wake(&self) {
if let Err(e) = self.0.send(ServoEvent::Wake) {
log::error!("Failed to send wake up event to servo thread: {}", e);
if let Err(e) = self.0.send_event(()) {
log::error!("Failed to send wake up event to servo embedder: {}", e);
}
}
}

View File

@@ -1,10 +1,13 @@
use crossbeam_channel::Sender;
use raw_window_handle::HasRawWindowHandle;
use url::Url;
use winit::{event_loop::EventLoopProxy, window::Window};
use crate::{Rect, Result, WebContext, WebViewAttributes, RGBA};
use crate::{Rect, Result, WebContext, WebViewAttributes, WebViewBuilder, RGBA};
use self::embedder::{ServoEvent, SERVO};
use self::{
embedder::{Embedder, EmbedderWaker},
window::WebView,
};
mod embedder;
mod prefs;
@@ -12,12 +15,13 @@ mod resources;
mod window;
pub(crate) struct InnerWebView {
embedder_tx: Sender<ServoEvent>,
servo: Embedder,
}
impl InnerWebView {
pub fn new<W: HasRawWindowHandle>(
window: &W,
pub fn new_servo(
window: Window,
proxy: EventLoopProxy<()>,
attributes: WebViewAttributes,
pl_attrs: super::PlatformSpecificWebViewAttributes,
web_context: Option<&mut WebContext>,
@@ -25,21 +29,28 @@ impl InnerWebView {
resources::init(web_context);
prefs::init();
let embedder_tx = SERVO.sender();
embedder_tx
.send(ServoEvent::NewWebView(window.raw_window_handle()))
.expect("Fail to send event to Servo thread.");
let webview = WebView::new(window);
let callback = EmbedderWaker(proxy);
let servo = Embedder::new(webview, callback);
Ok(Self { embedder_tx })
Ok(Self { servo })
}
pub fn new<W: HasRawWindowHandle>(
_window: &W,
_attributes: WebViewAttributes,
_pl_attrs: super::PlatformSpecificWebViewAttributes,
_web_context: Option<&mut WebContext>,
) -> Result<Self> {
todo!()
}
pub fn new_as_child<W: HasRawWindowHandle>(
parent: &W,
attributes: WebViewAttributes,
pl_attrs: super::PlatformSpecificWebViewAttributes,
web_context: Option<&mut WebContext>,
_parent: &W,
_attributes: WebViewAttributes,
_pl_attrs: super::PlatformSpecificWebViewAttributes,
_web_context: Option<&mut WebContext>,
) -> Result<Self> {
// Ok(Self)
todo!()
}
@@ -57,10 +68,6 @@ impl InnerWebView {
Ok(())
}
fn init(&self, js: &str) -> Result<()> {
Ok(())
}
#[cfg(any(debug_assertions, feature = "devtools"))]
pub fn open_devtools(&self) {}
@@ -86,21 +93,31 @@ impl InnerWebView {
Ok(())
}
pub fn set_bounds(&self, bounds: Rect) {
self.handle(ServoEvent::ResizeWebView(bounds));
}
pub fn set_bounds(&self, bounds: Rect) {}
pub fn set_visible(&self, visible: bool) {}
pub fn focus(&self) {}
pub fn handle(&self, event: ServoEvent) {
if let Err(e) = self.embedder_tx.send(event) {
log::error!("Failed to send servo event to servo thread: {}", e);
}
}
}
pub fn platform_webview_version() -> Result<String> {
Ok(String::from(""))
}
pub trait WebViewBuilderExtServo {
fn new_servo(window: Window, proxy: EventLoopProxy<()>) -> Self;
}
impl WebViewBuilderExtServo for WebViewBuilder<'_> {
fn new_servo(window: Window, proxy: EventLoopProxy<()>) -> Self {
Self {
attrs: WebViewAttributes::default(),
window: None,
as_child: false,
#[allow(clippy::default_constructed_unit_structs)]
platform_specific: super::PlatformSpecificWebViewAttributes::default(),
web_context: None,
winit: Some((window, proxy)),
}
}
}

View File

@@ -1,7 +1,6 @@
use crate::Rect;
use std::cell::Cell;
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use raw_window_handle::{HasRawWindowHandle, RawDisplayHandle, RawWindowHandle};
use servo::{
compositing::windowing::{AnimationState, EmbedderCoordinates, WindowMethods},
config::pref,
@@ -12,46 +11,35 @@ use servo::{
use surfman::{Connection, GLApi, GLVersion, SurfaceType};
// FIXME servo should re-export this.
use servo_media::player::context::{GlApi, GlContext, NativeDisplay};
use winit::window::Window;
/// This is the type for servo embedder. Not for public usage.
pub struct WebView {
webrender_surfman: WebrenderSurfman,
animation_state: Cell<AnimationState>,
rect: Cell<Rect>, //TODO hidpi
window: Window,
}
impl WebView {
pub fn new(window_handle: RawWindowHandle) -> Self {
pub fn new(window: Window) -> Self {
let connection = Connection::new().expect("Failed to create surfman connection");
let adapter = connection
.create_adapter()
.expect("Failed to create surfman adapter");
let native_widget = connection
.create_native_widget_from_rwh(window_handle)
.create_native_widget_from_rwh(window.raw_window_handle())
.expect("Failed to create surfman native widget");
let surface_type = SurfaceType::Widget { native_widget };
let webrender_surfman = WebrenderSurfman::create(&connection, &adapter, surface_type)
.expect("Failed to create webrender surfman");
log::trace!("Created webrender surfman for window {:?}", window_handle);
log::trace!("Created webrender surfman for window {:?}", window);
Self {
webrender_surfman,
animation_state: Cell::new(AnimationState::Idle),
rect: Cell::new(Rect {
x: 0,
y: 0,
width: 0,
height: 0,
}),
window,
}
}
pub fn set_bounds(&self, rect: Rect) {
self.rect.replace(rect);
let _ = self
.webrender_surfman
.resize(Size2D::new(rect.width as i32, rect.height as i32));
}
}
unsafe impl Send for WebView {}
@@ -59,16 +47,19 @@ unsafe impl Sync for WebView {}
impl WindowMethods for WebView {
fn get_coordinates(&self) -> EmbedderCoordinates {
let rect = self.rect.get();
let pos = Point2D::new(rect.x, rect.y);
let size = Size2D::new(rect.width as i32, rect.height as i32);
let size = self.window.inner_size();
let pos = Point2D::new(0, 0);
let viewport = Size2D::new(size.width as i32, size.height as i32);
let size = self.window.current_monitor().unwrap().size();
let screen = Size2D::new(size.width as i32, size.height as i32);
EmbedderCoordinates {
hidpi_factor: Scale::new(1.0),
screen: Size2D::new(3840, 2160),
screen_avail: Size2D::new(3840, 2160),
window: (size, pos),
framebuffer: size,
viewport: DeviceIntRect::new(pos, size),
hidpi_factor: Scale::new(self.window.scale_factor() as f32),
screen,
screen_avail: screen,
window: (viewport, pos),
framebuffer: viewport,
viewport: DeviceIntRect::new(pos, viewport),
}
}