Skip to content

WebView2 leaks GDI objects on GlazeWM workspace switch #264

@johnsideserf

Description

@johnsideserf

Description

WebView2 widget windows leak GDI objects every time a GlazeWM workspace switch occurs. Each switch leaks ~100-200 GDI objects per switch (with 2 widgets active). Over extended use with frequent workspace switching, this causes system-wide cursor stutter and UI lag as the process approaches the Windows per-process GDI limit (10,000).

Evidence

GDI object count was monitored using GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS) via a PowerShell script sampling every 30 seconds.

Baseline (no workspace switching)

GDI count stable at 30-36 for 30+ minutes:

00:36:11 | gdi=36
00:42:49 | gdi=36  (stable)

During workspace switching

Each GlazeWM workspace switch causes a GDI spike:

00:55:19 | gdi=1243
00:55:49 | gdi=1359  (+116, after switch)
00:56:49 | gdi=1468  (+109, after switch)
01:04:50 | gdi=1481  (stable between switches)
01:05:20 | gdi=1573  (+92, after switch)
01:06:50 | gdi=1802  (+218, after switch)
01:07:20 | gdi=1808  (stable after switch)

Key observations

  • Each workspace switch leaks ~100-200 GDI objects (with 2 widget windows)
  • GDI count never decreases — objects are never released
  • Ctrl+Alt+Delete (secure desktop switch) does NOT cause the leak — only GlazeWM workspace switches
  • Widgets are configured with visible_on_all_workspaces(true)

Instrumentation ruling out Zebar's Rust code

GDI counting instrumentation was added around three suspected code paths:

  1. icon_to_image() in systray-util (systray icon bitmap conversion)
  2. Systray::on_event() (systray event handler)
  3. Systray provider emission (PNG encoding + Tauri event emit)

Result: zero GDI delta logged across all three paths. The leak is entirely within the WebView2/Tauri window management layer, triggered by whatever window events GlazeWM sends during workspace switches.

Reproduction

  1. Run Zebar with 2+ widget windows and GlazeWM
  2. Monitor GDI objects: GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS)
  3. Switch GlazeWM workspaces repeatedly
  4. Observe GDI count climbing ~100-200 per switch

Environment

  • Windows 11
  • GlazeWM (latest)
  • Zebar with visible_on_all_workspaces: true widgets
  • WebView2 (via Tauri 2.x / WRY)

Possible causes

Since widgets use visible_on_all_workspaces(true), GlazeWM may still be sending window management messages (z-order changes, WM_WINDOWPOSCHANGED, etc.) to widget windows during workspace switches. WebView2 appears to allocate GDI resources (device contexts, bitmaps) in response to these messages without releasing the old ones.

Potential mitigation: GlazeWM could skip re-managing windows that have visible_on_all_workspaces set, or Tauri/WRY could be investigated for GDI cleanup during window position changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    📬 Needs triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions