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
.There is also a small collection of example rules with a description of what they do.
The new configuration files consist of one rule for each file, e.g.
(if (is (application_name) "firefox-bin") (set_workspace 2) )
Those files have the extension ds
and usually are located at ~/.devilspie
.
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") )
(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 logical expressions a
and b
.
Examples:
(and true true)
- true(and true false)
- false(and false false)
- false(or a b)
boolean or(boolean a, boolean b)
Returns the result of or-ing the logical expressions a
and b
.
Examples:
(or true true)
- true(or true false)
- true(or false false)
- false(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.
Once a window has been selected using a single matcher or a combination of matchers, on or more of the following actions can be applied to it.
(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, useful for debugging purposes.
(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”
(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.
(minimize)
void minimize()
Minimizes 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 aka “sticks” the matched window, making it visible on all workspaces.
(unpin)
void unpin()
Unpins the matched window.
(set_workspace num)
void set_workspace_num(int num)
Moves the matched window to workspace number num
.
Example:
(set_workspace 1)
(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”)
Andrew Conkling, 2006/01/26 05:49:
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.
Gina Haeussge, 2006/01/26 16:30:
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 extend src/s-expr.c
to allow a not statement, which shouldn’t be too difficult. Maybe write a nice email to Ross that he adds it
I have to correct myself here: There is a not
statement (should have used grep
earlier *cough*), will update the documentation shortly.
Andrew Conkling, 2006/01/27 18:05:
Very nice, thanks for updating. I got it working, but not that way. Here’s what I ended up with:
(if (and (is (application_name) "gaim") (not (is (window_name) "Buddy List")) (not (contains (window_name) "#")) ) (begin (geometry "+0+313") ) )
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.
Gina Haeussge, 2006/01/28 12:25:
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.
mato, 2006/02/05 22:06:
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.
Andrew Conkling, 2006/02/07 17:35:
mato, I think that kind of functionality exists in different window managers. Maybe seek out some other ones?
Gina Haeussge, 2006/02/08 10:45:
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 ;)
Johny, 2006/02/22 19:48:
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.
Gina Haeussge, 2006/02/23 12:34:
Thanks for the additions and corrections, I included them in the text.
Rich, 2006/02/24 17:50:
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 :)
Michael O’Dell II, 2006/03/02 04:02:
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)
)
)
Michael O’Dell II, 2006/03/02 04:06:
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) ) )
Filippo Rusconi, 2006/03/09 18:55:
Only to thank you very very much for one so helpful page. Sincerely,
Filippo Rusconi