Skip to content

Commit

Permalink
Initial effort to use IPC calls to control verso (#207)
Browse files Browse the repository at this point in the history
* Initial effort to use IPC calls to control verso

* Move functions to a struct

* Change simple example to main.rs

* Rename to `ipc_channel`

* Rename and give the thread a name
  • Loading branch information
Legend-Master authored Oct 24, 2024
1 parent decf3b3 commit 3843d4e
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = ["verso", "versoview_messages"]
[workspace.dependencies]
ipc-channel = "0.19"
serde = { version = "1.0", features = ["derive"] }
url = { version = "2.5.2", features = ["serde"] }

[package]
name = "versoview"
Expand Down Expand Up @@ -104,7 +105,7 @@ webxr-api = { git = "https://github.com/servo/webxr" }
cargo-packager-resource-resolver = { version = "0.1.1", features = [
"auto-detect-format",
], optional = true }
url = "2.5.2"
url = { workspace = true }
headers = "0.3"
versoview_messages = { path = "./versoview_messages" }

Expand Down
11 changes: 10 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use servo_config::opts::{default_opts, set_options, Opts};
pub struct CliArgs {
/// URL to load initially.
pub url: Option<url::Url>,
/// URL to load initially.
pub ipc_channel: Option<String>,
}

/// Configuration of Verso instance.
Expand All @@ -33,6 +35,12 @@ fn parse_cli_args() -> Result<CliArgs, getopts::Fail> {

let mut opts = getopts::Options::new();
opts.optopt("", "url", "URL to load on start", "URL");
opts.optopt(
"",
"ipc-channel",
"IPC channel name to communicate and control verso",
"",
);

let matches: getopts::Matches = opts.parse(&args[1..])?;
let url = matches
Expand All @@ -49,8 +57,9 @@ fn parse_cli_args() -> Result<CliArgs, getopts::Fail> {
None
}
});
let ipc_channel = matches.opt_str("ipc-channel");

Ok(CliArgs { url })
Ok(CliArgs { url, ipc_channel })
}

