How Hyprland Workspace Rules Work: Parsing, Storage, and Runtime Enforcement

Hyprland workspace rules bind workspaces to monitors, set persistence, and configure other attributes by parsing workspace lines into CWorkspaceRule objects that are stored in WorkspaceRuleManager and applied during workspace creation.

The hyprwm/Hyprland source code implements workspace rules through a dedicated pipeline that converts configuration directives into runtime behavior. Understanding how Hyprland workspace rules work requires examining the parser, the rule manager, and the workspace creation logic. The system relies on WorkspaceRuleManager to resolve which monitor should host a workspace and whether that workspace should persist across sessions.

Parsing Configuration into CWorkspaceRule Objects

When Hyprland reads hyprland.conf, it processes each line beginning with the workspace keyword. The parser normalizes the workspace selector and instantiates a CWorkspaceRule defined in src/config/shared/workspace/WorkspaceRule.hpp.

The Workspace Keyword Syntax

Each rule starts with the workspace directive followed by a selector and key-value pairs. The parser distinguishes between numeric IDs, named workspaces, and special workspaces using the special: prefix.

The following syntax patterns are supported according to the Hyprland source:

  • workspace = 1 monitor:DP-1 binds workspace 1 to monitor DP-1.
  • workspace = code persistent:true monitor:HDMI-A-1 creates a named workspace code, pins it to HDMI-A-1, and makes it persistent.
  • workspace = special:something enabled:false defines a special workspace that starts disabled.

CWorkspaceRule Data Structure

Every parsed rule populates a CWorkspaceRule object with these key fields:

  • m_workspaceString — the raw selector string from the configuration.
  • m_workspaceId — the resolved numeric workspace ID.
  • m_monitor — the target monitor name.
  • m_persistent — a flag indicating the workspace survives reloads.
  • m_enabled — a flag controlling whether the workspace starts active.

Special workspaces receive IDs in the range starting at SPECIAL_WORKSPACE_START, which the parser handles separately from standard numeric or named workspaces.

Storing Rules in WorkspaceRuleManager

After parsing, all CWorkspaceRule instances live inside the global Config::workspaceRuleMgr() singleton implemented in src/config/shared/workspace/WorkspaceRuleManager.cpp. This manager provides the central lookup API used by the compositor and plugins.

Rule Manager API

The manager exposes helper methods for querying rules without scanning the configuration again:

  • getBoundMonitorForWS(const std::string& wsName) returns the monitor name a workspace should occupy, if a rule exists.
  • getAllWorkspaceRules() yields the full list for iteration.

Persistent workspace IDs are tracked internally in the manager’s persistentWorkspaceIDs vector. This vector guarantees that persistent IDs are not reused and remain stable across configuration reloads.

Applying Workspace Rules at Runtime

Rule enforcement happens during workspace creation, not during parsing. When a new workspace is spawned, CWorkspaceStateTracker::createWorkspace(...) in src/state/WorkspaceState.cpp queries WorkspaceRuleManager to determine placement and properties. The runtime also uses utilities in src/state/WorkspaceQueryCore.hpp to locate workspaces by ID, name, or rule during processing.

Monitor Binding During Creation

Inside CWorkspaceStateTracker::createWorkspace(...), the compositor checks for a matching rule before finalizing the workspace:

// Inside CWorkspaceStateTracker::createWorkspace(...)
if (const auto pMonitor = Config::workspaceRuleMgr()->getBoundMonitorForWS(NAME); pMonitor)
    // The workspace is bound to a monitor → assign it

If getBoundMonitorForWS returns a monitor, the workspace is added to that monitor’s workspace list. The runtime also marks the monitor as seen for that workspace via m_seenMonitorWorkspaceMap, ensuring subsequent lookups respect the initial binding.

Persistent and Special Workspaces

Persistent workspaces rely on the persistentWorkspaceIDs vector inside the rule manager. Because these IDs are reserved, the compositor will not destroy or recycle them during normal session cleanup. Special workspaces identified by the special: prefix use distinct ID ranges and can carry flags such as enabled:false to start in a disabled state.

Practical Configuration Examples

You can define and query workspace rules from the configuration file, plugins, or external scripts.

hyprland.conf Syntax

Add the following line to hyprland.conf to pin workspace 3 to HDMI-A-1 and keep it persistent:


# Pin workspace 3 to HDMI-A-1 and keep it persistent

workspace = 3 monitor:HDMI-A-1 persistent:true

Querying Rules from a Plugin

Plugin authors can access the rule manager directly through the Config namespace. The snippet below logs the monitor binding for workspace 3:

auto* ruleMgr = Config::workspaceRuleMgr();
if (auto monitor = ruleMgr->getBoundMonitorForWS("3"); monitor) {
    Hyprland::Log::info("Workspace 3 is bound to monitor {}", *monitor);
}

Creating Rules via hyprctl

You can also inject workspace rules at runtime using a shell command:

hyprctl --batch "keyword workspace = chat persistent:true monitor:DP-1"

Summary

Hyprland workspace rules bridge configuration text and runtime window management through a three-stage pipeline:

Frequently Asked Questions

What is the syntax for binding a workspace to a monitor in Hyprland?

Use the workspace keyword followed by the workspace selector and monitor:<name>. For example, workspace = 1 monitor:DP-1 tells Hyprland to open workspace 1 on monitor DP-1. You can combine this with other flags like persistent:true in the same rule.

How does Hyprland store workspace rules internally?

The compositor stores every parsed rule as a CWorkspaceRule object inside Config::workspaceRuleMgr(), an instance of the class declared in src/config/shared/workspace/WorkspaceRuleManager.hpp. The manager keeps a vector of all rules and a separate persistentWorkspaceIDs vector to track which workspaces must survive reloads.

When does Hyprland apply workspace rules to a monitor?

Rules are applied during workspace creation inside CWorkspaceStateTracker::createWorkspace(...) in src/state/WorkspaceState.cpp. The function calls getBoundMonitorForWS() to determine whether a rule forces the workspace onto a specific monitor before the workspace becomes visible.

Can plugins query workspace rules at runtime?

Yes. Plugins can call Config::workspaceRuleMgr()->getBoundMonitorForWS(const std::string& wsName) to retrieve a workspace’s bound monitor, or getAllWorkspaceRules() to iterate over the entire rule set defined in src/config/shared/workspace/WorkspaceRuleManager.cpp.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →