Fix missing NUL terminator in X11 WM_CLASS (#62848)

## Objective

GPUI's X11 `WM_CLASS` property omits its final NUL terminator, so any
window
manager that relies on the terminator reads the class one byte short.

`WM_CLASS` is specified by [ICCCM
§4.1.2.5](https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.2.5)
as two consecutive NUL-**terminated** strings, `instance\0class\0`.
`set_app_id` writes the separator but not the final terminator:

```rust
let mut data = Vec::with_capacity(app_id.len() * 2 + 1);
data.extend(app_id.bytes()); // instance
data.push(b'\0');
data.extend(app_id.bytes()); // class   <- no trailing NUL
```

This affects Zed itself. `ReleaseChannel::app_id()` returns
`dev.zed.Zed` on
stable, so under X11 and XWayland the class is read as `dev.zed.Ze`. The
shipped
desktop file is `dev.zed.Zed.desktop`, so the association it depends on
is
exactly the string being truncated, and window-manager rules or taskbar
grouping
matching `dev.zed.Zed` silently fail to apply.

Wayland is unaffected, since the app id is set through `xdg_toplevel`
rather
than an X11 property.

## Solution

Append the missing terminator after the class string, and size the `Vec`
capacity to match (`* 2 + 2` rather than `* 2 + 1`).

## Testing

Tested manually on Arch Linux with Hyprland v0.56.2 (wlroots) via
XWayland.

**Reproducing the bug** — Zed 1.14.2, before the fix:

```
$ env -u WAYLAND_DISPLAY zeditor some-dir
$ hyprctl clients -j | jq '.[] | select(.xwayland) | .class'
"dev.zed.Zed\u0000dev.zed.Ze"
```

The same window read with `xprop`, which does not require the
terminator, shows
the intended value — confirming the property itself is malformed rather
than the
compositor misreading it:

```
$ xprop -id 0x800001 WM_CLASS
WM_CLASS(STRING) = "dev.zed.Zed", "dev.zed.Zed"
```

**Control** — on the same compositor and the same XWayland path,
Alacritty
(winit, which writes the trailing NUL) reports its class intact:

```
$ hyprctl clients -j | jq '.[] | select(.xwayland) | .class'
"Alacritty"
```

The same truncation was reproduced independently with a third-party GPUI
application using `app_id: Some("sprite")`, which reports
`sprite\u0000sprit`.

**Platforms:** verified on Linux/XWayland only. Wayland does not use
this code
path. macOS and Windows have their own `set_app_id` implementations and
are
unaffected by this change.

**Needs more testing:** confirmation on a bare X11 session (not
XWayland) and on
a non-wlroots window manager such as i3, KWin, or Mutter would be
welcome — the
malformed property is compositor-independent, but which readers visibly
truncate
it is not.

## Self-Review Checklist

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — *none added*
- [x] The content adheres to Zed's UI standards — *N/A, no UI change*
- [ ] Tests cover the new/changed behavior — *see note below*
- [x] Performance impact has been considered and is acceptable — *one
additional
      byte in a property set once per window*
      
"No automated test: asserting the property bytes requires a live X
server. Happy to add one if there's an existing pattern for X11 window
tests I've missed."

Co-authored-by: Lukas Wirth <lukas@zed.dev>
5dd0666dfbdavid lee committed on 8/26/2026, 8:15:17 AM· committed by GitHubparent081a45f
1 file changedLine totals unavailable