impl Config {
Expand Down
22 changes: 17 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use versoview::config::Config;
use versoview::verso::EventLoopProxyMessage;
use versoview::{Result, Verso};
use winit::application::ApplicationHandler;
use winit::event_loop::{self, DeviceEvents};
use winit::event_loop::{EventLoop, EventLoopProxy};

struct App {
verso: Option<Verso>,
proxy: EventLoopProxy<()>,
proxy: EventLoopProxy<EventLoopProxyMessage>,
}

impl ApplicationHandler for App {
impl ApplicationHandler<EventLoopProxyMessage> for App {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
let config = Config::new(resources_dir_path().unwrap());
self.verso = Some(Verso::new(event_loop, self.proxy.clone(), config));
Expand All @@ -30,15 +31,26 @@ impl ApplicationHandler for App {
}
}

fn user_event(&mut self, event_loop: &event_loop::ActiveEventLoop, _: ()) {
fn user_event(
&mut self,
event_loop: &event_loop::ActiveEventLoop,
event: EventLoopProxyMessage,
) {
if let Some(v) = self.verso.as_mut() {
v.handle_servo_messages(event_loop);
match event {
EventLoopProxyMessage::Wake => {
v.handle_servo_messages(event_loop);
}
EventLoopProxyMessage::IpcMessage(message) => {
v.handle_incoming_webview_message(message);
}
}
}
}
}

fn main() -> Result<()> {
let event_loop = EventLoop::new()?;
let event_loop = EventLoop::<EventLoopProxyMessage>::with_user_event().build()?;
event_loop.listen_device_events(DeviceEvents::Never);
let proxy = event_loop.create_proxy();
let mut app = App { verso: None, proxy };
Expand Down
61 changes: 57 additions & 4 deletions src/verso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ use profile;
use script::{self, JSEngineSetup};
use script_traits::WindowSizeData;
use servo_config::{opts, pref};
use servo_url::ServoUrl;
use style;
use versoview_messages::ControllerMessage;
use webgpu;
use webrender::{create_webrender_instance, ShaderPrecacheFlags, WebRenderOptions};
use webrender_api::*;
Expand Down Expand Up @@ -75,10 +77,36 @@ impl Verso {
/// - Canvas: Enabled
/// - Constellation: Enabled
/// - Image Cache: Enabled
pub fn new(evl: &ActiveEventLoop, proxy: EventLoopProxy<()>, config: Config) -> Self {
pub fn new(
evl: &ActiveEventLoop,
proxy: EventLoopProxy<EventLoopProxyMessage>,
config: Config,
) -> Self {
if let Some(ipc_channel) = &config.args.ipc_channel {
let sender =
IpcSender::<IpcSender<ControllerMessage>>::connect(ipc_channel.to_string())
.unwrap();
let (controller_sender, receiver) = ipc::channel::<ControllerMessage>().unwrap();
sender.send(controller_sender).unwrap();
let proxy_clone = proxy.clone();
std::thread::Builder::new()
.name("IpcMessageRelay".to_owned())
.spawn(move || {
while let Ok(message) = receiver.recv() {
if let Err(e) =
proxy_clone.send_event(EventLoopProxyMessage::IpcMessage(message))
{
log::error!("Failed to send controller message to Verso: {e}");
}
}
})
.unwrap();
};

// Initialize configurations and Verso window
let protocols = config.create_protocols();
let initial_url = config.args.url.clone();

config.init();
// Reserving a namespace to create TopLevelBrowsingContextId.
PipelineNamespace::install(PipelineNamespaceId(0));
Expand Down Expand Up @@ -494,6 +522,23 @@ impl Verso {
}
}

/// Handle message came from webview controller.
pub fn handle_incoming_webview_message(&self, message: ControllerMessage) {
match message {
ControllerMessage::NavigateTo(to_url) => {
if let Some(webview_id) = self.windows.values().next().and_then(|(window, _)| {
window.webview.as_ref().map(|webview| webview.webview_id)
}) {
send_to_constellation(
&self.constellation_sender,
ConstellationMsg::LoadUrl(webview_id, ServoUrl::from_url(to_url)),
);
}
}
_ => {}
}
}

/// Return true if one of the Verso windows is animating.
pub fn is_animating(&self) -> bool {
self.compositor
Expand All @@ -516,17 +561,25 @@ impl Verso {
}
}

/// Message send to the event loop
pub enum EventLoopProxyMessage {
/// Wake
Wake,
/// Message coming from the webview controller
IpcMessage(ControllerMessage),
}

#[derive(Debug, Clone)]
struct Waker(pub EventLoopProxy<()>);
struct Waker(pub EventLoopProxy<EventLoopProxyMessage>);

impl EventLoopWaker for Waker {
fn clone_box(&self) -> Box<dyn EventLoopWaker> {
Box::new(self.clone())
}

fn wake(&self) {
if let Err(e) = self.0.send_event(()) {
log::error!("Servo failed to send wake up event to Verso: {}", e);
if let Err(e) = self.0.send_event(EventLoopProxyMessage::Wake) {
log::error!("Servo failed to send wake up event to Verso: {e}");
}
}
}
Expand Down
1 change: 1 addition & 0 deletions verso/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ edition = "2021"
[dependencies]
ipc-channel = { workspace = true }
serde = { workspace = true }
url = { workspace = true }
versoview_messages = { path = "../versoview_messages" }
26 changes: 26 additions & 0 deletions verso/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
use std::{path::Path, process::Command};
use versoview_messages::ControllerMessage;

use ipc_channel::ipc::{IpcOneShotServer, IpcSender};

pub struct VersoviewController(IpcSender<ControllerMessage>);

impl VersoviewController {
/// Create a new verso instance and get the controller to it
pub fn new(verso_path: impl AsRef<Path>, initial_url: url::Url) -> Self {
let path = verso_path.as_ref();
let (server, server_name) =
IpcOneShotServer::<IpcSender<ControllerMessage>>::new().unwrap();
Command::new(path)
.arg(format!("--ipc-channel={server_name}"))
.arg(format!("--url={initial_url}"))
.spawn()
.unwrap();
let (_, sender) = server.accept().unwrap();
Self(sender)
}

/// Navigate to url
pub fn navigate(&self, url: url::Url) -> Result<(), Box<ipc_channel::ErrorKind>> {
self.0.send(ControllerMessage::NavigateTo(url))
}
}
16 changes: 16 additions & 0 deletions verso/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::{env::current_exe, thread::sleep, time::Duration};

fn main() {
let versoview_path = current_exe().unwrap().parent().unwrap().join("versoview");
let controller = verso::VersoviewController::new(
versoview_path,
url::Url::parse("https://example.com").unwrap(),
);
sleep(Duration::from_secs(10));
dbg!(controller
.navigate(url::Url::parse("https://docs.rs").unwrap())
.unwrap());
loop {
sleep(Duration::MAX);
}
}
1 change: 1 addition & 0 deletions versoview_messages/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ edition = "2021"
[dependencies]
ipc-channel = { workspace = true }
serde = { workspace = true }
url = { workspace = true }
6 changes: 6 additions & 0 deletions versoview_messages/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ControllerMessage {
NavigateTo(url::Url),
}

0 comments on commit 3843d4e

Please sign in to comment.