Using devilspie2 for disposable (dispxxxx) qubes

How when I start a new disposable (dispxxxx) qubes from a specific appvm, I can configure devilspie2 to make it so it goes into a specific workspace ?

Here’s a general guide:

You need to know what devilspie2 “sees”.
You can do this by creating a file in ~/.config/devilspie2/debug.lua:

debug_print("Application: " .. get_application_name())
debug_print("Window: " .. get_window_name());

Then run devilspie2 --debug in a dom0 terminal
When you open a disposableVM you will see the Application and Window
names - [dispxxxxx] Mozilla Firefox for example.

And now you hit a major problem - I don’t think there is a distinguishing
feature for qubes launched from a specific appvm.
The only thing that might work would be to use a named disposableVM
for that qube, and then you could match against that name to move any
windows to a specific workspace.

if string.match(get_window_name(), "[name_of_namedDisposable]") then

If you did this, then I would suggest using the qubes-idle package
configured with a short timeout to automatically close the named
disposableVM when you close a window.


qube = get_window_property("_QUBES_VMNAME");
ws = 0;

local function compare(t, s, n)

if n==1
then return t:sub(n, s:len())==s;
else return t:sub(-s:len())==s;


if compare(qube, “disp”, 1) then ws = 2
if (ws > 0) then



… there is probably also a windows property other then the VM name
that will tell you whether it’s a DispVM. Worth looking into.



See also: Autostart program in specific workspace and Open application in specific workspace (xfce).


Yes you are right it’s easier to use a named disposableVM instead, it’s working perfectly fine

I don’t undersand this part, what is this doing ?

I didn’t find a list of proprieties that could help in any documentation, so I looked in the code and found this list of variable : qubes-gui-daemon/xside.c at a6bbe5309d15848d3cf244c8655ffd428de6c3ee · QubesOS/qubes-gui-daemon · GitHub

atoms_to_intern[] = {
        { &g->tray_selection, tray_sel_atom_name },
        { &g->tray_opcode, "_NET_SYSTEM_TRAY_OPCODE" },
        { &g->xembed_message, "_XEMBED" },
        { &g->xembed_info, "_XEMBED_INFO" },
        { &g->wm_state, "_NET_WM_STATE" },
        { &g->wm_state_fullscreen, "_NET_WM_STATE_FULLSCREEN" },
        { &g->wm_state_demands_attention, "_NET_WM_STATE_DEMANDS_ATTENTION" },
        { &g->wm_state_hidden, "_NET_WM_STATE_HIDDEN" },
        { &g->wm_workarea, "_NET_WORKAREA" },
        { &g->frame_extents, "_NET_FRAME_EXTENTS" },
        { &g->wm_state_maximized_vert, "_NET_WM_STATE_MAXIMIZED_VERT" },
        { &g->wm_state_maximized_horz, "_NET_WM_STATE_MAXIMIZED_HORZ" },
        { &g->qubes_label, "_QUBES_LABEL" },
        { &g->qubes_label_color, "_QUBES_LABEL_COLOR" },
        { &g->qubes_vmname, "_QUBES_VMNAME" },
        { &g->qubes_vmwindowid, "_QUBES_VMWINDOWID" },
        { &g->net_wm_icon, "_NET_WM_ICON" },
        { &g->wm_current_desktop, "_NET_CURRENT_DESKTOP" },
        { &g->wmDeleteMessage, "WM_DELETE_WINDOW" },

And tried different things like :

domainn = get_window_property('_QUBES_VMWINDOWID');

domainnn = get_window_property('_QUBES_LABEL_COLOR');

domain = get_window_property('_QUBES_VMNAME');

and so on, but none of them seems usefull

1 Like

I don’t understand this part, what is this doing ?

If n=0 it is comparing the end of t to s and if n=1 it is comparing the
beginning of t to s


compare(“disp1234”, “disp”, 1) returns true
compare(“work-web”, “web”, 0) returns true

See also the

So t:sub(n, s:len()) returns the part of t that starts at letter n and
is s:len() letters long. s:len() returns the length of s.

t:sub(-s:len()) returns the last s:len() letters/characters of t.

And tried different things like: and so on, but none of them seems useful

I was hoping for someone like @unman to come and drop a nugget of
knowledge. But he already answered that there is no other way and you
even checked the code (nice!). So I guess this was a dead end.

So I found a little solution to move a dispxxxx window depending of his template to a specific workspace with devilspie2 with this config :

if (get_window_type() ~= "WINDOW_TYPE_NORMAL") then

-- change these to customize
workspaceAssociation = {};
workspaceAssociation["vault"] = 1;
workspaceAssociation["dom0"] = 33;

-- dispVM
workspaceAssociation["whonix-ws-15-dvm"] = 2;

domain = get_window_property('_QUBES_VMNAME');
if (domain == "") then
        domain = "dom0";
elseif (string.find(string.lower(domain), "disp")) then
        amazingCommand = "sudo qvm-ls|grep " .. domain ..  " | tr -s ' ' | cut -d ' ' -f 5'"
        local handle = io.popen(amazingCommand)
        result = handle:read("*a")
        result = result:sub(1, -2)

        debug_print("This is a dispVM : " .. domain .. " with the following template " .. result)
        domain = result;

if (workspaceAssociation[domain] ~= nil and workspaceAssociation[domain] > 0) then
        if (workspaceAssociation[domain] <= get_workspace_count()) then
                debug_print("Moving ".. get_application_name() .." to workspace ".. workspaceAssociation[domain] .."\n\n")
endqube = get_window_property("_QUBES_VMNAME");
ws = 0;


With the name of a disposable VM with dispxxxx, I use qvm-ls to know his template, and from there I can specify in which workspace I want it to be in.

With the name of a disposable VM with dispxxxx, I use qvm-ls to know
his template, and from there I can specify in which workspace I want
it to be in.


1 Like