How to get the list of running GUI applications in the Unity Launcher?
I need a list with only those apps that are currently open/running in Dash, the ones that have that small white arrow(s) on the left side of the icon.
Is there any way to get it?
Solution 1:
Interesting question.
As always, there are different ways to get a list of these applications, each of them with specific ad- and disadvantages.
Getting a list of processes, represented in the Launcher, using the window list
Since only applications with a (mapped) window appear in the Launcher, using:
wmctrl -lp
(wmctrl
is not installed by default), we can get a list of opened windows and the process- id the windows belong to. The format of the output is:
0x05204641 0 12618 jacob-System-Product-Name verhaal (~) - gedit
where for us most important information is in:
- the first string (
0x05204641
); this is the window -id - the third string (
12618
); this is the process id (pid) the window belongs to, and, - the last section (
verhaal (~) - gedit
); this is the window name.
Once we have the pid, we can look up the corresponding process name by the command:
ps -p <pid> -o comm=
We can script the steps above, and list the output(s) for existing windows, looking like (using python
):
{'gnome-terminal', 'nautilus', 'gedit', 'GuitarPro.exe', 'firefox', 'thunderbird', 'soffice.bin'}
Complications
This seems straightforward. However, as always, reality is a bit more complicated. There are a few exceptions and complications we need to take care of:
- Some windows will belong to pid 0, which will raise an error when trying to get their properties. Windows of
Idle
(python
IDE) ortkinter
are such windows. - Some windows are no "real" or windows, like transient windows (windows, which are called from, and belonging to other windows) or for example the desktop itself. These windows are listed as windows in the output of
wmctrl
, but do not appear separately in Dash. - In some cases, the application's name is quite different from the process name, for example in the case of
LibreOffice
where all modules have the process name ofsoffice.bin
. At the same time, running the commandsoffice.bin
will not work. In case you need to identify the modules (Calc
,Writer
etc.) separately, you'd need to get additional information, from the window's name for example. - Another example is the process name of
gnome-terminal
, as it appears in the process list, as in the output ofps -e ww
. In 14.04,gnome-terminal
appears asgnome-terminal
, however, in 15.04 / 15.10 it shows:/usr/lib/gnome-terminal/gnome-terminal-server
.
What we need to fix at least
To fix the most important issues above, you need to:
-
add a check if the window is a "real" or "normal" window, checking with
xprop -id <window_id>
If the output includes the line:
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
The window is a valid window in the sense of the Unity Launcher
- To fix the process name of
gnome-terminal
in15.x
(assuming you want the presented process name to begnome-terminal
) we need to add an exception, to rename the process name intognome-terminal
if it appears as/usr/lib/gnome-terminal/gnome-terminal-server
Script
#!/usr/bin/env python3
import subprocess
import sys
try:
listed = sys.argv[1]
except IndexError:
listed = []
get = lambda cmd: subprocess.check_output(cmd).decode("utf-8").strip()
def check_wtype(w_id):
# check the type of window; only list "NORMAL" windows
return "_NET_WM_WINDOW_TYPE_NORMAL" in get(["xprop", "-id", w_id])
def get_process(w_id):
# get the name of the process, owning the window
proc = get(["ps", "-p", w_id, "-o", "comm="])
proc = "gnome-terminal" if "gnome-terminal" in proc else proc
return proc
wlist = [l.split() for l in subprocess.check_output(["wmctrl", "-lp"])\
.decode("utf-8").splitlines()]
validprocs = set([get_process(w[2]) for w in wlist if check_wtype(w[0]) == True])
if listed == "-list":
for p in validprocs:
print(p)
else:
print(validprocs)
How to use
-
The script needs
wmctrl
:sudo apt-get install wmctrl
copy the script above into an empty file, save it as
get_running.py
-
run it by the command:
python3 /path/to/get_running.py
It will output like:
{'gnome-terminal', 'nautilus', 'gedit', 'GuitarPro.exe', 'firefox', 'thunderbird', 'soffice.bin'}
or, run with the argument
-list
:thunderbird nautilus gnome-terminal firefox gedit GuitarPro.exe soffice.bin
Notes
From your question, it is not completely clear what is exactly the purpose of the found list. If you need to have the application's name, as it appears in the interface ("readable" names), a completely different approach might be suited:
- All globally installed applications do have a
.desktop
file in/usr/share/applications
. In far most cases, we can conclude the process name and the interface name of the application from its.desktop
file. Using this information, we could relatively easy create a list of running GUI applications, presented by their "readable" name.
Also in this case however, reality is also more complicated then theory, as explained here.
Solution 2:
The way to do it with qdbus
and org.ayatana.bamf
interface.
List of open applications by .desktop
file:
$ qdbus org.ayatana.bamf /org/ayatana/bamf/matcher \
> org.ayatana.bamf.matcher.RunningApplicationsDesktopFiles
/usr/share/applications/compiz.desktop
/usr/share/applications/firefox.desktop
/usr/share/applications/x-terminal-emulator.desktop
Using org.ayatana.bamf.matcher.RunningApplications
and org.ayatana.bamf.view.Name
methods
$ qdbus org.ayatana.bamf /org/ayatana/bamf/matcher \
> org.ayatana.bamf.matcher.RunningApplications | \
> xargs -I {} qdbus org.ayatana.bamf {} org.ayatana.bamf.view.Name
Firefox Web Browser
MY CUSTOM TERMINAL
Compiz