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:
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.
com.canonical.Unity.Launcher favorites
do you?