diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5f7188edc..147cdd751 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -10,6 +10,8 @@ mod dev; mod events; mod jump_list; #[cfg(any(target_os = "linux", test))] +mod linux_webkit; +#[cfg(any(target_os = "linux", test))] mod linux_window_state; mod logging; #[cfg(target_os = "macos")] @@ -68,6 +70,8 @@ pub fn run() { // Before the first window: one taskbar identity for every launch path. jump_list::set_app_id(&context.config().identifier); logging::init(&context.config().identifier); + #[cfg(target_os = "linux")] + linux_webkit::configure(); let refresh_shell_path = ide_host::shell_env::inherit_login_shell_path(&context.config().identifier); // Managed LSP/DAP installs; must run before other threads start and before PATH is cached. diff --git a/src-tauri/src/linux_webkit.rs b/src-tauri/src/linux_webkit.rs new file mode 100644 index 000000000..9e20f6f09 --- /dev/null +++ b/src-tauri/src/linux_webkit.rs @@ -0,0 +1,51 @@ +//! WebKitGTK's DMA-BUF renderer fails on Wayland with the NVIDIA driver: the window +//! never shows and GDK logs "Error 71 (Protocol error) dispatching to Wayland display". +//! On that combination the renderer is turned off before the first web view is created. + +const DISABLE_DMABUF: &str = "WEBKIT_DISABLE_DMABUF_RENDERER"; + +/// Run before other threads start (`set_var`). +#[cfg(target_os = "linux")] +pub fn configure() { + let nvidia = ["/proc/driver/nvidia/version", "/sys/module/nvidia"].iter().any(|p| std::path::Path::new(p).exists()); + if disable_dmabuf(|k| std::env::var(k).ok(), nvidia) { + tracing::info!("Wayland with the NVIDIA driver: {DISABLE_DMABUF}=1"); + unsafe { std::env::set_var(DISABLE_DMABUF, "1") }; + } +} + +/// A Wayland session on the NVIDIA driver, unless the user already set the variable. +fn disable_dmabuf(var: impl Fn(&str) -> Option, nvidia: bool) -> bool { + if !nvidia || var(DISABLE_DMABUF).is_some() { + return false; + } + let wayland_display = var("WAYLAND_DISPLAY").is_some_and(|v| !v.is_empty()); + // GDK tries the backends of `GDK_BACKEND` in order. + let gdk_wayland = + var("GDK_BACKEND").is_none_or(|b| matches!(b.split(',').next().map(str::trim), Some("wayland" | "*" | ""))); + wayland_display && gdk_wayland +} + +#[cfg(test)] +mod tests { + use super::*; + + fn decide(vars: &[(&str, &str)], nvidia: bool) -> bool { + disable_dmabuf(|k| vars.iter().find(|(name, _)| *name == k).map(|(_, v)| v.to_string()), nvidia) + } + + #[test] + fn turns_the_dmabuf_renderer_off_on_wayland_with_nvidia_only() { + assert!(decide(&[("WAYLAND_DISPLAY", "wayland-0")], true)); + assert!(decide(&[("WAYLAND_DISPLAY", "wayland-0"), ("GDK_BACKEND", "wayland,x11")], true)); + assert!(!decide(&[("WAYLAND_DISPLAY", "wayland-0")], false)); + assert!(!decide(&[("DISPLAY", ":0")], true)); + assert!(!decide(&[("WAYLAND_DISPLAY", "")], true)); + assert!(!decide(&[("WAYLAND_DISPLAY", "wayland-0"), ("GDK_BACKEND", "x11")], true)); + } + + #[test] + fn keeps_the_users_own_setting() { + assert!(!decide(&[("WAYLAND_DISPLAY", "wayland-0"), (DISABLE_DMABUF, "0")], true)); + } +}