Linux下的图标与文件关联机制:freedesktop

目前主流的Linux发行版中,使用的桌面基本都是基于GNOME、KDE、Xfce等环境,这几种桌面环境中,关于桌面图标,文件关联,应用程序启动等方面的实现,全部都使用的是FreeDesktop,可以说虽然Linux的发行版众多,但关于图标文件关联这部分机制,基本都使用的是同一套,研究明白了freedesktop基本可以在绝大多数的Linux桌面系统里得到应用。

freedesktop.org最初的名称叫XDesktopGroup(X桌面工作组),缩写就是我们熟悉的"XDG",在一个桌面的终端输入 env | grep XDG就会看到很多相关的环境变量。
在这里插入图片描述
freedesktop是完全开源的,不过我们作为系统的使用者,完全不用研究freedesktop的代码实现,只需要学习它的规范就可以了。

所有的内容都可以在其官网上找到:freedesktop.org

以下的内容,主要是根据个人经验和理解对官方文档进行了粗略的翻译,国内目前没有特别详尽的中文版freedesktop文档,如有纰漏请各位大佬指正

freedesktop规范

顾名思义,规范就是只要你遵照它要求的目录和写法进行配置文件的编写,就可以实现对应的功能。freedesktop已经为你提供了足够的工具、目录以及一些范例

下面是官网列出来的一些最常使用的规范:

  • 自动启动:freedesktop提供了用户登录后自动启动应用程序的规范
  • 桌面基础目录 (basedir):桌面应如何定位文件,例如配置文件或应用程序数据文件。
  • 桌面条目 (.desktop):描述有关应用程序信息的文件,例如名称、图标和描述。这些文件用于应用程序启动器和创建可启动的应用程序菜单。也是freedeskop机制里非常重要的组成部分
  • 桌面菜单(menu):如何从桌面条目构建菜单,这个在ubuntu下貌似看不到,如果使用麒麟或者UOS,会在开始菜单中看到明显的效果
  • 文件管理器 D-Bus 接口:与桌面文件管理器交互的常用方式。使用过一点,我的评价是非常灵活,但是非常复杂。
  • 文件 URI:如何创建和解释解释file://URI,用于拖放和其​​他桌面用途。
  • 免费媒体播放器规范:跨播放器和媒体格式存储和读取元数据的标准方法。
  • 图标主题:存储图标主题的常用方法。
  • 媒体播放器远程接口规范 (MPRIS):用于控制媒体播放器的 D-Bus 接口
  • 共享 MIME 数据库 (shared-mime-info):包含常见的 MIME 类型、描述和用于确定文件类型的规则。这里可以创建属于你自己的文件类型。
  • 启动通知:一种允许桌面环境跟踪应用程序启动、提供用户反馈和其他功能的机制。
  • 垃圾箱:类似Windows的回收站机智,虽然Linux用户更习惯于使用rm -rf,但XDG确实提供了类Windows的回收站机制
  • XML 书签交换语言 (XBEL):一种互联网“书签”交换格式。

还有更多的额外规范可以查阅:freedesktop.org/wiki/Specifications

规范详情

接下来对规范的具体内容详细描述,不需要全记住,当做一个文档来查阅就可以,掌握了这些内容可以对Linux桌面系统有更加深入的理解。

内容主要来自官网:specifications.freedesktop.org

Desktop文件

Desktop顾名思义就是图标文件,在Linux下桌面的图标,还有任务管理器中出现的图标,其实都是一个个后缀名为.desktop的文件,这些文件以文本的形式描述了它所对应的应用程序,它关联的文件类型以及它展现的图标等等。

Desktop文件内容是整个freedesktop的核心,包含的内容特别多。下面我进行了简单的总结

有余力的同学可以直接学习官网的详细文档:specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html

一个最基本的名为foo.desktop的Desktop文件示例长这个样子:

[Desktop Entry]
Version=1.0
Type=Application
Name=Foo Viewer
Comment=The best viewer for Foo objects available!
TryExec=fooview
Exec=fooview %F
Icon=fooview
MimeType=image/x-foo;

