diff --git a/docs/platform-flags.md b/docs/platform-flags.md index 23fe04d..56a0590 100644 --- a/docs/platform-flags.md +++ b/docs/platform-flags.md @@ -230,23 +230,35 @@ initialise on an MTA thread. view. That is the concrete reason the design uses one WebView hosting N terminals rather than one per tab: twenty tabs would mean twenty of those trees. -**The Avalonia WebView on Linux remains unproven, and is still the largest risk in the plan.** The -package's own release notes say `NativeWebView` gained Linux support via a **WPE** backend -(`libwpewebkit-2.0`), which is much less widely installed than WebKitGTK — and it ships a separate -`NativeWebDialog` described as *"particularly useful for platforms like Linux where embedded WebView -controls might not be available"*, which is the vendor confirming the concern. *Unverified:* a spike -must cover Ubuntu on both Wayland and X11, Fedora KDE, and macOS 15. +**The Linux WebView needs `ExperimentalOffscreen`, or the terminal is blank.** This entry used to predict +that WPE (`libwpewebkit-2.0`) would be the Linux backend and be too rarely installed; the prediction was +wrong in its details and right about the outcome. Measured on Fedora 44 with +`Avalonia.Controls.WebView` 12.0.1, by a spike that hosts a `NativeWebView` and reads `AdapterInfo`: + +- The adapter is **WebKitGTK 2.52.5**, not WPE, and it reports `IsSupported = True`. Fedora packages no + WPE WebKit at all — `dnf search wpe` returns a computer-algebra package and nothing else — so the WPE + path is not merely rare there, it is unavailable. +- In its default mode that adapter reports **`SupportedScenarios = NativeDialog`**: a window of its own, + and nothing that can be hosted in place. The same under X11 and under Wayland, so this is the adapter's + answer rather than a session-type problem. +- Setting **`ExperimentalOffscreen`** on `GtkWebViewEnvironmentRequestedEventArgs` changes the same + adapter's answer to **`OffscreenRenderer`** — the compositor-drawn mode, which is what + `NativeWebViewCompositorHost` (mentioned below as an unknown) exists to host. `MainWindow` sets it; see + `OnTerminalEnvironmentRequested`, which is a no-op on Windows and macOS by type rather than by OS check. +- In both modes the page loads and `InvokeScript` answers, which is the trap: **the failure has no + diagnostics.** Everything except the pixels works, so it reads as the terminal being broken rather than + as the host having nowhere to draw. `AdapterInfo.SupportedScenarios` is the thing to look at first. + +*Still unverified:* whether the offscreen mode actually paints, and how it behaves for input, IME and +resizing. The spike could not answer it — an XWayland root capture is black under a Wayland compositor +and `RenderTargetBitmap` does not capture a compositor surface — so it needs eyes on a running client. +macOS 15 remains untested entirely. `ITerminalHost` was supposed to be the seam that keeps a backend swap cheap, and it is **declared but not implemented** — nothing in the application uses it, and the view navigates `NativeWebView.Source` directly. Swapping backends today means editing `MainWindow.axaml` and its code-behind. That is a small job, but do not plan around a seam that is currently only a file. -One more reason the Linux picture may be better than this entry assumes: the package also ships -`NativeWebViewCompositorHost`, a non-windowed host drawn through Avalonia's compositor. A compositor host -would not have the airspace problem described below at all. Whether it can be selected deliberately is -unknown and worth establishing during the spike, because it would change how overlays can be built. - **`Avalonia.Diagnostics` has no 12.x release** (latest is 11.3.18), so the developer tools overlay is unavailable on Avalonia 12. Development-only, so nothing ships differently — but debugging a layout problem currently means reasoning rather than inspecting. diff --git a/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs b/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs index 7922dc6..08aa512 100644 --- a/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs +++ b/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Platform; using Avalonia.Threading; using DodoSSH.Client.Shell.ViewModels; @@ -38,6 +39,9 @@ internal sealed partial class MainWindow : Window DataContextChanged += (_, _) => Attach(DataContext as MainWindowViewModel); + // Before anything navigates: the environment is settled once, when the adapter is built. + Terminal.EnvironmentRequested += OnTerminalEnvironmentRequested; + Terminal.WebMessageReceived += (_, e) => { // Compared against a constant rather than parsed: the page sends exactly one message and @@ -50,6 +54,38 @@ internal sealed partial class MainWindow : Window }; } + /// + /// Asks the Linux backend for the one mode it can actually draw inside this window. + /// + /// + /// + /// Without this the terminal is blank on Linux, and blank in the most confusing way available: + /// the page loads, scripts run, the renderer connects — everything except pixels. Measured on Fedora 44 + /// with Avalonia.Controls.WebView 12.0.1, where the backend is WebKitGTK 2.52.5 (WPE, the backend the + /// package's Linux notes describe, is not packaged for Fedora at all). In its default mode that adapter + /// reports SupportedScenarios = NativeDialog — it can open a window of its own and nothing else, + /// so a control asked to host it in place has nothing to show. Setting ExperimentalOffscreen + /// changes the same adapter's answer to OffscreenRenderer, which is the mode Avalonia's + /// compositor can draw. + /// + /// + /// Windows and macOS are untouched, and by construction rather than by an OS check: the argument is a + /// GTK type there and this method does nothing. WebView2 and WKWebView both host in place already. + /// + /// + /// Experimental is the vendor's word and worth repeating. If a future release makes the GTK + /// adapter host in place properly, this becomes unnecessary rather than wrong — and if the flag is + /// withdrawn, the terminal goes back to being blank on Linux, which is the thing to check first. + /// + /// + private static void OnTerminalEnvironmentRequested(object? sender, EventArgs e) + { + if (e is GtkWebViewEnvironmentRequestedEventArgs gtk) + { + gtk.ExperimentalOffscreen = true; + } + } + /// /// Where the keyboard belongs when the terminal is not holding it. ///