Skip to content

niri: occupied state not detected for persistent named workspaces (open-on-output) — despite #2538 #2828

Description

@dedguy21

Summary

On niri, workspaces declared as persistent named workspaces (workspace "name" { open-on-output "…" }) never register as occupied in the bar's workspace switcher, even when they contain windows — they render identically to empty workspaces. The #2538 fix ("workspace: fix occupied color in niri", 09ca37c) is present in my build but does not cover this case.

The same root cause also makes showOccupiedWorkspacesOnly a no-op for these workspaces (empty named workspaces are never hidden).

Versions

  • DankMaterialShell: 1.5.0+date=2026-07-08_bdfd565 (commit bdfd565, current stable HEAD)
  • niri: stable v25.08 (commit 01be0e65f4eb91a9cd624ac0b76aaeab765c7294)
  • Quickshell: 0.3.0 (tag-v0.3.0, Nixpkgs)
  • OS: NixOS 26.05.20260611.a037402, installed via dms.homeModules (flake)
  • Dual monitor (one landscape, one rotated portrait)

niri config (persistent named workspaces pinned per output)

workspace "01" { open-on-output "DP-3" }
workspace "02" { open-on-output "DP-3" }
// …
workspace "07" { open-on-output "DP-5" }
// …
workspace "10" { open-on-output "DP-5" }

DMS settings used to diagnose

"workspaceOccupiedColorMode": "custom",
"workspaceOccupiedCustomColor": "#47b5d8"   // a distinct cyan

Steps to reproduce

  1. Configure persistent named workspaces with open-on-output as above.
  2. Set a distinct workspaceOccupiedColorMode / color.
  3. Open a window on a non-active named workspace (e.g. a terminal on 02).

Expected

Workspace 02 shows the occupied color.

Actual

Workspace 02 shows the unfocused/empty color — isOccupied is false even though the workspace has a window. Every non-active workspace looks empty regardless of contents.

Evidence the data is present

  • niri msg -j windows shows the window's workspace_id matches the workspace's id (e.g. window workspace_id: 2, workspace 02 has id: 2).
  • The FocusedApp widget correctly reads NiriService.windows[].workspace_id — the per-monitor focused-window title works — so NiriService.windows is populated with workspace_id, and it matches NiriService.allWorkspaces[].id.
  • Yet WorkspaceSwitcher.qml isOccupied (the occupied workspace color not working. #2538-fixed version):
    if (CompositorService.isNiri)
        return NiriService.windows?.some(win => win.workspace_id === modelData?.id) ?? false;
    returns false for these workspaces.

Hypotheses (unverified — for maintainer to check)

Two candidate causes, either or both:

  1. Stale reactive binding. isOccupied may not re-evaluate when windows change. NiriService.windows is populated asynchronously (IPC), so if the binding first evaluates before windows load, and the dependency on NiriService.windows isn't re-tracked (possibly through the ?. in NiriService.windows?.some(...)), isOccupied stays false. This would also explain why showOccupiedWorkspacesOnly acts as a no-op, and why the differently-structured FocusedApp reactive path works.
  2. modelData?.id not resolving to the workspace id in the switcher delegate for named workspaces.

The isOccupied and is_urgent checks (WorkspaceSwitcher.qml:1529) both depend on this match, so occupied and urgent coloring are affected. I haven't verified either hypothesis — happy to test a patch and report back.

Possibly related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions