99 Commits
Author SHA1 Message Date
xiaofei 1f37291957 gpui_macos: Release the accessibility adapter when a window is dropped (#64143)
## Objective

Closing a GPUI window on macOS does not release that window's native
view tree, so every
open/close cycle leaves a `GPUIView`, its `CAMetalLayer` and the
renderer's command queue
alive in the process. A long-running app that opens and closes windows
grows without bound;
this is the mechanism described in #50737.

The reason is a retain cycle:

```
MacWindowState
  -> accesskit_adapter                       (crates/gpui_macos/src/window.rs:710)
  -> NSWindow.contentView                    (strongly retained by accesskit_macos)
  -> GPUIView                                (a subview of that content view)
  -> Arc<Mutex<MacWindowState>> in its ivar  (back to the start)
```

`accesskit_macos::SubclassingAdapter::for_window` keeps a strong
`Id<NSView>` of the
window's content view, and both the `NSWindow` and the `GPUIView` store
an
`Arc<Mutex<MacWindowState>>` in their `windowState` ivar.
`MacWindow::drop` resets the
delegate and tears down the frame source and the visibility callback,
but nothing drops the
adapter, so the native view tree outlives the window.

Worth noting while reading the existing teardown:
`gpui_apple::metal_renderer::destroy()`
is currently a no-op (`crates/gpui_apple/src/metal_renderer.rs`), so the
`renderer.destroy()`
call that is already there is not evidence that GPU-side resources were
released — what
matters is whether the state that owns the renderer can run its
destructor at all.

## Solution

One line in `MacWindow::drop`:

```rust
let mut this = self.0.lock();
drop(this.accesskit_adapter.take());
this.renderer.destroy();
```

Nothing else in the teardown changes:

- `frame_source.take()` stays. `WindowFrameSource`'s `Drop` cancels
`frame_requests`, and
that dispatch source's context points at the native view, so it has to
be cancelled while
  the view is still valid.
- `visibility_callback.take()` stays, as do the sheet handling and the
delegate reset.
- No view or layer is released by hand. The native view is already
handed to its parent view
  via `autorelease`; the problem is the cycle, not the ownership.

## Testing

Done locally on macOS (aarch64), with `stable`:

- `cargo fmt -p gpui_macos -- --check` — clean.
- `cargo test -p gpui_macos --lib` — 7 passed, 0 failed.
- `cargo clippy -p gpui_macos --release --all-targets --all-features --
--deny warnings` — clean.

Note: the adapter is only created when Zed runs with
`ZED_EXPERIMENTAL_A11Y=1`, so the leak
only affects that path and the fix is a no-op otherwise.

What isn't covered:

- **No automated test.** The cycle needs a real `NSWindow`/`contentView`
pair, and the change
is a `Drop` impl; a unit test here would be testing `NSView` ownership
rather than this
crate's logic. `gpui_macos` has no window-lifecycle test harness today,
and adding one is
  more than this fix should carry.
- **No memory measurement by me.** I did not run
Instruments/`Allocations` against this
change, and the minimal-repro numbers quoted in #50737 were measured on
a different fork
revision, not this one. So the leak here is established from the
ownership graph above
rather than from a footprint curve I produced. The follow-up that would
settle it is a
before/after count of live `GPUIView` / `CAMetalLayer` / command-queue
objects after closing
a window, with accessibility enabled and disabled; I have not run that,
and I am not
  claiming this explains every memory-growth report.
- Only the macOS backend is touched, so there is nothing
Windows/Linux-specific here.

## 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 (UI/UX and icon
guidelines) — no UI change
- [ ] Tests cover the new/changed behavior — not applicable, see "What
isn't covered"
- [x] Performance impact has been considered and is acceptable

## AI disclosure

Built with substantial LLM assistance: I had help reading the
`accesskit_macos` and
`gpui_macos` ownership graph and drafting this description, and I
reviewed every line of the
diff myself. The change is one line plus a comment. English isn't my
first language, so the
prose here was machine-polished; happy to restate anything in plain
terms.

---

Release Notes:

