Skip to content
This repository has been archived by the owner on Dec 18, 2024. It is now read-only.

Improved swallowing #140

Merged
merged 14 commits into from
Jan 4, 2022
1 change: 1 addition & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ The following developers have contributed major code to bling:
* [HumblePresent](https://github.com/HumblePresent)
* [Kasper24](https://github.com/Kasper24)
* [undefinedDarkness](https://github.com/undefinedDarkness)
* [eylles](https://github.com/eylles)
5 changes: 3 additions & 2 deletions docs/module/swal.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ bling.module.window_swallowing.toggle() -- toggles window swallowing

### Theme Variables
```lua
theme.dont_swallow_classname_list = {"firefox", "Gimp"} -- list of class names that should not be swallowed
theme.dont_swallow_filter_activated = true -- whether the filter above should be active
theme.parent_filter_list = {"firefox", "Gimp"} -- class names list of parents that should not be swallowed
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
Expand Down
90 changes: 65 additions & 25 deletions module/window_swallowing.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,93 @@ local helpers = require(tostring(...):match(".*bling") .. ".helpers")
local window_swallowing_activated = false

-- 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
eylles marked this conversation as resolved.
Show resolved Hide resolved
or beautiful.dont_swallow_classname_list
or { "firefox", "Gimp", "Google-chrome" }
local activate_dont_swallow_filter = beautiful.dont_swallow_filter_activated
or true
local child_filter_list = beautiful.child_filter_list
or beautiful.dont_swallow_classname_list or { }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought I have reading this : There is a corner case where the user can require the module before calling beautiful.init. In this situation, the user defined lists in their theme will be ignored because these variables here are global to the module and initialized only here, at the module first load.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is a corner case with all of bling and the docs specify requiring bling after beautiful.init

https://blingcorp.github.io/bling/#/?id=installation


-- checks if client classname matches with any entry of the dont-swallow-list
local function check_if_swallow(c)
if not activate_dont_swallow_filter then
return true
end
for _, classname in ipairs(dont_swallow_classname_list) do
if classname == c.class then
return false
-- for boolean values the or chain way to set the values breaks with 2 vars
-- and always defaults to true so i had to do this to se the right value...
local swallowing_filter = true
local filter_vars = { beautiful.swallowing_filter, beautiful.dont_swallow_filter_activated }
for _, var in pairs(filter_vars) do
swallowing_filter = var
end

-- check if element exist in table
-- 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
eylles marked this conversation as resolved.
Show resolved Hide resolved
end
end
return true
return res
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the function returns false when window_swallowing_activated is false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from what i can tell no, since window_swallowing_activated = false means that the swallowing function will be disconected from all clients.

105 local function start()
106     client.connect_signal("manage", manage_clientspawn)
107     window_swallowing_activated = true
108 end
109
110 local function stop()
111     client.disconnect_signal("manage", manage_clientspawn)
112     window_swallowing_activated = false
113 end
114
115 local function toggle()
116     if window_swallowing_activated then
117         stop()
118     else
119         start()
120     end
121 end

however if you meant the boolean value of swallowing_filter then reffering back to the truth table the output value of this function should only be false when the filter is active and either parent or child is in any of the filtering lists.

Copy link

@Aire-One Aire-One Dec 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I have forgotten the use of this variable and thought it has what the user change to activate the module... My bad.

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
local function manage_clientspawn(c)
-- get the last focused window to check if it is a parent window
local parent_client = awful.client.focus.history.get(c.screen, 1)
if not parent_client then
return
elseif parent_client.type == "dialog" or parent_client.type == "splash" then
return
Comment on lines +76 to +77

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as mentioned earlier some programs with splash windows can have the splash window as parent process of the main window, and in the case of krita it's splash wrongfully has the dialog type, now if krita is the only program that has that bug i don't know, will have to keep checking the window type of programs with splash windows to be sure.

end

-- io.popen is normally discouraged. Should probably be changed
local handle = io.popen(
[[pstree -T -p -a -s ]]
.. tostring(c.pid)
.. [[ | sed '2q;d' | grep -o '[0-9]*$' | tr -d '\n']]
)
local parent_pid = handle:read("*a")
handle:close()

get_parent_pid(c.pid, function(err, ppid)
if err then
return
end
parent_pid = ppid
if
(tostring(parent_pid) == tostring(parent_client.pid))
and check_if_swallow(c)
-- will search for "(parent_client.pid)" inside the parent_pid string
( tostring(parent_pid):find("("..tostring(parent_client.pid)..")") )
and check_swallow(parent_client.class, c.class)
then
c:connect_signal("unmanage", function()
helpers.client.turn_on(parent_client)
helpers.client.sync(parent_client, c)
if parent_client then
helpers.client.turn_on(parent_client)
helpers.client.sync(parent_client, c)
end
end)

helpers.client.sync(c, parent_client)
helpers.client.turn_off(parent_client)
end
end)
end

-- without the following functions that module would be autoloaded by require("bling")
Expand Down