Improved swallowing (#140)

* Make swallowing more robust

Changed the flags for the pstree command so it is a single line with all
ancestors.
Removed usage of sed, grep and tr so the iopopen call should be faster.
Find and match of parent pid in the ancestors string is done in lua so
should be faster.

* Improved swallow filter

Now you cna filter window swallowing with 2 tables, one for clients that
cannot be swallowed (dont_swallow_classname_list) and one for clients
that cannot swallow their parents (cant_swallow_classname_list).

* remove occurences of res var

* renamed functions and theme vars more descriptive

* updated module documentation

* check functions combined, is_in_table  1 return

* add old theme vars for compatibility

* short swallow logic and robust filter assignment

* ignore splash and dialog parents

this prevents krita and similar missbehaving programs from swallowing their
splash window.

* consistent docs formatting

* convert module to use an async function

* removed undefined error() function call
this was probably an error handling/logging on the code i used as template for
the async function and it's callback.

* deal with corner case: parent exits before client

if the parent is destroyed "on background" then awesome will show a warning.

* added meself to authors

i don't see my additions as substantial enough to justify this but... ¯\_(ツ)_/¯
This commit is contained in:
eylles 2022-01-04 04:14:12 -06:00 committed by GitHub
parent e0423796b0
commit 7542251b72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 27 deletions

View File

@ -7,3 +7,4 @@ The following developers have contributed major code to bling:
* [HumblePresent](https://github.com/HumblePresent) * [HumblePresent](https://github.com/HumblePresent)
* [Kasper24](https://github.com/Kasper24) * [Kasper24](https://github.com/Kasper24)
* [undefinedDarkness](https://github.com/undefinedDarkness) * [undefinedDarkness](https://github.com/undefinedDarkness)
* [eylles](https://github.com/eylles)

View File

@ -13,8 +13,9 @@ bling.module.window_swallowing.toggle() -- toggles window swallowing
### Theme Variables ### Theme Variables
```lua ```lua
theme.dont_swallow_classname_list = {"firefox", "Gimp"} -- list of class names that should not be swallowed theme.parent_filter_list = {"firefox", "Gimp"} -- class names list of parents that should not be swallowed
theme.dont_swallow_filter_activated = true -- whether the filter above should be active theme.child_filter_list = { "Dragon" } -- class names list that should not swallow their parents
theme.swallowing_filter = true -- whether the filters above should be active
``` ```
### Preview ### Preview

View File

@ -11,53 +11,93 @@ local helpers = require(tostring(...):match(".*bling") .. ".helpers")
local window_swallowing_activated = false local window_swallowing_activated = false
-- you might want to add or remove applications here -- you might want to add or remove applications here
local dont_swallow_classname_list = beautiful.dont_swallow_classname_list local parent_filter_list = beautiful.parent_filter_list
or beautiful.dont_swallow_classname_list
or { "firefox", "Gimp", "Google-chrome" } or { "firefox", "Gimp", "Google-chrome" }
local activate_dont_swallow_filter = beautiful.dont_swallow_filter_activated local child_filter_list = beautiful.child_filter_list
or true or beautiful.dont_swallow_classname_list or { }
-- checks if client classname matches with any entry of the dont-swallow-list -- for boolean values the or chain way to set the values breaks with 2 vars
local function check_if_swallow(c) -- and always defaults to true so i had to do this to se the right value...
if not activate_dont_swallow_filter then local swallowing_filter = true
return true local filter_vars = { beautiful.swallowing_filter, beautiful.dont_swallow_filter_activated }
for _, var in pairs(filter_vars) do
swallowing_filter = var
end end
for _, classname in ipairs(dont_swallow_classname_list) do
if classname == c.class then -- check if element exist in table
return false -- returns true if it is
local function is_in_table(element, table)
local res = false
for _, value in pairs(table) do
if element:match(value) then
res = true
break
end end
end end
return true return res
end end
-- if the swallowing filter is active checks the child and parent classes
-- against their filters
local function check_swallow(parent, child)
local res = true
if swallowing_filter then
local prnt = not is_in_table(parent, parent_filter_list)
local chld = not is_in_table(child, child_filter_list)
res = ( prnt and chld )
end
return res
end
-- async function to get the parent's pid
-- recieves a child process pid and a callback function
-- parent_pid in format "init(1)---ancestorA(pidA)---ancestorB(pidB)...---process(pid)"
function get_parent_pid(child_ppid, callback)
local ppid_cmd = string.format("pstree -A -p -s %s", child_ppid)
awful.spawn.easy_async(ppid_cmd, function(stdout, stderr, reason, exit_code)
-- primitive error checking
if stderr and stderr ~= "" then
callback(stderr)
return
end
local ppid = stdout
callback(nil, ppid)
end)
end
-- the function that will be connected to / disconnected from the spawn client signal -- the function that will be connected to / disconnected from the spawn client signal
local function manage_clientspawn(c) local function manage_clientspawn(c)
-- get the last focused window to check if it is a parent window -- get the last focused window to check if it is a parent window
local parent_client = awful.client.focus.history.get(c.screen, 1) local parent_client = awful.client.focus.history.get(c.screen, 1)
if not parent_client then if not parent_client then
return return
elseif parent_client.type == "dialog" or parent_client.type == "splash" then
return
end end
-- io.popen is normally discouraged. Should probably be changed get_parent_pid(c.pid, function(err, ppid)
local handle = io.popen( if err then
[[pstree -T -p -a -s ]] return
.. tostring(c.pid) end
.. [[ | sed '2q;d' | grep -o '[0-9]*$' | tr -d '\n']] parent_pid = ppid
)
local parent_pid = handle:read("*a")
handle:close()
if if
(tostring(parent_pid) == tostring(parent_client.pid)) -- will search for "(parent_client.pid)" inside the parent_pid string
and check_if_swallow(c) ( tostring(parent_pid):find("("..tostring(parent_client.pid)..")") )
and check_swallow(parent_client.class, c.class)
then then
c:connect_signal("unmanage", function() c:connect_signal("unmanage", function()
if parent_client then
helpers.client.turn_on(parent_client) helpers.client.turn_on(parent_client)
helpers.client.sync(parent_client, c) helpers.client.sync(parent_client, c)
end
end) end)
helpers.client.sync(c, parent_client) helpers.client.sync(c, parent_client)
helpers.client.turn_off(parent_client) helpers.client.turn_off(parent_client)
end end
end)
end end
-- without the following functions that module would be autoloaded by require("bling") -- without the following functions that module would be autoloaded by require("bling")