如上所示,一个.desktop文件以配置的形式描述了此文件的各种属性,包括上述文件中出现的关键字,官网中还列出了一份详细的字段说明,下面是我结合自己的经验和理解进行的翻译:

  • Type:类型,规范提供了三种,分别是Application、Link、Directory,实际上目前我们见到的desktop文件99%都是Application 写法示例:Type=Application
  • Version:遵循的规范版本,现在最新的是1.5,大多数都写得1.0,如果你用到一些新版本才有的字段,最好写成1.5。写法示例Version=1.0
  • Name:软件名称,一般会体现在任务栏上,比如你打开某个应用程序,然后把鼠标悬停在它任务栏的图标上,这时任务栏显示的名称,就是这个字段里的文本。写法示例Name=Mozilla
  • Name[zh_CN]:中文环境下显示的名称,示例:Name[zh_CN]=火狐浏览器
  • GenericName:一般是写这个软件的通用描述,例如:GenericName=Web Browser
  • GenericName[zh_CN]:中文环境下的软件描述,例如:GenericName[zh_CN]=网页浏览器
  • NoDisplay:只能写true或false,默认为false,如果是true的话,代表这个图标不再应用程序列表显示,比如ubuntu左下角的显示应用程序这里,将搜不到这个图标。但是启动对应的应用程序仍然可以正确地将图标和可执行程序关联,示例NoDisplay=true
  • Comment:使用提示,例如Comment=View sites on the Internet
  • Icon:在文件管理器、菜单栏、任务栏等地方显示的图标,支持jpg、png和svg,如果写的是一个绝对路径,它会用绝对路径中的图,最好写最大尺寸的路径,遇到小尺寸它能自己缩放;如果写的不是绝对路径而是一个图标名,它会根据图标配置找对应的文件,一般来说是在/usr/share/icons/hicolor/下,例1:Icon=/usr/share/icons/hicolor/256x256/mimetypes/wps-office-wps.png,例2:Icon=wps-office-wps 像例2中它会自己去找系统中的/usr/share/icons/hicolor/256x256/mimetypes/application-wps-office.wps.png,其中的256x256会根据当前图标大小自动调整。
  • OnlyShowIn,NotShowIn:限定这个程序运行的桌面环境,例如:OnlyShowIn=UnityNotShowIn=GNOME,分别代表这个程序只在Unity桌面运行和这个程序不再GNOME桌面运行。如果你不知道你要限定的目标桌面环境是哪一种,可以echo $XDG_CURRENT_DESKTOP来查看。
  • Hidden:值只有true或false,说实话我从来没用过,描述也没有看太明白。
  • DBusActivatable:指定此应用程序是否支持 D-Bus 激活。如果缺少此键,则默认值为false。如果值为,true 则实现应忽略该Exec键并发送 D-Bus 消息以启动应用程序。DBusActivatable=true
  • Exec:要执行的程序,也就是图标被点击或者被双击时需要执行的可执行程序,可能带有参数。程序必须是绝对路径,比如:Exec=/usr/bin/firefox %f,几个最常用的参数说明如下:
    • %f: 单个文件名,如果传了多个文件名,会创建多个程序副本分别作为参数传递给可执行程序
    • %F: 一个或多个文件名,如果传了多个文件名,每个文件都作为单独参数传递给可执行程序
    • %u: 单个 URL。本地文件可以作为 file: URL 或作为文件路径传递。
    • %U: URL 列表。每个 URL 作为单独的参数传递给可执行程序。本地文件可以作为 file: URL 或作为文件路径传递。
  • TryExec:用于确定程序是否实际安装的可执行文件的路径。如果路径不是绝对路径,则在 $PATH 环境变量中查找文件。如果文件不存在或不可执行,则该条目可能会被忽略
  • Path:运行程序的工作目录,例Path=/opt/google/
  • Terminal:程序是否在终端窗口中运行,例:Terminal=true,默认为false。
  • Actions: 行为,可以为应用程序提供更多的扩展性为,会体现在任务栏上面点击右键,上面的示例里面没有体现,下面桌面图标一节写一个带Action的示例展示这个功能。
  • Categories:条目应显示在菜单中的类别,例如一个基于qt实现的图片阅读器,它的此字段写法就是Categories=Qt;Graphics;RasterGraphics;Viewer;一般某些包含分类功能的软件管理器内会采用这个字段进行分类。
  • MimeType:此应用程序支持的 MIME 类型。多种类型之间用分号隔开,例如QtCreator编辑器它支持的MimeType写法就是:text/x-c++src;text/x-c++hdr;text/x-xsrc;application/x-designer;application/vnd.qt.qmakeprofile;application/vnd.qt.xml.resource;后面会有一整个篇章详细介绍MimeType
  • Implements:此应用程序实现的接口列表,没用过,而且默认情况下,桌面文件不实现任何接口。
  • StartupNotify:值为true或者false,官方的解释非常抽象,它说如果这个值是true的话,会在设置了环境变量DESKTOP_STARTUP_ID的情况下启动程序时发送一个remove消息。这个解释我是看不明白。不过有个直观的现象可以解释这个字段:在某些系统上,如果这个字段的值为false的话,会导致启动程序时任务栏的图标不被复用,会在下面创建一个新的图标如图:
  • StartupWMClass:值是一个字符串,你可以简单的理解成通过这个desktop启动的程序从属于哪一个窗体组,比如你如果没有指定这个选项,你通过desktop启动的Foo程序一切正常,但是如果你直接命令行启动程序很可能无法正常显示.desktop文件里配置的图标。这时候你就需要在这个Desktop里面加上StartupWMClass=Foo,这样以后无论从哪里启动Foo进程,任务栏等地方都能正常显示图标。这个字段一般还会影响程序的快捷键等功能。如果你无法确定你的程序这个值应该配置成什么,可以再终端输入xprop,光标变化后点击一下你的程序窗体,它会输出这个窗体的各种信息,其中WM_CLASS(STRING)这一行的值就是你需要的。
  • URL:如果你这是一个Link类型的desktop,这个字段些它链接的URL。
  • PrefersNonDefaultGPU: 值是true或者false,如果是true的话,应用程序会更倾向于在配置更高的GPU上运行,是一个建议值,根据实现不同可能不一定能生效。
  • SingleMainWindow: 有点类似于单例模式,如果为 true,则应用程序只有一个主窗口,并且不支持打开另一个主窗口。取决于桌面环境的实现,不一定能在所有平台上生效,如果想做单例进程的话还是在程序本身上用代码实现比较好。
  • InitialPreference【仅KDE】:优先级,支持1到9,仅KDE下生效,用于抢文件关联时使用,如果不同的Desktop都注册了相同的mimetype,会遵循此优先级进行关联,数值越大优先级越高,例InitialPreference=3
  • X-Deepin-CreatedBy【仅UOS】:由谁创建,一般值是com.deepin.dde.daemon.Launcher
  • X-Deepin-AppID【仅UOS】:进程ID,一般就是可执行程序的进程名。

最后两个UOS的专属变量一般不用手动添加,这个UOS签名工具会自动帮加上,如果没有这两个字段,会导致一个问题是,用户手动启动程序然后在任务栏右键,驻留。卸载时驻留的图标不会跟着删除,原因就是desktop里没有这两个字段导致的。

以上就是比较完整的.desktop文件规范,只要按照这个规范书写的文本文件,命名为xxx.desktop就可以,这个.desktop文件在Linux桌面环境中经常扮演着很重要的角色,比如开机自启动、应用程序管理器、任务栏图标、文件关联、桌面图标等等都用到它,接下来一一介绍这些相关的规范。

桌面图标

其实桌面这个东西原本就是Windows的,Linux本身并不推崇这种Windows的使用习惯,在fedora、centos的新版中只有名为桌面或者Desktop的目录存在,但系统本身使用时,默认已经看不到有所谓桌面的痕迹了,所有的应用程序都通过终端或者任务管理器来打开。

不过作为图形界面的重要一环,桌面图标仍然存在在freedesktop标准中。

而其实在Linux系统上,桌面图标本身就是一个.desktop文件。

想要在Linux的桌面上创建一个桌面图标,其实非常简单,就把对应的desktop文件拷贝到桌面上就可以了。 一般图形化界面的程序安装包都会在/usr/share/applications目录下放一个.desktop文件,如果你想从桌面快速打开它,就找到它对应的desktop文件拷贝到桌面就可以了,一般按名字搜就能找到。

比如谷歌浏览器的.desktop文件就是/usr/share/applications/google-chrome.desktop,你可以直接拷贝它到桌面,然后右键允许启动,就会得到谷歌浏览器的桌面图标。

如果是没有安装包的可执行程序,我们也可以为它编写一个.desktop文件放到桌面,方便我们自己的使用。

下面我们以一个示例来说明,手动编写一个电子书阅读器的桌面图标文件

我提前下载了一个单文件的电子书阅读器Koodo-Reader-1.3.9.AppImage,并且创建了一个软连接在/usr/bin下,现在通过终端直接输入Koodo可以直接打开电子书软件。但是这个软件没有图标,也无法从桌面打开,每次都起一个终端来调起它并不方便,下面我们就为它写一个桌面图标。

