devilspie desktop auto window placement

Devilspie2 for Automatic Placement of Windows on a Linux Desktop

Problem – Need to Automate opening of apps and placement of Windows on a Linux Desktop

devilspie desktop auto window placement
Task Bar showing tiled placement of Thunderbird mail client windows on 2nd workspace

I agree with Donald Knuth when he states:

…it seems to me that 15 years of email is plenty for one lifetime.

I have not positioned myself such that I can do without email, though, and it may be years before I can walk away from email.  In fact, as much as I don’t enjoy email, I rely on it quite heavily to the point that I have several email account that I must monitor.  One day I woke up and realized that for me to monitor the various email accounts I have, it needed it to be easy.  Easy like, I don’t think about it easy.  Easy, like it just pops up when I log into my desktop.

What I figured out was a flow for automating the opening and placement of various apps/windows.  I’m writing this up in hopes that the afternoon I spent figuring this out will be of benefit to others.  Enjoy.

Automating Startup of Apps in a Linux Desktop

At first, this seems easy.  In theory, you should be able to write a simple bash shell script to launch all of your everyday apps and just have this launch at startup.  One of the better articles I found on this is found at http://askubuntu.com/questions/48321/how-do-i-start-applications-automatically-on-login.

Launching apps at startup is a great start.  However, apps like XTerm give you the freedom to force a location on startup, but don’t let you specify which workspace to load the application into.  If you only have a single workspace, this is fine, but if you have a bunch, then you’re at the mercy of which desktop your session starts in.  Other applications [like FireFox and Thunderbird] don’t have any hooks in place to automate the placement of the window.

Devilspie2 Comes to the Rescue

Devispie2 is an updated program based on the original Devilspie.  It uses an Lua scripting interface.  When new windows are opened/created, the Devilspie2 program will look at all the .lua scripts in a specified folder and run through to see if any of the code lines up with the window just opened.  Since it interacts with Metacity which is a foundation for most [if not all] of the Linux desktops, you should be able to automate the placement of just about any application you use regardless of your desktop.

Installing Devilspie2

For tinkering and such, I would strongly recommend installing Lua in addition to Devilspie2.  Lua is NOT required for the material I show here, but if you’re new to Lua and just trying to figure out the syntax, it’s nice to have a Lua shell open for playing around in until you’re familiar with the language.  At the time of writing, I was using UbuntuStudio 16.04 LTS.  I ran the following commands to install Lua and Devilspie2.

sudo apt-get install -y devilspie2
sudo apt-get install -y lua5.2

Running Devilspie2 Manually / Tinkering / Playing Around

First, create a directory where you want to create your .lua scripts.

mkdir ~/devilspie2

Once this is created, create/open a file in this directory with a .lua extension in a text editor. Devilspie2 is looking for the .lua extension and will load ALL files that end with a .lua extension.

From here, you can manually run devilspie to test out the code you have written.

devilspie2 --folder /home/USERNAME/devilspie2 --debug

Example Code to place Thunderbird Windows

The example I have here will check to see if a window is the Thunderbird mail client. Since there is nothing unique, or no way to give a unique title to the windows, I set a counter and increment as Thunderbird windows are opened, placing each on a different location/workspace as needed. In this example, the first 4 Thunderbird windows are placed across workspace 2. All remaining Thunderbird windows [which would have to be opened manually] would be moved to workspace 3.

-- debug_print command does only print to stdout
-- if devilspie2 is run using the --debug option

debug_print( "get_window_name:                      " .. get_window_name())
debug_print( "get_application_name:                 " .. get_application_name())
debug_print( "get_window_geometry:                  " .. get_window_geometry())
debug_print( "get_window_client_geometry:           " .. get_window_client_geometry())
debug_print( "get_window_type:                      " .. get_window_type())
debug_print( "get_class_instance_name:              " .. get_class_instance_name())
debug_print( "get_window_role:                      " .. get_window_role())
debug_print( "get_window_xid:                       " .. get_window_xid())
debug_print( "get_window_class:                     " .. get_window_class())
debug_print( "get_workspace_count:                  " .. get_workspace_count())
screen_x, screen_y = get_screen_geometry()
debug_print( "get_screen_geometry                   " .. screen_x .. "x" .. screen_y )
window_x, window_y, window_w, window_h = xywh()
debug_print( "xywh:                                 " .. window_x .. "," .. window_y .."+" .. window_w .. "+" .. window_h )
-- debug_print( "get_screen_geometry:                  " .. get_screen_geometry())
-- debug_print( "get_window_has_name:                  " .. get_window_has_name())
-- debug_print( "get_window_is_maximized:              " .. get_window_is_maximized())
-- debug_print( "get_window_is_maximized_vertically:   " .. get_window_is_maximized_vertically())
-- debug_print( "get_window_is_maximized_horizontally: " .. get_window_is_maximized_horizontally())
-- debug_print( "get_window_property:                  " .. get_window_property())
-- debug_print( "get_window_fullscreen:                " .. get_window_fullscreen())
debug_print( "\n" )


