ac94de78ab696fc897b82de467490c1a57e72a6d
89
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ad0d789038 |
gpui_linux: Prevent panic while reporting Wayland errors (#63159)
`wayland-backend` falls back to `eprintln!` when its optional `log` feature is disabled. If a Wayland connection fails while standard error is unavailable, that fallback panics while trying to report the original error. Closes ZED-8BW Enable the existing `log` feature on `gpui_linux`'s direct `wayland-backend` dependency. `gpui_linux` already depends on the `log` facade, so this introduces no new logging system; it routes backend errors through the configured logger and avoids the secondary standard-error panic. Testing performed: Release Notes: - Fixed a Linux crash when reporting a Wayland connection error while standard error is unavailable. |
||
|
|
16faa9aa2c |
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> |
||
|
|
8a6aaa73ef |
gpui: Support Windows Restart Manager shutdown (#62987)
Handle the `WM_QUERYENDSESSION` and `WM_ENDSESSION` messages, which are used by Windows when shutting down an application. For example, Restart Manager uses this mechanism to gracefully close applications (e.g. when installing an update to the application). Release Notes: - N/A |
||
|
|
9d3ee5bffd |
gpui_linux: Release X11 client state before close callback (#63089)
Release Notes: - Fixed double-lease panic on X11 when reacting to WM_DELETE_WINDOW event. |
||
|
|
bf560a4572 |
gpui: Make the Wayland render loop demand-driven (#60690)
Closes #55345. Wayland is the only platform whose frame ticks are conditional: a wl_surface frame callback only arrives after a commit the compositor goes on to repaint. GPUI assumed unconditional ticks (any previously our Wayland backend faked them), so an idle fullscreen window stopped receiving callbacks and froze until external damage arrived. This PR makes the Wayland render loop demand-driven, instead parking when there's nothing to draw, and stops Zed committing empty frames on every tick as the artificial heartbeat from the compositor. --- Release Notes: - Fixed the UI freezing in fullscreen on some Wayland compositors - Fixed idle windows waking at the display's refresh rate on Wayland --------- Co-authored-by: Philipp Schaffrath <philipp.schaffrath@gmail.com> Co-authored-by: Daan De Meyer <daan@amutable.com> Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com> |
||
|
|
d5ff068e91 |
gpui_linux: Drain buffered X11 events after foreground work (#62081)
# Objective Closes #52429 Closes #56735 Closes #62495 Closes #62878 Reverts #13071 This PR fixes Zed windows sometimes remaining blank when first opened on X11. The issue remained very inconsistent across WMs and conditions to reproduce and was finally consistently reproducable on my dwm build with no compositor and on i3 with no compositor after removing the workaround earlier added, which made it occur in i3 but not on my dwm build because it was more aggressive with it's EWMH policy. It happened only on opening a newer repo not opened the last time. While running foreground work, a synchronous X11 request can read events from the socket and place them in x11rb's internal event queue, calloop monitors the underlying socket rather than this internal queue, so if the socket is empty when the foreground work finishes, these events may remain unprocessed until unrelated X11 activity occurs. window is mapped -> events are buffered by x11rb -> X11 socket is no longer readable -> calloop does not wake -> MapNotify is not processed -> refresh loop does not start -> window remains blank This explains why the problem was inconsistent and differed between window managers. Later focus, scrolling (on any window), exposure, or other X11 activity could make the connection readable again and incidentally process the older events. My earlier PR #61162 fixed the related case where an already processed `Expose` event could wait indefinitely for a stopped refresh loop. It did not fix this initial-window case because the relevant events had not reached Zed's event handler yet, which was again not perfectly reproducable and lead me to assume was fixed. ## Solution Process any X11 events buffered by x11rb after each foreground runnable completes: foreground work completes -> buffered X11 events are processed -> MapNotify starts the refresh loop -> initial window contents are displayed This also removes the unconditional [`SetInputFocus`](https://tronche.com/gui/x/xlib/input/XSetInputFocus.html) added in #13071. That call frequently masked the issue by generating additional X11 activity, but also bypassed the window manager's focus policy despite Zed already sending the standard `_NET_ACTIVE_WINDOW` request. With the event queue handled correctly, Zed no longer needs to force focus to make the initial frame appear. ## Testing Repeatedly opened fresh and existing workspaces under dwm while another application retained focus with a new-project being opened everytime (at least not the previous one). Before this change, fresh windows could remain blank until later X11 activity occurred. After this change, they render immediately without focusing, scrolling, resizing, or switching workspaces. Also verified that window activation is left to the window manager through `_NET_ACTIVE_WINDOW`. ## Self-Review Checklist: * [x] I've reviewed my own diff for quality, security, and reliability * [x] Unsafe blocks (if any) have justifying comments * [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) * [ ] Tests cover the new/changed behavior * [x] Performance impact has been considered and is acceptable Release Notes: * Fixed Zed windows sometimes remaining blank when first opened on X11 * Fixed Zed overriding window-manager focus policies when activating windows |
||
|
|
255880598d |
Switch from cargo-machete to cargo-shear (#62643)
This results in ~110 crates being removed, and some orphaned files also. --- Release Notes: - N/A Signed-off-by: Neel <neel@zed.dev> |
||
|
|
0b5de324a4 |
gpui_linux: Clear X11 urgency hint when the window becomes active (#61619)
# Objective
On X11, GPUI raises the ICCCM `WM_HINTS` urgency flag to ask for
attention, but
it never clears it. The window stays urgent for the rest of its life.
`request_attention` was the only thing touching the bit. There was no
code
anywhere to clear it.
Docks and taskbars latch on the rising edge, so they show the attention
state
once and then sit there with a permanent urgent indicator, no further
notifications.
## Solution
Clear the flag when the window becomes active.
The read/modify/write moves out of `request_attention` into a helper,
`set_wm_hints_urgency(xcb, x_window, urgent)`. The raise and clear sites
are on
different self types (`X11Window` vs `X11WindowStatePtr`), so it takes
the
connection and window id.
The clear goes in `set_wm_properties`, where `state.active` gets set
from
`_NET_WM_STATE_FOCUSED`. That's the same field `request_attention`
already checks
in its `if self.is_active() { return; }` guard.
Two implementation notes:
* The clear is edge triggered (`state.active && !was_active`).
`set_wm_properties` recomputes everything on every `_NET_WM_STATE`
change, so a
level triggered check would do a blocking `WmHints::get` round trip on
every
maximize, fullscreen, hide and workspace switch while focused. Edge
triggering
is safe here because `request_attention` early returns when active, so
urgency
only ever gets raised while inactive, and the false to true transition
always
follows a raise.
* Clearing reads the hints first and skips the write if the bit isn't
set.
Otherwise focusing a window that never asked for attention would create
a
`WM_HINTS` property just to say "not urgent".
The raise path behaves exactly as before. The skip can't fire when
`urgent` is
true, so repeat raises still write every time.
Focusing clears the flag even if a second thread is still waiting.
That's already
how it works, since `request_attention` early returns when the window is
active.
## Testing
Tested on X11, Cinnamon with Muffin.
On an unpatched build the window had `_NET_WM_STATE_FOCUSED` and the
urgency bit
set at the same time.
W=$(xdotool search --class "dev.zed.Zed" | head -1)
xprop -id $W WM_HINTS
xprop -spy -id $W WM_HINTS
1. Fresh window says `WM_HINTS: not found`, and still does after
focusing it.
2. With focus on another window, the urgency bit shows up when attention
is
requested. `request_attention` early returns when active, so the window
has to
be unfocused for this to fire.
3. Focusing Zed clears the bit.
4. Repeating 2 and 3 re-arms it every cycle, not just once.
5. No `WM_HINTS` writes during normal use (resize, maximize, workspace
switch).
No automated test. It's an X11 round trip on a real window and there's
no X11
test harness in the tree.
## 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 (no UI change)
- [x] Tests cover the new/changed behavior (see Testing)
- [x] Performance impact has been considered and is acceptable
Note this was manually tested, there wasn't a good way to test it
through the current harness.
---
Release Notes:
- Fixed the X11 urgency hint never being cleared, which left Zed showing
a permanent attention indicator in taskbars and docks after the first
notification
|
||
|
|
d7f85eed4e |
Preserve --user-data-dir across restarts (#62022)
# Objective Fixes #57701. When Zed is launched with `--user-data-dir`, a Zed-initiated restart—such as `workspace: clear trusted worktrees` or Restart to Update—can relaunch Zed without that argument. The restarted process then uses the default data directory instead of the directory selected by the user. ## Solution - Store the resolved custom data directory as GPUI restart arguments alongside the existing optional restart executable path. - Forward those arguments when relaunching on Linux, macOS, and Windows. - Preserve argument boundaries and native `OsString` values, including paths containing spaces. - On Windows updates, carry the arguments through `auto_update_helper.exe` when it launches the updated `Zed.exe`. Normal launches without `--user-data-dir` continue to restart without additional arguments. This does not attempt to preserve unrelated CLI arguments or general launch state. ## Testing - `cargo fmt --all -- --check` - `cargo test -p gpui` - `cargo test -p auto_update --features workspace/test-support` - `cargo check -p auto_update_helper --tests --target x86_64-pc-windows-msvc` - `./script/clippy -p gpui -p gpui_macos -p auto_update -p zed` - `./script/clippy -p auto_update_helper --target x86_64-pc-windows-msvc` The regression tests cover simultaneous restart executable and argument forwarding, Windows paths containing spaces and trailing backslashes, updater-helper argument parsing, and the final `Zed.exe` command. Compilation and unit tests were run on macOS. Linux and Windows runtime behavior was reviewed but not run locally. A full `gpui_windows` cross-target check was attempted, but its `psm` dependency requires the MSVC `lib.exe` tool, which is not available on this macOS host; the standalone Windows updater helper and its tests compile successfully for the Windows target. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards (no UI changes) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed Zed forgetting an explicitly passed `--user-data-dir` argument after restarting (e.g. clicking "Restart to Update") --------- Co-authored-by: Tom Houlé <tom@tomhoule.com> |
||
|
|
be3db5feff |
gpui_linux: Handle XKB context initialization failure (#62868)
Release Notes: - Added check for XKB context initialization failure |
||
|
|
22a3412edf |
Prevent inactive windows from updating the IME position on linux/Wayland (#62086)
# Objective Fixes #62084 Fix the Wayland IME candidate window flickering between two positions when an inactive Zed window has a terminal producing continuous output. Terminal wakeup events in the inactive window can trigger IME position updates, causing its cursor bounds to alternate with those from the active window. https://github.com/zed-industries/zed/blob/db73efafe4240e69f80eca2857758eaf8151a0b1/crates/terminal_view/src/terminal_view.rs#L1130-L1136 https://github.com/zed-industries/zed/blob/db73efafe4240e69f80eca2857758eaf8151a0b1/crates/gpui/src/window.rs#L5617-L5627 ## Solution Ignore IME position updates from inactive Wayland windows. This ensures that only the active window can update the cursor rectangle of the shared Wayland text-input object. ## Testing To test manually on Linux with Wayland and Fcitx5: 1. Open two Zed windows. 2. Start continuous terminal output in the first window. 3. Use the Chinese IME in an editor in the second window. 4. Verify that the IME candidate window remains at a stable position. ## Self-Review Checklist: - [X] I've reviewed my own diff for quality, security, and reliability - [X] Unsafe blocks (if any) have justifying comments - [X] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uxui-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [X] Performance impact has been considered and is acceptable ## Showcase after fix: [2026-08-02 22-43-35.webm](https://github.com/user-attachments/assets/ff2e3bc7-7772-481e-82f8-92e6e3fa7275) --- Release Notes: - Fixed the IME candidate window flickering when another Zed window has continuous terminal output |
||
|
|
1ce15ce1fe |
gpui_linux: Add wl_data_source support for outbound drags on Wayland (#61947)
# Objective - Enable drag'n'drop from Zed in Wayland. - Partially addresses #13186 - Related to #58161, #61940 ## Solution - Implement wl_data_source support for outbound drags on Wayland ## Testing Tested on KDE Wayland, kwin 6.7.3 I dragged files to Firefox, to other Zed windows and into Dolphin. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase Can try to record if requested Release Notes: - Added support for dragging files from the Project Panel to external Linux Wayland apps. |
||
|
|
922d2dd05c |
x11: Request repaint after exposure instead of within blocked loop (#61162)
# Objective Closes #61054 This PR fixes Zed window not repainting after they are uncovered on X11. When an X11 window becomes visible after being covered, X11 sends an `Expose` event to tell the application that the window contents need to be presented again. Previously, Zed stored this event and waited for the periodic refresh loop to request a presentation but, the refresh loop is stopped while a window is fully hidden to avoid unnecessary rendering. This meant there might be no refresh tick to handle the pending exposure. window is fully hidden -> periodic refresh loop stops window is uncovered -> expose event -> waits for refresh loop -> window may remain blank This change requests one presentation directly after processing the pending X11 events. Multiple `Expose` events for the same window are coalesced, and unmapped windows are ignored. ## Solution window is uncovered -> expose event -> presentation is requested -> window contents are displayed Required presentations are also excluded from inactive-window frame throttling, so the request is not dropped when Zed is unfocused. Regular rendering for unfocused or fully obscured windows remains throttled. ## Testing Check the issue being closed on steps to reproduction. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed Zed windows sometimes not repainting after being uncovered on X11 |
||
|
|
d5a4de9392 |
gpui_linux: Fix Wayland serial token tracking (#61454)
Closes #58651 Closes FR-123 https://github.com/zed-industries/zed/pull/50406/ while fixing one issue regressed serial tracking in Wayland. What happens is we conflated unrelated serials into a common value which is wrong. For example, `InputMethod` serial does not come from the same source as `MousePress` or `KeyPress`, and if Zed is running for a while and the user performs lots of IME commits (`zwp_text_input_v3.commit`), `InputMethod` will overtake other serial values and will be incorrectly used for clipboard authorisation/selection by the compositor such as Mutter or kWin. This will effectively poison the clipboard making Zed the indefinite owner until we kill Zed. More concretely, we use `get_latest()` on the serial tracker to extract *the largest serial REGARDLESS of kind* which is wrong since not every serial kind comes from the same pool. Release Notes: - Fixed copy-paste behaviour in Wayland. |
||
|
|
484ae39d7c |
zed: Unify dependencies (#61381)
# Objective Unify dependencies inside of the root `Cargo.toml` to prevent versions from going out of sync and creating copies of dependencies. ## Testing - All tests pass, nothing touches the `Cargo.lock` ## Review Review is easiest by going through commit by commit. Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
cd91c84675 |
gpui: Add system notification platform APIs (#61189)
Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: John Tur <john-tur@outlook.com> |
||
|
|
9948409b40 |
gpui: Unify raw-window-handle dependency (#61193)
Just something I noticed while skimming through dependencies Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
6c4862e1f2 |
gpui_linux: Fix unbounded memory growth when composing text with Fcitx5 under KWin/Wayland (#61079)
# Objective Close #61034. Fix unbounded memory growth when composing text with Fcitx5 under KWin/Wayland. While a non-empty preedit is cached, KWin resends it whenever it receives a text-input-v3 `commit()` ([relevant KWin code](https://github.com/KDE/kwin/blob/0e473e87a64d5d404c2fecf5516a9a5c3cf17d91/src/wayland/textinput_v3.cpp#L444-L504)). Zed handles the replayed preedit as a buffer edit. This invalidates the IME character coordinates, causing Zed to submit the same cursor rectangle and call `commit()` on the next frame. That commit makes KWin replay the cached preedit again, forming an infinite feedback loop. The observed memory growth is caused by retained CRDT state accumulating indefinitely. ## Solution Following [SDL's solution to the same problem](https://github.com/libsdl-org/SDL/commit/81479d8784d0e1a61eafb3a444dd3865ad762fb2), cache the last submitted cursor rectangle and compare it with the new rectangle before calling `set_cursor_rectangle()`. If the rectangles are equal, skip both the rectangle update and its associated `commit()`. The initial cursor rectangle and actual rectangle changes during an active composition are still submitted, preserving the candidate-window positioning behavior introduced by #59911. ## Testing Manually tested on KDE Plasma with `fcitx5-chinese-addons`. Before the fix, memory usage grew continuously while preedit text was present, with `WAYLAND_DEBUG=client` showing text-input events repeating indefinitely. After the fix, the repeated events stopped and memory usage remained stable. No automated test was added because reproducing the feedback loop requires compositor-specific KWin text-input-v3 behavior. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed excessive memory usage when composing text with Fcitx5 on KDE Wayland. --------- Co-authored-by: Tom Houlé <tom@tomhoule.com> |
||
|
|
e963df8029 |
gpui: Linux wayland set exclusive zone and edge (#60163)
# Objective The exclusive zone and exclusive edge of a wlr-layer-shell surface could only be set once, at creation, through `LayerShellOptions`. This adds runtime setters so a live layer-shell window such as a panel can update them without being recreated. ## Solution Add two methods on `Window`: - `set_exclusive_zone` updates how much screen space the surface reserves. A positive value reserves that distance from the anchored edge, 0 lets the surface be moved out of others' exclusive zones, and -1 ignores reserved space and may extend under other surfaces. - `set_exclusive_edge` chooses which anchored edge the exclusive zone applies to, which is needed to disambiguate a corner-anchored surface. Setting an exclusive edge the surface is not anchored to is a fatal protocol error, so the edge is validated (it must be a single edge that the surface anchor contains) and an invalid edge is logged and ignored. The same validation now also guards the creation path. Both setters only commit the surface when a change actually applies, and are no-ops on non-layer-shell windows and on other platforms. This is where these 2 are documented: https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_exclusive_zone https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_exclusive_edge ## Testing - Verified with a small local layer-shell window (a top bar anchored TOP, LEFT, RIGHT) with buttons that call the setters at runtime - No automated test was added, since this calls through to the compositor. - Reviewers without a layer-shell capable compositor (for example GNOME/Mutter) cannot exercise this, as `zwlr_layer_shell_v1` is unavailable there, I tested this on the [Smithay](https://github.com/Smithay/smithay/) based compositor [niri](https://github.com/niri-wm/niri) ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - N/A |
||
|
|
9a7d33a52f |
gpui_linux: split xkbcommon wayland/x11 features (#60834)
# Objective When xkbcommon compiled with either wayland or x11 only, the current manifests will prevent it from compiling, despite gpui is already wayland/x11 seperated. close #60943 ## Solution Simple changes to manifests. ## Testing ``` cargo tree -p gpui_platform --no-default-features --features {wayland,x11} -e features -i xkbcommon ``` ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - N/A Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
a932df5550 |
gpui: Implement window attention (#58779)
Release Notes: - On agent notifications, the corresponding zed window will now mark itself for OS level attention --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
756116c2b0 |
Fix "Task polled after completion" panic (#60693)
Dropping a scheduled runnable cancels its task and makes the next poll of any awaiter panic with "Task polled after completion." The only paths where we drop these runnables seem to be during shutdown or extreme resource exhaustion, so, let's leak the runnables instead of crashing. On Windows, we also moved to calling the Win32 thread pool API directly, because 1) WinRT thread pool API is just a wrapper that adds overhead we don't need, and 2) the closure we pass to the `WorkItemHandler` object takes ownership of the runnable object, so if the WinRT thread pool releases the delegate, it can free the runnable without our control. Release Notes: - N/A |
||
|
|
4f6d05dde0 |
Update wayland-backend to fix Wayland file dialog crash (#60621)
On Wayland, closing a window in the brief gap between requesting a portal file dialog and ashpd exporting the window's surface (used to parent the dialog) crashed Zed with `Unknown opcode 0 for object <anonymous>@0` ([ZED-9KB](https://zed-dev.sentry.io/issues/7568720776/)). When the export request fails because the surface is already dead, wayland-scanner's generated code silently returns an inert proxy, and ashpd's `Drop` impl later sends `destroy` on it — which wayland-backend 0.3.11 answers with a panic, because it looks up the request opcode before checking whether the object is null. wayland-backend 0.3.15 fixes this ([Smithay/wayland-rs#890](https://github.com/Smithay/wayland-rs/issues/890)) by returning an error instead, which the generated destructor discards, so the drop becomes a harmless no-op. This bumps the lockfile to 0.3.15 and raises the version floor in `gpui_linux` so the fix can't silently regress via a fresh lockfile. Closes FR-100 Release Notes: - Fixed a crash on Linux (Wayland) when a window was closed just as a file dialog was being opened. |
||
|
|
01d5096a3f |
gpui: Fix hover state not clearing when mouse leaves window (#60275)
# Objective Fix two gaps in element hover tracking at window boundaries. Hover was only re-evaluated on `MouseMove`, so when the pointer left the window no event fired `on_hover(false)` and the element stayed hovered. Symmetrically on Wayland, no `Motion` follows `Enter` until the pointer moves again, so hover was not established at the entry pixel. Both cases are easy to miss since most hover-styled elements don't sit flush against the window edge, but they surfaced while implementing layer_shell popups with input_regions, which should close when stop hovering. ## Solution The hover compare-and-fire logic in `div` is refactored into a shared `update_hover` closure, and a second listener on `MouseExitEvent` clears hover when the pointer leaves the window. It clears unconditionally because `MouseExited` doesn't update the tracked mouse position, so a hit test during that dispatch would still report the element as hovered. On Wayland, a `MouseMove` is synthesized at the entry position on `wl_pointer.enter`, mirroring the `MouseExited` already dispatched on `Leave`. ## Testing Tested manually on Wayland/Linux: hover on a window-edge element clears when the pointer leaves the window, and hover is established immediately when the pointer enters a surface with an element under the entry pixel. Not tested on other platforms. The `div` change relies on each platform's existing `MouseExited` dispatch: macOS and X11 emit it, so they get the exit fix too. Windows never dispatches `MouseExited` (`WM_MOUSELEAVE` only flips the window-level hover flag), so the stuck-hover case might remain there, unchanged from before. Before: https://github.com/user-attachments/assets/6af83bb3-de9d-40e2-a64c-bdefc98fc96d After: https://github.com/user-attachments/assets/721a9b5c-108a-499f-867e-da111836a34a Here is an example application to test this: ```rs #![cfg_attr(target_family = "wasm", no_main)] use gpui::{ App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size, }; use gpui_platform::application; struct HoverExit { hovered: bool, } impl Render for HoverExit { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { // Fills the whole window so its edge is the window edge: moving the mouse // out of the window is what exercises the MouseExited path. div() .id("hover-exit") .size_full() .flex() .justify_center() .items_center() .text_xl() .text_color(rgb(0xffffff)) .bg(if self.hovered { rgb(0x585f58) } else { rgb(0x505050) }) .child(if self.hovered { "HOVERED" } else { "not hovered" }) .on_hover(cx.listener(|this, hovered, _, cx| { this.hovered = *hovered; cx.notify(); })) } } fn run_example() { application().run(|cx: &mut App| { let bounds = Bounds::centered(None, size(px(240.), px(160.0)), cx); cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), app_id: Some("gpui-hover-exit".to_string()), ..Default::default() }, |_, cx| cx.new(|_| HoverExit { hovered: false }), ) .unwrap(); cx.activate(true); }); } #[cfg(not(target_family = "wasm"))] fn main() { run_example(); } #[cfg(target_family = "wasm")] #[wasm_bindgen::prelude::wasm_bindgen(start)] pub fn start() { gpui_platform::web_init(); run_example(); } ``` ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed element hover state not clearing when the mouse leaves the window |
||
|
|
636e764737 |
gpui: Add parent-anchored native popup windows (with wayland xdg_popup implementation only so far) (#60232)
# Objective gpui can't show UI that extends past the window it belongs to. Menus, dropdowns and tooltips are drawn as elements inside the window, so they clip at its edges. This PR adds a window kind for platform-native popups anchored to a parent window, as groundwork for real native menus, dropdowns and tooltips. ## Solution `WindowKind::AnchoredPopup(PopupOptions)` opens a popup positioned relative to a parent window. Instead of giving the popup an absolute position, you describe where it should go and the platform figures out the rest: - `parent`: the window to anchor to - `anchor_rect`: a rectangle in the parent, e.g. the button that opened the menu - `anchor` and `gravity`: which point of that rect to attach to, and which direction to grow - `constraint_adjustment`: what the platform may do if the popup would leave the screen (slide, flip, resize) - `grab`: menu behavior, the popup takes focus and is dismissed when clicking outside the app The popup's size comes from `WindowOptions::window_bounds`. This model mirrors Wayland's `xdg_positioner`, where the compositor owns positioning and the client can only describe intent. Since that's the most restrictive case, the other platforms can implement the same description later with simple math against screen bounds. Only Wayland is implemented so far, via `xdg_popup` on top of the existing surface implementation. Popups can be parented to toplevels, layer-shell surfaces (a menu opened from a panel) and other popups (nested menus). macOS, Windows, X11 and web reject the kind with `PopupNotSupportedError`, so callers can detect that and fall back to in-window popovers. Some Wayland details that might help during review: - Anchor rects are translated from gpui coordinates into the parent's window geometry space and clamped to it. A rect outside the geometry, or with zero size, is a fatal protocol error - Resizing a mapped popup goes through `xdg_popup.reposition` - Mouse press serials are now recorded on press only, not release. Compositors decline grabs and interactive moves that reference a release serial ## Testing Tested manually on Wayland with an example app: the menu opens anchored below its button, extends past the parent window, flips above the button near the bottom of the screen, and a grabbing popup is dismissed when clicking into another application. Nested menus were tested in one of my projects (ignore that they are ugly, that's just a prototype 😛): https://github.com/user-attachments/assets/2cd3e2e9-87f7-4b02-986f-48e5633e205c I also have a complete runnable example demonstrating it. I did not add it to the PR, because this might give the impression that `WindowKind::AnchoredPopup` are a complete implementation, despite only working on wayland so far: <details> <summary>Click to view example</summary> ```rust //! Example and manual test for platform-native popups (`WindowKind::AnchoredPopup`). //! //! A native popup is a real, parent-anchored window that can extend beyond its parent onto the //! screen, unlike gpui's in-window popovers. Run it, open the menu, and confirm the points listed //! in the window. On a platform without an implementation the button reports that popups are not //! supported instead of opening anything. //! //! Run with: cargo run -p gpui --example popup #![cfg_attr(target_family = "wasm", no_main)] use gpui::{ AnyWindowHandle, App, Bounds, Context, MouseButton, SharedString, Window, WindowBounds, WindowHandle, WindowKind, WindowOptions, div, point, popup::*, prelude::*, px, rgb, size, }; use gpui_platform::application; /// The trigger button, at a fixed position so the popup can anchor to a known rectangle. Real code /// would anchor to the measured bounds of whatever element opens the popup. const BUTTON_BOUNDS: Bounds<gpui::Pixels> = Bounds { origin: point(px(24.), px(24.)), size: size(px(200.), px(32.)), }; const POPUP_SIZE: gpui::Size<gpui::Pixels> = size(px(260.), px(320.)); struct Menu; impl Render for Menu { fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { let item = |label: &str| { div() .id(label.to_string()) .px_3() .py_1() .rounded_sm() .hover(|this| this.bg(rgb(0x3a3a3a))) .cursor_pointer() .child(label.to_string()) .on_click(|_, window, _| window.remove_window()) }; div() .id("menu-root") .size_full() .p_1() .flex() .flex_col() .gap_0p5() .bg(rgb(0x2a2a2a)) .text_color(gpui::white()) .rounded_md() .border_1() .border_color(rgb(0x454545)) .child(item("Foo")) .child(item("Bar")) .child(item("Baz")) .child(item("Qux")) .child(item("Alice")) .child(item("Bob")) } } struct PopupExample { menu: Option<WindowHandle<Menu>>, status: SharedString, } impl Default for PopupExample { fn default() -> Self { Self { menu: None, status: "Click \"Open menu\" to open a native popup.".into(), } } } impl PopupExample { /// Closes the menu if it is open. Returns true if a menu was actually open. fn close_menu(&mut self, cx: &mut App) -> bool { match self.menu.take() { Some(menu) => menu .update(cx, |_, window, _| window.remove_window()) .is_ok(), None => false, } } fn toggle_menu(&mut self, parent: AnyWindowHandle, cx: &mut App) { if self.close_menu(cx) { return; } match open_menu(parent, cx) { Ok(menu) => { self.menu = Some(menu); self.status = "Menu open. Dismiss it by selecting an item, clicking elsewhere in \ this window, or clicking another application." .into(); } // A real application would fall back to an in-window popover here. Err(error) => { self.status = format!("Failed to open a native popup: {error}").into(); log::error!("failed to open popup: {error}"); } } } } impl Render for PopupExample { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let bullet = |text: &str| div().child(format!("• {text}")); div() .id("root") .size_full() .bg(rgb(0xf7f7f7)) .text_color(rgb(0x222222)) // Same-app clicks don't auto-dismiss a grabbing popup (see `PopupOptions::grab`). .on_mouse_down( MouseButton::Left, cx.listener(|this, _, _window, cx| { this.close_menu(cx); }), ) .child( div() .size_full() .p_5() .pt(px(76.)) .flex() .flex_col() .gap_3() .child(div().text_xl().child("Native popup test")) .child(div().text_sm().child( "WindowKind::AnchoredPopup opens a real, parent-anchored window that can \ extend past this window onto the screen. Only some platforms implement \ it so far.", )) .child( div() .flex() .flex_col() .gap_1() .text_sm() .text_color(rgb(0x555555)) .child(div().child("Verify:")) .child(bullet("The menu opens anchored below the button.")) .child(bullet( "The menu extends past the bottom edge of this window.", )) .child(bullet( "Near the bottom of the screen, the menu flips above the button.", )) .child(bullet("Clicking another application dismisses the menu.")) .child(bullet( "Selecting an item or clicking in this window dismisses it.", )), ) .child( div() .text_sm() .text_color(rgb(0x333333)) .child(self.status.clone()), ), ) .child( div() .absolute() .left(BUTTON_BOUNDS.origin.x) .top(BUTTON_BOUNDS.origin.y) .w(BUTTON_BOUNDS.size.width) .h(BUTTON_BOUNDS.size.height) .flex() .items_center() .justify_center() .bg(rgb(0xffffff)) .border_1() .border_color(rgb(0xd0d0d0)) .rounded_md() .cursor_pointer() .id("open-menu") .active(|this| this.bg(rgb(0xeeeeee))) .child("Open menu ▾") // Open on mouse-down, not on click, so the grab is taken while the button is still held. .on_mouse_down( MouseButton::Left, cx.listener(|this, _, window, cx| { // Don't let the window handler above close the menu we are opening. cx.stop_propagation(); this.toggle_menu(window.window_handle(), cx); }), ), ) } } fn open_menu(parent: AnyWindowHandle, cx: &mut App) -> anyhow::Result<WindowHandle<Menu>> { cx.open_window( WindowOptions { titlebar: None, // Sizes the popup. The platform decides the position, so the origin is ignored. window_bounds: Some(WindowBounds::Windowed(Bounds { origin: point(px(0.), px(0.)), size: POPUP_SIZE, })), kind: WindowKind::AnchoredPopup(PopupOptions { parent, anchor_rect: BUTTON_BOUNDS, // Anchor to the button's bottom-left and grow down-right so the menu drops beneath it. anchor: PopupAnchor::BottomLeft, gravity: PopupGravity::BottomRight, // Slide horizontally and flip vertically if the menu would leave the screen. constraint_adjustment: PopupConstraintAdjustment::SLIDE_X | PopupConstraintAdjustment::FLIP_Y, offset: point(px(0.), px(4.)), // Grab input so the compositor dismisses the popup on clicks into other applications. grab: true, }), ..Default::default() }, |_, cx| cx.new(|_| Menu), ) } fn run_example() { application().run(|cx: &mut App| { cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(Bounds { origin: point(px(100.), px(100.)), size: size(px(420.), px(300.)), })), ..Default::default() }, |_, cx| cx.new(|_| PopupExample::default()), ) .unwrap(); cx.activate(true); }); } #[cfg(not(target_family = "wasm"))] fn main() { run_example(); } #[cfg(target_family = "wasm")] #[wasm_bindgen::prelude::wasm_bindgen(start)] pub fn start() { gpui_platform::web_init(); run_example(); } ``` </details> ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - N/A |
||
|
|
3699843254 |
gpui: Refresh mouse position after bounds changes (#60421)
## Summary - Refresh GPUI's cached mouse position when window bounds change so hover hit-testing uses the current cursor position after live resize. - Return X11 mouse positions in window-relative logical pixels to keep `PlatformWindow::mouse_position()` consistent with other backends. Fixes #57354 ## Testing - `cargo fmt -p gpui -p gpui_linux` - `cargo check -p gpui_linux` - `cargo check -p gpui` ## Suggested .rules additions - In GPUI platform backends, `PlatformWindow::mouse_position()` should return window-relative logical pixels; use separate APIs or fields for global/device-pixel coordinates. Release Notes: - Fixed incorrect hover state while resizing GPUI windows. |
||
|
|
b992659378 |
gpui: Add input region support for Wayland windows (#60161)
# Objective Wayland windows have no way to restrict which parts of the surface accept pointer and touch input. This adds support for setting an input region, so events outside it pass through to whatever is below the window. This is useful for shaped or partially click-through windows. Clicks in green area can pass through the window, clicks in red area do not: <img width="1057" height="395" alt="image" src="https://github.com/user-attachments/assets/2039af62-e43b-4834-b877-edad2a8f5ccf" /> ## Solution Add `Window::set_input_region`, which takes `Option<&[Bounds<Pixels>]>`: - `Some(rects)` restricts pointer and touch input to the union of the rectangles, in window coordinates. - `Some(&[])` is an empty region, so the window receives no input at all and is fully click-through. - `None` resets the region to the default, so the whole window receives input again. On Wayland this maps to `wl_surface.set_input_region`, building a `wl_region` from the rectangles or clearing it for `None`, and commits so the change applies immediately rather than waiting for the next frame. The method is a no-op on other platforms. ## Testing Tested on Linux with Wayland. - Tested in my own GPUI application, which uses a fullscreen layer for overlays while allowing clicks outside of the rendered elements to be passed through to the underlying windows. - No automated test was added, since this calls through to the compositor and is checked by observing input routing. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - N/A |
||
|
|
885d599cc1 |
terminal: Fix IME candidate window not following cursor in TUI apps (#59911)
# Objective Fix IME candidate window not following cursor position in the integrated terminal when running fullscreen TUI applications like opencode. When using IME (Input Method Editor) in Zed's terminal with fullscreen TUI applications (e.g., opencode), the candidate window does not follow the cursor position. This issue does not occur in normal terminal usage (e.g., bash shell). # Solution The root cause was three-layer blocking preventing IME position updates in ALT_SCREEN mode: 1. **ALT_SCREEN blocking**: `selected_text_range()` returned `None` in ALT_SCREEN mode, causing `selected_bounds()` to return `None` 2. **Missing trigger**: `Event::Wakeup` did not call `invalidate_character_coordinates()`, so cursor movement did not trigger IME position updates 3. **Composition blocking**: `update_ime_position()` skipped updates when `state.composing` was true Fixes: - Remove ALT_SCREEN check in `selected_text_range()` so IME position updates work in fullscreen TUI apps - Add `window.invalidate_character_coordinates()` in `Event::Wakeup` to trigger IME position updates when terminal cursor moves - Remove `state.composing` check in `update_ime_position()` to allow IME position updates during text-input-v3 composition - Remove unused `terminal` field from `TerminalInputHandler` struct # Testing **Did you test these changes? If so, how?** - Yes, tested on Linux GNOME Wayland with iBus input method - Verified IME candidate window correctly follows cursor in opencode - Verified normal terminal usage with IME still works correctly **Are there any parts that need more testing?** - Other IMEs (fcitx, etc.) may need testing **How can other people (reviewers) test your changes?** 1. Open Zed's integrated terminal 2. Run a fullscreen TUI application like `opencode` 3. Activate IME (e.g., iBus with Chinese input) 4. Type text and observe the IME candidate window follows the cursor **What platforms did you test these changes on?** - Linux (GNOME Wayland) - tested - macOS - not tested (may have different IME behavior) - Windows - not tested (different code path) # Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards (UX/UI and icon guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable # Showcase <details> <summary>Before</summary> <video src="https://github.com/user-attachments/assets/ee2fac4e-801b-49af-a57e-32ce25e01db5" width="320" height="180" /> </details> <details> <summary>After</summary> <video src="https://github.com/user-attachments/assets/c669ea99-2ffc-4179-b587-a47873e33e70" width="320" height="180" /> </details> --- Release Notes: - Fixed IME candidate window not following cursor in terminal TUI apps |
||
|
|
2fff6597d6 |
gpui_linux: Add support for open_window in headless client (#60359)
This actually makes it possible for Linux headless client to have working windows for computation (well, we are in headless mode after all). This also matches macOS headless client behaviour. Release Notes: - N/A |
||
|
|
8f6e462a9b |
gpui_linux: Consume Wayland startup activation token (#59995)
When opening Zed from GNOME on Wayland, the cursor can stay stuck in the launch spinner state for several seconds because GPUI never tells the compositor that the launched app's first window has loaded. Desktop launchers pass this information through `XDG_ACTIVATION_TOKEN`, which Wayland clients are expected to consume once their first toplevel surface is ready. This PR fixes that by reading and removing `XDG_ACTIVATION_TOKEN` during Wayland client startup, storing it on the client state, and consuming it on the first XDG toplevel surface via the existing `xdg_activation_v1.activate(token, surface)` plumbing. Anthony manually verified this on GNOME Wayland. docs ref: https://wayland.app/protocols/xdg-activation-v1 Release Notes: - Fixed Linux Wayland cursor being stuck as a spinner for a couple of seconds. |
||
|
|
2ef7f39587 |
gpui_linux: Suppress dead code warning on wake_sender (#60090)
This PR adds a conditional `allow(dead_code)` to the `wake_sender` field
to fix this error when building Collab:
```
error: field `wake_sender` is never read
--> crates/gpui_linux/src/linux/platform.rs:126:5
|
116 | pub(crate) struct LinuxCommon {
| ----------- field in this struct
...
126 | wake_sender: Sender<()>,
| ^^^^^^^^^^^
|
= note: `-D dead-code` implied by `-D warnings`
= help: to override `-D warnings` add `#[expect(dead_code)]` or `#[allow(dead_code)]`
error: could not compile `gpui_linux` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...
```
https://github.com/zed-industries/zed/actions/runs/28374439219/job/84060893437
Release Notes:
- N/A
|
||
|
|
fea22b3934 |
gpui: Fix xdg_toplevel app_id set to None at first commit on Wayland (#55583)
This PR fixes the default behaviour of setting `app_id` to `None` which breaks the rules based management by KWin as the first commit doesn't have the actual `app_id` and apply initially setting stops checking for the window by when it updates. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #53962 Release Notes: - Fixed KWin not respecting Zed's rules due to mismatch of toplevel app_id at startup |
||
|
|
5824dca06e |
gpui_linux: Improve error message when X11 and Wayland feature flags are missing (#58685)
Improves `gpui_linux` error messages for missing feature flags. Currently, you'd need to look at the source & Rust backtrace to diagnose the error shown when the feature flags are missing. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Improve `gpui_linux` error messages for missing feature flags --------- Co-authored-by: Tom Houlé <tom@tomhoule.com> |
||
|
|
e85a546880 |
gpui: Add system wake callback (#59831)
# Objective GPUI apps currently don't have a cross-platform way to react when the system wakes from sleep. ## Solution Add `Application::on_system_wake`, backed by platform hooks for macOS, Windows, and Linux. The platform listeners are registered lazily once an app installs a wake callback. ## Testing - N/A ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
8aca6b1eda |
Remove gpui's dependency on async-process (#59358)
This removes gpui's dependency on the `util` crate, which depends on async process. It now depends on `gpui_util` only, and uses `std::process::Command` for the two cases where it was previously using an async `Command`. This lifts the requirement of non-Zed consumers of gpui to depend on our forked async-process (see #59156). Release Notes: - N/A |
||
|
|
53367edf64 |
linux: Fix Wayland clipboard reads blocking indefinitely (#58826)
Closes FR-44 Unlike X11, where Zed already times out clipboard reads, on Wayland we had no timeout at all. We read the clipboard from a pipe the source app writes into, and we were doing a blocking `read_to_end` on it. So if the other app opened the pipe but never wrote anything, or stalled partway through, the read would block forever and hang Zed. This PR replaces it with a non-blocking read driven by `poll` with a 4s timeout, so: - a stalled writer fails cleanly instead of freezing us. - a slow writer that's still making progress on a large payload keeps working. This roughly mirrors what the X11 path already does. I have tested pasting text, large image, drag-n-drop, etc cases. To Reproduce: 1. Copy text from a web page in Firefox. 2. `kill -STOP "$(pgrep -o firefox)"` 3. Paste into Zed. Zed hangs indefinitely. <img width="400" src="https://github.com/user-attachments/assets/532d1c55-1500-4143-8227-127e0024efba" /> Release Notes: - Fixed a freeze on Linux Wayland when reading the clipboard from a slow or unresponsive application. |
||
|
|
b1f9188767 |
Fix Wayland IME handling with multiple windows (#58712)
Self-Review Checklist: - [X] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Fix an issue introduced by PR #58237 where IME could not be enabled when multiple windows were open. The fix ensures that `update_ime_enabled` only updates IME state for the active window. Release Notes: - N/A |
||
|
|
a39afde4ac |
Disable the IME on linux/wayland when text input is unexpected (#58237)
Self-Review Checklist: - [X] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable This PR follows the approach from #51041 and currently includes only the Wayland implementation. [录屏 2026-06-01 23-11-17.webm](https://github.com/user-attachments/assets/c9f87503-4d64-4868-b9d8-5b832ade3e5a) Release Notes: - On Linux wayland, the IME is disabled in Vim normal and visual modes. |
||
|
|
2991e00e5e |
gpui: Anchor IME candidate window to the start of the visual line (#55876)
## Summary
This PR fixes horizontal jumping of the IME candidate window during text
composition.
On the shared `selected_bounds` path used by Windows, the candidate
window no longer follows the preedit caret character-by-character.
Instead, it anchors to the start of the current visual line, which keeps
the candidate window stable while typing.
Linux Wayland uses a different IME positioning path, so it was not
affected by the original implementation in this PR. This PR now also
includes a Wayland-specific adjustment so that Wayland uses the same
visual-line anchoring behavior.
## Changes
1. **Shared / Windows path**
Updated `selected_bounds` in `crates/gpui/src/platform.rs` to use a
visual-line-aware anchor for preedit text.
This removes the distracting horizontal movement of the candidate window
while typing and keeps the anchor aligned with the active visual line.
2. **Linux Wayland path**
Updated the Wayland IME area calculation in
`crates/gpui_linux/src/linux/wayland/window.rs` to use the same
visual-line-start anchoring strategy for preedit text.
This brings Linux Wayland in line with the Windows behavior while
preserving Wayland's platform-specific IME handling.
## Notes
I had previously tried a separate Wayland-specific fix in
|
||
|
|
a0d71a483a |
Log worst hanging tasks and actions (#57835)
We have a lot of long blocking tasks on both the foreground and background, this is a start of getting some insight into those. We will now log tasks running longer then 100ms on the foreground or background. Hanging actions will also be logged including their name. We simultaneously collect statistics on task and action performance and send those to telemetry. This includes quantiles and averages for each hanging task. Finally this adds tree dev actions: - hang action - hang foreground - hang background These cause a hang to check if hang reporting is working and in the future telemetry. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - Added logging and telemetry of tasks and actions with performance issues |
||
|
|
761fda75cd |
gpui: Accesskit support (#56065)
GPUI AccessKit integration This PR is replacing #51097 , and is much more limited in scope. This PR *ONLY* adds AccessKit support to GPUI, and doesn't touch Zed. Once this lands, we can start adding aria attributes to Zed's components. This PR is the first step to addressing #41138 . Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: John Tur <john-tur@outlook.com> Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com> Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de> Co-authored-by: Agus Zubiaga <agus@zed.dev> |
||
|
|
ec4517faf3 |
gpui: Prefer Mailbox present mode on Wayland to avoid FIFO stalls (#57077)
The WgpuRenderer defaults to VK_PRESENT_MODE_FIFO_KHR (vsync), which blocks vkQueuePresentKHR until the compositor releases a buffer via wl_surface.frame. On some Wayland compositor+driver combinations (notably NVIDIA proprietary + Hyprland, but also observed on KDE/GNOME + AMD RADV), these frame callbacks can be delayed or lost, stalling the entire calloop event loop for tens of seconds. VK_PRESENT_MODE_MAILBOX_KHR does not block on vblank: it replaces the pending frame in a single-entry queue. This avoids the stall entirely. The renderer already falls back to Fifo automatically if Mailbox is unsupported by the driver. The WgpuSurfaceConfig has had a preferred_present_mode field since #50815 (added for Android lifecycle transitions with the same rationale). This commit sets it to Mailbox in the Wayland window creation path only. X11 is not affected. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Note on tests: This change is in the Wayland platform's window creation path (WaylandWindowState::new). The surface configuration is delegated to WgpuRenderer which already has test coverage for preferred_present_mode fallback logic. A full integration test would require a running Wayland compositor in CI. Verified manually and tested against the renderer's unwrap_or(Fifo) safety net by inspecting surface_caps.present_modes on both NVIDIA proprietary and Mesa RADV drivers. Closes: #50229 Closes: #55345 Closes: #39097 Closes: #50734 Refs: #38497, #52009, #52403, #50574, #49961, #47750, #46203, #50195, #50283, #42164, #39156, #39234, #35948, #32618 Release Notes: - Fixed UI freezes on Linux (Wayland) when on certain GPU/driver combinations --------- Co-authored-by: Neel <neel@zed.dev> |
||
|
|
083d16624c |
Fix text copy via mouse click on Wayland (#50406)
I've been working on a GPUI application which has a button for copying text. After starting the app in Wayland in Linux I noticed that when I click the button to copy text it does not work. After interacting with other buttons and/or copying text via keyboard shortcuts then my copy button works. I thought I was handling something wrong, but then I noticed that Zed also exhibits this behavior: upon starting Zed if you highlight text and right-click and select "Copy" then nothing happens. After you interact with other UI elements and/or keyboard shortcuts then copy buttons seem to work fine. This paper cut has been annoying me and with the help of Claude Code I arrived at this small fix. This solves the problem and this appears to be the way the serial should be handled for clipboard actions via button clicks in Wayland, as far as I've been able to learn. Here are a couple related Wayland documentation pages that I double checked: 1. https://wayland.app/protocols/wayland#wl_data_device:request:set_selection 2. https://wayland.freedesktop.org/docs/html/ch04.html#sect-Protocol-data-sharing-devices - [x] Added ~a solid test coverage and/or~ screenshots from doing manual testing - Here's a screen recording of the behavior without this fix: [wayland-clipboard-problem.webm](https://github.com/user-attachments/assets/6c7d3b3b-56fe-4083-a011-4906ee9bfbec) - Here's a screen recording of the behavior with this fix: [wayland-clipboard-fix.webm](https://github.com/user-attachments/assets/167ee731-118a-4f67-8489-6cfaca4389ce) - Here's a screen recording of the problem in Zed itself: [wayland-clipboard-problem-zed.webm](https://github.com/user-attachments/assets/e41a40e1-54aa-4b3f-a2c6-f3e90d06d50f) - [x] Done a self-review taking into account security and performance aspects - None that I could find or think of - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - I don't believe this is applicable... Release Notes: - Improved first mouse-driven text copy action on Wayland |
||
|
|
156a47d0d0 |
gpui(windows): Fix unwrap panic when monitor goes missing (#55630)
Fixes ZED-5K1 Release Notes: - Fixed a panic on windows when a monitor disappears from windows monitor enumeration --------- Co-authored-by: John Tur <john-tur@outlook.com> |
||
|
|
2ae08181de |
gpui_linux: Fix Wayland flickering under CPU load by skipping redundant surface commit (#54214)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #32792 Closes #38266 Closes #54133 Release Notes: - Fixed graphical corruption that could occur when using Wayland ---- **What**: Fixes flickering on Wayland (Sway/wlroots) under CPU load (e.g. rust-analyzer running). The bug only reproduces in release builds - debug builds are too slow to hit the race window. Environment where this was reproduced: Intel GPU (both Xe KMD and i915), Mesa 26.0.4, Sway 1.11. **Why**: When wgpu presents a frame on Wayland+Vulkan, it calls `vkQueuePresentKHR,` which - as required by the Vulkan spec - synchronously issues `wl_surface.attach`, `wl_surface.damage`, and `wl_surface.commit` to the compositor before returning. The commit also picks up any pending frame callbacks. Zed's `completed_frame()` independently calls `state.surface.commit()`. This is a redundant second commit on the same surface. Under load, the Wayland socket dispatch can be delayed enough that both commits are in flight in close succession, and the ordering becomes timing-dependent. When Zed's commit reaches the compositor before Mesa's attach+commit sequence has been fully flushed, the compositor sees a commit with no buffer attached, fires `wl_callback::Done` immediately, and Zed starts the next frame too early - Mesa's real buffer arrives late, causing the visible flicker. Under no load, Mesa's synchronous commit consistently reaches the compositor first, so the bug doesn't appear. **Fix**: Track whether `renderer.draw()` actually called `frame.present()`. When it did, Mesa owns the `wl_surface.commit()` for that frame - skip Zed's commit in `completed_frame()`. Only commit ourselves when wgpu didn't present (surface not configured, lost, occluded, etc.) - in those cases Mesa won't commit, and we need to keep the frame callback alive. --------- Co-authored-by: Benjamin Laib <b5l@users.noreply.github.com> Co-authored-by: John Tur <john-tur@outlook.com> |
||
|
|
c6514d7397 |
Try to reduce linux wGPU crashes better (#55343)
Updates #54349 There were two problems: * The crash never happened, instead we'd always retry. * When re-trying it seemed like we were picking llvmpipe. Claude's suggestion was that immediately after wake, the real GPU isn't yet awake, and so we pick llvm. Avoid this by disallowing llvmpipe on retry Release Notes: - linux: Reduced crash rate when recovering GPUs |
||
|
|
ba6d258b0f |
Handle hiding cursor on keyboard input at GPUI level (#55664)
Instead of manually handing hiding the cursor on keyboard input at the editor level, GPUI will now take care of it. This makes it significantly easier to handle the edge cases, and allows delegating the cursor restoration to the platform itself in the macOS case. On Linux and Windows, we still have to restore the cursor on movement ourselves, but this now happens at the platform-specific level. Bugs fixed by this change: - No cursor when "Unsaved edits" prompt appears - Cursor disappears when clicking a panel button if it contains a search bar (e.g. collab panel) ### Setting rename The `hide_mouse` setting value `"on_typing_and_movement"` has been renamed to `"on_typing_and_action"` to better reflect what it actually does — it hides the cursor when a keystroke resolves to an action (e.g. cursor movement, deletion). Existing settings are migrated automatically. ### Tested platforms - [x] macOS - [x] Wayland - [x] X11 - [x] Windows - [x] Web Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Renamed the `hide_mouse` setting value `on_typing_and_movement` to `on_typing_and_action` to better describe its behavior (existing settings are auto-migrated) - Fixed a few situations where the mouse cursor would be incorrectly hidden |
||
|
|
f01d6816cb |
Support BGR subpixel layout (#55174)
Release Notes: - Added text rendering support for BGR subpixel layouts. |
||
|
|
ae7a265a20 |
Fix showing unsupported window controls on Linux Wayland (#50609)
Release Notes: - Fixed showing unsupported window controls on Linux Wayland Before: <img width="207" height="84" alt="image" src="https://github.com/user-attachments/assets/174d3488-7c40-4b36-a1b5-76b85e19b796" /> After: <img width="185" height="90" alt="image" src="https://github.com/user-attachments/assets/82f10cbb-a834-4db9-a8e7-9e5156b3397e" /> Co-authored-by: Ben Kunkle <ben@zed.dev> |
||
|
|
c49ffd1e88 |
Fix bad GitHub merge queue merge (#54721)
No, sadly, the title is not a typo. See https://www.githubstatus.com/incidents/zsg1lk7w13cf for the context. I'll read with joy and popcorn through that root cause analysis. It makes literally zero sense what happened here, but for some completly bonkers reason GitHub completely messed up the merge queue with https://github.com/zed-industries/zed/pull/54632. I have no idea how it happened. It makes literally zero sense. A PR going into the merge queue should have the same LoC when getting out of it. GitHub obviously does not check this. GitHub causes extra work with a feature that is supposed to save time. Thanks, I guess. Release Notes: - N/A --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |