#1426·glazewm

[Feature request] Bind workspaces to a monitor by stable identifier (device path / hardware id)

Author: lecram89Created Aug 28, 2026Updated Aug 28, 2026

Feature request: bind workspaces to a monitor by stable identifier (device path / hardware id)

Problem

bind_to_monitor takes a numeric index into GlazeWM's geometrically sorted monitor list (leftmost = 0, see wm-platform/src/dispatcher.rs:470). That index depends purely on how displays are arranged, so it changes from desk to desk and there is no way to express "this workspace belongs on this physical screen".

Concrete case (three monitors at the office, two at home):

arrangement leftmost (index 0) index 1 index 2
Office desk laptop panel primary 1920x1080 second 1920x1080
Home dock primary ultrawide laptop panel

With keep_alive: true workspaces bound to index 0, every desk change either puts the workspaces on the wrong screen or (worse) leaves a monitor with no matching binding and no inactive workspace left, in which case GlazeWM aborts at startup:

"No workspace config available to activate workspace"

That abort occurred with nine bound workspaces serving two monitors when a third display was connected. The only workarounds today are per-desk config edits or a convention like "always arrange the main screen leftmost in Windows" — the latter is what I currently do, and it is fragile.

Evidence that the identity data already exists

MonitorDto (wm-common/src/dtos/monitor_dto.rs) already carries device_name, device_path and hardware_id, populated from EnumDisplayDevices in wm-platform/src/platform_impl/windows/display.rs:205-232.

More importantly, the matching logic this feature needs already exists. find_matching_monitor in wm/src/events/handle_display_settings_changed.rs matches a display to an existing monitor by:

  1. its handle,
  2. its device path,
  3. its hardware id (guarded by a uniqueness check, since the hardware id identifies a model, not a unit).

That function's own comment documents the trade-offs: handles and device paths are unique but can change; the hardware id is stable but not guaranteed unique.

Proposal

Add a new optional field to WorkspaceConfig, e.g.:

yaml
workspaces:
  - name: '1'
    bind_to_monitor_id:
      type: hardware_id        # or: device_path | device_name
      value: 'DELA1B9'

Backwards compatible: bind_to_monitor keeps working exactly as today; a workspace may use either binding, and the id-based one wins when both are set. Matching reuses the precedence semantics of find_matching_monitordevice_path matches first (unique, changes if the cable moves), hardware_id second (stable, ambiguous if two identical monitors are attached). device_name (\\.\DISPLAY1) is also available and has the same caveat as the numeric index but at least survives re-sorting.

Why this solves the reported crash too

With an id-based binding, the "which monitor am I" question no longer depends on sorting at all — move_bounded_workspaces_to_new_monitor can match monitors by identity. The sorted_displays TODO at dispatcher.rs:470 ("Need to assign workspaces after sorting monitors because of bind_to_monitor") is exactly the coupling this would remove.

Implementation outline (v3.10.1)

  1. wm-common/src/parsed_config.rs:384 — extend WorkspaceConfig with bind_to_monitor_id: Option<MonitorBinding> (#[serde(default)]).
  2. wm/src/commands/monitor/add_monitor.rs:52 — extend the filter in move_bounded_workspaces_to_new_monitor to match by identity, reusing find_matching_monitor's comparison semantics.
  3. wm/src/user_config.rs:313 (workspace_config_for_monitor) and :332 (next_inactive_workspace_config) — same.
  4. wm/src/commands/workspace/activate_workspace.rs:37 — resolve the id to a monitor via monitor.native_properties() instead of monitor.index().
  5. wm/src/commands/workspace/update_workspace_config.rs:37-39 and wm-common/src/app_command.rs:430 — carry the new field through the config merge and the invoke update-workspace-config CLI.
  6. resources/assets/sample-config.yaml — document the field. (bind_to_monitor is currently undocumented there at all.)

No new Win32 code is required; the identity plumbing is complete.

Related issues

  • #503 — bind_to_monitor only applies on full restart (still the case for existing workspaces; a config reload never re-binds)
  • #649 — workspaces are not moved to their bound monitor on config reload
  • #1193 — "Workspace bind to monitor is not intuitive"

Open questions for maintainers

  • Field shape: second field vs. an untagged enum on bind_to_monitor (integer or string)? A second field is simpler and fully backwards compatible; an untagged enum is more ergonomic but a breaking shape change for anyone deserializing the config.
  • Should id-based binding also make wm-reload-config re-bind (#649), or stay consistent with the current "startup and monitor add only" semantics?