- [GPUI] Fixed a memory leak on macOS where closing a window with
accessibility enabled left its native view, `CAMetalLayer` and the
renderer's resources alive.
- Fixed a memory leak on macOS where closing a window left its native
view, `CAMetalLayer` and the renderer's resources alive.
2026-09-20 08:26:45 +00:00
Finn Evers 20d55798db Fix cargo shear warnings (#64340)
Also updates CI to always run this - that way we ensure that disabling
or enabling tests does not become stale at some point and thus can
safely add this.

Release Notes:

- N/A
- [GPUI] N/A
2026-09-17 08:49:31 +00:00
tidely eb55524b57 gpui_macos: Fix traffic lights animation during fullscreen exit (#64339)
# Objective

Fixes #64050

Going out of fullscreen on MacOS currently gives the traffic lights a
janky animation. This seems to be a timing issue during ownership
transfer of traffic light rendering from AppKit to Zed.

## Solution

Add an additional boolean flag (yes, sad), which keeps track of whether
the animation from full screen to windows has completed.

## Testing

Tested this on a mac, animation is now buttery smooth.

## Showcase

<details>
  <summary>Click to view showcase</summary>

Before:



https://github.com/user-attachments/assets/f629f2d8-2466-47e3-9b8b-65975bdd1c61

After:


https://github.com/user-attachments/assets/a393d3a7-647d-4784-bc2a-9d67fac9f951

</details>

---

Release Notes:

- Fixed MacOS traffic lights animation when exiting fullscreen
- [GPUI] Fixed MacOS traffic lights animation when exiting fullscreen
2026-09-17 08:29:46 +00:00
tidely 85b8aa5898 gpui_macos: Migrate screen capture to objc2 (#64251)
# Objective

Get rid of instances of the `block` crate, as it will not compile in a
future version of Rust. This gets rid of the `block` crate inside of
`gpui_macos`, which means were on the home stretch.

## Solution

Migrate `screen_capture.rs` completely to `objc2`. While a more targeted
fix is theoretically possible, there were enough upsides where I thought
this was worth a full migration.

Completely gets rid of the `media` crate, which had manual bindings to
`core_media`, which `objc2` can provide. Gets rid of tons of unsafe
code, and a lot of code in general.

## Testing

Tested manual sharing inside of a Zed channel

Release Notes:

- N/A
- [GPUI] Migrated macOS screen capture to `objc2`
2026-09-17 01:46:02 +00:00
Smit Barmase 658460cc2b gpui_macos: Fix hover feedback when another application is focused (#64301)
Follow-up #64234

If Zed is inactive, hovering over it produces no hover effects. While
this matches other apps like Slack, I think it’s better to preserve
existing behavior. This change keeps the bug fix of the original PR.

This PR uses `ActiveAlways` for normal windows, matching popup windows.

Release Notes:

- N/A
- [GPUI] N/A
2026-09-16 13:41:43 +00:00
Smit BarmaseandAnthony Eid 7a3199f7f2 gpui_macos: Improve mouse tracking for normal windows (#64234)
To reproduce:

1. Place Safari over part of Zed, then focus Zed.
2. Right-click Safari to open its context menu, then left-click Safari
anywhere in it.
3. Move the pointer over Safari where it covers the Zed. Zed shows hover
effects underneath.

Use a view tracking area and disable `acceptsMouseMovedEvents` for
normal macOS windows. This addresses hover effects on covered content
when the pointer moves over another window.

[Chromium also requests mouse movement through tracking
areas](https://github.com/chromium/chromium/blob/5a43095e95ffe566d1512b9d67d534e3ae05b975/components/remote_cocoa/app_shim/bridged_content_view.mm#L436-L465).
A search of its macOS window/view code found no explicit use of
`acceptsMouseMovedEvents`, which [defaults to
`false`](https://developer.apple.com/documentation/appkit/nswindow/acceptsmousemovedevents).

Tracking stays enabled in unfocused windows while the app is active.

Before:


https://github.com/user-attachments/assets/f21cd3e5-04d5-4d34-b495-02f8d0f68ecf

After:


https://github.com/user-attachments/assets/89a8b9c9-5aa3-4075-aa91-13e200ea3613

Release Notes:

- [GPUI] Fixed mouse tracking on macOS so moving the pointer over
another app’s window no longer triggers hover effects in covered GPUI
windows.
- Fixed an issue on macOS where moving the pointer over another app
could trigger hover effects in a Zed window underneath it.

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
2026-09-15 16:28:53 +00:00
Anthony Eid 6736a09abe gpui: Add is_visible and on_visibility_changed API on Window (#64107)
Adds a `WindowVisibility` API to GPUI: `Window::visibility()` /
`Window::is_visible()`, `Window::observe_window_visibility`, and
`Context::observe_window_visibility`, backed by one platform signal per
backend — `NSWindow.occlusionState` on macOS, `WS_VISIBLE` plus the
minimized state on Windows, the xdg-shell v6 `suspended` toplevel state
on Wayland, mapped state plus `VisibilityNotify` on X11, and the Page
Visibility API on web. Only transitions are reported, the initial value
is read from the platform when the window is created, and the two
`PlatformWindow` methods have no default so a backend cannot silently
opt out. The per-platform limits (Windows can't see a fully covered
window, pre-v6 Wayland compositors never report `Hidden`) are documented
on the type.

It also adds `Platform::on_system_sleep` / `App::on_system_sleep`,
mirroring the existing wake hook, on macOS
(`NSWorkspaceWillSleepNotification`), Windows (`PBT_APMSUSPEND`), and
Linux (logind `PrepareForSleep(true)`, which the listener previously
discarded). Nothing in Zed subscribes to either yet; the hang-telemetry
journal will use visibility to stop measuring frames that could not have
been presented, and the sleep/wake pair to bracket time the process was
not running.

Release Notes:

- N/A
2026-09-12 03:25:52 +00:00
tidelyandKirill Bulatov c70724205d gpui_macos: Migrate start_external_drag to objc2 (#64033)
# Objective

Get rid of instances of the `block` crate, as it will not compile in a
future version of Rust.

## Solution

Migrate an independent code block to the `objc2` crate family.

## Testing

Tested dragging/dropping files & folders from Zed into Finder. There
seems to be some highlighting bug with folders, but that existed
previously as well.

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2026-09-10 14:52:46 +00:00
tidely 00365ad222 gpui_macos: Migrate get_sources to objc2 (#64031)
# Objective

Get rid of instances of the `block` crate, as it will not compile in a
future version of Rust.

## Solution

Migrate an independent code block to the `objc2` crate family.

## Testing

I tested screen sharing inside of a Zed channel

Release Notes:

- N/A
2026-09-10 12:02:06 +00:00
tidely 2b56eae5a2 gpui_macos: Migrate prompt_for_path to objc2 (#63994)
# Objective

Get rid of instances of the `block` crate, as it will not compile in a
future version of Rust.

## Solution

Migrate to the `objc2` crate family.

This gets rid of `block` inside of `platform.rs` 🎉 

Release Notes:

- N/A
2026-09-10 07:28:42 +00:00
tidelyandKirill Bulatov ded89e373b gpui_macos: Migrate register_url_scheme to objc2 (#63945)
# Objective

The `block` crate will no longer compile with some unspecified future
version of Rust. The long term goal is to get rid of the `block` crate
entirely, and this is one such step.

## Solution

Migrate a standalone function to the `objc2` crate family. Removes many
lines of unsafe code, and switches from `block` to `block2`.

## Testing

All of the calls to `objc2` are identical to the ones in the old code.

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2026-09-09 13:53:54 +00:00
Xiaobo LiuandKirill Bulatov 3d0649186f gpui: Prevent idle sleep during AI response streaming (#53130)
Release Notes:

- Added button tu prevent idle sleep during AI response streaming


**Context / Motivation:**
When an AI agent takes a long time to generate a response, the operating
system might enter an idle sleep (or display sleep) state if there is no
user interaction (mouse or keyboard). This can interrupt the workflow.
This PR aims to prevent the system from sleeping during an active AI
turn by invoking native OS APIs, and provides a UI toggle for users to
control this behavior.

**Key Changes:**

1. **Cross-Platform Idle Sleep Prevention in GPUI (`gpui`, `gpui_*`)**
- Added a new `prevent_idle_sleep` method to the `Platform` trait. It
returns a RAII-based `PreventIdleSleepToken`, which automatically
restores the original sleep policy when dropped.
- **macOS**: Utilizes `NSProcessInfo`'s `beginActivityWithOptions` with
`NSActivityIdleDisplaySleepDisabled`.
- **Windows**: Uses `SetThreadExecutionState` to prevent display and
system sleep, incorporating a reference counter to handle concurrent
requests safely.
- **Linux**: Uses `ashpd` to request `org.freedesktop.portal.Inhibit`
(inhibiting `Idle` and `Suspend` states) via D-Bus.

2. **AI Agent State Integration (`crates/acp_thread`)**
- Introduced idle sleep prevention logic in `AcpThread`: Acquires the
token at the beginning of `run_turn`, and automatically releases it when
the turn completes normally, errors out, or is explicitly canceled by
the user.
- Added comprehensive unit tests to ensure the token is correctly
acquired and released under various cancellation and concurrency
scenarios.

3. **Settings and UI (`crates/agent_settings`, `crates/agent_ui`)**
   - Added `AgentOutputIdleSleepControl` to global state.
- Added a new toggle button (screen icon) to the toolbar at the bottom
of the `ThreadView` (AI conversation panel). Users can hover to view the
tooltip and click to toggle the feature on or off.

<img width="419" height="132" alt="截屏2026-04-04 16 09 08"
src="https://github.com/user-attachments/assets/619da9ff-10f7-46e0-b959-d7c74b16b918"
/>

---------

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-09-09 13:49:13 +00:00
Toru NayukiandKirill Bulatov a6cf4032e6 Restore macOS windows to their original Space after restart (#58886)
Addresses #9640 (the Spaces part).

On macOS, Zed already restores each window's size, position, and
display, but not its Space (virtual desktop). So after a restart, all
windows open on whatever Space is active at launch, not the Space they
were on before. This is the most reported problem in #9640.

A window's Space is not part of the window bounds we save. It is handled
by AppKit's own window state restoration. So instead of tracking the
Space myself, this PR uses that AppKit feature:

- I mark `GPUIWindow` as restorable and encode its native state (frame +
Space) by calling `encodeRestorableStateWithCoder:` with an
`NSKeyedArchiver`. A delegate skips the `NSView`/`NSWindow` objects that
do not support `NSSecureCoding`.
- I save the blob per workspace in a new `restorable_window_states`
table.
- When a saved workspace is opened again, I create the window and then
replay the blob with `restoreStateWithCoder:`. AppKit then moves the
window back to its Space.

On other platforms, and when there is no saved blob, this does nothing.

Verification:

- Added a round-trip test for the database part (save / overwrite /
read, and `ON DELETE CASCADE`).
- The macOS encode/restore part calls into AppKit, so I could not
unit-test it. I checked it by hand: I put several windows on different
Spaces, quit with Cmd+Q, started Zed again, and each window came back on
the Space it was closed on. I also checked that the saved blob is a real
AppKit archive (it starts with `bplist00`).

For now this only works for local workspaces. Remote workspaces could
use the same way later.

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:

- macOS: Restored windows now reopen on the Space (virtual desktop) they
were on, instead of all landing on the currently active Space.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-09-09 13:48:50 +00:00
b753928cc7 macos: Honor "Tiled windows have margins" for the Fill titlebar action (#63759)
# Objective

Fixes #52884

On macOS, with **"Double-click a window's title bar"** set to **Fill**
and **"Tiled windows have margins"** enabled (System Settings > Desktop
& Dock), double-clicking Zed's title bar fills the whole visible frame
with no margins, while native apps leave the margin in place.

The `"Fill"` arm of `titlebar_double_click` currently falls back to
`zoom:`, with a comment stating that there is no documented API for the
Fill action. `zoom:` is the classic maximize: it targets the screen's
visible frame and never goes through the system tiling path, so the
margin preference cannot apply.

## Solution

AppKit does implement the action: `_zoomFill:` is the selector behind
**Window > Move & Resize > Fill**, and it goes through the system tiling
path. Prefer it for the `"Fill"` case, keeping `zoom:` as a fallback
when the window does not respond to it.

This means the margin geometry stays owned by the OS: neither
`EnableTiledWindowMargins` nor `TiledWindowSpacing` has to be read or
reimplemented in gpui, and the behavior follows the setting when the
user toggles it.

The selector is underscore-prefixed, hence the `respondsToSelector:`
guard and the `zoom:` fallback.

## Testing

Measured on macOS 26.6.2 (25G76), aarch64, on a 2560x1440 display with
the Dock on the left, so `visibleFrame` is `(80, 0, 2480, 1410)`. Probe:
a plain `NSWindow` (`titled`, `resizable`) in a standalone AppKit
binary, with `EnableTiledWindowMargins = 1` and `TiledWindowSpacing`
unset (default 8).

| action | resulting frame | insets vs `visibleFrame` |
| --- | --- | --- |
| `zoom:` | `(80, 0, 2480, 1410)` | 0 on all four sides |
| `_zoomFill:` | `(88, 8, 2464, 1394)` | 8 pt on all four sides |

`respondsToSelector: _zoomFill:` returns `YES` on that build. `cargo
check -p gpui_macos` and `cargo fmt -p gpui_macos -- --check` both pass.

To reproduce as a reviewer: enable both settings above, then
double-click the title bar. Before this change the window touches the
screen edges, after it the margin matches Finder or Safari. With "Tiled
windows have margins" off, both behave the same.

One behavior difference worth flagging: `zoom:` toggles, so a second
double-click used to restore the previous frame, while `_zoomFill:`
stays filled. AppKit exposes `_zoomUntile:` for the reverse direction,
which restores the pre-fill frame in my testing. I left it out to keep
the diff minimal, but I am happy to add the toggle if you would prefer
that a second double-click untiles.

I could not find a way to cover this with an automated test, since the
assertion would be about AppKit's own tiling geometry.

## 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

---

Release Notes:

- Fixed double-clicking the title bar ignoring the macOS "Tiled windows
have margins" setting when "Double-click a window's title bar" is set to
"Fill".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Um4zWW8chJ5rwbGXhW8APh

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-09-07 09:12:55 +00:00
tidely fd35a813e9 gpui_macos: Migrate PlatformWindow::prompt to objc2 on MacOS (#59572)
# Objective

Rust 1.96 emits a warning when compiling the `block` crate, that it will
no longer compile past some future version of Rust. The `block` crate
provides `ConcreteBlock` and `RcBlock`, which are stack- and heap-based
functions often passed as callbacks to MacOS. The old `objc` crate
family is deprecated and no longer maintained.

The goal of the PR is to replace one call to `block::ConcreteBlock` by
migrating a standalone portion of code to the objc2 crate family. A full
migration to `objc2` by Zed has been long on the horizon (#22408)

Additional benefits: The new code is completely typed, uses automatic
memory management, and reduces lines of unsafe Rust from 50+ to 1. The
lingering unsafe converts a raw pointer stored by `self` to the
respective `objc2` type, since the rest of the window implementation
does not use the new crates yet.

## Solution

Migrate `PlatformWindow::prompt`'s MacOS implementation to `objc2`.

## Testing

Direct platform code is quite fragile and hard to test, however you can
exercise the codepath by running the following `gpui` example and
clicking on the prompt buttons.

```sh
cargo run -p gpui --example window
```

I've also built Zed with these changes and tried codepaths which
activate prompts, such as deleting a file.

## 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 or Added/Fixed/Improved ...
2026-08-31 14:31:41 +00:00
Anthony Eid 328ad39817 gpui_platform: Expose headless renderer through bench support (#63244)
GPUI's `bench-support` feature provides the benchmark context and
headless-renderer interface without enabling `test-support`, but the
platform implementation remained available only through
`gpui_platform/test-support`. As a result, consumers could not use
`#[gpui::bench]` with a fully isolated feature graph because the
generated benchmark harness calls
`gpui_platform::current_headless_renderer()`.

This threads `bench-support` through `gpui_platform`, `gpui_macos`, and
`gpui_apple`, exposing the existing Metal headless renderer without
widening unrelated test window or prompt APIs. The existing benchmark
package now requests the platform's canonical `bench-support` feature.
Its other test-backed fixtures are intentionally unchanged and remain
outside this focused dependency fix.

Validation:

- Confirmed `gpui_apple/bench-support`, `gpui_macos/bench-support`, and
`gpui_platform/bench-support` resolve no `test-support` feature with
`cargo tree --locked --no-default-features --features bench-support -e
no-dev,features`.
- Ran `cargo check --locked -p benchmarks --benches` to compile every
existing `#[gpui::bench]` target against the real platform headless
renderer.
- Ran `cargo nextest run --locked -p gpui_apple -p gpui_macos -p
gpui_platform`.
- Ran `./script/clippy` for each changed platform package with
`--no-default-features --features bench-support`.
- Ran `cargo fmt --check`.

Release Notes:

- N/A
2026-08-26 11:00:10 +00:00
John Tur 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
2026-08-24 02:59:15 +00:00
Smit Barmase 12c7b62b90 gpui_macos: Fix fullscreen behavior on non-macOS and after restart (#62819)
Follow-up to #60020.

- Fixes simple fullscreen preventing fullscreen from working on Linux
and Windows (since I use shared dotfiles).
- Fixes quitting Zed in simple fullscreen not correctly restoring the
previous window bounds, causing it to reopen maximized. We are not
handling opening it back in simple fullscreen yet.

Release Notes:

- N/A
2026-08-19 14:46:01 +00:00
Kevin BravoandTom Houlé 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>
2026-08-19 14:32:19 +00:00
Remco Smits e8fcbd476d gpui_macos: Add simple fullscreen mode that covers the notch (#60020)
Closes #60013

# Objective

Right now Zed can go full screen but it does not allow you to fix the
hole screen,
by that I mean that Zed can go behind the notch so you don't have extra
useless room left.

## Solution

You can now use the `fullscreen_mode` = `simple` setting to use the new
simple full screen feature, that lives besides the normal full screen
feature. But allows you to have an option to go 100% full screen without
losing any useless space on your macbook screen.
**Note** this is mostly usefull when you have a macbook that has a notch
whitch is kinda in the way of your work flow.

## 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

**Before**
<img width="5712" height="4284" alt="IMG_0339"
src="https://github.com/user-attachments/assets/9f908ffd-7cef-4999-a454-c80f72c40dc8"
/>

**After** (Note now Zed is behind your notch when using the simple full
screen feature)
<img width="5712" height="4284" alt="IMG_0360"
src="https://github.com/user-attachments/assets/5917ed4d-2a64-4464-a794-bc46fd034521"
/>


---

Release Notes:

- Added support for simple fullscreen mode using the `fullscreen_mode`
setting, set it to `simple` to try it out.
2026-08-18 07:00:34 +00:00
Anthony Eid 232eaba704 Extract shared Apple renderer from gpui_macos (#62649)
Extract parts of `platform_macos` to `gpui_apple` to be used as a shared
crate between `gpui_macos` and a future `gpui_ios` for iOS/iPadOS apps.

Release Notes:

- N/A
2026-08-14 19:53:25 +00:00
Kirill Bulatov 3692029a80 Bump rustc to 1.97 (#62395)
Release Notes:

- N/A
2026-08-09 22:29:52 +00:00
John Tur ec5ddcf55e Tweak renderer resource management (#62069)
Factoring some changes out of
https://github.com/zed-industries/zed/pull/61461:
- On Metal and wgpu, bulk-upload instance buffers at the start of the
frame rather than uploading chunks as we draw them. The depth buffer
patch needs this architecture since all opaque quads in the scene are
painted before anything else. This also lets us get rid of the awkward
retry loop when the GPU buffer is too small for the scene.
- On Windows, use a constant buffer for the instance offset rather than
creating SRVs with different `FirstElement`s. `FirstElement` is buggy on
some vendors' drivers, and reusing the SRV object should reduce D3D
runtime overhead.
- Misc. refactoring: remove redundant parameters, deduplicate code,
create fewer bind groups on wgpu, etc.

Fixes https://github.com/zed-industries/zed/issues/49588
Fixes https://github.com/zed-industries/zed/issues/59192

Release Notes:

- Fixed an issue where UI elements would flicker on some Windows
devices.
2026-08-03 14:37:21 +00:00
Mayank Verma 938f8d7454 gpui: Add resizable and minimizable state to Window (#61473)
### Objective

PR #36859 made native macOS and Windows controls respect `is_resizable`
and `is_minimizable`, but custom window decorations cannot access those
values from `Window`. Linux custom controls and client-side resize
interactions remain active, macOS custom titlebar double-click does not
check either option and custom Windows caption buttons still appear
enabled even though the backend blocks their actions.

### Solution

Expose `is_resizable` and `is_minimizable` on `Window`. Use them to
disable custom window controls and titlebar actions and omit client-side
resize cursors and hitboxes for non-resizable windows.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] 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
2026-07-31 06:38:08 +00:00
Smit Barmase a8b25c3131 gpui: Restore file drags when re-entering the source window on macOS (#61940)
Follow-up to #58161, which handed file drags to AppKit once the pointer
left the window but left the handoff one-way. Re-entering the source
window and dropping was not possible.

GPUI now suspends the original drag when handing it to the platform and
restores it when the drag re-enters the source window, so drop targets
receive the original typed payload instead of the synthetic
external-paths payload.

No release notes this hasn't shipped yet.

Release Notes:

- N/A
2026-07-30 16:11:24 +00:00
Lee ByeongJunandSmit Barmase 9a6ed13221 Add macOS file drag-out from project panel (#58161)
## Description

Adds native macOS file drag out support for Project Panel entries. Local
file paths are resolved only when a drag gesture starts, remote or
missing paths are skipped, and unsupported platforms keep the default
no-op behavior.

The macOS implementation starts an AppKit drag session with file URL
pasteboard items, allows Finder and other external apps to choose copy
or move, keeps in-app GPUI drag/drop behavior in control for internal
drags, and clears the active drag state when the native session ends.


https://github.com/user-attachments/assets/42909389-1a32-4574-a200-9a14b913f55f

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
- [ ] Performance impact has been considered and is acceptable

Closes #55370

Release Notes:

- Added support for dragging files from the Project Panel to external
macOS apps.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-07-29 18:55:25 +00:00
Mayank Verma 5aca79139a gpui: Defer appearance change callback to avoid reentrant borrow (#61789)
### Objective

PR #58902 made native macOS window chrome follow the selected theme, but
AppKit can synchronously invoke `viewDidChangeEffectiveAppearance` while
the settings observer holds an `App` borrow. `handle.update` then fails
with `RefCell already borrowed`, leaving `Window::appearance()` and its
observers stale.

### Steps to reproduce

1. Set Zed's theme mode to Dark.
2. Set macOS Appearance to Light.
3. Change Zed's theme mode to System.
4. Notice that the window chrome switches to light, but Zed's UI remains
dark.


https://github.com/user-attachments/assets/f91962cc-76ba-4d8e-b587-2a2343f365ba

### Expectation

Zed should switch to its configured light theme when returning to System
mode.

### Solution

Defer `Window::appearance_changed` to the foreground executor so the
current `App` borrow finishes before refreshing the cached appearance
and notifying observers.

### Testing

- Added a regression test.

### Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] 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

Release Notes:

- N/A
2026-07-29 13:35:25 +00:00
Uriah Galang 90727b44e5 gpui_macos: Drop legacy pre-macOS 12 blur path and its private CGS APIs (#56315)
## Summary

`gpui_macos` links two private CoreGraphics Services APIs
unconditionally:

```rust
#[link(name = "CoreGraphics", kind = "framework")]
unsafe extern "C" {
    fn CGSMainConnectionID() -> id;
    fn CGSSetWindowBackgroundBlurRadius(...) -> i32;
}
```

Apple rejects App Store submissions that reference private APIs, so any
application built on GPUI is ineligible for the Mac App Store —
regardless of
whether it ever sets `WindowBackgroundAppearance::Blurred`, since the
symbols are
linked either way.

Their only call site is the `NSAppKitVersionNumber <
NSAppKitVersionNumber12_0`
branch: macOS 11 and earlier. On macOS 12+ the same effect already goes
through
`NSVisualEffectView`, which the existing comment notes is the better
path anyway
(it downsamples the backdrop and gives more control over the effect
layer).

This deletes the legacy branch and the extern block.

## Changed from the previous revision of this PR

This originally added a `macos_app_store` feature gate. @reflectronic
suggested
deleting the legacy `WindowBackgroundAppearance::Blurred` support
instead of
carrying a flag — that is the better call and this PR now does that.

It is the stronger fix on the merits, not just the smaller one: deleting
makes
every GPUI consumer App Store eligible, whereas a feature flag only
helps those
who opt in and leaves the default build ineligible. It also removes a
conditional-compilation path rather than adding one. The diff went from
3 files / +13 −7 to **1 file / +23 −52**.

## Notes for review

Two details that are easy to miss:

- `this.native_window.windowNumber()` resolved through cocoa's
**`NSEvent`**
trait rather than `NSWindow` — `id` is untyped, so it picks up every
trait in
scope. Removing that call makes the `NSEvent` import genuinely unused,
so it is
dropped. Verified against a build of unmodified `main`, which does not
emit
  that warning, to confirm this is a consequence of the change and not
  pre-existing.
- `NSAppKitVersionNumber` and `NSAppKitVersionNumber12_0` had no other
users and
  are dropped with it.

The `NSVisualEffectView` branch is preserved verbatim; the only change
to it is
one level of de-indentation.

## Behaviour change

On macOS 11 and earlier, `WindowBackgroundAppearance::Blurred` no longer
applies
a blur — the window falls back to the `NSVisualEffectView` path, which
those
releases handle via `CAProxyLayer` rather than concrete sublayers. macOS
11 is
past end of life. Every other platform and macOS 12+ are unaffected.

## Verification

`cargo check -p gpui_macos` on macOS — clean, no errors and no new
warnings.

The identical change was also applied to a downstream GPUI fork and
built against
a ~60k-line application using `gpui-component`, where CI passed on
macOS, Ubuntu
and Windows.

Release Notes:

- N/A
2026-07-28 21:01:15 +00:00
Jason LeeandClaude Opus 4.8 ff57679788 Match the macOS app appearance to the selected theme (#58902)
Closes #5049.

When the system is in light mode but Zed uses a dark theme, the macOS
window chrome — the window border and the titlebar — is rendered light,
leaving a pale, low-contrast edge that makes the window look washed out.
Zed's own content renders correctly; the affected chrome is drawn by
AppKit, not Zed.

Why it happens: `NSWindow` conforms to `NSAppearanceCustomization`, so a
window's appearance is inherited app → window → view and resolved
through `effectiveAppearance` unless an explicit appearance is set. Zed
never set one, so every window inherited the *system* appearance and
AppKit drew the chrome for light mode regardless of the dark theme.

The fix uses the override AppKit provides for exactly this: set
`NSApplication.appearance` app-wide, so every window inherits it. VSCode
(`window.systemColorTheme`) and JetBrains IDEs do the same.

<img width="2600" height="1346" alt="after-light"
src="https://github.com/user-attachments/assets/c31376af-8f51-4c36-a62f-55714f63b59c"
/>

## GPUI

- Add `App::set_window_appearance(Option<WindowAppearance>)`, the setter
paired with the existing `App::window_appearance()` getter. `Some(_)`
forces a light/dark appearance; `None` clears the override and follows
the system again.
- macOS sets `NSApplication.appearance` (`None` → `nil`); no-op on other
platforms.
- Demo: `cargo run -p gpui --example window_appearance`.

## Zed

- An explicit light/dark theme (or a static theme) forces the matching
chrome; `System` follows the OS.
- Wired once at startup via a global settings observer, so every window
— including the settings window — stays in sync.

| Before | After |
| --- | --- |
| <img width="3630" height="1878" alt="before-light"
src="https://github.com/user-attachments/assets/44c9ab66-c0cf-433a-93b0-17446b34a284"
/> | <img width="3686" height="1832" alt="SCR-20260609-nzjk"
src="https://github.com/user-attachments/assets/32b0412b-0913-4e68-acba-bf39dcb6aa6a"
/> |


References:
[`NSAppearanceCustomization`](https://developer.apple.com/documentation/appkit/nsappearancecustomization)
·
[`NSApplication.appearance`](https://developer.apple.com/documentation/appkit/nsapplication/appearance)

Release Notes:

- Fixed the macOS window border and titlebar not matching the selected
theme when the system appearance differed (for example, a dark theme
under a light system theme).
([#5049](https://github.com/zed-industries/zed/issues/5049))

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 08:45:03 +00:00
tidely 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 ...
2026-07-21 07:58:16 +00:00
Prithvish Baidya 9eb43a8356 gpui_macos: Retain pasteboard objects (#61024)
## Objective

Prevent a use-after-free when a macOS pasteboard wrapper outlives the
autorelease pool in which it was created.

Pasteboard cached its NSPasteboard and custom zed-text-hash and
zed-metadata NSString objects as raw ids. These constructors return
autoreleased objects. A later clipboard read could therefore pass a
dangling type identifier to dataForType and crash in objc_msgSend.

## Solution

- Retain the pasteboard and custom type objects for the Pasteboard
lifetime with StrongPtr.
- Copy NSData contents into owned Rust byte buffers before the
autoreleased data object can expire.
- Add a regression test that drains the creation autorelease pool and
then accesses the retained custom types.

## Testing

- cargo test -p gpui_macos --features runtime_shaders
test_custom_types_survive_creation_autorelease_pool --lib
- Built an optimized macOS embedder against the patched GPUI checkout.

## Self-Review Checklist:

- [x] I have reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks have justifying comments
- [x] The content adheres to Zed UI and icon standards; this change has
no UI
- [x] Tests cover the changed lifetime behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Fixed a macOS crash that could occur when reading from the clipboard.

---
AI Disclosure: I ran into this bug on a project where I'm using GPUI.
For my purposes I patched it on my fork I maintain. I had codex (gpt
5.6-sol) send this PR because I think this fix should be upstreamed.
2026-07-21 03:57:04 +00:00
Lukas WirthandJohn Tur 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>
2026-07-17 18:52:23 +00:00
tidely 9948409b40 gpui: Unify raw-window-handle dependency (#61193)
Just something I noticed while skimming through dependencies

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-17 13:41:05 +00:00
Richard Feldman 42d5c64723 gpui_macos: Replace per-window CVDisplayLinks with a per-display registry (#60696)
Fixes ZED-7XR

CVDisplayLink has no safe teardown: `CVDisplayLinkStop` only flags the
io thread to exit "soon" and returns, `CVDisplayLinkRelease` frees the
link immediately, and there is no way to learn when a final output
callback has finished. This produced two distinct use-after-free crash
classes on the display-link thread: the io thread reading the freed
link's internals (`CVHWTime::reset`, worked around by leaking the link
in #32116), and a straggler output callback dereferencing the freed
dispatch source through its context pointer
(`dispatch_source_merge_data`, ZED-7XR). Because we tore down a
`DisplayLink` on every resize tick, window activation, and occlusion
change, the existing workaround also leaked a stopped CVDisplayLink per
teardown, unboundedly.

This replaces per-window links with a static registry holding one
immortal `CVDisplayLink` per display. Windows subscribe to their
display's link with the dispatch source that paces their frames; the
output callback's context is a display id rather than a pointer, so the
only memory it ever dereferences is the registry itself, under its lock.
Links start and stop at subscriber-count edges (so multiple windows on
one display can't interleave starts/stops incorrectly), and a window's
dispatch source lives for the life of the window, is removed from the
registry on close, and is then genuinely released. Both crash classes
become unreachable — the link because it is never released, the source
because a straggler callback either finds it live in the registry or
doesn't find it at all — and there are no leaks and no `mem::forget`.
Since we never hold the registry lock across CoreVideo calls, no lock
cycle through CVDisplayLink's undocumented internal locks is possible;
the module doc comment spells out the invariants.

This supersedes #60481, which fixed ZED-7XR by leaking the dispatch
source on teardown.

Release Notes:

- Fixed a rare crash on macOS when closing, resizing, or switching
between windows, and eliminated a memory leak in display-link teardown
2026-07-13 14:30:50 +00:00
Lukas Wirthandzed-zippy[bot] 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>
2026-07-13 11:03:51 +00:00
Remco Smits 9ba1a0116a macos: Fix window move controls are disabled (#60620)
Closes https://github.com/zed-industries/zed/issues/60595

cc Anthony-Eid since you did an initial pass on this.

# Objective

Currenly on macOs there is no way right now after the following
regression to use window management short keys to move them around. This
worked before, but was regressed initially in
https://github.com/zed-industries/zed/pull/59836 that tried fixing a
macos 27 clicking delay issue on the titlebar. That accidentally set the
`native_window.setMovable_(is_movable as BOOL)` to false. Because of
this mocos detects this and disables the window managements actions
because it thinks the window can/should not be able to move.

## Solution

The macOS 27 click delay actually comes from AppKit's native titlebar
dragging, not from `NSWindow.isMovable`. The problem is that
`is_movable` is also what enables the Window menu's tiling options, and
#59836 used it to turn off dragging, which disabled the tiling menu as a
side effect.

To fix this, I restored `is_movable` to its real meaning so the tiling
menu works again, and added a separate
`WindowOptions::app_owns_titlebar_drag` flag for windows that draw their
own titlebar and handle dragging via `Window::start_window_move`. On
macOS this tells AppKit to stop owning titlebar drags (removing the
click delay) without affecting `is_movable`. Zed's main window now sets
both `is_movable: true` and `app_owns_titlebar_drag: true`, so the
tiling menu stays enabled, the click delay is gone. I also added a
`window_movable` gpui example to make this behavior easy to test
manually.

## Testing

I added a gpui test example that helps testing this bug. You can run the
newly added gpui example with 4 different window configurations that
should confirm that this change is the right one for all of the window
types that we have.

## 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

**After** (note this now shows the window managements entries as they
were greyed out.)


https://github.com/user-attachments/assets/192cc0e1-15d8-486f-b788-a92bb51fb7d1

**GPUI** (test examples with different window configurations.)


https://github.com/user-attachments/assets/c9e0f4d3-606b-4cd7-b4d8-a5bb16508164

---

Release Notes:

- Fix macos window management controlls were grayed out/not working
2026-07-12 22:46:47 +00:00
Philipp Schaffrath 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
2026-07-08 19:22:23 +00:00
Lysastriel 542bf6e106 gpui_macos: Guard start_display_link against nil screens (#60419)
# Objective

Prevent a macOS crash in GPUI when AppKit temporarily reports that a
visible window has no associated `NSScreen` during display
reconfiguration.

The crash can happen on this path:

```text
NSScreen::deviceDescription
gpui_macos::window::display_id_for_screen
gpui_macos::window::MacWindowState::start_display_link
gpui_macos::window::window_did_change_screen
```

`start_display_link` checked the window occlusion state before creating
a display link, but it still assumed `NSWindow.screen()` was non-null.
During display changes, sleep/wake, lid close/open, or monitor
reconfiguration, AppKit can transiently return `nil` for `screen`, and
passing that into `NSScreen::deviceDescription` aborts with a null
pointer dereference.

I observed this on macOS 26.5 after the system entered a rare display
state: once in that state, running GPUI applications would crash
immediately after wake. I have not identified the exact OS/display
condition that triggers it, so the full wake/reconfiguration scenario is
not deterministic, but the crash report consistently points to
`NSWindow.screen()` being `nil` when `start_display_link` tries to
create a display link.

## Solution

Make `display_id_for_screen` explicitly handle a null `NSScreen` by
returning `None`.

Callers now handle that case by either:

- returning early from `start_display_link`, because there is no valid
display id to create a display link for yet
- skipping a null screen while iterating `NSScreen::screens`

The normal non-null screen path is unchanged.

This keeps the nil-screen guard at the FFI boundary where
`NSScreen::deviceDescription` is called.

## Testing

Tested on macOS 26.5.

Commands run:

```bash
cargo fmt --check -p gpui_macos
cargo test -p gpui_macos display_id_for_screen_returns_none_for_null_screen
cargo check -p gpui_macos
```

Added a unit test covering the new boundary behavior:
`window::tests::display_id_for_screen_returns_none_for_null_screen`

## 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

---

Release Notes:

- Fixed a macOS crash that could occur when display configuration
changes while a GPUI window is temporarily not associated with a screen.
2026-07-07 09:02:29 +00:00
Ankan MisraandLukas Wirth 5d3498a3df gpui_macos: Fix glyph rendering when fonts share a PostScript name (#57250)
On macOS, `load_family` was inserting every font into
`font_ids_by_postscript_name` without checking for duplicates. When two
installed font files claim the same PostScript name — typically an older
Geist Mono left behind alongside the current brew cask — the second
insert overwrote the first. After shaping, `id_for_native_font` looked
up that PostScript name and got back the *other* font's `FontId`, so the
rasterizer drew glyphs from the wrong table
See #55472 for the screenshot

This adds a local `HashSet` to dedup within a single `load_family` call.
Scoping it to one call (rather than the global map) matters: the same
family gets reloaded under different `FontKey`s when `FontFeatures` or
`FontFallbacks` change, and a global check would skip every font on the
reload and break weight selection.

Cross-family PostScript name collisions and CoreText fallback
substitutions that re-introduce a conflict are out of scope here

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:

- Fixed glyph rendering when fonts share a PostScript name on macOS

---------

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2026-07-07 08:59:55 +00:00
Agus Zubiaga 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
2026-06-24 19:59:34 +00:00
Vitaly Kravchenko 55cc234090 Reapply traffic light position after appearance changes (#59712)
## Bug

Before switching macOS appearance:
<img width="154" height="39" alt="before switching OS theme"
src="https://github.com/user-attachments/assets/2444fafe-21a7-47f6-84ac-ec33a61a6523"
/>

After switching macOS appearance:
<img width="167" height="36" alt="after switching OS theme"
src="https://github.com/user-attachments/assets/d4a34555-2964-4e92-8d6a-24da7620fc49"
/>

After focusing the window following the macOS appearance switch:
<img width="151" height="37" alt="after focusing the window after
switching the OS theme"
src="https://github.com/user-attachments/assets/6bc2e996-256d-448d-a11b-fdc2f0e01f58"
/>

## Fix

Reapply GPUI's custom macOS traffic-light button position after AppKit
reports an effective appearance change and after GPUI's appearance
observers have been notified.

## Cause

GPUI stores a custom `traffic_light_position` for windows that use
transparent/custom titlebars. When macOS switches Light/Dark appearance,
AppKit can relayout the standard window buttons, which resets the
traffic lights to AppKit's default vertical position. The next normal
window focus/layout pass moves them back, causing a visible jump.

This change keeps the existing appearance observer callback behavior,
then re-runs `move_traffic_light()` after the callback has completed.

## Validation

- `cargo check -p gpui_macos --release --features runtime_shaders`
- Manually verified with a GPUI window using a transparent titlebar,
custom `traffic_light_position`, and an appearance observer that
refreshes the window.
- Toggled macOS Light/Dark appearance while the window was visible;
traffic lights no longer shifted upward.

Release Notes:

- N/A
2026-06-24 14:00:13 +00:00
Cole Miller 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
2026-06-16 13:47:55 +00:00
Sergei G. 215ee03f57 gpui_macos: Warn when falling back to NoopTextSystem (#59247)
# Objective

Without the `font-kit` feature, `MacPlatform` silently substitutes
`gpui::NoopTextSystem`, which accepts fonts and resolves font ids but
rasterizes every glyph to an empty bitmap.

I hit this with the `component_preview` example
https://github.com/zed-industries/zed/pull/59241.

## Solution

Log a warning at startup in `MacPlatform::new()` when falling back to
`NoopTextSystem`. Skipped in headless mode, where no text is expected to
render and the fallback is a reasonable configuration.

## Testing

Checked as part of https://github.com/zed-industries/zed/pull/59241

## 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)

## Showcase

It just render as like without any error:
<img width="1312" height="945" alt="cp_pr1_before"
src="https://github.com/user-attachments/assets/f5faf3c0-9523-4c5f-9756-e65cbaf3456f"
/>


---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-06-15 03:49:04 +00:00
Cole Miller 4f75f5e25c Revert "gpui: Fix title bar clicks being delayed on macOS 27 (#58947)" (#59214)
This reverts commit 5cf4935947.

Reverting this because it broke dragging the settings UI window.

Closes https://github.com/zed-industries/zed/issues/59163

Release Notes:

- Fixed not being able to drag the settings UI window on macOS.
2026-06-12 19:36:52 +00:00
Mayank VermaandYara 🏳️‍⚧️ 1a9b9f0d35 gpui_macos: Fix traffic light hitbox after repositioning (#58534)
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 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:

- N/A

This is a follow up to https://github.com/zed-industries/zed/pull/58169
and https://github.com/zed-industries/zed/pull/58518 after removing a
bunch of unsafe usage.

Before this, GPUI moved traffic lights by directly changing the native
button frames inside AppKit’s existing titlebar layout and the old
y-coordinate was based on the native titlebar height, so the buttons
moved visually while the titlebar tracking geometry could still be based
on the old layout.

Now the native titlebar container is resized and repositioned to contain
the traffic lights. We capture the original AppKit titlebar/button
frames, resize the titlebar container to fit the requested position,
move the native close/minimize/zoom buttons inside that container and
update AppKit tracking areas. This keeps the visible buttons and
hover/click geometry aligned.

### Before


https://github.com/user-attachments/assets/77dde6cb-3fc8-4166-bc86-22cf2c93e119

### After


https://github.com/user-attachments/assets/da84ade6-ef47-4e53-ae4a-3779315773f9

---------

Co-authored-by: Yara 🏳️‍⚧️ <git@yara.blue>
2026-06-12 14:08:25 +00:00
Marshall BowersandMikayla Maki 7f74def4b5 gpui_macos: Initialize fallback fonts with primary font weight and style (#56771)
This PR fixes an issue where font properties like bolds and italics
would be lost when falling back to a fallback font.

The problem we identified was that when loading the fallback fonts, we
were just loading them by name. This meant that when the original font
had a different font weight (e.g., bold) or style applied to it this
would not cascade to the fallback font.

To reproduce the problem, we used the following text in a Markdown file:

```md
**한글 bold**
```

With the following font configuration in Zed:

```json
"buffer_font_family": "Victor Mono",
"buffer_font_fallbacks": [
  "D2Coding"
]
```

The fonts can be obtained from their respective locations:

- [Victor Mono](https://rubjo.github.io/victor-mono/)
- [D2Coding](https://github.com/naver/d2codingfont)

#### Before

<img width="168" height="46" alt="Screenshot 2026-05-14 at 1 15 15 PM"
src="https://github.com/user-attachments/assets/f6883e4b-8f2b-4285-a110-0e18d562c51d"
/>

#### After

<img width="176" height="53" alt="Screenshot 2026-05-14 at 1 16 04 PM"
src="https://github.com/user-attachments/assets/88d67207-6711-473b-8027-e0acfa58c258"
/>

Closes #51460.

Release Notes:

- macOS: Fixed fallback fonts missing weight/style.

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2026-06-11 16:50:36 +00:00
Anthony Eid 6d9ddb96b2 Bench app context phase 2 (#58202)
This PR builds out GPUI's benchmark harness so render benchmarks measure
realistic frame costs using GPUI-owned, runtime-gated instrumentation.
It adds measurements for frame draw time, dirty-to-draw latency,
invalidation coalescing, and frame-budget overruns, and runs benchmark
workloads with production-like concurrency.

## How it works

**Frame timings flow through the GPUI profiler.** `Window::draw` emits a
`FrameTiming { window_id, dirty_at, invalidations, draw_start, draw_end
}` event into a global ring buffer in `gpui::profiler`, mirroring the
existing task-timing channel. Collection is runtime-gated by
`profiler::set_frame_trace_enabled` (one relaxed atomic load when
disabled — no `Instant::now` calls in production). `BenchReport` is a
pure listener: it drains events through a cursor-based
`FrameTimingCollector` and builds histograms in the bench layer.
`Window` carries no bench-only cfg fields, and the same event channel
can later feed the miniprofiler UI or an in-app frame-time HUD.

**`BenchDispatcher`** is a multithreaded `PlatformDispatcher` for
benchmarks: background tasks run on a worker pool (same priority queue
as `LinuxDispatcher`, with task-profiler hooks), timers fire in real
time on a dedicated thread, and foreground tasks queue until the bench
thread drains them with a blocking `run_until_idle()`. Unlike
`TestDispatcher`, work executes in parallel in real time, so wall-clock
measurements reflect production concurrency. In-flight accounting is
panic-safe via drop guards.

**`gpui::bench_platform()`** returns a per-process `TestPlatform` backed
by the `BenchDispatcher`, cached in a thread-local so worker threads
persist across Criterion calibration passes. This replaces the earlier
approach of constructing a real platform per invocation, which had
process-global singleton issues, never ran foreground tasks (no run loop
pumped the main queue), and couldn't open windows on headless CI.

**Text shaping** uses `NoopTextSystem`: deterministic across
machines/font installations and CI-portable. Measured cost of this
trade: ~10% of editor draw time vs `MacTextSystem` (Noop still emits one
glyph per character at fixed advances, so downstream layout/paint
structure is preserved).

**GPU coverage (macOS only for now).** `PlatformHeadlessRenderer` gained
`render_scene`, which encodes and submits the scene to Metal against a
cached offscreen target without blocking on completion or reading pixels
back — matching production `present()` CPU cost (`render_scene_to_image`
would overstate it: it waits for the GPU and copies pixels back).
`TestWindow::draw` forwards scenes to the renderer, the real
`MetalAtlas` means glyph/SVG rasterization happens during paint, and
`bench_renderer` presents after each measured update. Platforms without
a headless renderer degrade to discarding the scene.

## Example

```rust
#[gpui::bench]
fn editor_render(cx: &mut BenchAppContext) {
    init_context(cx);

    let buffer = cx.update(|cx| { /* build a MultiBuffer */ });

    let mut window = cx.add_empty_window();
    let editor = window.update(|window, cx| {
        let editor = window.replace_root(cx, |window, cx| {
            let mut editor = Editor::new(EditorMode::full(), buffer, None, window, cx);
            editor.set_style(editor::EditorStyle::default(), window, cx);
            editor
        });
        window.focus(&editor.focus_handle(cx), cx);
        editor
    });

    let mut move_down = true;
    cx.bench_renderer(editor, move |editor, window, cx| {
        if move_down {
            editor.move_down(&MoveDown, window, cx);
        } else {
            editor.move_up(&MoveUp, window, cx);
        }
        move_down = !move_down;
    });
}
```

## Example output (release, M-series)

```
editor_render           time:   [329.75 µs 330.17 µs 330.69 µs]

GPUI bench report (all observed iterations): editor_render
  note: includes Criterion warmup/calibration
  window dirty-to-draw:
    samples: 31533
    mean: 0.321ms
    p50: 0.322ms
    p90: 0.336ms
    p95: 0.342ms
    p99: 0.360ms
    max: 0.504ms
    frame budget overruns total: 0
    frame budget overruns max: 0
  window draw:
    samples: 31533
    mean: 0.295ms
    p50: 0.295ms
    p90: 0.307ms
    p95: 0.313ms
    p99: 0.330ms
    max: 0.455ms
    frame budget overruns total: 0
    frame budget overruns max: 0
  invalidations per frame: mean 5.00, max 5
```

(`invalidations per frame: mean 5.00` is real signal: each `move_down`
notifies the window five times before the draw.)

## Known limitations

- **Draw-per-flush**: the harness draws synchronously when effects flush
rather than coalescing invalidations to a vsync tick, so `dirty-to-draw`
excludes queueing delay, and `frame budget overruns` is a draw-time
budget proxy rather than actual missed presents. A frame-paced mode is
natural follow-up work.
- **GPU submission is measured on macOS only**; other platforms have no
headless renderer yet.
- The GPUI report includes Criterion warmup/calibration samples (noted
in the output); Criterion's `time` is the regression-gating number.
- `run_until_idle` waits for queued, running, and already-due work, but
not for timers that haven't reached their due time — the dispatcher runs
in real time and can't skip ahead like `TestDispatcher`'s virtual clock.

## Future work

- A vsync-like frame-pacing mode (suppress draw-on-flush; tick-driven
draw + present) so dirty-to-draw captures queueing delay
- Record present duration in `FrameTiming` so the report can split draw
vs present
- Benches that scroll through novel content (cold layout caches) and an
agent-panel render bench
- Headless renderers for Windows/Linux
- Move benches into a dedicated crate

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
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A
2026-06-10 09:26:50 +00:00
Anthony Eid 5cf4935947 gpui: Fix title bar clicks being delayed on macOS 27 (#58947)
Fixes a macOS 27 beta regression where clicks on custom titlebar
controls were delayed by the system double-click interval.

After updating to macOS 27 dev beta, I noticed that our titlebar buttons
had a delay before click events took effect. After some digging, I
discovered that AppKit can treat that titlebar region as system-owned
and delay click delivery while waiting to disambiguate double-clicks. I
noticed that Slack had titlebar buttons that weren't affected by the
double-click disambiguation delay, so I had Claude dig through the
Chromium/Electron codebase to discover what they did differently.

Chromium uses a private AppKit SPI,
`_opaqueRectForWindowMoveWhenInTitlebar`, that allows them to set what
regions should be treated as owned by a view vs. owned by AppKit. My fix
uses the same API and returns the view bounds when
`NSWindowStyleMask::NSFullSizeContentViewWindowMask` is set, which
happens when a GPUI app uses a full-size content view for a custom
titlebar. Otherwise, we return bounds of `NSRect::new(NSPoint::new(0.,
0.), NSSize::new(0., 0.))` so apps using macOS's native titlebar still
work as expected.

### Before


https://github.com/user-attachments/assets/ba9e0d2d-b85c-4d98-bc7d-bea00368d9cf

### After 


https://github.com/user-attachments/assets/b78bd7d4-6b43-48d3-a615-2c7a29e32e71


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

Release Notes:

-  Fixed clicks on titlebar controls being delayed on the macOS 27 beta.
2026-06-09 19:01:30 +00:00
Anthony Eid cc84a7bdd6 gpui: Free atlas tile space when removing tiles (#58874)
Partially addresses #54659

`PlatformAtlas::remove` removed the tile from `tiles_by_key` and
decremented the texture's refcount, but never returned the tile's
rectangle to the etagere allocator. Removed tiles' space could only be
reclaimed if the entire texture was dropped, which doesn't happen if
glyph/emoji share the texture. Because we don't have correct glyph/emoji
removal

I added regression tests on all three platforms to prevent this from
occurring as well.

Note: this doesn't fix glyph eviction or the nil-texture panic from
#54659; those are follow ups.

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

Closes #ISSUE

Release Notes:

- gpui: Fix GPUI memory growing unboundedly when images are repeatedly
loaded and released
2026-06-08 23:09:47 +00:00
Tiou Lims 5f9f1cc561 Fix macOS system font fallback cascade append (#58020)
Fixes #57916.

`append_system_fallbacks` used `.map(...)` for the `CFArrayAppendValue`
side effect, but the iterator was never consumed.

This changes it to a `for` loop so the fallback descriptors are actually
appended.
2026-06-05 15:21:46 +00:00