在 Unity 桌面中,当我启动 GUI 应用程序时,它的图标就会出现在启动器中(如果它还不存在)。
现在,当我右键单击此图标时,我将获得选项Lock to Launcher或Unlock from Launcher,具体取决于应用程序是否已锁定到启动器。
我的问题是:
如果不存在文件,当我单击这两个选项之一时,会发生什么情况.desktop
?
.desktop
如果找不到简单文件,它能自动创建吗?在什么情况下会发生这种情况?固定的启动器项保存在哪里?
答案1
如果你将应用程序锁定/解锁到启动器会发生什么情况
不确定这个答案是否足够深刻,但这是发生的事情:
您可以通过以下命令获取Unity Launcher的当前内容:
gsettings get com.canonical.Unity.Launcher favorites
它将生成一个列表,如下所示:
['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']
列表中的提及显然是基于相应文件的名称.desktop
。
现在,当您运行 GUI 应用程序时,右键单击启动器中的图标并选择Lock to Launcher,当前选定的项目将添加到列表中,而Unlock from Launcher将从列表中删除该项目。
通过编程方式编辑 Unity Launcher
重新阅读你问题下面的(第一条)评论:正如提到的那样,你可以得到使用以下命令删除当前启动器项目:
gsettings get com.canonical.Unity.Launcher favorites
和放以下命令可能改变的列表:
gsettings set com.canonical.Unity.Launcher favorites "[item1, item2, etc]"
当然,您也可以通过编程方式编辑 Unity Launcher 的内容,就像这样做一样这里。
如果应用程序没有 .desktop 文件
如果您运行没有现有文件的 GUI 应用程序.desktop
,Unity 会在本地(在 中~/.local/share/applications
)创建一个基本文件,以可执行文件 ( ) 命名application.desktop
。在行中Exec=
,您将找到运行的命令,以调用该应用程序。
如果你查看以.desktop
这种方式创建的文件,它包含以下行:
X-UnityGenerated=true
笔记
正如 @muru (谢谢!) 所述,在少数情况下 (似乎是例外),Unity 无法成功创建可执行文件的“缺失”文件。然而,我能找到的唯一示例是 Tkinter 窗口的情况,它在 的输出中.desktop
归 拥有。pid 0
wmctrl -lp
答案2
单击Lock To Launcher选项时,Unity 将更改dconf
启动器收藏夹的特定架构并调用几个dbus
方法。对于程序员和应用程序开发人员来说,关键是架构的变化dconf
。(Jacob 的回答依赖于,但这个想法本质上与只是带有健全性检查的前端gsettings
相同)。在这里,我只想提出一些观察结果。gsettings
dconf
附注:在这里,我使用没有.desktop
文件的自定义 Python 应用程序测试所有内容
Dconf 更改
运行后dconf watch /
会发现发生了以下变化:
$ 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']
为应用程序创建 .desktop 文件
首先,检查.desktop
应用程序是否存在该文件。如果文件存在 - 很好。如果不存在 - Unity 将发出对服务方法dbus
的调用。这可用于自动创建文件。虽然这不会显示在输出中,但我相信这是 Unity 可能使用的方法之一。org.ayatana.bamf.control.CreateLocalDesktopFile
org.ayatana.bamf
.desktop
dbus-monitor
这是一个小演示:
# 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']
还有一种不同的 dbus 方法,可以破坏文件:
dbus-monitor 启示
我已经通过运行命令执行了锁定和解锁操作。下面您可以看到对接口和 Zeitgeist 的dbus-monitor --profile
几个方法(由指定mc
)的调用。ca.desrt.dconf.Writer
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
如果执行更详细的查看,dconf-monitor
您将看到对 dconf 的调用写入字节序列,而 zeitgeist 记录添加的条目。我已经测试了几次,每次执行的操作都相同。
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 源代码:
launcher/ApplicationLauncherIcon.cpp
Unity 源代码中定义了处理该问题的具体代码
/* (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);
但实际工作是由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
}
那我们该怎么办?
了解启动器所做的更改dconf
和具体行为可以帮助我们扩展其功能。我和 Jacob 举的例子包括:
dbus
创建文件的方法的特殊用途.desktop
在于允许自动为自定义应用程序创建快捷方式,稍后可以使用gsettings
Jacob 描述的方法将其锁定到启动器。