fix(vdisplay): compositor availability follows the live session, not the env
GNOME/Mutter reported "Unavailable" on a host sitting in a live Mutter session — and, on the same request, "Default", because the two columns had different sources. available() asked each backend, and those probes read the process env (XDG_CURRENT_DESKTOP for Mutter, WAYLAND_DISPLAY for KWin's registry handshake, SWAYSOCK for sway) — env a host started outside the session (systemd --user, a TTY, ssh) never inherited. It is only retargeted at the live session on the connect path, so the answer also flipped depending on whether anyone had connected yet. Both columns now come from the same /proc scan detect() already used: the live session's compositor is usable by definition, as is an explicit operator pin, and the per-backend probe stays as the fallback for backends that are not the live session (gamescope, which spawns its own). A live KWin without the zkde_screencast grant now surfaces as available and fails at create with that probe's precise message, which beats "no usable compositor" on a box visibly running KDE. Mutter's env sniff stays deliberately narrow — one var, not three. XDG_CURRENT_DESKTOP is the one apply_session_env owns end to end (written per connect, scrubbed when nothing is live); sniffing DESKTOP_SESSION alongside would resurrect the bug that scrub exists to prevent, where a stale value after a gnome-shell crash routes the next client into a dead session. Non-Linux hosts now report no compositors at all rather than five Linux backends flagged unavailable with no default — on Windows the pf-vdisplay driver is the only backend and vdisplay::open ignores the argument, so the old list read as broken detection instead of "not applicable here". The console says so explicitly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit eb7ba3d6177552f5c3ed6a15439404084869c636)
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
"compositor_available": "Verfügbar",
|
||||
"compositor_unavailable": "Nicht verfügbar",
|
||||
"compositor_default": "Standard",
|
||||
"compositor_none": "Dieser Host hat keine Compositor-Backends – die gibt es nur unter Linux. Virtuelle Displays laufen hier über den mitgelieferten pf-vdisplay-Treiber.",
|
||||
"host_gpus": "GPUs",
|
||||
"host_gpus_help": "Die GPU, auf der der Host aufnimmt und encodiert. Automatisch wählt die beste GPU; eine bevorzugte GPU bindet Aufnahme + Encoding an sie. Eine Änderung gilt ab der nächsten Sitzung.",
|
||||
"gpu_automatic": "Automatisch",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"compositor_available": "Available",
|
||||
"compositor_unavailable": "Unavailable",
|
||||
"compositor_default": "Default",
|
||||
"compositor_none": "This host has no compositor backends — they are a Linux concept. Virtual displays here run on the bundled pf-vdisplay driver.",
|
||||
"host_gpus": "GPUs",
|
||||
"host_gpus_help": "The GPU the host captures and encodes on. Automatic picks the best GPU; preferring one pins capture + encode to it. A change applies to the next session.",
|
||||
"gpu_automatic": "Automatic",
|
||||
|
||||
@@ -94,33 +94,41 @@ export const HostView: FC<{
|
||||
error={compositors.error}
|
||||
refetch={compositors.refetch}
|
||||
>
|
||||
<ul className="divide-y rounded-md border">
|
||||
{compositors.data?.map((c) => (
|
||||
<li
|
||||
key={c.id}
|
||||
className="flex items-center justify-between gap-4 px-4 py-3"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{c.label}</span>
|
||||
{c.default && (
|
||||
<Badge variant="secondary">
|
||||
{m.compositor_default()}
|
||||
</Badge>
|
||||
)}
|
||||
{/* Empty is a real answer, not a load failure: a Windows host drives the
|
||||
pf-vdisplay driver and has no compositor backends at all. */}
|
||||
{compositors.data?.length === 0 ? (
|
||||
<p className="rounded-md border p-4 text-sm text-muted-foreground">
|
||||
{m.compositor_none()}
|
||||
</p>
|
||||
) : (
|
||||
<ul className="divide-y rounded-md border">
|
||||
{compositors.data?.map((c) => (
|
||||
<li
|
||||
key={c.id}
|
||||
className="flex items-center justify-between gap-4 px-4 py-3"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{c.label}</span>
|
||||
{c.default && (
|
||||
<Badge variant="secondary">
|
||||
{m.compositor_default()}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<code className="text-xs text-muted-foreground">
|
||||
{c.id}
|
||||
</code>
|
||||
</div>
|
||||
<code className="text-xs text-muted-foreground">
|
||||
{c.id}
|
||||
</code>
|
||||
</div>
|
||||
<Badge variant={c.available ? "default" : "outline"}>
|
||||
{c.available
|
||||
? m.compositor_available()
|
||||
: m.compositor_unavailable()}
|
||||
</Badge>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Badge variant={c.available ? "default" : "outline"}>
|
||||
{c.available
|
||||
? m.compositor_available()
|
||||
: m.compositor_unavailable()}
|
||||
</Badge>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</QueryState>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -16,6 +16,11 @@ type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
/** A non-Linux host: compositor backends don't exist there, so the list is empty by design. */
|
||||
export const NoCompositors: Story = {
|
||||
args: { compositors: { data: [], isLoading: false, error: null } },
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
host: { data: undefined, isLoading: true, error: null },
|
||||
|
||||
Reference in New Issue
Block a user