First of all: What is Devil's Pie? From the developer's site:
A window-matching utility, inspired by Sawfish's “Matched Windows” option and the lack of the functionality in Metacity. Metacity lacking window matching is not a bad thing — Metacity is a lean window manager, and window matching does not have to be a window manager task.
Devil's Pie can be configured to detect windows as they are created, and match the window to a set of rules. If the window matches the rules, it can perform a series of actions on that window. For example, I can make all windows created by X-Chat appear on all workspaces, and the main Gkrellm1 window does not appear in the pager or task list.
With the release of version 0.13 the configuration files changed from an xml-based format to s-expressions. This wiki node represents my try at documenting the new format, as sadly there is no official documentation yet. The information I provide here was kinda extracted from the source code of Devil's Pie.
I will give a generic syntax definition for each function, followed by a Java-Representation in order to describe the defined datatypes, using the following conventions:
String means a valid string, e.g. “firefox-bin”.int means a valid integer value, e.g. 0 or 800.boolean means a boolean value, either true or false.Object means a mixed type determined at runtime.There is also a small collection of example rules with a description of what they do.
The current version of this document covers Devil's Pie from 0.13 up to 0.20.2. I do my best to update it as soon as I find time to search the codebase of current versions for changes and describe them.
The configuration files consist of rules formulated in an s-expression based syntax, with ; denoting comment-lines which are not parsed, e.g.
; Move firefox to workspace 2 (if (is (application_name) "firefox-bin") (set_workspace 2) )
Those files have the extension ds and usually are located at ~/.devilspie.
What you can do though is encapsulate several rules in one begin block, this works like a charm. Additonally, with version 0.20 Devil's Pie also seems to have support for multiple expressions per file natively, though I haven't tested this yet myself. Whether you want to use this is up to you - I prefer the “one-rule-per-file” variant for the sake of clarity
.
The configuration currently in use on my ThinkPad can be found here.
(if (is (application_name) "firefox-bin") (begin (set_workspace 2) (maximize) ) )
(if (and (is (application_name) "gaim") (is (window_name) "Buddy List") ) (begin (pin) (geometry "340x630+4+150") ) )
(if (matches (application_name) "^Skype") (begin (geometry "300x600") (center) (above) (skip_pager) (skip_tasklist) ) )
(if (and (is (application_name) "gaim") (not (is (window_name) "Buddy List")) (not (contains (window_name) "#")) ) (geometry "+0+313") )
(undecorate)
(begin (if (is (application_name) "firefox-bin") (begin (set_workspace 2) (maximize) ) ) (if (and (is (application_name) "gaim") (not (is (window_name) "Buddy List")) (not (contains (window_name) "#")) ) (geometry "+0+313") ) )
(if a b) (if a b c)
if (a) { b; } if (a) { b; } else { c; }
A conditional flow based on the boolen expression a.
Example:
(if (is (application_name) “firefox_bin”) (set_workspace 2)) - Moves the window to workspace 2 if the application it belongs to is Firefox.(begin a b c ...)
{ a; b; c; // ... }
A sequential execution of actions a, b, c, …
Example:
(if (is (application_name) “firefox_bin”) (begin (set_workspace 2) (maximize))) - If the window belongs to the application Firefox, move it to workspace 2 and maximize it.(and a b ...)
boolean and(boolean a, boolean b, ...)
Returns the result of and-ing the given logical expressions.
Examples:
(and true true) - true(and true false) - false(and false false) - false(and true true true) - true(and true false true) - false(or a b ...)
boolean or(boolean a, boolean b, ...)
Returns the result of or-ing the given logical expressions.
Examples:
(or true true) - true(or true false) - true(or false false) - false(or false false false) - false(or false false true) - true(not a)
boolean not(boolean a)
Returns the negation of the given logical expression a.
Examples:
(not true) - false(not false) - true(is a b)
boolean is(String a, String b)
Returns true if a and b are the same.
Examples:
(is “foo” “foo”) - true(is “foo” “bar”) - false(contains haystack needle)
boolean contains(String haystack, String needle)
Returns true if needle is a sub-string of haystack.
Examples:
(contains “somefoobar” “foo”) - true(contains “somefoobar” “fnord”) - false(matches str pattern)
boolean matches(String str, String pattern)
Returns true if pattern matches on string. pattern is a Regular Expression.
Examples:
(matches “foobar” ”[o]{2}b”) - true(matches “foobar” ”[0-9]+”) - falseMatchers return certain properties of the available windows – like windowtitle, applicationame, … – and are used to formulate conditions to match certain programs and/or windows.
(window_name)
String window_name()
Returns a STRING containing the name of the window as displayed in the windows titlebar.
(window_role)
String window_role()
Returns a STRING describing the current window role of the matched window as defined by it's WM_WINDOW_ROLE hint.
(application_name)
String application_name()
Returns a STRING containing the name of a windows application.
(window_workspace)
int window_workspace()
Returns an INT containing the number of a windows workspace aka virtual desktop.
(window_xid)
int window_xid()
Returns an INT containing the XID of a window.
(window_property propertyname)
String window_property(String propertyname)
Returns a STRING containing the value of the requested property of a window. See the "Application Window Properties" part of the "Extended Window Manager Hints" specification for a list of possible window properties to request.
One or more of the following actions can be applied to a set of selected windows, which can be either a subset of all windows on the screen (created by using a single matcher or a combination of matchers and the if statement) or simply all windows.
(debug)
void debug()
Prints information to stdout about matched open windows, including applicationname, windowtitle, windowrole and geometry.
(print text)
void print(String text)
Prints the String text to stdout (without a newline at the end), useful for debugging purposes.
(println text)
void println(String text)
Prints the String text to stdout (with a newline at the end), useful for debugging purposes.
(str value)
String str(Object value)
Converts given value into a String and returns it. The value can be boolean, int, a String, an array pointer or even a timestamp which is then formatted into a date respresentation as defined by the current locale.
(hex num)
String hex(int value)
Converts given value into a String containing the value's hexadecimal representation (including a 0x at it's beginning).
(geometry geo)
void geometry(String geo)
Sets the geometry of the matched window. geo must be a STRING containing a valid X-GeometryString as parsed by XParseGeometry. Excerpt from man XParseGeometry:
By convention, X applications use a standard string to indicate window
size and placement. XParseGeometry makes it easier to conform to this
standard because it allows you to parse the standard window geometry.
Specifically, this function lets you parse strings of the form:
[=][<width>{xX}<height>][{+-}<xoffset>{+-}<yoffset>]
Examples:
(geometry “400×300+0-22”)(geometry “640×480”)(geometry ”+10+10”)Dialogs and splashscreens don’t accept position and size commands. They have to be turned into normal windows with the wintype command before devilspie can direct them to some other place.
(fullscreen)
void fullscreen()
Sets the matched window into fullscreen mode.
(focus)
void focus()
Gives focus to the matched window.
(center)
void center()
Centers the matched window on the screen.
(maximize)
void maximize()
Maximizes the matched window horizontally and vertically.
(maximze_vertically)
void maximize_vertically()
Maximizes the matched window vertically.
(maximize_horizontally)
void maximize_horizontally()
Maximizes the matched window horizontally.
(unmaximize)
void unmaximize()
Unmaximizes the matched window horizontally.
(minimize)
void minimize()
Minimizes the matched window.
(unminimize)
void unminimize()
Unminimizes the matched window.
(shade)
void shade()
Shades aka “rolls up” the matched window.
(unshade)
void unshade()
Unshades aka “rolls down” the matched window.
(close)
void close()
Closes the matched window.
(pin)
void pin()
Pins the matched window, making it visible on all workspaces.
(unpin)
void unpin()
Unpins the matched window.
(stick)
void stick()
Sticks the matched window, making it visible on all viewports.
(unstick)
void unstick()
Unsticks the matched window.
(set_workspace num)
void set_workspace(int num)
Moves the matched window to workspace number num (counting from 1).
Example:
(set_workspace 1)(set_viewport num)
void set_viewport(int num)
Moves the matched window to viewport number num (counting from 1).
Example:
(set_viewport 2)(skip_pager)
void skip_pager()
Stops the matched window from being shown in the pager.
(skip_tasklist)
void skip_tasklist()
Stops the matched window from being shown in the tasklist.
(above)
void above()
Makes the matched window be always on top.
(below)
void below()
Makes the matched window stay under all other windows.
(undecorate)
void undecorate()
Removes the window manager decoration from the matched window.
(wintype type)
void wintype(String type)
Sets the window type of the matched window. type can be one of the following values: “normal”, “dialog”, “menu”, “toolbar”, “splashscreen”, “utility”, “dock”, “desktop”.
See the _NET_WM_WINDOW_TYPE part of the "Extended Window Manager Hints" specification for more details on what exactly this can do.1)
Examples:
(wintype “normal”)(wintype “toolbar”)(opacity percent)
boolean opacity(int percent)
Sets the opacity of the matched window to the given percentage. Returns true upon success.
(spawn_sync command) (spawn_sync command1 command2 ...)
String spawn_sync(String command) String spawn_sync(String[] command)
Executes the given command (given as either one String or a list of multiple Strings) in the foreground and returns its output as a String.
(spawn_async command) (spawn_async command1 command2 ...)
boolean spawn_async(String command) boolean spawn_async(String[] command)
Executes the given command (given as either one String or a list of multiple Strings) in the background and returns true upon successful command start.
Discussion
First of all, thanks for such clear and comprehensive documentation. Let’s hope this makes it into devilspie.
I was just wondering if it were possible to create a “not” condition, e.g.:
(is (application_name) “gaim”) (is not (window_name) “Buddy List”) (contains not (window_name) “#”)The goal is to match a Gaim conversation window that’s not the buddy list or an IRC chat window.Sadly, such a construct doesn’t seem to be implemented in the s-expression engine devilspie uses. In theory, it should be possible to write an s-expression emulating a not by simply putting the action to perform into the else arm of the if construct and just some empty statement in the other:
(if (is (window_title) “Buddy List”) (+ 1 1) (pin)). This seems to work with one tier, but when I just tried to extend this to two tiers (by encapsulating the above construct with an addition(if (is (application_name) “gaim”) (...))) it didn’t execute the pin action, although the debug action worked (and printed information only about the chat-window, not the buddylist, so it matched correctly). I don’t understand this behaviour though. The best thing would probably be to extendsrc/s-expr.cto allow a not statement, which shouldn’t be too difficult. Maybe write a nice email to Ross that he adds itI have to correct myself here: There is a
notstatement (should have usedgrepearlier *cough*), will update the documentation shortly.Very nice, thanks for updating. I got it working, but not that way. Here’s what I ended up with:
Also, is it possible to do something with the main Firefox window, but leave any secondary windows (dialogs, popups, etc.) alone? I can’t figure any way to match just the main window.
I can’t find one either. Sadly, Firefox doesn’t set any window role hints, at least devilspie doesn’t detect them, otherwise one could have depended on those to match.
hi, i’m thinking of this – i’d like some windows that suddenly spring open not to steal focus (that is not to get focused). but i can’t see unfocus among the actions. how could i achieve it pls? cheers, m.
mato, I think that kind of functionality exists in different window managers. Maybe seek out some other ones?
XFCE for example - smaller, better, faster ;)
Anyway, if I needed to implement an unfocus action, I’d try to make devilspie track the focused windows, keeping in mind the currently and priorily focused one and then refocusing the priorily one if the current one matches an unfocus action. But I’m not sure whether it is possible to track the current focus (sadly I have no experience at all at X11 programming), but if someone sees the need for implementing such a functionality althouh a window manager can already provide it, go ahead ;)
Hi, thanks for this great reference, it helped me finally understand the new system.
However, there are few things that need to be corrected:
1. (print “something”) action – that’s pretty obvious. Prints the string parameter to stdout (like debug). For example, (begin (print “You have opened: “) (print (window_name)) ) will print the exact name of the window that has been matched.
2. (below) action is not the opposite state of (above), as your comment might suggest. There are actually three states – above, below and normal (no action used). Your description for above is correct, but your description for below belongs to normal. Below means that the window will _always_ be at the bottom, no matter if it has been clicked, has focus, whatever.
Thanks for the additions and corrections, I included them in the text.
Back to the focus issue. I as well would like to only have one window always have focus and have made appropriate .ds files. However I get a timestamp 0 error and it never gets focused. It has been suggested that I can force the use of timestamps but have not seen any reference to any forcing or timestamps. Anyone care to clear this up? An example perhaps? Please :)
I may have found a way to do as requested by Andrew regarding Firefox; do something to the main window without affecting other windows. I assume this means without multiple browser windows open. I don’t need this functionality, and thus haven’t tested it, but it seems like it should work. Also, I don’t expect it to work with the Titlebar Tweaks extension for Firefox. Here it is:
; Move the Firefox browser window to workspace 2 and maximize it. (if (and (matches (application-name) “^firefox-bin$”) (matches (window-name) “^.*-.Mozilla.Firefox$”) ) (begin (set_workspace 2) (maximize) ) )Bah. Stupid parsing. Let’s try this again:
; Move the Firefox brwoser window to workspace 2 and maximize it. (if (and (matches (application-name) "^firefox-bin$") (matches (window-name) "^.*-.Mozilla.Firefox$") ) (begin (set_workspace 2) (maximize) ) )Only to thank you very very much for one so helpful page. Sincerely,
Filippo Rusconi
It took me quite some time to realize that devilspie stops parsing an *.ds file after the (debug) statement. Also, it is by far not obvious that devilspie will take only the first statement of every file and ignores the rest. This is not only contrary to the behaviour of the previous versions but also highly unusual. You may add both snippets of information in large and friendly letters at the appropiate places of the wiki.
Thank you for providing the information, the author of the tool didn’t bother to put into the docs. —<(kaimartin)>—
Sorry for posting the above comments twice. Can you remove one of them? Thank you.
Yet another gotcha that may go into the tutorial: Dialogs and splashscreens don’t accept position and size commands. They have to be turned into normal windows with the wintype command before devilspie can direct them to some other place. —<(kaimartin)>—
i was searching how to undecorate every windows on all my workspaces...something like this:
(if (is (application_name) “*”) (undecorate))
or around this solution...but I haven’t found nothing that work!
Currently, I’ve solved this problem using this method:
(if (matches “str” “str”) (undecorate))
but isn’t logical and I don’t like it.
Someone knows something about this? Thanks a lot.
@Kai-Martin
Thanks for your additions! I will incorporate them ASAP.
About the debug thing though, I would be grateful if you could provide an example rule that shows that behaviour. I just tried to reproduce it, but couldn’t bring devilspie to ignore the rest of my test rule.
@Zeno
A simple
(undecorate)already should do the trick. Just put it in a .ds file in~/.devilspie.Update
I just realised I haven’t been very clear on the usage of actions, as it sounded like you needed to select a subset of windows first with some matchers. I rewrote that accordingly to reflect reality a bit better
I see, that my comments already made it into the text
Maybe I wasn’t clear about the debug thing. I missunderstood the debug rule as an option and placed it in the beginning of a *.ds file. Devilspie reads it as the first S-expression and does not read any further. This is of course a consequence of devilspie current (miss-) behaviour to parse the first expression only. It would be nice, if the tutorial would recommand to put the (debug) statement in a seperate file, or inside the action section of a rule.
This is the *.ds file that is read only until the end of the debug statement.
(debug) (if (is (window_name) "Transformation") (begin (geometry "+1000+400") ) )Yeah, I was trying to chicken out of learning for uni this morning and updating the wiki came as a handy excuse to do so
Anyway, now I get the problem with the debug statement... I will add a small word of warning somewhere
Hi again, I just discovered that it is possible to keep all rules in one file: Just put them inside the parenthesis of a “(begin foo)” statement. Pretty obvious with hindsight... Example: (begin
(if (is (window_name) "Selection") (begin (wintype "normal") (geometry "+1100+130") (print "Edit2DProfile") ) ) (if (is (window_name) "3D Snap") (begin (wintype "normal") (geometry "+1100+130") (print "3D Snap") ) ) )Thanks again, just incorporated that piece of info as well :)
@Gina Thanks for the terrific write-up! :)
@Contributers Thanks for the ideas :D
I have a question regarding DP. I haven’t had the chance to look at the code, but is there a way to set display (in a multi-display setup) for windows?
I haven’t encountered something like this as of now during my code studies, but it would be a great addition IMO :D
I’ll second that (Gina Haeussge). Can any of you confirm for sure that it isn’t possible to use it to change display (I’m not THAT good at reading code), becuase I’ve seen a lot of threads in the Ubuntu forums, where people ask if it is possible to control window behavior across dual displays in a Xinerama configuration, and all the time, the question is answered with something like “I don’t know if it’ll work but you could try to use Devil’s pie”. So it would be nice to know for sure if that isn’t possible so we can look for another solution, (or hope that it’ll be included in Devil’s pie at some point;) ).
I would like for my terminal (in Gnome) to open fullscreen everytime. So I made the following terminal.ds file:
(if (matches (application_name) "me@machine") (maximize) )‘me@machine’ corresponds to the display on top of my terminal window.
So I put the devilspie command in my session startup programs, and it doesnt do anything. If I open a terminal window, and type ‘devilspie’ it will maximize that single session only, and concurrent sessions will not maximize unless I specifically type devilspie each time. Is there a way to have devilspie constantly running in the background and automatically maximize each terminal window as it is opened? Thanks for the help.
Try putting
devilspie &instead ofdevilspiein your startup file. I simply put that line in my.xsession, works flawlessly.Great guide. Was this written by a woman?
Actually yes... dunno though why this would matter ;)
Doesn’t matter at all. I could just tell ;)
Just discovered the ability to match the the window class. Works great for editors like LyX. You might add “window_class” to the list of matchers. —<(kaimartin)>—
Is it possible to clear a flag, such as skip_pager or skip_tasklist? And why is it even called skip_tasklist, I thought it was supposed to be skip_taskbar?
Hello, I’m using Ubuntu 6.06 LTS with the latest updates and GNOME with MetaCity, though I can’t get Devil’s Pie to get running properly on my system. For example “application_name” doesn’t seem to work for me at all. Also, (if (is (window_name) “Terminal”) (geometry “640×500+100+100”)) X and Y position has no effect whatever numbers i put here, what could be the problem? I store my *.ds files in ~./devilspie Thanks in advance.
I have the same problem with Ubuntu 7.10 and devilspie 0.22
@Ciprian Popovici: First of all sorry for the late answer, I just now saw this. About an undo action for skip_pager or skip_tasklist - I didn’t find one in the source. The used library would allow one, the developer didn’t include one though as to my knowledge, although I have no idea why he did skip to implement this...
@saiko: Maybe someone reading this here might be able to help you with this problem, as I’m using neither Ubuntu nor Gnome I sadly can’t.
But just to clearify again, I am not the developer of Devil’s Pie. I’m just a user of it as well, I just found the total lack of documentation for the tool intolerable and therefore wrote one by reading the sourcecode of the tool. So any bugs, naming concerns or whatever are nothing I can do anything about, you should post them in the official bugtracker of Devil’s Pie.
May be more or less usefull, but I made a VIM syntax file. It’s a first try and not very complex/solid in its design but it works. Please have a look, http://wiki.goatpron.de/linux/vim/syntax/devilspie
You might instead try looking at the already existing vim syntax file for scheme or for common lisp. Formatting s-exps is a solved problem, so we might as well just reuse what’s already been done. I usually edit the files with a .scm extension, and then rename them to have the .ds extension when I’m done. It would probably be fairly simple to change the syntax files to recognize .ds as a scheme (or common lisp) file.
Such an addition into .vimrc should be enough:
Ok, I have devilspie installed and set to run upon login via sessions. The sample gaim rule that pins the conversations windows to each workspace appears to work just fine. Now, any and all Firefox rules I’ve tried to implement will not work for me upon login, but will work if I open a terminal and type ‘devilspie -a’. This is very confusing and any help would be greatly appreciated in making these Firefox rules work upon login along with the gaim rules.
Thanks, Mike
Ok, so now it seems to suddenly be working correctly. I have no idea why, maybe all we both needed was a good night of rest. My Firefox is now positioned correctly, but, the dialogs are taking on the same position and size as my main Firefox window. Does anyone know how to ignore dialog boxes? Such as the warning for closing multiple tabs or the preferences dialog.
Thanks again, Mike
Hm.. could you please paste the rule you used to match Firefox? Maybe that would give a hint as to why it is behaving like you said.
devilspie really is great and works fine for me, except...
problem with Radrails: But I can’t get a program called Radrails (IDE for Rails) to go to a specific workspace. Do you’ve any suggestions?
my radrails.ds file in $HOME/.devilspie:
(if (is (application_name) “radrails”) (set_workspace 2))
What output does the debug.ds create? Is it really the correct appname?
Well, after spending about an hour on this, I cannot get it to properly configure GIMP. For the image window, I have:
(if (is (window_role) “gimp-image-window”) (begin geometry “732×689+292+73”(set_workspace 4)))
This works perfectly, opening up any image window in the fourth desktop and resizing it properly. Unfortunately, I cannot get the toolbox window to send itself to another desktop with any consistency, it is just strange. Its properties, as far as I can tell are:
Window role : “gimp-toolbox” Window name : “The GIMP” Window class: “gimp” Application name: “gimp”
All I want is for it to open up on the fourth workspace whenever I launch it. I have tries lots of variations of:
(if (is (window_role) “gimp-toolbox”) (begin (set_workspace 4)))
Now, if run it and just wait, it will open itself on the current desktop. However, if make GIMP’s splash screen lose focus (by clicking into another window), it will send itself to another desktop. Why does it do that? Has anybody had any success in configuring GIMP? Also, setting its geometry always works fine, it’s just sending it to another workspace that it has troubles with,