首先,在桌面创建一个空的deskop文件:

cd ~/Desktop # 有些系统上是 cd ~/桌面
touch chrome.desktop

此时你看下桌面上的koodo.desktop,它还是一个普通的文本文件

接下在文件中输入如下的内容:

[Desktop Entry]
Version=1.0
Name=Koodo
Name[zh_CN]=Koodo阅读器
GenericName=Book Reader
GenericName[zh_CN]=电子书阅读器
Comment=Read epub files
Comment[zh_CN]=阅读电子书
Exec=/usr/bin/Koodo %F
StartupNotify=true
Terminal=false
Icon=google-chrome
Type=Application
Categories=Network;WebBrowser;
MimeType=application/epub+zip
Actions=new-window;open-nautilus;
StartupWMClass=koodo-reader

[Desktop Action new-window]
Name=New Window
Name[zh_CN]=新建窗口
Exec=/usr/bin/Koodo

[Desktop Action open-nautilus]
Name=New Window
Name[zh_CN]=打开文件管理器
Exec=/usr/bin//usr/bin/nautilus

我们上面借用了谷歌浏览器的图标,保存文件,回到桌面查看这个.desktop是否已经变了,如果没有的话,在有些系统上需要右键,允许启动(只要文件里有了Type=Application这一句,右键就会出现这个选项)。允许启动后就会看到图标的效果,双击这个桌面图标,就会启动我们的电子书程序:

需要注意的是

  1. 桌面图标只在桌面起作用,如果你用文件管理器打开~/桌面这个目录,会发现.desktop文件显示的仍然是一个配置文件的图标,双击会用默认的文本编辑器打开。
  2. 通过桌面图标启动的程序仍然没有任务栏图标,我们上面写的action也没有看到效果,这个问题将在后面应用程序管理器 && 任务栏图标 && 文件关联一节得到解决。

图标文件

如果你想在.desktop文件里使用自己设计的软件图标,需要在指定Icon=xxxxx,这个字段在上面介绍了,可以写绝对路径,比如通过Icon=/opt/apps/myapp/icons/test.png的形式。但这种写法的缺点是.desktop文件会比较丑,还有你只能使用一种大小的图片,如果你使用的图标尺寸较小而用户设置显示大图标的话,就会导致图片始终很小或者因被强行放大而虚化。

正确的姿势应该是将不同尺寸大小的图标(比如叫myproject.png)放置到/usr/share/icons/hicolor/nxn下,其中nxn代表不同的尺寸,系统下有128x128 192x192 22x22 256x256 36x36 42x42 512x512 64x64 80x80 96x96 16x16 20x20 24x24 32x32 40x40 48x48 60x60 72x72 8x8,放置后在.desktop文件里写Icon=myproject系统会自动检索此目录下对应尺寸并且名为myproject.png的图标文件。

这里要注意图像文件必须是以下类型之一:PNG、XPM 或 SVG,扩展名必须为“.png”、“.xpm”或“.svg”(小写)。

其实不止/usr/share/icons,系统默认的是查找$HOME/.icons$XDG_DATA_DIRS/icons/usr/share/pixmaps,一般情况下icons目录下有多个主题的图标,其中hicolor就是让第三方应用程序安装其图标的目录。

/usr/share/icons下可以看到多个主题:

  • Adwaita
  • DMZ-Black
  • gnome
  • handhelds
  • hicolor
  • locolor

每个主题中都至少包含一个名为index.theme的文件,描述了主题的一般属性
这个文件的内容和格式很复杂,一般也接触不到,感兴趣的话可以阅读这里:https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html

每个目录下还有一个icon-theme.cache数据库文件,用户快速检索对应的图标文件,如果安装了png或者svg图片后仍然没有生效,可以使用update-icon-caches命令更新一下图标缓存数据库。

开机自启动

如果你想要在Linux实现开机自动启动一个程序,只需要做两件事:

  1. 编写一个传统规范的.desktop文件
  2. 把这个文件放到/etc/xdg/autostart目录下

只要你的.desktop文件格式正确,重启后就会发现进入系统后程序被自动启动了。下面提供一个自动启动的.desktop文件示例:

[Desktop Entry]
Type=Application
Name=Koodo Autostart
Name[zh_CN]=电子书 自动启动
Exec=/usr/bin/Koodo 
NoDisplay=true
X-GNOME-AutoStart=true
X-GNOME-Autostart-Phase=Initialization
X-GNOME-Bugzilla-Bugzilla=GNOME

注意这里添加了NoDisplay=true,防止这个.desktop出现在应用程序管理器里。

关于目录其实不止/etc/xdg/autostart这一个,只是这是一个比较通用并且肯定会生效的目录,规范中的定义其实是: 通过将应用程序的 .desktop 文件放在 Autostart 目录之一中,应用程序将在用户登录后启动用户桌面环境期间自动启动。

其中Autostart自启动目录则包括:

  1. $XDG_CONFIG_DIRS/autostart
  2. $XDG_CONFIG_HOME/autostart
  3. ~/.config/autostart/
  4. /etc/xdg/autostart/

每个目录下的.desktop文件都会自动启动,如果环境变量不存在则直接在目录下面找,如果出现同名的.desktop则只有高优先级目录下的.desktop生效

挂载自启动

其实freedesktop的标准中还支持挂载自启动的功能,但处于安全原因并不常用。有兴趣的同学可以参考下官网关于这部分描述:specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html

应用程序管理器 && 任务栏图标

如果想要在系统的应用程序管理器里看到你的程序,只需要以下三步:

  1. .desktop文件放到系统目录 /usr/share/applications
  2. 确保你的.desktop文件中,存在StartupWMClass={WM_CLASS}字段,其中{WM_CLASS}要替换成对应程序的窗体WM_CLASS,可以通过xprop工具查看。(这一点决定了你启动的进程能够成功关联到图标)
  3. 执行sudo update-desktop-database 生效配置

完后就能在所有程序中找到对应的应用程序,通过这个启动的应用程序,就会有图标:
在这里插入图片描述

基本路径

探讨这部分规范之前,先思考一个问题,在有些XC的系统下,我们有时候会看到既存在~/Desktop目录,也存在~/桌面目录,那么究竟真实的桌面用的是哪个目录?每个系统都一样吗?如果我们想拷贝桌面图标应该拷贝到中文目录下还是英文目录下? 这一小节的规定就是解决的此类问题。

