Defer startup with request::rules
If you write a custom module that wants to add a ruled.client or ruled.notification rule, the obvious approach is to append the rule at module load time:
-- my_module.lua
local ruled = require("ruled")
table.insert(ruled.client.rules, {
rule = { class = "Firefox" },
properties = { tag = "web" },
})
This works until two modules both want to add rules and the order they're require-d in matters, or a module is require-d after clients have already been scanned, or your rc.lua reorganizes and one module suddenly loads before the rules table exists.
request::rules is the structured way around this. Both ruled.client and ruled.notification emit it once, on client.scanning, after all modules have had a chance to load and before any client is matched against rules.
The pattern
-- my_module.lua
local ruled = require("ruled")
ruled.client.connect_signal("request::rules", function()
ruled.client.append_rule {
rule = { class = "Firefox" },
properties = { tag = "web" },
}
end)
You connect the handler at module load time. The handler doesn't run until the compositor is ready to apply rules, which gives every other module a chance to register first.
Why this is better than appending directly
- Order independence. Modules can be
require-d in any order inrc.lua. The rules table is built once, all at once, whenrequest::rulesfires. - Late
requireworks. If yourequire("my_module")from inside another callback (lazy loading), and that happens before scanning, the handler still runs at the right moment. - It matches the library's expectation.
ruled.clientis designed aroundrequest::rules. Other modules that introspect or modify the rule list assume the rules are added through this signal.
For notifications
ruled.notification works the same way:
ruled.notification.connect_signal("request::rules", function()
ruled.notification.append_rule {
rule = { app_name = "spotify" },
properties = { ignore = true },
}
end)
If a Spotify notification arrives, naughty applies the rule and ignores it.
Multiple handlers compose
Each module can connect its own request::rules handler. They all run in connect order. You don't need to coordinate between modules:
-- module_a.lua
ruled.client.connect_signal("request::rules", function()
ruled.client.append_rule { ... }
end)
-- module_b.lua
ruled.client.connect_signal("request::rules", function()
ruled.client.append_rule { ... }
end)
Both rules end up in the rules table by the time the first client is matched.
When not to use request::rules
The signal fires once at startup. If you want to add a rule at runtime (e.g. user toggles a setting), connecting to request::rules won't help; that signal already fired. Use ruled.client.append_rule directly:
function on_user_setting_changed()
ruled.client.append_rule {
rule = { class = "Slack" },
properties = { tag = "chat" },
}
end
This applies to clients managed after the rule is appended. Already-managed clients won't be re-evaluated.
Loading order, in full
The full startup sequence looks like:
rc.luaruns. Modules arerequire-d. Each connects itsrequest::ruleshandler.- Compositor begins scanning for already-running clients.
client.scanningfires.ruled.clientemitsrequest::rules. All connected handlers run, in order. Rules are appended.ruled.notificationemits its ownrequest::rules.- Each pre-existing client is managed, with rules now in place.
client.scannedfires. New clients from this point also see the rules.
See Signals (concepts) for the larger startup sequence.