Recently I was updating my dotfiles, because I wanted to ensure that media-players were "always on top", when launched, as this suits the way I work.
For many years I've used devilspie to script the placement of new windows, and once I googled a recipe I managed to achieve my aim.
However during the course of my googling I discovered that devilspie is unmaintained, and has been replaced by something using Lua - something I like.
I'm surprised I hadn't realized that the project was dead, although I've always hated the configuration syntax it is something that I've used on a constant basis since I found it.
Unfortunately the replacement, despite using Lua, and despite being functional just didn't seem to gell with me. So I figured "How hard could it be?".
In the past I've written softare which iterated over all (visible) windows, and obviously I'm no stranger to writing Lua bindings.
However I did run into a snag. My initial implementation did two things:
- Find all windows.
- For each window invoke a lua script-file.
This worked. This worked well. This worked too well.
The problem I ran into was that if I wrote something like "Move window 'emacs' to desktop 2" that action would be applied, over and over again. So if I launched emacs, and then manually moved the window to desktop3 it would jump back!
In short I needed to add a "stop()" function, which would cause further actions against a given window to cease. (By keeping a linked list of windows-to-ignore, and avoiding processing them.)
The code did work, but it felt wrong to have an ever-growing linked-list of processed windows. So I figured I'd look at the alternative - the original devilspie used libwnck to operate. That library allows you to nominate a callback to be executed every time a new window is created.
If you apply your magic only on a window-create event - well you don't need to bother caching prior-windows.
So in conclusion :
I think my code is better than devilspie2 because it is smaller, simpler, and does things more neatly - for example instead of a function to get geometry and another to set it, I use one. (e.g. "xy()" returns the position of a window, but xy(3,3) sets it.).
kpie also allows you to run as a one-off job, and using the simple primitives I wrote a file to dump your windows, and their size/placement, which looks like this:
shelob ~/git/kpie $ ./kpie --single ./samples/dump.lua -- Screen width : 1920 -- Screen height: 1080 .. if ( ( window_title() == "Buddy List" ) and ( window_class() == "Pidgin" ) and ( window_application() == "Pidgin" ) ) then xy(1536,24 ) size(384,1032 ) workspace(2) end if ( ( window_title() == "feeds" ) and ( window_class() == "Pidgin" ) and ( window_application() == "Pidgin" ) ) then xy(1,24 ) size(1536,1032 ) workspace(2) end ..
As you can see that has dumped all my windows, along with their current state. This allows a simple starting-point - Configure your windows the way you want them, then dump them to a script file. Re-run that script file and your windows will be set back the way they were! (Obviously there might be tweaks required.)
I used that starting-point to define a simple recipe for configuring pidgin, which is more flexible than what I ever had with pidgin, and suits my tastes.