基本路径的规范通过一系列环境变量来定义系统应在何处查找某些文件。常用的有一下这些:

  • $XDG_DATA_HOME : 存储用户特定数据文件的基本目录,如果为空或者未设置,默认为$HOME/.local/share
  • $XDG_CONFIG_HOME : 存储用户特定配置文件的基本目录,如果为空或者未设置,默认为:$HOME/.config
  • $XDG_STATE_HOME : 存储用户特定状态文件的基本目录,如果为空或者未设置,默认为:$HOME/.local/state。(这个仅在应用程序重启过程中记录一些中间数据,操作历史,程序当前状态等)
  • 用户特定的可执行文件可能存储在 $HOME/.local/bin 中。一般也会加在 $PATH 环境变量中的适当位置。
  • $XDG_DATA_DIRS :存储除了$XDG_DATA_HOME基本目录之外的数据文件,用:分隔,如果为空或未设置,默认为/usr/local/share/:/usr/share/
  • $XDG_CONFIG_DIRS :存储除$XDG_CONFIG_HOME基本目录之外的配置文件,用:分隔,如果为空或未设置,默认为/etc/xdg 顺序代表优先级,$XDG_CONFIG_HOME优先级最高,然后是$XDG_CONFIG_DIRS的路径按顺序优先级越高越靠前,相同的配置高优先级目录下生效。
  • $XDG_CACHE_HOME : 存储用户的非必要数据文件的基本目录,也是用户缓存数据的存储目录。如果为空或者未设置,默认为:$HOME/.cache
  • $XDG_RUNTIME_DIR : 存储用户的非必要运行时文件和其他文件对象(如套接字、命名管道等)应该存储的基本目录。所有者必须是用户,并且只有此用户对它有读写权限,权限位必须是700。这个目录的生命周期与正在登录的用户严格绑定。用户首次登录时自动创建,如果用户完全注销,此目录就会被删除。如果用户多次登录,它会被指向同一个目录,并且从他第一次登录到他最后一次注销系统,该目录必须继续存在,并且在这期间不能被删除。目录中的文件必须不能在重新启动或完整的注销/登录周期后继续存在。

如果$XDG_RUNTIME_DIR没有被设置,应用程序应该退回到具有类似功能的替换目录并且打印警告信息,应用程序会使用这个目录进行通信或同步,还有不要在这个目录中放置大文件。

以上环境变量都应该设置成可访问的路径,可以设置多个值,如果有多个路径,中间用:隔开。

所有的路径必须是绝对路径,如果遇到相对路径系统会认为路径无效自动忽略。

回到一开始的问题,XDG在哪里规定了系统将哪个目录当做桌面?答案是~/.config/user-dirs.dirs,这个文件中定义了包括桌面、下载在内的几个基本目录,例如在中文环境的Ubuntu20系统上配置如下:

XDG_DESKTOP_DIR="$HOME/桌面"
XDG_DOWNLOAD_DIR="$HOME/下载"
XDG_TEMPLATES_DIR="$HOME/模板"
XDG_PUBLICSHARE_DIR="$HOME/公共的"
XDG_DOCUMENTS_DIR="$HOME/文档"
XDG_MUSIC_DIR="$HOME/音乐"
XDG_PICTURES_DIR="$HOME/图片"
XDG_VIDEOS_DIR="$HOME/视频"

如果有系统既存在~/Desktop目录,也存在~/桌面目录,就看他的~/.config/user-dirs.dirs文件里记录的XDG_DESKTOP_DIR是谁,就是它正在使用的真是目录。

同样还有比较常用的$XDG_TEMPLATES_DIR目录,放在这个目录下的文件,会自动出现在Linux系统的右键新建文档里面,作为文件模板。

需要注意的是不同的操作系统对于模板创建文件的处理方式不尽相同,比如你在桌面上右键>>新建文档>>WPS文档,在部分系统上新建的文件修改时间就是创建文件的时间,有些系统上修改时间和会和模板文件创建的时间保持一致。可以理解成有些执行的是cp 命令,有些执行的是cp -a加了-a参数

文件类型

MimeType

首先,这里的文件类型指的是普通文件的类型,我们都知道Linux有七大文件类型,分别是:

- 	普通文件
d 	目录文件
l	软链接
b	块设备文件,如硬盘,光驱等
p	管道文件
c	字符设备文件,如猫等串口设备
s	套接字文件,如mysql.sock等

而普通文件又可以有很多分类,比如文本文件、二进制文件、动态库文件、可执行文件等等,本文规范里的文件类型,讨论的就是针对普通文件所进行的分类。

不同于windows简单粗暴地使用后缀名来标识一个文件类型,许多程序和桌面使用MIME Type来将文件划分为多种类型,方便对其进行统一的管理。MIME Type是用于描述文件的类型的一种表述方法,指定了文件的类型名称、描述、图标信息,同时通过与.desktop应用程序描述文件整合,指定了文件的打开方式。

确定一个文件的MIME类型有很多种方法,比如通过http的协议头、电子邮件的协议头提供的mime类型,不过一般在Linux下,文件管理器打开某个文件时,通常使用文件后缀名(glob)和magic匹配来判断一个文件的类型:

  1. 文件后缀名: Linux中,系统级别的从文件后缀到 MIME 类型的映射存放在/etc/mime.types文件中,用户可以在~/.mime.types中自定义自己的后缀名和文件类型匹配,不过传统情况下Linux是忽略文件后缀名的。
  2. magic数匹配: 通过判断文件中的某一位置的数据是否是特定的字符串或者byte数据来确定文件类型,比如:
    • #!/usr/bin/awk开头的文件是awk脚本
    • \037\213开头的文件是tar.gz压缩文件
    • 文件以\177ELF开头并且第5位是1,第16位是3的文件是动态库
  3. 如果无法确定 MIME 类型,则默认为 application/octet-stream
  4. 应用程序的规则: 应用程序可能有自己的 MIME 规则。他们如何实施和记录这些规则取决于每个应用程序。这个应用程序可能是一个浏览器也可能是GNOME、KDE桌面,正是因为这些规则各自为政杂乱无章,于是freedesktop就出现,收集并整理了各大平台桌面mime机制的通用化规则,形成了统一的规范。

目前几乎所有的Linux系统都采用这一套机制来对文件进行分类。

个人认为MIME这种文件类型的表示方式更加灵活,但也更加复杂。它不止通过后缀名、还通过文件内容对类型进行判断,这样要比仅通过后缀名判断准确的多,也避免了单纯的通过后缀名标识文件而引发的很多问题。

我们可以通过file命令来查看一个文件的类型,加--mime-type参数查看文件的mimetype

username@PCname:~$ file test*
test:     ASCII text
test2:    empty
test2.sh: Bourne-Again shell script, ASCII text executable
test.sh:  ASCII text
username@PCname:~$ file test* --mime-type 
test:     text/plain
test2:    inode/x-empty
test2.sh: text/x-shellscript
test.sh:  text/plain

