tauri-apps / tauri-apps/plugins-workspace

[window-state] Window grows by the decoration extent on every restart (Linux/GTK CSD): inner_size() is saved, set_size() restores

Open
#3,553 0 comments 0 reactions 0 assignees View on GitHub
platform: linux plugin: window-state type: bug
Dominant language
Rust
Stars
1.8k
Forks
602
Avg merge
4d 14h
Merged PRs (30d)
9

Description

### Summary

On a desktop with client-side decorations (GNOME/Wayland), a window restored by
`window-state` is **larger on every launch than it was when it closed**, growing by the
decoration extent each time and compounding without limit. After ten restarts it is roughly
+520 x +1090 physical px and walking off the screen.

The cause is that the two halves of the round trip do not measure the same thing:

- `update_state` saves **`Window::inner_size()`** (`plugins/window-state/src/lib.rs`)
- `restore_state` applies it with **`Window::set_size()`**

On GTK those differ. `set_size` sizes the window *excluding* the CSD shadow and titlebar,
while `inner_size()` reports the allocated surface *including* them. So a window restored to
saved size `S` settles at `S + E`, and `S + E` is what gets written back on close. Next
launch starts from `S + E` and settles at `S + 2E`.

Notably `inner_size()` and `outer_size()` return **identical** values here once the window is
mapped, so the difference is not observable through those two APIs — it only shows up as the
gap between what you *ask* for and what you are then *told* you have.

### Steps to reproduce

1. GNOME/Wayland, a plain Tauri app with `tauri_plugin_window_state::Builder::default().build()`.
2. Launch, resize the window, close it with the window button (so the `RunEvent::Exit` hook runs).
3. Note `width`/`height` in `~/.config//.window-state.json`.
4. Relaunch and close again **without touching the window**.
5. The saved size has grown. Repeat — it grows by the same amount every time.

### Measured

Seeding the state file by hand and closing without resizing, so nothing but the plugin moves
the numbers:

| Saved size going in | Saved size coming out | Delta |
|---|---|---|
| 960 x 640 (seeded) | 1012 x 749 | +52, +109 |
| 1012 x 749 | 1064 x 858 | +52, +109 |
| 2037 x 1309 | 2089 x 1418 | +52, +109 |

`E` = 52 x 109 physical px = 26px shadow per side horizontally; 57px titlebar plus the same
52px of shadow vertically. Instrumenting the app confirms the mechanism directly: with
`1064 x 858` in the state file, the window reports `1116 x 967` (= saved + E) once the restore
has settled, and that inflated value is what the exit hook writes.

Two further observations from the same instrumentation, in case they are useful:

- At `setup` the window is still at the size `tauri.conf.json` asked for — the restore lands
later, from the `on_window_ready` hook — so anything trying to correct this from `setup`
reads a pre-restore size.
- `outer_size()` returns `0 x 0` before the window is mapped.

### Environment

- `tauri-plugin-window-state` 2.4.1 (also present on `v2` HEAD at the time of writing)
- `tauri` 2.11.5, `wry` 0.55.1
- Ubuntu 24.04.4, GNOME Shell 46.0, Wayland, GTK 3.24.41, scale factor 1.0

### Suggested fix

Make the save and the restore measure the same thing. Either save `outer_size()` to match
what `set_size` consumes, or restore through an API that sets the inner size, so that
`set_size(x)` followed by `inner_size()` is the identity on every platform.

A no-op-elsewhere alternative, if the platform difference is hard to reach from here: after
restoring, compare the reported size against the value that was just applied and re-apply the
difference once. That is the shape of the workaround I am carrying downstream, and it costs
nothing on platforms where the two already agree.

### Related but distinct

- #3521 is macOS and multiplicative (scale factor), not an additive decoration extent.
- #251 was zero-valued metadata on first run, fixed long ago.

Contributor guide

Open the contributing guide

Research direction

Start in plugins/window-state/src/lib.rs by tracing the update_state save path and the restore_state path invoked from on_window_ready, then compare the size APIs used on GTK CSD. Reproduce the restart cycle on Linux/Wayland and verify that restoring and saving the same window no longer increases the recorded dimensions on successive launches.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
desktop
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.