2009-08-24 16:20:11 +02:00
|
|
|
---------------------------------------------------------------------------
|
2014-05-20 13:02:39 +02:00
|
|
|
--- Apply rules to clients at startup.
|
2014-05-19 12:27:10 +02:00
|
|
|
--
|
2009-08-24 16:20:11 +02:00
|
|
|
-- @author Julien Danjou <julien@danjou.info>
|
|
|
|
-- @copyright 2009 Julien Danjou
|
|
|
|
-- @release @AWESOME_VERSION@
|
2014-05-20 13:02:39 +02:00
|
|
|
-- @module awful.rules
|
2009-08-24 16:20:11 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Grab environment we need
|
|
|
|
local client = client
|
2016-04-10 11:03:01 +02:00
|
|
|
local screen = screen
|
2009-11-08 20:40:38 +01:00
|
|
|
local table = table
|
2009-08-24 16:20:11 +02:00
|
|
|
local type = type
|
|
|
|
local ipairs = ipairs
|
|
|
|
local pairs = pairs
|
2016-04-10 11:15:21 +02:00
|
|
|
local atag = require("awful.tag")
|
2009-08-24 16:20:11 +02:00
|
|
|
|
2012-06-12 20:13:09 +02:00
|
|
|
local rules = {}
|
2009-08-24 16:20:11 +02:00
|
|
|
|
2014-05-19 12:27:10 +02:00
|
|
|
--[[--
|
|
|
|
This is the global rules table.
|
|
|
|
|
|
|
|
You should fill this table with your rule and properties to apply.
|
|
|
|
For example, if you want to set xterm maximized at startup, you can add:
|
|
|
|
|
|
|
|
{ rule = { class = "xterm" },
|
|
|
|
properties = { maximized_vertical = true, maximized_horizontal = true } }
|
|
|
|
|
|
|
|
If you want to set mplayer floating at startup, you can add:
|
|
|
|
|
|
|
|
{ rule = { name = "MPlayer" },
|
|
|
|
properties = { floating = true } }
|
|
|
|
|
|
|
|
If you want to put Firefox on a specific tag at startup, you can add:
|
|
|
|
|
|
|
|
{ rule = { instance = "firefox" },
|
|
|
|
properties = { tag = mytagobject } }
|
|
|
|
|
2016-04-10 11:15:21 +02:00
|
|
|
Alternatively, you can specify the tag by name:
|
|
|
|
|
|
|
|
{ rule = { instance = "firefox" },
|
|
|
|
properties = { tag = "3" } }
|
|
|
|
|
2016-04-10 11:03:01 +02:00
|
|
|
If you want to put Thunderbird on a specific screen at startup, use:
|
|
|
|
|
|
|
|
{ rule = { instance = "Thunderbird" },
|
|
|
|
properties = { screen = 1 } }
|
|
|
|
|
|
|
|
Assuming that your X11 server supports the RandR extension, you can also specify
|
|
|
|
the screen by name:
|
|
|
|
|
|
|
|
{ rule = { instance = "Thunderbird" },
|
|
|
|
properties = { screen = "VGA1" } }
|
|
|
|
|
2014-05-19 12:27:10 +02:00
|
|
|
If you want to put Emacs on a specific tag at startup, and immediately switch
|
|
|
|
to that tag you can add:
|
|
|
|
|
|
|
|
{ rule = { class = "Emacs" },
|
|
|
|
properties = { tag = mytagobject, switchtotag = true } }
|
|
|
|
|
|
|
|
If you want to apply a custom callback to execute when a rule matched,
|
|
|
|
for example to pause playing music from mpd when you start dosbox, you
|
|
|
|
can add:
|
|
|
|
|
|
|
|
{ rule = { class = "dosbox" },
|
|
|
|
callback = function(c)
|
2015-09-30 00:05:56 +02:00
|
|
|
awful.spawn('mpc pause')
|
2014-05-19 12:27:10 +02:00
|
|
|
end }
|
|
|
|
|
|
|
|
Note that all "rule" entries need to match. If any of the entry does not
|
|
|
|
match, the rule won't be applied.
|
|
|
|
|
2016-04-10 11:01:53 +02:00
|
|
|
If a client matches multiple rules, they are applied in the order they are
|
2014-05-19 12:27:10 +02:00
|
|
|
put in this global rules table. If the value of a rule is a string, then the
|
|
|
|
match function is used to determine if the client matches the rule.
|
|
|
|
|
|
|
|
If the value of a property is a function, that function gets called and
|
|
|
|
function's return value is used for the property.
|
|
|
|
|
|
|
|
To match multiple clients to a rule one need to use slightly different
|
|
|
|
syntax:
|
|
|
|
|
|
|
|
{ rule_any = { class = { "MPlayer", "Nitrogen" }, instance = { "xterm" } },
|
|
|
|
properties = { floating = true } }
|
|
|
|
|
|
|
|
To match multiple clients with an exception one can couple `rules.except` or
|
|
|
|
`rules.except_any` with the rules:
|
|
|
|
|
|
|
|
{ rule = { class = "Firefox" },
|
|
|
|
except = { instance = "Navigator" },
|
|
|
|
properties = {floating = true},
|
|
|
|
},
|
|
|
|
|
|
|
|
{ rule_any = { class = { "Pidgin", "Xchat" } },
|
|
|
|
except_any = { role = { "conversation" } },
|
2016-04-10 11:18:07 +02:00
|
|
|
properties = { tag = "1" }
|
2014-05-19 12:27:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
{ rule = {},
|
|
|
|
except_any = { class = { "Firefox", "Vim" } },
|
|
|
|
properties = { floating = true }
|
|
|
|
}
|
|
|
|
]]--
|
2012-06-12 20:13:09 +02:00
|
|
|
rules.rules = {}
|
2009-08-24 16:20:11 +02:00
|
|
|
|
2014-03-09 22:03:32 +01:00
|
|
|
--- Check if a client matches a rule.
|
2015-02-15 22:33:23 +01:00
|
|
|
-- @client c The client.
|
|
|
|
-- @tab rule The rule to check.
|
2014-05-19 12:27:10 +02:00
|
|
|
-- @treturn bool True if it matches, false otherwise.
|
2012-06-12 20:13:09 +02:00
|
|
|
function rules.match(c, rule)
|
2011-03-27 17:22:50 +02:00
|
|
|
if not rule then return false end
|
2009-08-24 16:20:11 +02:00
|
|
|
for field, value in pairs(rule) do
|
2009-08-24 16:43:00 +02:00
|
|
|
if c[field] then
|
2009-08-26 17:49:38 +02:00
|
|
|
if type(c[field]) == "string" then
|
|
|
|
if not c[field]:match(value) and c[field] ~= value then
|
|
|
|
return false
|
|
|
|
end
|
2009-08-24 16:43:00 +02:00
|
|
|
elseif c[field] ~= value then
|
|
|
|
return false
|
|
|
|
end
|
2009-09-03 19:21:59 +02:00
|
|
|
else
|
|
|
|
return false
|
2009-08-24 16:20:11 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2014-03-09 22:03:32 +01:00
|
|
|
--- Check if a client matches any part of a rule.
|
2015-02-15 22:33:23 +01:00
|
|
|
-- @client c The client.
|
|
|
|
-- @tab rule The rule to check.
|
2014-05-19 12:27:10 +02:00
|
|
|
-- @treturn bool True if at least one rule is matched, false otherwise.
|
2012-06-12 20:13:09 +02:00
|
|
|
function rules.match_any(c, rule)
|
2011-03-27 17:22:50 +02:00
|
|
|
if not rule then return false end
|
2010-09-13 22:48:16 +02:00
|
|
|
for field, values in pairs(rule) do
|
|
|
|
if c[field] then
|
|
|
|
for _, value in ipairs(values) do
|
|
|
|
if c[field] == value then
|
|
|
|
return true
|
|
|
|
elseif type(c[field]) == "string" and c[field]:match(value) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2015-08-30 13:01:59 +02:00
|
|
|
--- Does a given rule entry match a client?
|
|
|
|
-- @client c The client.
|
|
|
|
-- @tab entry Rule entry (with keys `rule`, `rule_any`, `except` and/or
|
2015-10-14 00:21:29 +02:00
|
|
|
-- `except_any`).
|
2015-08-30 13:01:59 +02:00
|
|
|
-- @treturn bool
|
|
|
|
function rules.matches(c, entry)
|
|
|
|
return (rules.match(c, entry.rule) or rules.match_any(c, entry.rule_any)) and
|
|
|
|
(not rules.match(c, entry.except) and not rules.match_any(c, entry.except_any))
|
|
|
|
end
|
|
|
|
|
2014-03-09 22:03:32 +01:00
|
|
|
--- Get list of matching rules for a client.
|
2015-02-15 22:33:23 +01:00
|
|
|
-- @client c The client.
|
|
|
|
-- @tab _rules The rules to check. List with "rule", "rule_any", "except" and
|
2015-10-14 00:21:29 +02:00
|
|
|
-- "except_any" keys.
|
2015-02-15 22:33:23 +01:00
|
|
|
-- @treturn table The list of matched rules.
|
2014-03-09 22:03:32 +01:00
|
|
|
function rules.matching_rules(c, _rules)
|
|
|
|
local result = {}
|
|
|
|
for _, entry in ipairs(_rules) do
|
2015-08-30 13:01:59 +02:00
|
|
|
if (rules.matches(c, entry)) then
|
2014-03-09 22:03:32 +01:00
|
|
|
table.insert(result, entry)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Check if a client matches a given set of rules.
|
2015-02-15 22:33:23 +01:00
|
|
|
-- @client c The client.
|
2015-08-30 13:01:59 +02:00
|
|
|
-- @tab _rules The rules to check. List of tables with `rule`, `rule_any`,
|
|
|
|
-- `except` and `except_any` keys.
|
2014-05-19 12:27:10 +02:00
|
|
|
-- @treturn bool True if at least one rule is matched, false otherwise.
|
2015-08-30 13:01:59 +02:00
|
|
|
function rules.matches_list(c, _rules)
|
|
|
|
for _, entry in ipairs(_rules) do
|
|
|
|
if (rules.matches(c, entry)) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2014-03-09 22:03:32 +01:00
|
|
|
end
|
|
|
|
|
2014-03-16 01:45:31 +01:00
|
|
|
--- Apply awful.rules.rules to a client.
|
2015-02-15 22:33:23 +01:00
|
|
|
-- @client c The client.
|
2012-06-12 20:13:09 +02:00
|
|
|
function rules.apply(c)
|
2016-04-16 12:23:09 +02:00
|
|
|
|
2009-10-24 15:05:09 +02:00
|
|
|
local props = {}
|
2009-11-08 20:40:38 +01:00
|
|
|
local callbacks = {}
|
2014-03-09 22:03:32 +01:00
|
|
|
|
|
|
|
for _, entry in ipairs(rules.matching_rules(c, rules.rules)) do
|
|
|
|
if entry.properties then
|
|
|
|
for property, value in pairs(entry.properties) do
|
|
|
|
props[property] = value
|
2009-11-08 20:40:38 +01:00
|
|
|
end
|
2009-08-24 16:20:11 +02:00
|
|
|
end
|
2014-03-09 22:03:32 +01:00
|
|
|
if entry.callback then
|
|
|
|
table.insert(callbacks, entry.callback)
|
|
|
|
end
|
2009-08-24 16:20:11 +02:00
|
|
|
end
|
2009-10-24 15:05:09 +02:00
|
|
|
|
2014-03-16 01:45:31 +01:00
|
|
|
rules.execute(c, props, callbacks)
|
|
|
|
end
|
|
|
|
|
2016-04-16 12:23:09 +02:00
|
|
|
local function add_to_tag(c, t)
|
|
|
|
if not t then return end
|
|
|
|
|
|
|
|
local tags = c:tags()
|
|
|
|
table.insert(tags, t)
|
|
|
|
c:tags(tags)
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Extra rules properties.
|
|
|
|
--
|
|
|
|
-- These properties are used in the rules only and are not sent to the client
|
|
|
|
-- afterward.
|
|
|
|
--
|
|
|
|
-- To add a new properties, just do:
|
|
|
|
--
|
|
|
|
-- function awful.rules.extra_properties.my_new_property(c, value)
|
|
|
|
-- -- do something
|
|
|
|
-- end
|
|
|
|
--
|
|
|
|
-- @tfield table awful.rules.extra_properties
|
|
|
|
rules.extra_properties = {}
|
|
|
|
|
|
|
|
--- Extra high priority properties.
|
|
|
|
--
|
|
|
|
-- Some properties, such as anything related to tags, geometry or focus, will
|
|
|
|
-- cause a race condition if set in the main property section. This is why
|
|
|
|
-- they have a section for them.
|
|
|
|
--
|
|
|
|
-- To add a new properties, just do:
|
|
|
|
--
|
|
|
|
-- function awful.rules.high_priority_properties.my_new_property(c, value)
|
|
|
|
-- -- do something
|
|
|
|
-- end
|
|
|
|
--
|
|
|
|
-- @tfield table awful.rules.high_priority_properties
|
|
|
|
rules.high_priority_properties = {}
|
|
|
|
|
|
|
|
rules.delayed_properties = {
|
|
|
|
focus = true,
|
|
|
|
switchtotag = true,
|
|
|
|
}
|
2014-03-16 01:45:31 +01:00
|
|
|
|
|
|
|
--- Apply properties and callbacks to a client.
|
2015-02-15 22:33:23 +01:00
|
|
|
-- @client c The client.
|
|
|
|
-- @tab props Properties to apply.
|
2015-02-26 22:06:34 +01:00
|
|
|
-- @tab[opt] callbacks Callbacks to apply.
|
2014-03-16 01:45:31 +01:00
|
|
|
function rules.execute(c, props, callbacks)
|
2016-04-16 12:23:09 +02:00
|
|
|
|
|
|
|
-- Some properties need to be handled first. For example, many properties
|
|
|
|
-- depend that the client is tagged, this isn't yet the case.
|
|
|
|
for prop, handler in pairs(rules.high_priority_properties) do
|
|
|
|
local value = props[prop]
|
|
|
|
|
|
|
|
if value ~= nil then
|
|
|
|
if type(value) == "function" then
|
|
|
|
value = value(c, props)
|
|
|
|
end
|
|
|
|
|
|
|
|
handler(c, props[prop])
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
2016-04-12 09:33:39 +02:00
|
|
|
|
2009-10-24 15:05:09 +02:00
|
|
|
for property, value in pairs(props) do
|
2016-04-16 12:23:09 +02:00
|
|
|
|
|
|
|
|
2012-07-06 17:30:35 +02:00
|
|
|
if property ~= "focus" and type(value) == "function" then
|
2016-04-20 06:02:38 +02:00
|
|
|
value = value(c, props)
|
2012-07-06 17:30:35 +02:00
|
|
|
end
|
2016-04-16 12:23:09 +02:00
|
|
|
|
|
|
|
-- Some properties are handled elsewhere
|
|
|
|
if not rules.high_priority_properties[property] and not rules.delayed_properties[property] then
|
|
|
|
if property == "tag" then
|
|
|
|
local t = value
|
|
|
|
if type(t) == "string" then
|
|
|
|
t = atag.find_by_name(props.screen, t)
|
|
|
|
elseif type(t) == "function" then
|
|
|
|
t = value(c, props)
|
|
|
|
end
|
|
|
|
|
|
|
|
if t then
|
|
|
|
c.screen = t.screen
|
|
|
|
c:tags{ t }
|
|
|
|
end
|
|
|
|
elseif property == "height" or property == "width" or
|
|
|
|
property == "x" or property == "y" then
|
|
|
|
local geo = c:geometry();
|
|
|
|
geo[property] = value
|
|
|
|
c:geometry(geo);
|
|
|
|
elseif rules.extra_properties[property] then
|
|
|
|
rules.extra_properties[property](c, value)
|
|
|
|
elseif type(c[property]) == "function" then
|
2016-04-12 09:33:39 +02:00
|
|
|
c[property](c, value)
|
|
|
|
else
|
|
|
|
c[property] = value
|
|
|
|
end
|
2009-10-24 15:05:09 +02:00
|
|
|
end
|
|
|
|
end
|
2009-11-08 20:40:38 +01:00
|
|
|
|
2016-04-10 11:32:53 +02:00
|
|
|
-- Only do this after the tag has been (possibly) set
|
2016-04-16 12:23:09 +02:00
|
|
|
if props.switchtotag and c.first_tag then
|
2016-04-10 11:32:53 +02:00
|
|
|
c.first_tag:view_only()
|
|
|
|
end
|
|
|
|
|
2014-03-16 01:45:31 +01:00
|
|
|
-- Apply all callbacks.
|
2014-04-01 10:48:18 +02:00
|
|
|
if callbacks then
|
2016-02-07 15:02:25 +01:00
|
|
|
for _, callback in pairs(callbacks) do
|
2014-04-01 10:48:18 +02:00
|
|
|
callback(c)
|
|
|
|
end
|
2009-11-08 20:40:38 +01:00
|
|
|
end
|
|
|
|
|
2015-07-11 22:53:57 +02:00
|
|
|
-- Do this at last so we do not erase things done by the focus signal.
|
2012-07-06 17:30:35 +02:00
|
|
|
if props.focus and (type(props.focus) ~= "function" or props.focus(c)) then
|
2015-07-10 15:07:53 +02:00
|
|
|
c:emit_signal('request::activate', "rules", {raise=true})
|
2009-10-24 15:05:09 +02:00
|
|
|
end
|
2009-08-24 16:20:11 +02:00
|
|
|
end
|
|
|
|
|
2015-09-30 00:05:56 +02:00
|
|
|
function rules.completed_with_payload_callback(c, props)
|
|
|
|
rules.execute(c, props, type(props.callback) == "function" and
|
|
|
|
{props.callback} or props.callback )
|
|
|
|
end
|
|
|
|
|
|
|
|
client.connect_signal("spawn::completed_with_payload", rules.completed_with_payload_callback)
|
|
|
|
|
2012-06-12 20:13:09 +02:00
|
|
|
client.connect_signal("manage", rules.apply)
|
2009-08-24 16:20:11 +02:00
|
|
|
|
2012-06-12 20:13:09 +02:00
|
|
|
return rules
|
|
|
|
|
2011-09-11 16:50:01 +02:00
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|