可以看到test.shtest2.sh都是同样的后缀名却标识成了不同的文件类型,原因是test2.sh的开头写了#!/bin/bashtest.sh没有,文管系统(file manager)通过识别文件内容给他们分类成了不同类型的文件。

那么,文管系统是通过什么样的机制来做文件类型区分的,就是这部分重点要讨论的内容。

需要注意的是,MIME type是被file manager使用,而不是Gnome或者Ubuntu系统本身。而且并不是freedesktop提供的mimetype这套机制,freedesktop只是提供了一个规范,好统一一下GNOME、KDE等各个桌面平台杂乱无章的文件类型。比如在freedesktop统一之前,可能同一个文件在不同的桌面平台被识别成不同的文件类型,Linux桌面环境又多,这样会造成很大的困扰。

目录布局

mimetype的信息主要记录在几个固定的目录下,默认为XDG_DATA_HOME:XDG_DATA_DIRS和用户目录,一般情况下,这个目录包括:

  • /usr/share/mime
  • /usr/local/share/mime
  • ~/.local/share/mime

我们后面的讲述统一以/usr/share/mime为例。

在mime目录下,最基础的内容是/usr/share/mime/packages/*.xml,这里xml就是mime类型的描述文件,所有的基础mime类型都来自这里,每一个xml包含了一个或多个文件类型,文件里通过配置的形式详细描述了每一个文件类型(MIME type)的参数特征(名称、、别名、描述、图标),以及将文件归类为此文件类型的判断标准。

其中,来自于包shared-mime-info的文件/usr/share/mime/packages/freedesktop.org.xml就是freddesktop标准统一归类的文件类型。这里面几乎包含了所有我们常见的文件类型,初期的数据整合了早期KDE和GNOME的文件类型描述信息,并逐步更新。

shared-mime-info包还提供了命令工具:update-mime-database,每当/usr/share/mime/packages/下的内容有更新时,就执行这个命令生效改动,执行此命令生效改动会生成如下文件:

  • globs(包含从名称到 MIME 类型的映射)有了globs2后被弃用,现在都用的是globs2。
  • globs2(包含从名称到 MIME 类型和全局权重的映射)
  • magic(包含从文件内容到 MIME 类型的映射)
  • subclasses(包含从 MIME 类型到它们继承的类型的映射)
  • aliases(包含从别名到 MIME 类型的映射)
  • icons(包含从 MIME 类型到图标的映射)
  • generic-icons(包含从 MIME 类型到通用图标的映射)
  • XMLnamespaces(包含从 XML (namespaceURI, localName) 对到 MIME 类型的映射)
  • mime.cache(采用二进制的形式将上面几个文件的内容全部整合到了一起,存储成可映射格式)

当系统需要判断一个文件是什么类型是,去检索每一个xml文件效率很低,因此freedesktop将这些信息以二进制的形式全部整合到了mime.cache数据库当中方便查询,此cache文件不建议用户修改,可以通过修改上面提到的xml文件,并通过update-mime-database /usr/share/mime/命令更新cache文件

xml文件

如果你的应用程序需要给系统安装一个自己的文件类型,需要做两件事情:

  1. 提供一个xml文件放到三个mime信息目录之一/usr/share/mime/packages/(.xml)
  2. xml放好后,执行update-mime-database命令更新数据信息,参数是刚才放置mime信息的目录。update-mime-database /usr/share/mime/

这里对xml文件的格式和内容规范做一个详细的说明,下面是一个文件类型xml文件示例,如果你想写一个自己的文件类型,可以照抄下面的示例,也可以在/usr/share/mime/packages/目录下参考现有的写法:

<?xml version="1.0"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
    <mime-type type="text/x-diff">
        <comment>Differences between files</comment>
        <comment xml:lang="af">verskille tussen lêers</comment>
        ...
        <magic priority="50">
          <match type="string" offset="0" value="diff\t"/>
          <match type="string" offset="0" value="***\t"/>
          <match type="string" offset="0" value="Common subdirectories: "/>
        </magic>
        <generic-icon name="generic-diff"/>
        <glob pattern="*.diff" weight="99"/>
        <glob pattern="*.patch"/>
    </mime-type>
</mime-info>

文档元素可以包含零个或多mime-type个子节点,以任何顺序,每个子节点描述一个单一的 MIME 类型。每个元素都有一个type 属性,给出了它所描述的 MIME 类型。

每个mime-type节点可以包含以下元素的任意组合,并且以任意顺序:

  • globglob元素有一个pattern属性。任何名称与此模式匹配的文件都将被赋予此 MIME 类型(当然,受制于其他文件中的冲突规则)。还有一个可选weight属性,用于解决与其他全局匹配的冲突。默认权重值为 50,最大值为 100。
  • glob-deleteall:用来过滤掉一部分之前glob字段的匹配结果,比如你想让所有"*.txt"中的"CMakeLists.txt"不适用txt的文件类型,就可以用<glob-deleteall="CMakeLists.txt"/>过滤掉。它和glob共用一样的规则。
  • magicmagic元素包含一个match列表,满足任何一个都可以匹配到当前类型(或的关系),包含的所有项都有一个可选的priority属性,可以给一些比较通用的类型写一个比较小的数值,而且一些比较特殊的子类型写一个比较大的数值,默认50,最高100(比如gzip类型和一些基于gzip的文档格式)。每一个match元素包含一下属性:
    • type:匹配类型,支持:string, host16, host32, big16, big32, little16, little32, byte.
    • offset:偏移量,表示从文件的哪一位开始匹配数值。可以是一个数值也可以是一个范围,以起始位:结束位的形式,范围的话会检查范围内的所有偏移量,包括这个头和尾。
    • value:用于比较文件内容的值,采用 type 属性指示的格式。字符串类型支持 C 字符转义(\0、\t、\n、\r、\xAB 表示十六进制,\777 表示八进制)。
    • mask(非必须):将文件内容与value中的值做“与”运算。数字类型的掩码可以是任何数字,而字符串的掩码必须是16进制数,并以 0x 开头。
    • 可能有些抽象,举几个例子:
      • bash文件:<match type="string" value="/bin/bash" offset="2:16"/>
      • elf文件:<match type="string" value="\177ELF" offset="0">
      • xz文件:<match type="string" value="\xfd\x37\x7a\x58\x5a\x00" offset="0"/>
      • cdr文件:<match type="string" value="CDRXvrsn" mask="0xffffff00ffffffff" offset="8"/>
  • magic-deleteall:用来反向过滤调之前magic中的一部分匹配结果
  • alias:用type属性给这个类型起一个别名,比如<mime-type type="application/zip">类型的别名:<alias type="application/x-zip-compressed"/><alias type="application/x-zip"/>
  • sub-class-of:这个元素代表当前类型属于某个类型的子类型,有一部分数据也命中了父类型的匹配规则,匹配的时候会优先识别成当前类型。有type属性指定,例如bash类型它就既是文本类型的子类型,也是可执行类型的子类型:<sub-class-of type="application/x-executable"/> <sub-class-of type="text/plain"/>
  • comment:给出此MIME类型一个文本描述,比如你通过file命令查看一个文件,就会显示这个字段作为文件类型而不是直接显示mimetype,一般用xml:lang属性标识语言,例如:<comment xml:lang="zh-CN">shell 脚本</comment> <comment xml:lang="zh-TW">shell 指令稿</comment>
  • acronym:给出一个文档类型一个简洁描述,比如XML、PDF、HTML
  • expanded-acronym:acronym类型的扩展解释,比如eXtensible Markup Language、Portable Document Format、HyperText Markup Language
  • icon:指定这个mime类型使用的图标,由name属性指定,一个类型只允许存在一个icon元素(实践中并不常见,更常用的是generic-icon
  • generic-icon:制定这个mime类型的通用图标的图标,由name属性指定。例如:<generic-icon name="wps-office-doc"/>,这样写的话,如果遇到doc文件,则系统会使用wps-office-doc.png来作为图标,路径一般是/usr/share/icons/hicolor/{n}x{n}/mimetypes/application-wps-office.doc.png或者/usr/share/icons/hicolor/{n}x{n}/mimetypes/wps-office.doc.png,{n}代表尺寸,8、16、32、64、128、256等等,如果想要一个自动搜索路径以外的图标,这里也可以直接写一个绝对路径。每个类型只能指定一个generic-icon,如果没有指定这个属性的话,会默认使用它的顶级类型+“-x-generic”作为它的图标,比如之前的例子里就是application-x-generic
  • root-XML:这个元素有namespaceURIlocalName两个属性,如果一个类型是XML类型的子类型,这个元素允许根据文档元素的命名空间和本地名称选择更具体的MIME类型。比如XHTML类型的这个字段就写的是<root-XML namespaceURI="http://www.w3.org/1999/xhtml" localName="html"/>

以上是整个mimetype最核心的规范描述,只要按照格式编写或修改xml文件,就可以定义对应的文件类型及属性。

上面还提到freedesktop通过update-mime-database生成了一系列映射文件方便系统快速查找文件类型的映射关系,比如globs、globs2、magic、subclasses、aliases、icons、generic-icons、XMLnamespaces、mime.cache等,这些文件都有各自特定的格式和规范,这里不详细描述,感兴趣的同学可以自行查阅:specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html

子类型(Subclassing)

如果某个类型的任何示例都是第二种类型的示例,则它就是另一种类型的子类,比如image/svg+xml文件,它既是application/xml,也是text/plainapplication/octet-stream文件,子类型是根据格式划分,而不是根据数据类型。

另外还有一些隐含的子类规则:

  • 所有的text/*类型都是text/plain的子类型,即所有的文本文件类型。
  • 所有的“可流式传输”的类型都是application/octet-stream的子类,也就是除了inode/*类型,以外的所有文件类型都是application/octet-stream类型的子类型。

除了这些规则之外,还可以使用sub-class-of元素给出明确的子类信息。
一个类型可以是多个类型的子类型,所以也可以有多个sub-class-of元素。

文件类型的检索顺序

如果你编写自己的应用程序需要判断文件的mimetype,这里官网提供了一个推荐的检索方式:

  1. 如果文件明确提供了MIME类型,比如通过HTTP标头,MIME电子邮件附件等方式,就直接使用这个类型。
  2. 如果没有,则对文件名先进行全局匹配,只保留权重最高的文件名。如果匹配规则不一样,就保留最长的匹配,如果在此之后,有一个或多个匹配结果,并且所有匹配结果都指向相同的mimetype,则使用该 mimetype 作为结果。
  3. 如果文件名匹配失败,或者匹配到了不同的mimetype,就读取文件内容做magic匹配,如果没有magic规则命中或者文件内容不可读,那二进制文件就认为是application/octet-stream,文本文件就认为是 text/plain,如果有匹配,就将magic的匹配结果作为mimetype的结果。

    注意:可以通过检查文件的前 128 个字节是否有 ASCII 控制字符来判断文件是二进制文件还是文本文件,不过,具有高位集字符的文件仍应被视为文本,因为这些字符可能出现在UTF-8 文本,与控制字符不同。

  4. 如果文件名匹配的结果和magic匹配的结果重合或者有子类型关系,那就使用这个结果作为文件的mime类型
  5. 如果以上都不行,就使用权重最高的文件名匹配的结果。

无论如何都应该以文件名的检索优先,因为通过magic来判断文件内容开销非常大。

非常规文件

也就是文章开头我们说的Linux七大文件类型里的其他六大,在freedesktop中也给出了各自的文件类型:

  • <mime-type type="inode/blockdevice">
  • <mime-type type="inode/chardevice">
  • <mime-type type="inode/directory">
  • <mime-type type="inode/fifo">
  • <mime-type type="inode/mount-point">
    • <sub-class-of type="inode/directory"/>
  • <mime-type type="inode/socket">
  • <mime-type type="inode/symlink">

文件关联

上面介绍 freedesktop 通过文件类型规范提供了一种存储有关 MIME 类型和用于确定文件类型的规则的静态信息的方法。

通过desktop规范来使应用程序的一些属性和系统产生关联,并且有一个字段允许应用程序声明它们支持的MIME类型。

那么,默认该由哪个应用程序来打开某种类型的文件?如何让用户更改程序的默认打开方式?以及如何让用户添加或删除应用程序和 mimetypes 之间的关联?就是这一小节文件关联的规范讨论的内容

文件名和位置

在freedesktop规范中,通过将信息写入名为mimeinfo.cachemimeapps.list的文件中创建和修改文件关联。

mimeinfo.cache文件和mimeapps.list都由update-desktop-database命令自动生成,通过从每个.desktop文件中抓取其关联的MIME type,并形成mime type与application关联数据库,mimeinfo.cache里记录了所有的文件关联数据,不过这个顺序比较随机,对于同一个文件类型,一般先扫描到哪个desktop,哪个程序就写在前面,因此不能很好的满足定制优先级的需求。mimeapps.list则对优先级作了补充,负责指定每个mime type在发生多程序关联时,哪个程序优先级高

这两个文件都可以手动更改,不过一般更建议将写好的.desktop文件放到/usr/share/applications/下并执行sudo update-desktop-database /usr/share/applications来更新文件关联信息。

查询某个类型的文件关联的方式:xdg-mime query default 文件类型
例如:

xdg-mime query default application/pdf
xdg-mime query default image/jpg

默认的优先级如下:

  1. $XDG_CONFIG_HOME/$desktop-mimeapps.list,默认是:~/.config/gnome-mimeapps.list
  2. $XDG_CONFIG_HOME/mimeapps.list,默认是:~/.config/mimeapps.list
  3. $XDG_CONFIG_DIRS/$desktop-mimeapps.list,默认是:/etc/xdg/gnome-mimeapps.list
  4. $XDG_CONFIG_DIRS/mimeapps.list,默认是:/etc/xdg/.config/mimeapps.list
  5. $XDG_DATA_HOME/applications/$desktop-mimeapps.list,默认是:~/.local/share/applications/gnome-mimeapps.list
  6. $XDG_DATA_HOME/applications/mimeapps.list,默认是:~/.local/share/applications/mimeapps.list
  7. $XDG_DATA_DIRS/applications/$desktop-mimeapps.list,默认是:/usr/local/share/applications/gnome-mimeapps.list:/usr/share/applications/gnome-mimeapps.list
  8. $XDG_DATA_DIRS/applications/mimeapps.list,默认是:/usr/local/share/applications/mimeapps.list:/usr/share/applications/mimeapps.list
  9. /usr/share/applications/mimeinfo.cache,最后兜底的文件,系统所有的类型都在这里有配置。

上面的 $desktop 是当前桌面的名称之一,小写(例如,kde、gnome、xfce 等)

如果修改了mimeapps.list文件,且更新了数据库缓存,但是依旧不生效,就需要看一下修改的文件的优先级,有没有更高级别的文件覆盖了你的操作。

文件内容

上述提到的mimeinfo.cachemimeapps.list文件都以配置文件的格式来配置文件关联:

mimetype1=default1.desktop;default2.desktop;

key是文件类型,value是分号分隔的桌面文件 ID 列表

添加/删除文件关联

在 mimeapps.list 文件中可以使用以下语法在 mimetypes 和应用程序之间添加和删除关联:

[Added Associations]
mimetype1=foo1.desktop;foo2.desktop;foo3.desktop;
mimetype2=foo4.desktop;
[Removed Associations]
mimetype1=foo5.desktop;

[Added Associations][Removed Associations] 这两个组不能出现在.desktop文件中

[Added Associations]组定义了应用程序与mimetype的添加关联,就好像.desktop文件在第一个位置列出了这个mimetype

[Removed Associations] 组移除应用程序与mimetype间的关联

一般情况下[Added Associations]节写的文件关联具有最高的优先级。不能在添加关联和删除关联下给同一个文件类型写一样的关联内容。

默认应用程序

通过写入文件 mimeapps.list 中的组 [Default Applications] 来指示给定 mimetype 的默认应用程序。比如在文件管理器中双击文件时,就会启动默认程序。

[Default Applications]
mimetype1=default1.desktop;default2.desktop;

如果找到的第一个程序已经被卸载了那就找第二个以此类推。

如果在默认程序里没到找到,才会去找添加组里的文件关联,最后是mimeinfo.cache里的关联。

菜单

类似于Windows的开始菜单里面,有些Linux系统也设计了左下角的开始菜单,开始菜单中的条目并不固定,如果你想配置一个自己的条目,就可以参照这部分规范。

这部分规范定义了如何构建用户可见的应用程序层次结构,通常显示为菜单。允许第三方软件添加适用于所有桌面的菜单项,也允许系统管理员编辑菜单。

基本方案比较简单,每个应用程序的信息都保存在.desktop文件中,然后由.menu文件来配置菜单项的层次布局和序列,用.directorys文件来关联菜单和显示的目录。

文件位置

系统菜单的规范规定以下几个目录的文件:

  1. $XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu,比如:/etc/xdg/menus/gnome-applications.menu,这个文件以xml的格式定义了所有应用程序的主菜单。
  2. $XDG_CONFIG_DIRS/menus/applications-merged/,默认合并目录,一般情况下第三方软件如果想添加新的.menu文件来创建和扩展自己的子菜单就放到这个目录下。比如/etc/xdg/menus/applications-merged/wps-office.menu
  3. $XDG_DATA_DIRS/applications/,包含所有程序的.desktop文件的目录,上面章节详细介绍过。
  4. $XDG_DATA_DIRS/desktop-directories/,这个目录下放一些.directorys文件,用来关联菜单和显示的目录,比如/usr/share/desktop-directories/wps-office.directory

除了上面讲过的.desktop文件,菜单的规范里新规定了.menu文件和.directorys文件,文件的格式全部都是XML,具体有哪些元素下面详细描述:

.menu文件的格式

示例:

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
    <Name>Applications</Name>
    <Menu>
        <Name>金山办公</Name>
        <Directory>wps-office.directory</Directory>
            <Include>
                <Filename>wps-office-wps.desktop</Filename>
                <Filename>wps-office-et.desktop</Filename>
                <Filename>wps-office-wpp.desktop</Filename>
                <Filename>wps-office-pdf.desktop</Filename>
                <Filename>wps-office-officeassistant.desktop</Filename>
                <Filename>wps-office-uninstall.desktop</Filename>
                <Filename>wps-office-prometheus.desktop</Filename>
                <Filename>wps-office-ofd.desktop</Filename>
            </Include>
    </Menu>
</Menu>

其中文件头是固定格式,必须这么写:

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">

元素:

  • <Menu>:根节点,每个 <Menu> 元素可以包含任意数量的嵌套 <Menu> 元素,表示子菜单。
  • <Name>:每个 <Menu> 元素必须有一个 <Name> 元素。<Name> 元素的内容是在引用给定菜单时使用的名称。给定 <Menu> 的每个子菜单必须具有唯一的名称。<Menu> 元素因此可以由菜单路径引用,例如“Applications/Graphics.”。<name> 字段不得包含斜杠字符(“/”)
  • <Directory>:代表这个菜单使用哪一个.directory文件,这个.directory文件里描述了这个菜单的名称,图标信息,每一个Menu标签包含任意数量的Directory标签,如果没有为Menu指定Directory标签,则会使用Name标签显示。默认情况下自动检索/usr/share/desktop-directories/路径下的同名文件
  • <Include>:代表这个菜单里包含了哪些desktop,使用元素 <And><Or><Not><All><Filename><Category> 指定匹配规则。
    • <Filename>:最基本的匹配规则,通过文件名匹配desktop文件,默认情况下自动检索/usr/share/applications/路径下的同名文件
  • 上面几个标签配置一个基本的自定义目录已经够了,.menu文件中还有更多的元素用法可以参照:https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html
.directory文件的格式

.directory的文件内容非常简单:

[Desktop Entry]
Type=Directory
Name=WPS Office
Name[zh_CN]=金山办公
Icon=wps-office-kingsoft

最近文件

这个用的并不太多,大多数的软件应用都会维护一个最近使用的文件列表,不过一般都是各自实现的,freedesktop也提供了一套标准机制来存储最近使用的文件列表,可以使用户桌面和应用程序提供比较统一的最近列表,尤其是一些需要面向所有桌面兼容的软件。

下面提供一个示例的写法:

<?xml version="1.0"?>
<RecentFiles>
  <RecentItem>
    <URI>file:///home/jwillcox/testfile.txt</URI>
    <Mime-Type>text/plain</Mime-Type>
    <Timestamp>1028181153</Timestamp>
    <Private/>
    <Groups>
      <Group>Recent File Test</Group>
    </Groups>
  </RecentItem>
  <RecentItem>
    <URI>file:///home/jwillcox/recent-file-spec.xml</URI>
    <Mime-Type>text/xml</Mime-Type>
    <Timestamp>1028181158</Timestamp>
    <Private/>
    <Groups>
      <Group>Recent File Test</Group>
    </Groups>
  </RecentItem>
</RecentFiles>

URI、Mime-Type、Timestamp 标签是必需的,但 Private、Groups 和 Group 标签不是必需的。Timestamp 标签应该是从 1970-01-01 00:00:00 UTC 开始的秒数,文档应存储在~/.recently-used中。

通知机制

最常用的Linux下的通用消息机制,主要有两种:

  • notify-send
  • dbus

这两个命令都可以触发Linux发送消息通知,根据不同桌面的实现,效果也不尽相同,用法提供两个示例:

notify-send
notify-send ["需要重启或注销"] "安装完XXX软件后需要重启或者注销系统" -i application-name
  • []内,是通知的标题
  • 后面跟的是通知的内容
  • -i 代表通知携带的图标,遵循icon的规范,在系统/usr/share/icons内查找名为application-name.png的图标
D-BUS接口
gdbus call --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications --method org.freedesktop.Notifications.Notify  dde-control-center 42 dde-control-center "[需要重启或注销]" "卸载完XXX软件后需要重启或者注销系统" "[]" "{}" 5000

其中最后的5000代表弹窗停留5000毫秒,后面两个[]{}参数,在UOS系统上用来定义按钮和行为:

gdbus call --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications --method org.freedesktop.Notifications.Notify  dde-control-center 42 dde-control-center "[需要重启或注销]" "安装完XXX软件后需要重启或注销系统,功能才会生效"  "['_reboot', '立即重启', '_delay', '稍后重启']" "{'x-deepin-action-_reboot': <'/sbin/reboot' >}"  5000

这两种工具都是对freedesktop通知机制的应用,完整的通知机制有非常复杂和详细的协议,如果需要更定制化的通知功能,可以参考:https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html

其他

还有一些其他规范没有翻译,包括系统托盘、启动通知、缩略图、回收站、窗管、等等,有需要的同学可以直接在官网获取。

示例

下面用一个完整的示例演示一下,在Linux上为一个电子书程序创建一套完整的配套设施。

准备应用程序

一般是你自己的产品或软件,我这里从网上下载了一个AppImage版的电子书,是一个单文件的可执行程序Koodo-Reader-1.3.9.AppImage并把它放到bin目录:

sudo ln -sf ~/Download/Koodo-Reader-1.3.9.AppImage /usr/bin/koodo

这个程序运行起来,默认没有桌面图标和文件关联。下面通过freedesktop的规范来补全这些内容。

编写mimetype文件

文件名为dianzishu.xml

图标直接使用了txt的通用图标

<?xml version='1.0' encoding='utf-8'?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
	<mime-type type="application/dianzishu">
		<comment>E Book</comment>
		<sub-class-of type="application/epub+zip"/>
		 <generic-icon name="text-x-generic"/>
		<glob pattern="*.epub" />
	</mime-type>
</mime-info>

编写desktop文件

文件名为koodo.desktop

这里我直接借用谷歌的图标作为电子书的图标。

[Desktop Entry]
Version=1.0
Name=Koodo
Name[zh_CN]=Koodo阅读器
GenericName=Book Reader
GenericName[zh_CN]=电子书阅读器
Comment=Read epub files
Comment[zh_CN]=阅读电子书
Exec=/usr/bin/Koodo %F
StartupNotify=true
Terminal=false
Icon=google-chrome
Type=Application
Categories=Network;WebBrowser;
MimeType=application/dianzishu
Actions=new-window;open-nautilus;
StartupWMClass=koodo-reader

[Desktop Action new-window]
Name=New Window
Name[zh_CN]=新建窗口
Exec=/usr/bin/Koodo

[Desktop Action open-nautilus]
Name=New Window
Name[zh_CN]=打开文件管理器
Exec=/usr/bin//usr/bin/nautilus

添加电子书文件类型

sudo cp dianzishu.xml /usr/share/mime/packages/
sudo update-mime-database /usr/share/mime/

创建文件关联、图标关联、添加到应用程序管理器

sudo cp koodo.desktop /usr/share/applications/
sudo update-desktop-database

在这里插入图片描述

设置开机自动启动

sudo cp koodo.desktop /etc/xdg/autostart 

拷贝后就可以生效,下次重启系统会发现开机自动启动了电子书程序

创建桌面图标

sudo cp koodo.desktop ~/Desktop/ 

拷贝后会看到桌面上的图标,双击图标就可以启动电子书程序(有些系统上需要先右键=>允许启动)

创建桌面菜单

编写文件koodo.menu,内容如下

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
    <Name>Applications</Name>
    <Menu>
        <Name>XXX公司</Name>
        <Directory>koodo.directory</Directory>
            <Include>
                <Filename>koodo.desktop</Filename>
            </Include>
    </Menu>
</Menu>

编写文件koodo.directory,内容如下

[Desktop Entry]
Type=Directory
Name=Koodo Reader 
Name[zh_CN]=Koodo电子书阅读器
Icon=google-chrome

拷贝到指定目录下生效:

sudo cp koodo.menu /etc/xdg/menus/applications-merged
sudo cp koodo.directory /usr/share/desktop-directories

以上的内容讲解绝大多数依据都是官方文档和个人经验,碍于本人的英文水平和技术积累,可能会有纰漏,如果有大佬发现有错误还望及时批评指正。

参考资料:

Logo

更多推荐