if (get_window_name()=="Mozilla Thunderbird") then
    thunderbird_window_xid = get_window_xid

    -- set a variable for the number of Thunderbird instances called
    if not thunderbird_instance then
        thunderbird_instance = 0
	debug_print( "thunderbird_instance = ", thunderbird_instance )

    else
        thunderbird_instance = thunderbird_instance + 1
	debug_print( "thunderbird_instance = ", thunderbird_instance )
    end

    if thunderbird_instance == 0 then
        undecorate_window()
        set_window_workspace( 2 )
        set_window_geometry( 0, 0, 960, 540 )
	maximize_vertically()

    elseif thunderbird_instance == 1 then
        undecorate_window()
        set_window_workspace( 2 )
        set_window_geometry( 961, 0, 960, 540 )    
	maximize_vertically()

    elseif thunderbird_instance == 2 then
        undecorate_window()
        set_window_workspace( 2 )
        set_window_geometry( 1921, 0, 960, 540 )    
	maximize_vertically()

    elseif thunderbird_instance == 3 then
        undecorate_window()
        set_window_workspace( 2 )
        set_window_geometry( 2881, 0, 960, 540 )    
	maximize_vertically()

    else
        set_window_workspace( 3 )

    end

    x, y, width, height = get_window_geometry()
    debug_print( "Thunderbird Window:  " .. x .. "x" .. y .. "+" .. width .. "+" .. height )

end

Automating the launch of the various GUI apps

Once you have your Devilspie2 script ironed out, the next step is to write a script to launch all of your GUI apps. In my case, the various instances of Thunderbird. Shown next is a simple bash script used to launch four different Thunderbird profiles.

#!/bin/bash

## initial sleep to ensure that devilspie2 is already up and running so
## windows can be modified

sleep 5


echo "loading Fire Media Lab email accounts in various Thunderbird windows:"

##############################
/usr/bin/thunderbird \
    -P PROFILE0 \
    -mail \
    -setDefaultMail \
    &

sleep 5

##############################
/usr/bin/thunderbird \
    -P PROFILE1 \
    -mail \
    -setDefaultMail \
    &

sleep 5

##############################
/usr/bin/thunderbird \
    -P PROFILE2 \
    -mail \
    -setDefaultMail \
    &

sleep 5

##############################
/usr/bin/thunderbird \
    -P PROFILE3 \
    -mail \
    -setDefaultMail \
    &

Automating All of This at Desktop Startup

Once you have a Devilspie2 Lua script automating everything as desired, the next step is to have this all happen automatically at startup. In my case, I have two processes that need to occur at desktop startup.

  • Launch DevilsPie. I like to do this in a terminal with the debug option so that I can see information as windows open. This helps with identifying key information if more needs to be automated at startup later on.
  • Launch bash script that launches GUI apps [In my case, the various Thunderbird profiles].

To do this, I follow the instructions in the tutorial given earlier. http://askubuntu.com/questions/48321/how-do-i-start-applications-automatically-on-login.

The tricky part is the code for launching Devilspie2. I want this to run in a terminal so I can see what is happening. To do this, launch a terminal [in this example, it’s the terminal with xfce]. I force the location/size of this terminal [although we could do this with devilspie2 after the fact] and then we run devilspie2 inside the terminal.

/usr/bin/xfce4-terminal \
    --geometry=60x73+0+27 \
    --title="devilspie2" \
    --hide-menubar \
    --command="/usr/bin/devilspie2 --debug --folder=/home/USERNAME/devilspie2/"

When complete, follow the same method for adding programs at desktop startup to run the shell script that launches the rest of your GUI apps. With Devilspie2 already running, the apps should get rearranged as needed.