Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions flake.lock

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

36 changes: 36 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};

rustToolchain = pkgs.rust-bin.stable."1.88.0".default.override {
extensions = [ "rust-src" "clippy" "rustfmt" ];
};
in {
devShell = pkgs.mkShell {
nativeBuildInputs = with pkgs; [
rustToolchain
];

buildInputs = with pkgs; [
gtk3
openssl
pkg-config
webkitgtk_4_1

rust-analyzer
];
};
}
);
}
4 changes: 2 additions & 2 deletions src/cef_impl/request_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,10 @@ pub(crate) fn wait_for_deferred_init(flag: &Arc<AtomicBool>) {
/// [`wait_for_deferred_init`] on this thread toggles the flag, which makes
/// nesting (e.g. an `on_initialized` continuation that creates another
/// webview) safe.
struct AllowNestableTasks;
pub(crate) struct AllowNestableTasks;

impl AllowNestableTasks {
fn enter() -> Self {
pub(crate) fn enter() -> Self {
NESTABLE_TASKS_DEPTH.with(|depth| {
let current = depth.get();
if current == 0 {
Expand Down
17 changes: 17 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@
use std::path::PathBuf;
use std::sync::OnceLock;

/// Linux window system selected before CEF and the event loop start.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum LinuxWindowing {
#[default]
X11,
/// A CEF Views-owned top-level window using Ozone/Wayland.
Wayland,
}

/// CEF runtime configuration, applied at `CefRuntime` creation (browser
/// process) and at custom-scheme registration (all processes).
#[derive(Debug, Clone)]
Expand All @@ -46,6 +55,8 @@ pub struct CefConfig {
/// by default, so `document.cookie` and `Set-Cookie` are inert on custom
/// schemes not listed here. The http/https defaults are preserved.
pub cookieable_schemes: Vec<String>,
/// Linux window system. Ignored on other platforms.
pub linux_windowing: LinuxWindowing,
}

impl Default for CefConfig {
Expand All @@ -60,6 +71,7 @@ impl Default for CefConfig {
deep_link_schemes: Vec::new(),
custom_schemes: vec!["tauri".into(), "ipc".into(), "asset".into()],
cookieable_schemes: Vec::new(),
linux_windowing: LinuxWindowing::X11,
}
}
}
Expand All @@ -76,3 +88,8 @@ pub fn configure(config: CefConfig) {
pub(crate) fn config() -> &'static CefConfig {
CONFIG.get_or_init(CefConfig::default)
}

#[cfg(target_os = "linux")]
pub(crate) fn native_wayland() -> bool {
config().linux_windowing == LinuxWindowing::Wayland
}
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ mod platform;
mod policy;
mod runtime;
mod streaming;
#[cfg(target_os = "linux")]
mod wayland;
mod webview;
mod window;
mod window_builder;
mod window_handle;

pub use config::{CefConfig, configure};
pub use config::{CefConfig, LinuxWindowing, configure};
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
Expand Down
5 changes: 5 additions & 0 deletions src/platform/linux/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ unsafe extern "C" fn x_io_error_handler(_display: *mut xlib::Display) -> c_int {
/// why cefclient installs its handlers *after* `gtk_init` rather than before.
/// Calling this more than once is harmless.
pub fn install_x_error_handlers() {
#[cfg(target_os = "linux")]
if crate::config::native_wayland() {
return;
}

let Some(xlib) = XLIB.as_ref() else {
return;
};
Expand Down
94 changes: 72 additions & 22 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ use crate::{
};
#[cfg(target_os = "macos")]
use winit::platform::macos::EventLoopBuilderExtMacOS;
#[cfg(target_os = "linux")]
use winit::platform::wayland::EventLoopBuilderExtWayland;
#[cfg(windows)]
use winit::platform::windows::EventLoopBuilderExtWindows;
#[cfg(any(
Expand Down Expand Up @@ -113,7 +115,8 @@ pub(crate) struct RuntimeContext<T: UserEvent> {
/// treated as valid beyond their callback.
#[derive(Clone, Copy)]
struct MainThreadDispatch<T: UserEvent> {
app: *mut WinitCefApp<T>,
app: *mut (),
handle: unsafe fn(*mut (), &dyn ActiveEventLoop, Message<T>),
event_loop: *const dyn ActiveEventLoop,
}

Expand Down Expand Up @@ -154,7 +157,7 @@ impl<T: UserEvent> Default for MainThreadDispatchSlot<T> {
}
}

struct MainThreadDispatchGuard<T: UserEvent> {
pub(crate) struct MainThreadDispatchGuard<T: UserEvent> {
context: RuntimeContext<T>,
dispatch: Box<MainThreadDispatch<T>>,
previous: *mut MainThreadDispatch<T>,
Expand All @@ -178,13 +181,11 @@ fn handle_main_thread_message<T: UserEvent>(
return Err(message);
};

// SAFETY: `WinitCefApp::install_current_dispatch` stores pointers to the currently
// executing winit application handler and event-loop callback. This function
// is only called on the runtime main thread while that callback is active.
let app = unsafe { &mut *dispatch.app };
// SAFETY: `install_current_dispatch` stores the currently executing application
// handler and event-loop callback. This only runs on the runtime main thread
// while that callback is active.
let event_loop = unsafe { &*dispatch.event_loop };

app.handle_message(event_loop, message);
unsafe { (dispatch.handle)(dispatch.app, event_loop, message) };

Ok(())
}
Expand All @@ -196,6 +197,25 @@ impl<T: UserEvent> fmt::Debug for RuntimeContext<T> {
}

impl<T: UserEvent> RuntimeContext<T> {
pub(crate) fn install_current_dispatch(
&self,
app: *mut (),
handle: unsafe fn(*mut (), &dyn ActiveEventLoop, Message<T>),
event_loop: &dyn ActiveEventLoop,
) -> MainThreadDispatchGuard<T> {
let mut dispatch = Box::new(MainThreadDispatch {
app,
handle,
event_loop: event_loop as *const _,
});
let previous = self.current_dispatch.install(dispatch.as_mut());
MainThreadDispatchGuard {
context: self.clone(),
dispatch,
previous,
}
}

pub(crate) fn send_message(&self, message: Message<T>) -> Result<()> {
let message = if self.is_main_thread() {
match handle_main_thread_message(self, message) {
Expand Down Expand Up @@ -464,18 +484,18 @@ impl<T: UserEvent> WinitCefApp<T> {
&mut self,
event_loop: &dyn ActiveEventLoop,
) -> MainThreadDispatchGuard<T> {
let mut dispatch = Box::new(MainThreadDispatch {
app: self as *mut _,
event_loop: event_loop as *const _,
});

let previous = self.context.current_dispatch.install(dispatch.as_mut());

MainThreadDispatchGuard {
context: self.context.clone(),
dispatch,
previous,
unsafe fn handle<T: UserEvent>(
app: *mut (),
event_loop: &dyn ActiveEventLoop,
message: Message<T>,
) {
unsafe { &mut *app.cast::<WinitCefApp<T>>() }.handle_message(event_loop, message);
}

let app = (self as *mut Self).cast();
self
.context
.install_current_dispatch(app, handle::<T>, event_loop)
}

fn drain_messages(&mut self, event_loop: &dyn ActiveEventLoop) {
Expand Down Expand Up @@ -1486,9 +1506,16 @@ impl<T: UserEvent> CefRuntime<T> {
));
}

// Force X11 usage on Linux
#[cfg(target_os = "linux")]
if crate::config::native_wayland() {
command_line_args.push(("ozone-platform".into(), Some("wayland".into())));
event_loop_builder.with_wayland();
} else {
command_line_args.push(("ozone-platform".into(), Some("x11".into())));
event_loop_builder.with_x11();
}

#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
Expand Down Expand Up @@ -1599,8 +1626,11 @@ impl<T: UserEvent> CefRuntime<T> {
// Baseline for embedders that never touch GTK. One that calls `gtk_init`
// must call `install_x_error_handlers` again afterwards — GTK's X11 backend
// replaces the handler during init.
#[cfg(target_os = "linux")]
if !crate::config::native_wayland() {
crate::platform::linux::install_x_error_handlers();
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd",
Expand Down Expand Up @@ -1674,6 +1704,13 @@ impl<T: UserEvent> Runtime<T> for CefRuntime<T> {
))]
fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
let mut event_loop_builder = EventLoopBuilder::default();
#[cfg(target_os = "linux")]
if crate::config::native_wayland() {
EventLoopBuilderExtWayland::with_any_thread(&mut event_loop_builder, true);
} else {
EventLoopBuilderExtX11::with_any_thread(&mut event_loop_builder, true);
}
#[cfg(not(target_os = "linux"))]
event_loop_builder.with_any_thread(true);
Self::init(event_loop_builder, args)
}
Expand Down Expand Up @@ -1777,6 +1814,19 @@ impl<T: UserEvent> Runtime<T> for CefRuntime<T> {

fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
let exit_code = Arc::new(std::sync::atomic::AtomicI32::new(0));
#[cfg(target_os = "linux")]
if crate::config::native_wayland() {
let app = crate::wayland::App::new(
self.context,
self.receiver,
Box::new(callback),
self.scheme_registry,
exit_code.clone(),
);
let _ = self.event_loop.run_app(app);
cef::shutdown();
return exit_code.load(Ordering::Acquire);
}
let app = WinitCefApp::new(
self.context,
self.receiver,
Expand Down
Loading
Loading