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-1binds workspace1to monitorDP-1.workspace = code persistent:true monitor:HDMI-A-1creates a named workspacecode, pins it toHDMI-A-1, and makes it persistent.workspace = special:something enabled:falsedefines 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:
- Parsing: The config parser reads
workspacelines and buildsCWorkspaceRuleobjects insrc/config/shared/workspace/WorkspaceRule.hpp. - Storage:
WorkspaceRuleManager.cppmaintains all rules and providesgetBoundMonitorForWS()for fast lookup. - Application:
WorkspaceState.cppapplies monitor bindings and persistence flags whenCWorkspaceStateTracker::createWorkspace(...)runs.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →