当我点击Unity中的“Lock to launcher”时会发生什么?
在Unity桌面中,当我启动GUI应用程序时,其图标将出现在启动器中(如果它尚未存在)。
现在,当我右键单击此图标时,我会选择Lock to Launcher或Ununc from Launcher ,具体取决于应用程序是否已锁定到启动器。
我的问题是:
如果没有.desktop
文件,当我点击这两个选项中的一个时会发生什么?
它是否可以自动创建简单的.desktop
文件,如果它找不到,在哪种情况下可能发生这种情况,以及固定的启动器项目在哪里得到保存?
如果您将应用程序锁定到启动器/从启动器解锁应用程序,会发生什么
不确定这个答案是否足够深入“引擎盖下”,但这是发生的事情:
您可以通过以下命令获取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应用程序时,右键单击启动器中的图标并选择“ 锁定到启动器” ,当前选择的项目将添加到列表中,而从启动器解锁将从列表中删除该项目。
以编程方式编辑Unity Launcher
重新阅读您的问题下面的(第一个)评论:如上所述,您可以通过命令获取当前的Launcher项目:
gsettings get com.canonical.Unity.Launcher favorites
并通过命令设置可能的更改列表:
gsettings set com.canonical.Unity.Launcher favorites "[item1, item2, etc]"
然后,您可以编程方式编辑Unity Launcher的内容,如此处所述 。
如果应用程序没有.desktop文件
如果您运行没有现有.desktop
文件的GUI应用程序,Unity会在本地创建一个基本的(在~/.local/share/applications
),以可执行文件( application.desktop
)命名。 在Exec=
行中,您将找到您运行的命令,以调用该应用程序。
如果您要查看以这种方式创建的.desktop
文件,它包含以下行:
X-UnityGenerated=true
注意
正如@muru所提到的那样(谢谢!),在一些(特殊的,看似似乎)情况下,Unity没有成功创建可执行文件的“缺失” .desktop
文件。 然而,我能找到的唯一例子是Tkinter窗口,它在wmctrl -lp
的输出中由pid 0
拥有。
单击“ 锁定到启动器”选项时会发生什么,Unity将更改启动器collections夹的特定dconf
架构并调用几个dbus
方法。 程序员和应用程序开发人员的关键是dconf
架构的变化。 (雅各布的答案依赖于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将在org.ayatana.bamf
服务上发出对org.ayatana.bamf.control.CreateLocalDesktopFile
方法的dbus
调用。 这可用于自动创建.desktop
文件。 虽然这在dbus-monitor
输出中没有显示,但我相信这是Unity可能使用的方法之一。
这是一个小型演示:
# 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启示
我已经执行了dbus-monitor --profile
命令运行的锁定和解锁操作。 你可以看到几个方法调用(由mc
指定)到ca.desrt.dconf.Writer
接口和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
如果使用dconf-monitor
执行更详细的视图,您将看到对dconf的调用会写入字节序列,并且zeitgeist会记录添加的条目。 我已经多次测试了这些,这些是在每种情况下执行的相同操作。
样本输出formsZeitgeist。
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源代码:
处理它的特定代码在Unity源代码的launcher/ApplicationLauncherIcon.cpp
中定义
/* (Un)Stick to Launcher */ glib::Object 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
所做的更改以及启动程序的特定行为可以帮助我们扩展其function。 我和雅各布的例子包括:
- 将Launcher设置为输入文件
- 重新排序以使活动应用程序顶部或底部项目
- 从用户到用户克隆启动器设置
- 每个工作区创建唯一的启动器
dbus
方法用于创建.desktop
文件的特殊用途允许自动创建自定义编写应用程序的快捷方式,稍后可以使用Jacob描述的gsettings
方法将其锁定到启动gsettings
。