目前主流的Linux发行版中,使用的桌面基本都是基于GNOME、KDE、Xfce等环境,这几种桌面环境中,关于桌面图标,文件关联,应用程序启动等方面的实现,全部都使用的是FreeDesktop,可以说虽然Linux的发行版众多,但关于图标文件关联这部分机制,基本都使用的是同一套,研究明白了freedesktop基本可以在绝大多数的Linux桌面系统里得到应用。
freedesktop.org最初的名称叫XDesktopGroup(X桌面工作组),缩写就是我们熟悉的"XDG",在一个桌面的终端输入 env | grep XDG
就会看到很多相关的环境变量。
freedesktop是完全开源的,不过我们作为系统的使用者,完全不用研究freedesktop的代码实现,只需要学习它的规范就可以了。
所有的内容都可以在其官网上找到:freedesktop.org
以下的内容,主要是根据个人经验和理解对官方文档进行了粗略的翻译,国内目前没有特别详尽的中文版freedesktop文档,如有纰漏请各位大佬指正
顾名思义,规范就是只要你遵照它要求的目录和写法进行配置文件的编写,就可以实现对应的功能。freedesktop已经为你提供了足够的工具、目录以及一些范例
下面是官网列出来的一些最常使用的规范:
file://URI
,用于拖放和其他桌面用途。还有更多的额外规范可以查阅:freedesktop.org/wiki/Specifications
接下来对规范的具体内容详细描述,不需要全记住,当做一个文档来查阅就可以,掌握了这些内容可以对Linux桌面系统有更加深入的理解。
内容主要来自官网:specifications.freedesktop.org
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
Version=1.0
Name=Mozilla
Name[zh_CN]
:中文环境下显示的名称,示例:Name[zh_CN]=火狐浏览器
GenericName=Web Browser
GenericName[zh_CN]=网页浏览器
NoDisplay=true
Comment=View sites on the Internet
/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=Unity
,NotShowIn=GNOME
,分别代表这个程序只在Unity桌面运行和这个程序不再GNOME桌面运行。如果你不知道你要限定的目标桌面环境是哪一种,可以echo $XDG_CURRENT_DESKTOP
来查看。DBusActivatable=true
Exec=/usr/bin/firefox %f
,几个最常用的参数说明如下:
Path=/opt/google/
Terminal=true
,默认为false。Categories=Qt;Graphics;RasterGraphics;Viewer;
一般某些包含分类功能的软件管理器内会采用这个字段进行分类。text/x-c++src;text/x-c++hdr;text/x-xsrc;application/x-designer;application/vnd.qt.qmakeprofile;application/vnd.qt.xml.resource;
后面会有一整个篇章详细介绍MimeType
。DESKTOP_STARTUP_ID
的情况下启动程序时发送一个remove
消息。这个解释我是看不明白。不过有个直观的现象可以解释这个字段:在某些系统上,如果这个字段的值为false的话,会导致启动程序时任务栏的图标不被复用,会在下面创建一个新的图标如图:Foo
程序一切正常,但是如果你直接命令行启动程序很可能无法正常显示.desktop文件里配置的图标。这时候你就需要在这个Desktop里面加上StartupWMClass=Foo
,这样以后无论从哪里启动Foo
进程,任务栏等地方都能正常显示图标。这个字段一般还会影响程序的快捷键等功能。如果你无法确定你的程序这个值应该配置成什么,可以再终端输入xprop
,光标变化后点击一下你的程序窗体,它会输出这个窗体的各种信息,其中WM_CLASS(STRING)
这一行的值就是你需要的。InitialPreference=3
com.deepin.dde.daemon.Launcher
最后两个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
这一句,右键就会出现这个选项)。允许启动后就会看到图标的效果,双击这个桌面图标,就会启动我们的电子书程序:
需要注意的是
- 桌面图标只在桌面起作用,如果你用文件管理器打开
~/桌面
这个目录,会发现.desktop
文件显示的仍然是一个配置文件的图标,双击会用默认的文本编辑器打开。- 通过桌面图标启动的程序仍然没有任务栏图标,我们上面写的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
下可以看到多个主题:
每个主题中都至少包含一个名为index.theme
的文件,描述了主题的一般属性
这个文件的内容和格式很复杂,一般也接触不到,感兴趣的话可以阅读这里:https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
每个目录下还有一个icon-theme.cache
数据库文件,用户快速检索对应的图标文件,如果安装了png或者svg图片后仍然没有生效,可以使用update-icon-caches
命令更新一下图标缓存数据库。
如果你想要在Linux实现开机自动启动一个程序,只需要做两件事:
.desktop
文件/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
自启动目录则包括:
每个目录下的.desktop文件都会自动启动,如果环境变量不存在则直接在目录下面找,如果出现同名的.desktop
则只有高优先级目录下的.desktop
生效
其实freedesktop的标准中还支持挂载自启动的功能,但处于安全原因并不常用。有兴趣的同学可以参考下官网关于这部分描述:specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html
如果想要在系统的应用程序管理器里看到你的程序,只需要以下三步:
.desktop
文件放到系统目录 /usr/share/applications 下StartupWMClass={WM_CLASS}
字段,其中{WM_CLASS}
要替换成对应程序的窗体WM_CLASS
,可以通过xprop工具查看。(这一点决定了你启动的进程能够成功关联到图标)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参数
首先,这里的文件类型指的是普通文件的类型,我们都知道Linux有七大文件类型,分别是:
- 普通文件
d 目录文件
l 软链接
b 块设备文件,如硬盘,光驱等
p 管道文件
c 字符设备文件,如猫等串口设备
s 套接字文件,如mysql.sock等
而普通文件又可以有很多分类,比如文本文件、二进制文件、动态库文件、可执行文件等等,本文规范里的文件类型,讨论的就是针对普通文件所进行的分类。
不同于windows简单粗暴地使用后缀名来标识一个文件类型,许多程序和桌面使用MIME Type来将文件划分为多种类型,方便对其进行统一的管理。MIME Type是用于描述文件的类型的一种表述方法,指定了文件的类型名称、描述、图标信息,同时通过与.desktop应用程序描述文件整合,指定了文件的打开方式。
确定一个文件的MIME类型有很多种方法,比如通过http的协议头、电子邮件的协议头提供的mime类型,不过一般在Linux下,文件管理器打开某个文件时,通常使用文件后缀名(glob)和magic匹配来判断一个文件的类型:
/etc/mime.types
文件中,用户可以在~/.mime.types
中自定义自己的后缀名和文件类型匹配,不过传统情况下Linux是忽略文件后缀名的。#!/usr/bin/awk
开头的文件是awk脚本\037\213
开头的文件是tar.gz
压缩文件\177ELF
开头并且第5位是1
,第16位是3
的文件是动态库application/octet-stream
。目前几乎所有的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.sh
和test2.sh
都是同样的后缀名却标识成了不同的文件类型,原因是test2.sh
的开头写了#!/bin/bash
而test.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/
下的内容有更新时,就执行这个命令生效改动,执行此命令生效改动会生成如下文件:
当系统需要判断一个文件是什么类型是,去检索每一个xml文件效率很低,因此freedesktop将这些信息以二进制的形式全部整合到了mime.cache
数据库当中方便查询,此cache文件不建议用户修改,可以通过修改上面提到的xml文件,并通过update-mime-database /usr/share/mime/
命令更新cache文件
如果你的应用程序需要给系统安装一个自己的文件类型,需要做两件事情:
/usr/share/mime/packages/(.xml)
update-mime-database
命令更新数据信息,参数是刚才放置mime信息的目录。update-mime-database /usr/share/mime/
这里对xml文件的格式和内容规范做一个详细的说明,下面是一个文件类型xml文件示例,如果你想写一个自己的文件类型,可以照抄下面的示例,也可以在/usr/share/mime/packages/
目录下参考现有的写法:
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="text/x-diff">
<comment>Differences between filescomment>
<comment xml:lang="af">verskille tussen lêerscomment>
...
<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节点可以包含以下元素的任意组合,并且以任意顺序:
glob
元素有一个pattern
属性。任何名称与此模式匹配的文件都将被赋予此 MIME 类型(当然,受制于其他文件中的冲突规则)。还有一个可选weight
属性,用于解决与其他全局匹配的冲突。默认权重值为 50,最大值为 100。glob
字段的匹配结果,比如你想让所有"*.txt"中的"CMakeLists.txt"不适用txt的文件类型,就可以用
过滤掉。它和glob
共用一样的规则。magic
元素包含一个match
列表,满足任何一个都可以匹配到当前类型(或的关系),包含的所有项都有一个可选的priority
属性,可以给一些比较通用的类型写一个比较小的数值,而且一些比较特殊的子类型写一个比较大的数值,默认50,最高100(比如gzip类型和一些基于gzip的文档格式)。每一个match
元素包含一下属性:
起始位:结束位
的形式,范围的话会检查范围内的所有偏移量,包括这个头和尾。type
属性指示的格式。字符串类型支持 C 字符转义(\0、\t、\n、\r、\xAB 表示十六进制,\777 表示八进制)。
type
属性给这个类型起一个别名,比如
类型的别名:
、
type
属性指定,例如bash类型它就既是文本类型的子类型,也是可执行类型的子类型:
xml:lang
属性标识语言,例如:shell 脚本 shell 指令稿
,这样写的话,如果遇到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
。namespaceURI
和localName
两个属性,如果一个类型是XML类型的子类型,这个元素允许根据文档元素的命名空间和本地名称选择更具体的MIME类型。比如XHTML类型的这个字段就写的是
以上是整个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
如果某个类型的任何示例都是第二种类型的示例,则它就是另一种类型的子类,比如image/svg+xml
文件,它既是application/xml
,也是text/plain
和application/octet-stream
文件,子类型是根据格式划分,而不是根据数据类型。
另外还有一些隐含的子类规则:
text/*
类型都是text/plain
的子类型,即所有的文本文件类型。application/octet-stream
的子类,也就是除了inode/*
类型,以外的所有文件类型都是application/octet-stream
类型的子类型。除了这些规则之外,还可以使用sub-class-of
元素给出明确的子类信息。
一个类型可以是多个类型的子类型,所以也可以有多个sub-class-of
元素。
如果你编写自己的应用程序需要判断文件的mimetype,这里官网提供了一个推荐的检索方式:
application/octet-stream
,文本文件就认为是 text/plain
,如果有匹配,就将magic的匹配结果作为mimetype的结果。
注意:可以通过检查文件的前 128 个字节是否有 ASCII 控制字符来判断文件是二进制文件还是文本文件,不过,具有高位集字符的文件仍应被视为文本,因为这些字符可能出现在UTF-8 文本,与控制字符不同。
无论如何都应该以文件名的检索优先,因为通过magic来判断文件内容开销非常大。
也就是文章开头我们说的Linux七大文件类型里的其他六大,在freedesktop中也给出了各自的文件类型:
上面介绍 freedesktop 通过文件类型规范提供了一种存储有关 MIME 类型和用于确定文件类型的规则的静态信息的方法。
通过desktop规范来使应用程序的一些属性和系统产生关联,并且有一个字段允许应用程序声明它们支持的MIME类型。
那么,默认该由哪个应用程序来打开某种类型的文件?如何让用户更改程序的默认打开方式?以及如何让用户添加或删除应用程序和 mimetypes 之间的关联?就是这一小节文件关联的规范讨论的内容
在freedesktop规范中,通过将信息写入名为mimeinfo.cache和mimeapps.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
默认的优先级如下:
$XDG_CONFIG_HOME/$desktop-mimeapps.list
,默认是:~/.config/gnome-mimeapps.list
$XDG_CONFIG_HOME/mimeapps.list
,默认是:~/.config/mimeapps.list
$XDG_CONFIG_DIRS/$desktop-mimeapps.list
,默认是:/etc/xdg/gnome-mimeapps.list
$XDG_CONFIG_DIRS/mimeapps.list
,默认是:/etc/xdg/.config/mimeapps.list
$XDG_DATA_HOME/applications/$desktop-mimeapps.list
,默认是:~/.local/share/applications/gnome-mimeapps.list
$XDG_DATA_HOME/applications/mimeapps.list
,默认是:~/.local/share/applications/mimeapps.list
$XDG_DATA_DIRS/applications/$desktop-mimeapps.list
,默认是:/usr/local/share/applications/gnome-mimeapps.list:/usr/share/applications/gnome-mimeapps.list
$XDG_DATA_DIRS/applications/mimeapps.list
,默认是:/usr/local/share/applications/mimeapps.list:/usr/share/applications/mimeapps.list
/usr/share/applications/mimeinfo.cache
,最后兜底的文件,系统所有的类型都在这里有配置。上面的
$desktop
是当前桌面的名称之一,小写(例如,kde、gnome、xfce 等)
如果修改了mimeapps.list
文件,且更新了数据库缓存,但是依旧不生效,就需要看一下修改的文件的优先级,有没有更高级别的文件覆盖了你的操作。
上述提到的mimeinfo.cache
和mimeapps.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文件来关联菜单和显示的目录。
系统菜单的规范规定以下几个目录的文件:
$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu
,比如:/etc/xdg/menus/gnome-applications.menu
,这个文件以xml的格式定义了所有应用程序的主菜单。$XDG_CONFIG_DIRS/menus/applications-merged/
,默认合并目录,一般情况下第三方软件如果想添加新的.menu
文件来创建和扩展自己的子菜单就放到这个目录下。比如/etc/xdg/menus/applications-merged/wps-office.menu
$XDG_DATA_DIRS/applications/
,包含所有程序的.desktop文件的目录,上面章节详细介绍过。$XDG_DATA_DIRS/desktop-directories/
,这个目录下放一些.directorys
文件,用来关联菜单和显示的目录,比如/usr/share/desktop-directories/wps-office.directory
除了上面讲过的.desktop
文件,菜单的规范里新规定了.menu
文件和.directorys
文件,文件的格式全部都是XML,具体有哪些元素下面详细描述:
示例:
DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
<Name>ApplicationsName>
<Menu>
<Name>金山办公Name>
<Directory>wps-office.directoryDirectory>
<Include>
<Filename>wps-office-wps.desktopFilename>
<Filename>wps-office-et.desktopFilename>
<Filename>wps-office-wpp.desktopFilename>
<Filename>wps-office-pdf.desktopFilename>
<Filename>wps-office-officeassistant.desktopFilename>
<Filename>wps-office-uninstall.desktopFilename>
<Filename>wps-office-prometheus.desktopFilename>
<Filename>wps-office-ofd.desktopFilename>
Include>
Menu>
Menu>
其中文件头是固定格式,必须这么写:
DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
元素:
:根节点,每个
元素可以包含任意数量的嵌套
元素,表示子菜单。
:每个
元素必须有一个
元素。
元素的内容是在引用给定菜单时使用的名称。给定
的每个子菜单必须具有唯一的名称。
元素因此可以由菜单路径引用,例如“Applications/Graphics.”。
字段不得包含斜杠字符(“/”)
:代表这个菜单使用哪一个.directory文件,这个.directory文件里描述了这个菜单的名称,图标信息,每一个Menu标签包含任意数量的Directory标签,如果没有为Menu指定Directory标签,则会使用Name标签显示。默认情况下自动检索/usr/share/desktop-directories/
路径下的同名文件
:代表这个菜单里包含了哪些desktop,使用元素
、
、
、
、
和
指定匹配规则。
:最基本的匹配规则,通过文件名匹配desktop文件,默认情况下自动检索/usr/share/applications/
路径下的同名文件.menu
文件中还有更多的元素用法可以参照:https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html.directory
的文件内容非常简单:
[Desktop Entry]
Type=Directory
Name=WPS Office
Name[zh_CN]=金山办公
Icon=wps-office-kingsoft
这个用的并不太多,大多数的软件应用都会维护一个最近使用的文件列表,不过一般都是各自实现的,freedesktop也提供了一套标准机制来存储最近使用的文件列表,可以使用户桌面和应用程序提供比较统一的最近列表,尤其是一些需要面向所有桌面兼容的软件。
下面提供一个示例的写法:
<RecentFiles>
<RecentItem>
<URI>file:///home/jwillcox/testfile.txtURI>
<Mime-Type>text/plainMime-Type>
<Timestamp>1028181153Timestamp>
<Private/>
<Groups>
<Group>Recent File TestGroup>
Groups>
RecentItem>
<RecentItem>
<URI>file:///home/jwillcox/recent-file-spec.xmlURI>
<Mime-Type>text/xmlMime-Type>
<Timestamp>1028181158Timestamp>
<Private/>
<Groups>
<Group>Recent File TestGroup>
Groups>
RecentItem>
RecentFiles>
URI、Mime-Type、Timestamp 标签是必需的,但 Private、Groups 和 Group 标签不是必需的。Timestamp 标签应该是从 1970-01-01 00:00:00 UTC 开始的秒数,文档应存储在~/.recently-used
中。
最常用的Linux下的通用消息机制,主要有两种:
这两个命令都可以触发Linux发送消息通知,根据不同桌面的实现,效果也不尽相同,用法提供两个示例:
notify-send ["需要重启或注销"] "安装完XXX软件后需要重启或者注销系统" -i application-name
[]
内,是通知的标题-i
代表通知携带的图标,遵循icon的规范,在系统/usr/share/icons
内查找名为application-name.png
的图标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的规范来补全这些内容。
文件名为dianzishu.xml
图标直接使用了txt的通用图标
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/dianzishu">
<comment>E Bookcomment>
<sub-class-of type="application/epub+zip"/>
<generic-icon name="text-x-generic"/>
<glob pattern="*.epub" />
mime-type>
mime-info>
文件名为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>ApplicationsName>
<Menu>
<Name>XXX公司Name>
<Directory>koodo.directoryDirectory>
<Include>
<Filename>koodo.desktopFilename>
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
以上的内容讲解绝大多数依据都是官方文档和个人经验,碍于本人的英文水平和技术积累,可能会有纰漏,如果有大佬发现有错误还望及时批评指正。