What happens internally when I click "Lock to launcher" in Unity?
Solution 1
What happens if you lock/unlock an application to/from the launcher
Not sure if this answer is deep enough "under the hood", but this is what happens:
You can get the current content of the Unity Launcher by the command:
gsettings get com.canonical.Unity.Launcher favorites
It will produce a list, looking like:
['application://extras-qlequicklisteditor.desktop', 'application://gedit.desktop', 'application://gnome-terminal.desktop', 'application://nautilus.desktop', 'application://firefox.desktop', 'application://thunderbird.desktop', 'application://gnome-screenshot.desktop', 'application://dconf-editor.desktop', 'application://virtualbox.desktop', 'application://gnome-tweak-tool.desktop', 'unity://running-apps', 'unity://devices', 'unity://expo-icon']
The mentions in the list are obviously based on the names of the corresponding .desktop
files.
Now when you run a GUI application, when you right- click on its icon in the launcher and choose Lock to Launcher, The currently chosen item is added to the list, while Unlock from Launcher will remove the item from the list.
Editing the Unity Launcher programmatically
Re- reading your (first) comment below your question: You can, as mentioned, get the current Launcher items by the command:
gsettings get com.canonical.Unity.Launcher favorites
and set a possible altered list by the command:
gsettings set com.canonical.Unity.Launcher favorites "[item1, item2, etc]"
You can then of course edit the Unity Launcher's content programmatically, as is done here.
If the application has no .desktop file
If You run a GUI application without an existing .desktop
file, Unity creates a basic one locally (in ~/.local/share/applications
), named after the executable (application.desktop
). In the Exec=
line, you will find the command you ran, to call the application.
If you would look into a .desktop
file, created this way, it includes the line:
X-UnityGenerated=true
Note
As mentioned by @muru (thanks!), in a few (exceptional, as it seems) situations, Unity does not succeed to create a "missing" .desktop
file of an executable. The only example I could find however was in case of Tkinter windows, which are owned by pid 0
in the output of wmctrl -lp
.
Solution 2
What happens when you click Lock To Launcher option, is that Unity will change specific dconf
schema for the launcher favorites and call couple of dbus
methods. The key for programmers and application developers is the change in dconf
schema. ( Jacob's answer relies on gsettings
, however the idea is essentially the same as gsettings
is just front end with sanity check for dconf
). Here, I just want to present a few observations made.
Side note: here, I'm testing everything with a custom python app which has no .desktop
file
Dconf changes
Running dconf watch /
will reveal that this is what gets changed:
$ dconf watch / # Lock to launcher
/com/canonical/unity/launcher/favorites
['application://gnome-terminal.desktop', 'application://firefox.desktop', 'application://gedit.desktop', 'application://sakura.desktop', 'application://mplab.desktop', 'unity://running-apps', 'application://pyqt_clock_py.desktop', 'unity://devices']
# Unlock from launcher
/com/canonical/unity/launcher/favorites
['application://gnome-terminal.desktop', 'application://firefox.desktop', 'application://gedit.desktop', 'application://sakura.desktop', 'application://mplab.desktop', 'unity://running-apps', 'unity://devices']
Creation of .desktop file for the app
Initially, there is check whether or not the .desktop
file exists for the app. If the file exists - good. If not - Unity will issue a dbus
call to org.ayatana.bamf.control.CreateLocalDesktopFile
method on org.ayatana.bamf
service. This can be used to automate .desktop
file creation. Although , this doesn't show-up in the dbus-monitor
output, I believe that is one of the methods that may be used Unity.
Here's a small demo:
# start custom app in background, app appears on the launcher
$> python /home/xieerqi/bin/python/pyqt_clock.py &
[1] 16768
# confirm that there is no .desktop file for that app
$> 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
$> ls .local/share/applications/pyqt_clock_py.desktop
ls: cannot access .local/share/applications/pyqt_clock_py.desktop: No such file or directory
# I use custom function to find list of running apps by their dbus path
$> typeset -f running_apps
running_apps() {
qdbus org.ayatana.bamf /org/ayatana/bamf/matcher org.ayatana.bamf.matcher.RunningApplications | xargs -I {} bash -c "echo {}; qdbus org.ayatana.bamf {} org.ayatana.bamf.view.Name"
}
$> running_apps
/org/ayatana/bamf/application/0x146bb90
Clock
/org/ayatana/bamf/application/1932146384 # that's what we want
Firefox Web Browser
/org/ayatana/bamf/application/1060483892
MY CUSTOM TERMINAL
/org/ayatana/bamf/application/885622223
Compiz
/org/ayatana/bamf/application/0x146b8f0
# Use the dbus method to create desktop file
$> qdbus org.ayatana.bamf /org/ayatana/bamf/control \
> org.ayatana.bamf.control.CreateLocalDesktopFile /org/ayatana/bamf/application/0x146bb90
# Verify its creation
$> ls .local/share/applications/pyqt*
.local/share/applications/pyqt_clock_py.desktop
# This doesn't however pin the program to launcher
# Different call to dbus will be issued
$ gsettings get com.canonical.Unity.Launcher favorites
['application://gnome-terminal.desktop', 'application://firefox.desktop', 'application://gedit.desktop', 'application://sakura.desktop', 'application://mplab.desktop', 'unity://running-apps', 'unity://devices']
There is a different dbus method, that destroys the file:
dbus-monitor revelations
I've performed locking and unlocking action with dbus-monitor --profile
command running. Bellow you can see several calls to methods ( designated by mc
) to ca.desrt.dconf.Writer
interface and Zeitgeist.
mc 1461904751 317156 3474 :1.32 /ca/desrt/dconf/Writer/user ca.desrt.dconf.Writer Change
mr 1461904751 317976 4520 3473 :1.32
mc 1461904751 320331 3475 :1.32 /org/gnome/zeitgeist/log/activity org.gnome.zeitgeist.Log InsertEvents
mc 1461904751 341474 118 :1.93 /org/gnome/zeitgeist/monitor/special org.gnome.zeitgeist.Monitor NotifyInsert
mr 1461904751 341576 119 3475 :1.32
mr 1461904751 341927 39 118 :1.93
mr 1461904751 356896 114 3474 :1.32
sig 1461904751 357892 115 /ca/desrt/dconf/Writer/user ca.desrt.dconf.Writer Notify
If perform more detailed view with dconf-monitor
you will see that calls to dconf writes sequence of bytes and zeitgeist logs the entry added. I've tested this several times, and these are the same actions performed in each case.
Sample output form Zeitgeist.
method call sender=:1.93 -> dest=org.gnome.zeitgeist.SimpleIndexer serial=104 path=/org/gnome/zeitgeist/monitor/special; interface=org.gnome.zeitgeist.Monitor; member=NotifyInsert
struct {
int64 1461904249994
int64 1461904249994
}
array [
struct {
array [
string "14288"
string "1461904249994"
string "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#AccessEvent"
string "http://www.zeitgeist-project.com/ontologies/2010/01/27/zg#UserActivity"
string "application://compiz.desktop"
string ""
]
array [
array [
string "application://pyqt_clock_py.desktop"
string "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Software"
string "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SoftwareItem"
string ""
string "application/x-desktop"
string "Clock"
string "unknown"
string "application://pyqt_clock_py.desktop"
string ""
]
]
array [
]
}
]
Unity Source Code:
The specific code that handles that is defined in launcher/ApplicationLauncherIcon.cpp
of Unity source code
/* (Un)Stick to Launcher */
glib::Object<DbusmenuMenuitem> menu_item(dbusmenu_menuitem_new());
const char* label = !IsSticky() ? _("Lock to Launcher") : _("Unlock from Launcher");
dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, label);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true);
dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);
But the actual job is performed by unity-shared/BamfApplicationManager.cpp
bool Application::SetSticky(bool const& param)
{
bool is_sticky = GetSticky();
if (param == is_sticky)
return false; // unchanged
bamf_view_set_sticky(bamf_view_, param);
return true; // value updated
}
Where does that leave us ?
Knowing the changes being made to dconf
and the specific behavior of the launcher can help us extend its functionality. The examples of that from both me and Jacob include:
- Setting up Launcher form an input file
- Reordering to make active app top or bottom item
- Cloning launcher setup from user to user
- Creating unique launcher per workspace
The particular usefulness of the dbus
method for creating .desktop
files allows automating shortcut creating for the custom-written apps , which later can be locked to the launcher using gsettings
method Jacob has described.
Related videos on Youtube
Byte Commander
Ask Ubuntu moderator♦, IT student and DevOps engineer. I love Ubuntu, Python, good music and coffee, although not necessarily in that order. You can easily contact me in the Ask Ubuntu General Room most of the time, or on Discord as @ByteCommander#2800. I'd also love to invite you to my Ubuntu Hideout Discord Server btw. PS: My profile picture is derived from "Wolf Tribals" by user HaskDitex (DeviantArt.com), which is under creative Commons 3.0 License. Currently I'm using the character "Dregg Morriss" from the game "Medieval Cop" by Vasant Jahav ("Gemini Gamer"). It can be found here.
Updated on September 18, 2022Comments
-
Byte Commander over 1 year
In the Unity desktop, when I start a GUI application, its icon appears in the launcher (if it isn't already there).
Now when I right-click this icon, I either get the option Lock to Launcher or Unlock from Launcher, depending on whether the application is already locked to the launcher or not.
My question is:
What happens under the hood, when I click one of those two options if no.desktop
file exists?Can it automatically create simple
.desktop
files if it can't find one, under which conditions may that happen, and where do the pinned launcher items get saved?-
muru over 8 yearsUse the source.
-
Byte Commander over 8 years@muru I mainly want to know whether it creates a new .desktop file automatically if none exists yet and how it stores which items are locked to the launcher. Maybe I should edit my question a bit... And did you downvote it? I'm not really looking forward to download and crawl through MBs of Unity source code, I don't even know where to start in there.
-
muru over 8 yearsYes, I did downvote. When you ask "what happens exactly under the hood", you're asking others to download and crawl through MBs of Unity source code. Because what happens exactly under the hood has only one answer: what's in the code.
-
Jacob Vlijm over 8 years@ByteCommander you don't mean what happens in
com.canonical.Unity.Launcher favorites
do you? -
Byte Commander over 8 years@muru You're probably right. Is my current version narrowed down enough?
-
Byte Commander over 8 years@JacobVlijm I don't know. Can't look it up today, need to go....
-
Jacob Vlijm over 8 yearsHi Byte, did you notice you have sn answer?
-
Byte Commander over 8 years@JacobVlijm No, I didn't notice it yet. I wasn't online since you posted it...
-
Sergiy Kolodyazhnyy about 8 years
Can it automatically create simple .desktop files if it can't find one, under which conditions may that happen, and where do the pinned launcher items get saved?
I didn't explicitly answer your question, but see my answer usingdbus
method. This creates a.desktop
file of a running application in.local/share/applications
folder
-
-
muru over 8 yearsThe latter is not necessarily true. I started a basic GUI from Python (with a sample code using tkinter). The Lock to Launcher is available, but it does nothing ... presumably because it can't figure out the command that started the window.
-
Byte Commander over 8 yearsGreat answer, but I would still be interested more in the additional note by @muru, because the fact that it sometimes seems to create a .desktop file and sometimes not made me very curious about what criteria have to be met to succeed.
-
Jacob Vlijm over 8 years@ByteCommander Well, at this stage, it is a guess, but looking at
tkinter
windows, they are owned by WM classtk
, and have pid 0. I assume that is the issue, but further testing would be needed. -
Jacob Vlijm over 8 years@muru, I tried a lot of different GUI executables, the ones failing are all
tkinter
windows with pid 0. -
Byte Commander about 8 yearsAre you going to answer every question that @JacobVlijm responded to? :D But a nice one indeed.
-
Sergiy Kolodyazhnyy about 8 years@ByteCommander Of course !!! Competition is one of the driving forces in the world :D Seriously speaking though , it just happens that these types of questions interest me as well.
-
WinEunuuchs2Unix over 3 yearsGreat answer. For tkinter to put an icon in launcher (after python starts) I use this:
pngimg = tk.Image("photo", file="mserve.png")
followed by this:toplevel.tk.call('wm', 'iconphoto', toplevel._w, pngimg)
. Others might changetoplevel
toroot
orgui
orapp
, etc.